WordPress Landing Pages - Version 2.5.8

Version Description

  • Updating docblocks for APIGen
  • [Restored] Impression tracking was failing in select theme environments.
Download this release

Release Info

Developer adbox
Plugin Icon 128x128 WordPress Landing Pages
Version 2.5.8
Comparing to
See all releases

Code changes from version 2.5.7 to 2.5.8

Files changed (64) hide show
  1. assets/tests/bin/phantomloader +0 -7
  2. assets/tests/bin/wpcept +0 -7
  3. assets/tests/build/php.conf +0 -4
  4. assets/tests/build/php.load +0 -1
  5. assets/tests/build/travis-ci-apache +0 -23
  6. assets/tests/codeception/_bootstrap.php +0 -14
  7. assets/tests/codeception/_data/dump.sql +0 -1
  8. assets/tests/codeception/_support/AcceptanceHelper.php +0 -10
  9. assets/tests/codeception/_support/FunctionalHelper.php +0 -10
  10. assets/tests/codeception/_support/UnitHelper.php +0 -10
  11. assets/tests/codeception/acceptance/AcceptanceTester.php +0 -1918
  12. assets/tests/codeception/acceptance/LoginCept.php +0 -48
  13. assets/tests/codeception/acceptance/StatisticsCept.php +0 -55
  14. assets/tests/codeception/acceptance/WelcomeCept.php +0 -7
  15. assets/tests/codeception/acceptance/_bootstrap.php +0 -2
  16. assets/tests/codeception/functional/FunctionalTester.php +0 -360
  17. assets/tests/codeception/functional/_bootstrap.php +0 -2
  18. assets/tests/codeception/unit/UnitTester.php +0 -300
  19. assets/tests/codeception/unit/_bootstrap.php +0 -2
  20. assets/tests/phpunit/bootstrap.php +0 -15
  21. assets/tests/phpunit/test.activations.php +0 -49
  22. assets/tests/travis-ci/test.statistics.php +0 -68
  23. classes/class.acf-integration.php +527 -527
  24. classes/class.activation.php +5 -2
  25. classes/class.activation.upgrade-routines.php +244 -251
  26. classes/class.admin-menus.php +3 -3
  27. classes/class.admin-notices.php +6 -0
  28. classes/class.click-tracking.php +0 -210
  29. classes/class.install.php +6 -0
  30. classes/class.landing-pages.php +7 -5
  31. classes/class.load-templates.php +5 -0
  32. classes/class.metaboxes.php +6 -1
  33. classes/class.post-type.landing-page.php +513 -509
  34. classes/class.postmeta.php +4 -1
  35. classes/class.row-actions.php +6 -0
  36. classes/class.settings.php +6 -0
  37. classes/class.sidebars.php +5 -1
  38. classes/class.statistics.php +178 -182
  39. classes/class.store.php +20 -170
  40. classes/class.template-management.php +5 -0
  41. classes/class.variations.php +600 -602
  42. classes/class.welcome.php +2 -6
  43. classes/class.widgets.php +8 -0
  44. classes/class.wp-list-table.templates.php +7 -0
  45. landing-pages.php +6 -3
  46. modules/module.redirect-ab-testing.php +7 -1
  47. modules/module.utils.php +9 -0
  48. readme.txt +5 -1
  49. shared/assets/js/admin/marketing-button.js +243 -243
  50. shared/assets/js/frontend/analytics-src/analytics.events.js +609 -609
  51. shared/assets/js/frontend/analytics-src/analytics.forms.js +1207 -1207
  52. shared/assets/js/frontend/analytics-src/analytics.hooks.js +404 -404
  53. shared/assets/js/frontend/analytics-src/analytics.init.js +130 -130
  54. shared/assets/js/frontend/analytics-src/analytics.page.js +394 -393
  55. shared/assets/js/frontend/analytics-src/analytics.start.js +17 -17
  56. shared/assets/js/frontend/analytics-src/analytics.utils.js +858 -858
  57. shared/assets/js/frontend/analytics/inboundAnalytics.js +3847 -3846
  58. shared/assets/js/frontend/analytics/inboundAnalytics.min.js +2 -2
  59. shared/assets/js/global/debug.js +0 -37
  60. shared/assets/js/global/inbound-dequeue-scripts.js +0 -49
  61. shared/classes/class.acf-bootstrap.php +93 -87
  62. shared/classes/class.ajax.php +153 -164
  63. shared/classes/class.confirm-double-optin.php +200 -194
  64. shared/classes/class.database-routines.php +312 -312
assets/tests/bin/phantomloader DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env sh
2
- SRC_DIR="`pwd`"
3
- cd "`dirname "$0"`"
4
- cd "../../vendor/jonnyw/php-phantomjs/bin"
5
- BIN_TARGET="`pwd`/phantomloader"
6
- cd "$SRC_DIR"
7
- "$BIN_TARGET" "$@"
 
 
 
 
 
 
 
assets/tests/bin/wpcept DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env sh
2
- SRC_DIR="`pwd`"
3
- cd "`dirname "$0"`"
4
- cd "../../vendor/lucatume/wp-browser"
5
- BIN_TARGET="`pwd`/wpcept"
6
- cd "$SRC_DIR"
7
- "$BIN_TARGET" "$@"
 
 
 
 
 
 
 
assets/tests/build/php.conf DELETED
@@ -1,4 +0,0 @@
1
- <IfModule mod_php5.c>
2
- AddType application/x-httpd-php .php .phtml .php5
3
- AddType application/x-httpd-php-source .phps
4
- </IfModule>
 
 
 
 
assets/tests/build/php.load DELETED
@@ -1 +0,0 @@
1
- LoadModule php5_module /usr/lib/apache2/modules/libphp5.so
 
assets/tests/build/travis-ci-apache DELETED
@@ -1,23 +0,0 @@
1
- <VirtualHost *:80>
2
- ServerAdmin tests@inboundnow.com
3
- DocumentRoot /var/www/inboundtesting.dev/
4
- ServerName inboundtesting.dev
5
- ErrorLog ${APACHE_LOG_DIR}/error.log
6
- CustomLog ${APACHE_LOG_DIR}/access.log combined
7
-
8
- # Wire up Apache to use Travis CI's php-fpm.
9
- <IfModule mod_fastcgi.c>
10
- AddHandler php5-fcgi .php
11
- Action php5-fcgi /php5-fcgi
12
- Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
13
- FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
14
- </IfModule>
15
-
16
- <Directory "/var/www/inboundtesting.dev/">
17
- Options FollowSymLinks MultiViews ExecCGI
18
- AllowOverride All
19
- Order deny,allow
20
- Allow from all
21
- </Directory>
22
- </VirtualHost>
23
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_bootstrap.php DELETED
@@ -1,14 +0,0 @@
1
- <?php
2
-
3
-
4
- /* load wp */
5
- require '../../../wp-load.php';
6
- require '../../../wp-admin/includes/plugin.php';
7
-
8
- /* load coception addons */
9
- require LANDINGPAGES_PATH . 'vendor/lucatume/wp-browser/src/Codeception/Module/WPBrowserMethods.php';
10
- require LANDINGPAGES_PATH . 'vendor/lucatume/wp-browser/src/Codeception/Module/WPBrowser.php';
11
-
12
- /* Set current users */
13
- wp_set_current_user( 1 );
14
- global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_data/dump.sql DELETED
@@ -1 +0,0 @@
1
- /* Replace this file with actual dump of your database */
 
assets/tests/codeception/_support/AcceptanceHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class AcceptanceHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_support/FunctionalHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class FunctionalHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_support/UnitHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class UnitHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/AcceptanceTester.php DELETED
@@ -1,1918 +0,0 @@
1
- <?php //[STAMP] 2d5aa64986f8f94bc0f812d88aa9dee6
2
-
3
- // This class was automatically generated by build task
4
- // You should not change it manually as it will be overwritten on next build
5
- // @codingStandardsIgnoreFile
6
-
7
-
8
- use Codeception\Module\PhpBrowser;
9
- use Codeception\Module\AcceptanceHelper;
10
-
11
- /**
12
- * Inherited Methods
13
- * @method void wantToTest($text)
14
- * @method void wantTo($text)
15
- * @method void execute($callable)
16
- * @method void expectTo($prediction)
17
- * @method void expect($prediction)
18
- * @method void amGoingTo($argumentation)
19
- * @method void am($role)
20
- * @method void lookForwardTo($achieveValue)
21
- * @method void comment($description)
22
- * @method void haveFriend($name, $actorClass = null)
23
- *
24
- * @SuppressWarnings(PHPMD)
25
- */
26
- class AcceptanceTester extends \Codeception\Actor
27
- {
28
-
29
- /**
30
- * [!] Method is generated. Documentation taken from corresponding module.
31
- *
32
- * Sets the HTTP header to the passed value - which is used on
33
- * subsequent HTTP requests through PhpBrowser.
34
- *
35
- * Example:
36
- * ```php
37
- * <?php
38
- * $I->setHeader('X-Requested-With', 'Codeception');
39
- * $I->amOnPage('test-headers.php');
40
- * ?>
41
- * ```
42
- *
43
- * @param string $name the name of the request header
44
- * @param string $value the value to set it to for subsequent
45
- * requests
46
- * @see \Codeception\Module\PhpBrowser::setHeader()
47
- */
48
- public function setHeader($name, $value) {
49
- return $this->scenario->runStep(new \Codeception\Step\Action('setHeader', func_get_args()));
50
- }
51
-
52
-
53
- /**
54
- * [!] Method is generated. Documentation taken from corresponding module.
55
- *
56
- * Deletes the header with the passed name. Subsequent requests
57
- * will not have the deleted header in its request.
58
- *
59
- * Example:
60
- * ```php
61
- * <?php
62
- * $I->setHeader('X-Requested-With', 'Codeception');
63
- * $I->amOnPage('test-headers.php');
64
- * // ...
65
- * $I->deleteHeader('X-Requested-With');
66
- * $I->amOnPage('some-other-page.php');
67
- * ?>
68
- * ```
69
- *
70
- * @param string $name the name of the header to delete.
71
- * @see \Codeception\Module\PhpBrowser::deleteHeader()
72
- */
73
- public function deleteHeader($name) {
74
- return $this->scenario->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args()));
75
- }
76
-
77
-
78
- /**
79
- * [!] Method is generated. Documentation taken from corresponding module.
80
- *
81
- * Authenticates user for HTTP_AUTH
82
- *
83
- * @param $username
84
- * @param $password
85
- * @see \Codeception\Module\PhpBrowser::amHttpAuthenticated()
86
- */
87
- public function amHttpAuthenticated($username, $password) {
88
- return $this->scenario->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args()));
89
- }
90
-
91
-
92
- /**
93
- * [!] Method is generated. Documentation taken from corresponding module.
94
- *
95
- * Opens the page for the given relative URI.
96
- *
97
- * ``` php
98
- * <?php
99
- * // opens front page
100
- * $I->amOnPage('/');
101
- * // opens /register page
102
- * $I->amOnPage('/register');
103
- * ?>
104
- * ```
105
- *
106
- * @param $page
107
- * @see \Codeception\Module\PhpBrowser::amOnPage()
108
- */
109
- public function amOnPage($page) {
110
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args()));
111
- }
112
-
113
-
114
- /**
115
- * [!] Method is generated. Documentation taken from corresponding module.
116
- *
117
- * Open web page at the given absolute URL and sets its hostname as the base host.
118
- *
119
- * ``` php
120
- * <?php
121
- * $I->amOnUrl('http://codeception.com');
122
- * $I->amOnPage('/quickstart'); // moves to http://codeception.com/quickstart
123
- * ?>
124
- * ```
125
- * @see \Codeception\Module\PhpBrowser::amOnUrl()
126
- */
127
- public function amOnUrl($url) {
128
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnUrl', func_get_args()));
129
- }
130
-
131
-
132
- /**
133
- * [!] Method is generated. Documentation taken from corresponding module.
134
- *
135
- * Changes the subdomain for the 'url' configuration parameter.
136
- * Does not open a page; use `amOnPage` for that.
137
- *
138
- * ``` php
139
- * <?php
140
- * // If config is: 'http://mysite.com'
141
- * // or config is: 'http://www.mysite.com'
142
- * // or config is: 'http://company.mysite.com'
143
- *
144
- * $I->amOnSubdomain('user');
145
- * $I->amOnPage('/');
146
- * // moves to http://user.mysite.com/
147
- * ?>
148
- * ```
149
- *
150
- * @param $subdomain
151
- *
152
- * @return mixed
153
- * @see \Codeception\Module\PhpBrowser::amOnSubdomain()
154
- */
155
- public function amOnSubdomain($subdomain) {
156
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args()));
157
- }
158
-
159
-
160
- /**
161
- * [!] Method is generated. Documentation taken from corresponding module.
162
- *
163
- * Low-level API method.
164
- * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly
165
- *
166
- * Example:
167
- *
168
- * ``` php
169
- * <?php
170
- * $I->executeInGuzzle(function (\GuzzleHttp\Client $client) {
171
- * $client->get('/get', ['query' => ['foo' => 'bar']]);
172
- * });
173
- * ?>
174
- * ```
175
- *
176
- * It is not recommended to use this command on a regular basis.
177
- * If Codeception lacks important Guzzle Client methods, implement them and submit patches.
178
- *
179
- * @param callable $function
180
- * @see \Codeception\Module\PhpBrowser::executeInGuzzle()
181
- */
182
- public function executeInGuzzle($function) {
183
- return $this->scenario->runStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args()));
184
- }
185
-
186
-
187
- /**
188
- * [!] Method is generated. Documentation taken from corresponding module.
189
- *
190
- * Perform a click on a link or a button, given by a locator.
191
- * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
192
- * For buttons, the "value" attribute, "name" attribute, and inner text are searched.
193
- * For links, the link text is searched.
194
- * For images, the "alt" attribute and inner text of any parent links are searched.
195
- *
196
- * The second parameter is a context (CSS or XPath locator) to narrow the search.
197
- *
198
- * Note that if the locator matches a button of type `submit`, the form will be submitted.
199
- *
200
- * ``` php
201
- * <?php
202
- * // simple link
203
- * $I->click('Logout');
204
- * // button of form
205
- * $I->click('Submit');
206
- * // CSS button
207
- * $I->click('#form input[type=submit]');
208
- * // XPath
209
- * $I->click('//form/*[@type=submit]');
210
- * // link in context
211
- * $I->click('Logout', '#nav');
212
- * // using strict locator
213
- * $I->click(['link' => 'Login']);
214
- * ?>
215
- * ```
216
- *
217
- * @param $link
218
- * @param $context
219
- * @see \Codeception\Lib\InnerBrowser::click()
220
- */
221
- public function click($link, $context = null) {
222
- return $this->scenario->runStep(new \Codeception\Step\Action('click', func_get_args()));
223
- }
224
-
225
-
226
- /**
227
- * [!] Method is generated. Documentation taken from corresponding module.
228
- *
229
- * Checks that the current page contains the given string.
230
- * Specify a locator as the second parameter to match a specific region.
231
- *
232
- * ``` php
233
- * <?php
234
- * $I->see('Logout'); // I can suppose user is logged in
235
- * $I->see('Sign Up','h1'); // I can suppose it's a signup page
236
- * $I->see('Sign Up','//body/h1'); // with XPath
237
- * ?>
238
- * ```
239
- *
240
- * @param $text
241
- * @param null $selector
242
- * Conditional Assertion: Test won't be stopped on fail
243
- * @see \Codeception\Lib\InnerBrowser::see()
244
- */
245
- public function canSee($text, $selector = null) {
246
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args()));
247
- }
248
- /**
249
- * [!] Method is generated. Documentation taken from corresponding module.
250
- *
251
- * Checks that the current page contains the given string.
252
- * Specify a locator as the second parameter to match a specific region.
253
- *
254
- * ``` php
255
- * <?php
256
- * $I->see('Logout'); // I can suppose user is logged in
257
- * $I->see('Sign Up','h1'); // I can suppose it's a signup page
258
- * $I->see('Sign Up','//body/h1'); // with XPath
259
- * ?>
260
- * ```
261
- *
262
- * @param $text
263
- * @param null $selector
264
- * @see \Codeception\Lib\InnerBrowser::see()
265
- */
266
- public function see($text, $selector = null) {
267
- return $this->scenario->runStep(new \Codeception\Step\Assertion('see', func_get_args()));
268
- }
269
-
270
-
271
- /**
272
- * [!] Method is generated. Documentation taken from corresponding module.
273
- *
274
- * Checks that the current page doesn't contain the text specified.
275
- * Give a locator as the second parameter to match a specific region.
276
- *
277
- * ```php
278
- * <?php
279
- * $I->dontSee('Login'); // I can suppose user is already logged in
280
- * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page
281
- * $I->dontSee('Sign Up','//body/h1'); // with XPath
282
- * ?>
283
- * ```
284
- *
285
- * @param $text
286
- * @param null $selector
287
- * Conditional Assertion: Test won't be stopped on fail
288
- * @see \Codeception\Lib\InnerBrowser::dontSee()
289
- */
290
- public function cantSee($text, $selector = null) {
291
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args()));
292
- }
293
- /**
294
- * [!] Method is generated. Documentation taken from corresponding module.
295
- *
296
- * Checks that the current page doesn't contain the text specified.
297
- * Give a locator as the second parameter to match a specific region.
298
- *
299
- * ```php
300
- * <?php
301
- * $I->dontSee('Login'); // I can suppose user is already logged in
302
- * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page
303
- * $I->dontSee('Sign Up','//body/h1'); // with XPath
304
- * ?>
305
- * ```
306
- *
307
- * @param $text
308
- * @param null $selector
309
- * @see \Codeception\Lib\InnerBrowser::dontSee()
310
- */
311
- public function dontSee($text, $selector = null) {
312
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args()));
313
- }
314
-
315
-
316
- /**
317
- * [!] Method is generated. Documentation taken from corresponding module.
318
- *
319
- * Checks that there's a link with the specified text.
320
- * Give a full URL as the second parameter to match links with that exact URL.
321
- *
322
- * ``` php
323
- * <?php
324
- * $I->seeLink('Logout'); // matches <a href="#">Logout</a>
325
- * $I->seeLink('Logout','/logout'); // matches <a href="/logout">Logout</a>
326
- * ?>
327
- * ```
328
- *
329
- * @param $text
330
- * @param null $url
331
- * Conditional Assertion: Test won't be stopped on fail
332
- * @see \Codeception\Lib\InnerBrowser::seeLink()
333
- */
334
- public function canSeeLink($text, $url = null) {
335
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args()));
336
- }
337
- /**
338
- * [!] Method is generated. Documentation taken from corresponding module.
339
- *
340
- * Checks that there's a link with the specified text.
341
- * Give a full URL as the second parameter to match links with that exact URL.
342
- *
343
- * ``` php
344
- * <?php
345
- * $I->seeLink('Logout'); // matches <a href="#">Logout</a>
346
- * $I->seeLink('Logout','/logout'); // matches <a href="/logout">Logout</a>
347
- * ?>
348
- * ```
349
- *
350
- * @param $text
351
- * @param null $url
352
- * @see \Codeception\Lib\InnerBrowser::seeLink()
353
- */
354
- public function seeLink($text, $url = null) {
355
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args()));
356
- }
357
-
358
-
359
- /**
360
- * [!] Method is generated. Documentation taken from corresponding module.
361
- *
362
- * Checks that the page doesn't contain a link with the given string.
363
- * If the second parameter is given, only links with a matching "href" attribute will be checked.
364
- *
365
- * ``` php
366
- * <?php
367
- * $I->dontSeeLink('Logout'); // I suppose user is not logged in
368
- * $I->dontSeeLink('Checkout now', '/store/cart.php');
369
- * ?>
370
- * ```
371
- *
372
- * @param $text
373
- * @param null $url
374
- * Conditional Assertion: Test won't be stopped on fail
375
- * @see \Codeception\Lib\InnerBrowser::dontSeeLink()
376
- */
377
- public function cantSeeLink($text, $url = null) {
378
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args()));
379
- }
380
- /**
381
- * [!] Method is generated. Documentation taken from corresponding module.
382
- *
383
- * Checks that the page doesn't contain a link with the given string.
384
- * If the second parameter is given, only links with a matching "href" attribute will be checked.
385
- *
386
- * ``` php
387
- * <?php
388
- * $I->dontSeeLink('Logout'); // I suppose user is not logged in
389
- * $I->dontSeeLink('Checkout now', '/store/cart.php');
390
- * ?>
391
- * ```
392
- *
393
- * @param $text
394
- * @param null $url
395
- * @see \Codeception\Lib\InnerBrowser::dontSeeLink()
396
- */
397
- public function dontSeeLink($text, $url = null) {
398
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args()));
399
- }
400
-
401
-
402
- /**
403
- * [!] Method is generated. Documentation taken from corresponding module.
404
- *
405
- * Checks that current URI contains the given string.
406
- *
407
- * ``` php
408
- * <?php
409
- * // to match: /home/dashboard
410
- * $I->seeInCurrentUrl('home');
411
- * // to match: /users/1
412
- * $I->seeInCurrentUrl('/users/');
413
- * ?>
414
- * ```
415
- *
416
- * @param $uri
417
- * Conditional Assertion: Test won't be stopped on fail
418
- * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl()
419
- */
420
- public function canSeeInCurrentUrl($uri) {
421
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args()));
422
- }
423
- /**
424
- * [!] Method is generated. Documentation taken from corresponding module.
425
- *
426
- * Checks that current URI contains the given string.
427
- *
428
- * ``` php
429
- * <?php
430
- * // to match: /home/dashboard
431
- * $I->seeInCurrentUrl('home');
432
- * // to match: /users/1
433
- * $I->seeInCurrentUrl('/users/');
434
- * ?>
435
- * ```
436
- *
437
- * @param $uri
438
- * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl()
439
- */
440
- public function seeInCurrentUrl($uri) {
441
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args()));
442
- }
443
-
444
-
445
- /**
446
- * [!] Method is generated. Documentation taken from corresponding module.
447
- *
448
- * Checks that the current URI doesn't contain the given string.
449
- *
450
- * ``` php
451
- * <?php
452
- * $I->dontSeeInCurrentUrl('/users/');
453
- * ?>
454
- * ```
455
- *
456
- * @param $uri
457
- * Conditional Assertion: Test won't be stopped on fail
458
- * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl()
459
- */
460
- public function cantSeeInCurrentUrl($uri) {
461
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args()));
462
- }
463
- /**
464
- * [!] Method is generated. Documentation taken from corresponding module.
465
- *
466
- * Checks that the current URI doesn't contain the given string.
467
- *
468
- * ``` php
469
- * <?php
470
- * $I->dontSeeInCurrentUrl('/users/');
471
- * ?>
472
- * ```
473
- *
474
- * @param $uri
475
- * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl()
476
- */
477
- public function dontSeeInCurrentUrl($uri) {
478
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args()));
479
- }
480
-
481
-
482
- /**
483
- * [!] Method is generated. Documentation taken from corresponding module.
484
- *
485
- * Checks that the current URL is equal to the given string.
486
- * Unlike `seeInCurrentUrl`, this only matches the full URL.
487
- *
488
- * ``` php
489
- * <?php
490
- * // to match root url
491
- * $I->seeCurrentUrlEquals('/');
492
- * ?>
493
- * ```
494
- *
495
- * @param $uri
496
- * Conditional Assertion: Test won't be stopped on fail
497
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals()
498
- */
499
- public function canSeeCurrentUrlEquals($uri) {
500
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args()));
501
- }
502
- /**
503
- * [!] Method is generated. Documentation taken from corresponding module.
504
- *
505
- * Checks that the current URL is equal to the given string.
506
- * Unlike `seeInCurrentUrl`, this only matches the full URL.
507
- *
508
- * ``` php
509
- * <?php
510
- * // to match root url
511
- * $I->seeCurrentUrlEquals('/');
512
- * ?>
513
- * ```
514
- *
515
- * @param $uri
516
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals()
517
- */
518
- public function seeCurrentUrlEquals($uri) {
519
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args()));
520
- }
521
-
522
-
523
- /**
524
- * [!] Method is generated. Documentation taken from corresponding module.
525
- *
526
- * Checks that the current URL doesn't equal the given string.
527
- * Unlike `dontSeeInCurrentUrl`, this only matches the full URL.
528
- *
529
- * ``` php
530
- * <?php
531
- * // current url is not root
532
- * $I->dontSeeCurrentUrlEquals('/');
533
- * ?>
534
- * ```
535
- *
536
- * @param $uri
537
- * Conditional Assertion: Test won't be stopped on fail
538
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals()
539
- */
540
- public function cantSeeCurrentUrlEquals($uri) {
541
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args()));
542
- }
543
- /**
544
- * [!] Method is generated. Documentation taken from corresponding module.
545
- *
546
- * Checks that the current URL doesn't equal the given string.
547
- * Unlike `dontSeeInCurrentUrl`, this only matches the full URL.
548
- *
549
- * ``` php
550
- * <?php
551
- * // current url is not root
552
- * $I->dontSeeCurrentUrlEquals('/');
553
- * ?>
554
- * ```
555
- *
556
- * @param $uri
557
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals()
558
- */
559
- public function dontSeeCurrentUrlEquals($uri) {
560
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args()));
561
- }
562
-
563
-
564
- /**
565
- * [!] Method is generated. Documentation taken from corresponding module.
566
- *
567
- * Checks that the current URL matches the given regular expression.
568
- *
569
- * ``` php
570
- * <?php
571
- * // to match root url
572
- * $I->seeCurrentUrlMatches('~$/users/(\d+)~');
573
- * ?>
574
- * ```
575
- *
576
- * @param $uri
577
- * Conditional Assertion: Test won't be stopped on fail
578
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches()
579
- */
580
- public function canSeeCurrentUrlMatches($uri) {
581
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args()));
582
- }
583
- /**
584
- * [!] Method is generated. Documentation taken from corresponding module.
585
- *
586
- * Checks that the current URL matches the given regular expression.
587
- *
588
- * ``` php
589
- * <?php
590
- * // to match root url
591
- * $I->seeCurrentUrlMatches('~$/users/(\d+)~');
592
- * ?>
593
- * ```
594
- *
595
- * @param $uri
596
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches()
597
- */
598
- public function seeCurrentUrlMatches($uri) {
599
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args()));
600
- }
601
-
602
-
603
- /**
604
- * [!] Method is generated. Documentation taken from corresponding module.
605
- *
606
- * Checks that current url doesn't match the given regular expression.
607
- *
608
- * ``` php
609
- * <?php
610
- * // to match root url
611
- * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~');
612
- * ?>
613
- * ```
614
- *
615
- * @param $uri
616
- * Conditional Assertion: Test won't be stopped on fail
617
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches()
618
- */
619
- public function cantSeeCurrentUrlMatches($uri) {
620
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args()));
621
- }
622
- /**
623
- * [!] Method is generated. Documentation taken from corresponding module.
624
- *
625
- * Checks that current url doesn't match the given regular expression.
626
- *
627
- * ``` php
628
- * <?php
629
- * // to match root url
630
- * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~');
631
- * ?>
632
- * ```
633
- *
634
- * @param $uri
635
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches()
636
- */
637
- public function dontSeeCurrentUrlMatches($uri) {
638
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args()));
639
- }
640
-
641
-
642
- /**
643
- * [!] Method is generated. Documentation taken from corresponding module.
644
- *
645
- * Executes the given regular expression against the current URI and returns the first match.
646
- * If no parameters are provided, the full URI is returned.
647
- *
648
- * ``` php
649
- * <?php
650
- * $user_id = $I->grabFromCurrentUrl('~$/user/(\d+)/~');
651
- * $uri = $I->grabFromCurrentUrl();
652
- * ?>
653
- * ```
654
- *
655
- * @param null $uri
656
- *
657
- * @internal param $url
658
- * @return mixed
659
- * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl()
660
- */
661
- public function grabFromCurrentUrl($uri = null) {
662
- return $this->scenario->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args()));
663
- }
664
-
665
-
666
- /**
667
- * [!] Method is generated. Documentation taken from corresponding module.
668
- *
669
- * Checks that the specified checkbox is checked.
670
- *
671
- * ``` php
672
- * <?php
673
- * $I->seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
674
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form.
675
- * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]');
676
- * ?>
677
- * ```
678
- *
679
- * @param $checkbox
680
- * Conditional Assertion: Test won't be stopped on fail
681
- * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked()
682
- */
683
- public function canSeeCheckboxIsChecked($checkbox) {
684
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args()));
685
- }
686
- /**
687
- * [!] Method is generated. Documentation taken from corresponding module.
688
- *
689
- * Checks that the specified checkbox is checked.
690
- *
691
- * ``` php
692
- * <?php
693
- * $I->seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
694
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form.
695
- * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]');
696
- * ?>
697
- * ```
698
- *
699
- * @param $checkbox
700
- * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked()
701
- */
702
- public function seeCheckboxIsChecked($checkbox) {
703
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args()));
704
- }
705
-
706
-
707
- /**
708
- * [!] Method is generated. Documentation taken from corresponding module.
709
- *
710
- * Check that the specified checkbox is unchecked.
711
- *
712
- * ``` php
713
- * <?php
714
- * $I->dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms
715
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form.
716
- * ?>
717
- * ```
718
- *
719
- * @param $checkbox
720
- * Conditional Assertion: Test won't be stopped on fail
721
- * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked()
722
- */
723
- public function cantSeeCheckboxIsChecked($checkbox) {
724
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args()));
725
- }
726
- /**
727
- * [!] Method is generated. Documentation taken from corresponding module.
728
- *
729
- * Check that the specified checkbox is unchecked.
730
- *
731
- * ``` php
732
- * <?php
733
- * $I->dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms
734
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form.
735
- * ?>
736
- * ```
737
- *
738
- * @param $checkbox
739
- * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked()
740
- */
741
- public function dontSeeCheckboxIsChecked($checkbox) {
742
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args()));
743
- }
744
-
745
-
746
- /**
747
- * [!] Method is generated. Documentation taken from corresponding module.
748
- *
749
- * Checks that the given input field or textarea contains the given value.
750
- * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
751
- *
752
- * ``` php
753
- * <?php
754
- * $I->seeInField('Body','Type your comment here');
755
- * $I->seeInField('form textarea[name=body]','Type your comment here');
756
- * $I->seeInField('form input[type=hidden]','hidden_value');
757
- * $I->seeInField('#searchform input','Search');
758
- * $I->seeInField('//form/*[@name=search]','Search');
759
- * $I->seeInField(['name' => 'search'], 'Search');
760
- * ?>
761
- * ```
762
- *
763
- * @param $field
764
- * @param $value
765
- * Conditional Assertion: Test won't be stopped on fail
766
- * @see \Codeception\Lib\InnerBrowser::seeInField()
767
- */
768
- public function canSeeInField($field, $value) {
769
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args()));
770
- }
771
- /**
772
- * [!] Method is generated. Documentation taken from corresponding module.
773
- *
774
- * Checks that the given input field or textarea contains the given value.
775
- * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
776
- *
777
- * ``` php
778
- * <?php
779
- * $I->seeInField('Body','Type your comment here');
780
- * $I->seeInField('form textarea[name=body]','Type your comment here');
781
- * $I->seeInField('form input[type=hidden]','hidden_value');
782
- * $I->seeInField('#searchform input','Search');
783
- * $I->seeInField('//form/*[@name=search]','Search');
784
- * $I->seeInField(['name' => 'search'], 'Search');
785
- * ?>
786
- * ```
787
- *
788
- * @param $field
789
- * @param $value
790
- * @see \Codeception\Lib\InnerBrowser::seeInField()
791
- */
792
- public function seeInField($field, $value) {
793
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args()));
794
- }
795
-
796
-
797
- /**
798
- * [!] Method is generated. Documentation taken from corresponding module.
799
- *
800
- * Checks that an input field or textarea doesn't contain the given value.
801
- * For fuzzy locators, the field is matched by label text, CSS and XPath.
802
- *
803
- * ``` php
804
- * <?php
805
- * $I->dontSeeInField('Body','Type your comment here');
806
- * $I->dontSeeInField('form textarea[name=body]','Type your comment here');
807
- * $I->dontSeeInField('form input[type=hidden]','hidden_value');
808
- * $I->dontSeeInField('#searchform input','Search');
809
- * $I->dontSeeInField('//form/*[@name=search]','Search');
810
- * $I->dontSeeInField(['name' => 'search'], 'Search');
811
- * ?>
812
- * ```
813
- *
814
- * @param $field
815
- * @param $value
816
- * Conditional Assertion: Test won't be stopped on fail
817
- * @see \Codeception\Lib\InnerBrowser::dontSeeInField()
818
- */
819
- public function cantSeeInField($field, $value) {
820
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args()));
821
- }
822
- /**
823
- * [!] Method is generated. Documentation taken from corresponding module.
824
- *
825
- * Checks that an input field or textarea doesn't contain the given value.
826
- * For fuzzy locators, the field is matched by label text, CSS and XPath.
827
- *
828
- * ``` php
829
- * <?php
830
- * $I->dontSeeInField('Body','Type your comment here');
831
- * $I->dontSeeInField('form textarea[name=body]','Type your comment here');
832
- * $I->dontSeeInField('form input[type=hidden]','hidden_value');
833
- * $I->dontSeeInField('#searchform input','Search');
834
- * $I->dontSeeInField('//form/*[@name=search]','Search');
835
- * $I->dontSeeInField(['name' => 'search'], 'Search');
836
- * ?>
837
- * ```
838
- *
839
- * @param $field
840
- * @param $value
841
- * @see \Codeception\Lib\InnerBrowser::dontSeeInField()
842
- */
843
- public function dontSeeInField($field, $value) {
844
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args()));
845
- }
846
-
847
-
848
- /**
849
- * [!] Method is generated. Documentation taken from corresponding module.
850
- *
851
- * Checks if the array of form parameters (name => value) are set on the form matched with the
852
- * passed selector.
853
- *
854
- * ``` php
855
- * <?php
856
- * $I->seeInFormFields('form[name=myform]', [
857
- * 'input1' => 'value',
858
- * 'input2' => 'other value',
859
- * ]);
860
- * ?>
861
- * ```
862
- *
863
- * For multi-select elements, or to check values of multiple elements with the same name, an
864
- * array may be passed:
865
- *
866
- * ``` php
867
- * <?php
868
- * $I->seeInFormFields('.form-class', [
869
- * 'multiselect' => [
870
- * 'value1',
871
- * 'value2',
872
- * ],
873
- * 'checkbox[]' => [
874
- * 'a checked value',
875
- * 'another checked value',
876
- * ],
877
- * ]);
878
- * ?>
879
- * ```
880
- *
881
- * Additionally, checkbox values can be checked with a boolean.
882
- *
883
- * ``` php
884
- * <?php
885
- * $I->seeInFormFields('#form-id', [
886
- * 'checkbox1' => true, // passes if checked
887
- * 'checkbox2' => false, // passes if unchecked
888
- * ]);
889
- * ?>
890
- * ```
891
- *
892
- * Pair this with submitForm for quick testing magic.
893
- *
894
- * ``` php
895
- * <?php
896
- * $form = [
897
- * 'field1' => 'value',
898
- * 'field2' => 'another value',
899
- * 'checkbox1' => true,
900
- * // ...
901
- * ];
902
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
903
- * // $I->amOnPage('/path/to/form-page') may be needed
904
- * $I->seeInFormFields('//form[@id=my-form]', $form);
905
- * ?>
906
- * ```
907
- *
908
- * @param $formSelector
909
- * @param $params
910
- * Conditional Assertion: Test won't be stopped on fail
911
- * @see \Codeception\Lib\InnerBrowser::seeInFormFields()
912
- */
913
- public function canSeeInFormFields($formSelector, $params) {
914
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args()));
915
- }
916
- /**
917
- * [!] Method is generated. Documentation taken from corresponding module.
918
- *
919
- * Checks if the array of form parameters (name => value) are set on the form matched with the
920
- * passed selector.
921
- *
922
- * ``` php
923
- * <?php
924
- * $I->seeInFormFields('form[name=myform]', [
925
- * 'input1' => 'value',
926
- * 'input2' => 'other value',
927
- * ]);
928
- * ?>
929
- * ```
930
- *
931
- * For multi-select elements, or to check values of multiple elements with the same name, an
932
- * array may be passed:
933
- *
934
- * ``` php
935
- * <?php
936
- * $I->seeInFormFields('.form-class', [
937
- * 'multiselect' => [
938
- * 'value1',
939
- * 'value2',
940
- * ],
941
- * 'checkbox[]' => [
942
- * 'a checked value',
943
- * 'another checked value',
944
- * ],
945
- * ]);
946
- * ?>
947
- * ```
948
- *
949
- * Additionally, checkbox values can be checked with a boolean.
950
- *
951
- * ``` php
952
- * <?php
953
- * $I->seeInFormFields('#form-id', [
954
- * 'checkbox1' => true, // passes if checked
955
- * 'checkbox2' => false, // passes if unchecked
956
- * ]);
957
- * ?>
958
- * ```
959
- *
960
- * Pair this with submitForm for quick testing magic.
961
- *
962
- * ``` php
963
- * <?php
964
- * $form = [
965
- * 'field1' => 'value',
966
- * 'field2' => 'another value',
967
- * 'checkbox1' => true,
968
- * // ...
969
- * ];
970
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
971
- * // $I->amOnPage('/path/to/form-page') may be needed
972
- * $I->seeInFormFields('//form[@id=my-form]', $form);
973
- * ?>
974
- * ```
975
- *
976
- * @param $formSelector
977
- * @param $params
978
- * @see \Codeception\Lib\InnerBrowser::seeInFormFields()
979
- */
980
- public function seeInFormFields($formSelector, $params) {
981
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args()));
982
- }
983
-
984
-
985
- /**
986
- * [!] Method is generated. Documentation taken from corresponding module.
987
- *
988
- * Checks if the array of form parameters (name => value) are not set on the form matched with
989
- * the passed selector.
990
- *
991
- * ``` php
992
- * <?php
993
- * $I->dontSeeInFormFields('form[name=myform]', [
994
- * 'input1' => 'non-existent value',
995
- * 'input2' => 'other non-existent value',
996
- * ]);
997
- * ?>
998
- * ```
999
- *
1000
- * To check that an element hasn't been assigned any one of many values, an array can be passed
1001
- * as the value:
1002
- *
1003
- * ``` php
1004
- * <?php
1005
- * $I->dontSeeInFormFields('.form-class', [
1006
- * 'fieldName' => [
1007
- * 'This value shouldn\'t be set',
1008
- * 'And this value shouldn\'t be set',
1009
- * ],
1010
- * ]);
1011
- * ?>
1012
- * ```
1013
- *
1014
- * Additionally, checkbox values can be checked with a boolean.
1015
- *
1016
- * ``` php
1017
- * <?php
1018
- * $I->dontSeeInFormFields('#form-id', [
1019
- * 'checkbox1' => true, // fails if checked
1020
- * 'checkbox2' => false, // fails if unchecked
1021
- * ]);
1022
- * ?>
1023
- * ```
1024
- *
1025
- * @param $formSelector
1026
- * @param $params
1027
- * Conditional Assertion: Test won't be stopped on fail
1028
- * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields()
1029
- */
1030
- public function cantSeeInFormFields($formSelector, $params) {
1031
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args()));
1032
- }
1033
- /**
1034
- * [!] Method is generated. Documentation taken from corresponding module.
1035
- *
1036
- * Checks if the array of form parameters (name => value) are not set on the form matched with
1037
- * the passed selector.
1038
- *
1039
- * ``` php
1040
- * <?php
1041
- * $I->dontSeeInFormFields('form[name=myform]', [
1042
- * 'input1' => 'non-existent value',
1043
- * 'input2' => 'other non-existent value',
1044
- * ]);
1045
- * ?>
1046
- * ```
1047
- *
1048
- * To check that an element hasn't been assigned any one of many values, an array can be passed
1049
- * as the value:
1050
- *
1051
- * ``` php
1052
- * <?php
1053
- * $I->dontSeeInFormFields('.form-class', [
1054
- * 'fieldName' => [
1055
- * 'This value shouldn\'t be set',
1056
- * 'And this value shouldn\'t be set',
1057
- * ],
1058
- * ]);
1059
- * ?>
1060
- * ```
1061
- *
1062
- * Additionally, checkbox values can be checked with a boolean.
1063
- *
1064
- * ``` php
1065
- * <?php
1066
- * $I->dontSeeInFormFields('#form-id', [
1067
- * 'checkbox1' => true, // fails if checked
1068
- * 'checkbox2' => false, // fails if unchecked
1069
- * ]);
1070
- * ?>
1071
- * ```
1072
- *
1073
- * @param $formSelector
1074
- * @param $params
1075
- * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields()
1076
- */
1077
- public function dontSeeInFormFields($formSelector, $params) {
1078
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args()));
1079
- }
1080
-
1081
-
1082
- /**
1083
- * [!] Method is generated. Documentation taken from corresponding module.
1084
- *
1085
- * Submits the given form on the page, optionally with the given form values.
1086
- * Give the form fields values as an array.
1087
- *
1088
- * Skipped fields will be filled by their values from the page.
1089
- * You don't need to click the 'Submit' button afterwards.
1090
- * This command itself triggers the request to form's action.
1091
- *
1092
- * You can optionally specify what button's value to include
1093
- * in the request with the last parameter as an alternative to
1094
- * explicitly setting its value in the second parameter, as
1095
- * button values are not otherwise included in the request.
1096
- *
1097
- * Examples:
1098
- *
1099
- * ``` php
1100
- * <?php
1101
- * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456'));
1102
- * // or
1103
- * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456'), 'submitButtonName');
1104
- *
1105
- * ```
1106
- *
1107
- * For example, given this sample "Sign Up" form:
1108
- *
1109
- * ``` html
1110
- * <form action="/sign_up">
1111
- * Login: <input type="text" name="user[login]" /><br/>
1112
- * Password: <input type="password" name="user[password]" /><br/>
1113
- * Do you agree to out terms? <input type="checkbox" name="user[agree]" /><br/>
1114
- * Select pricing plan <select name="plan"><option value="1">Free</option><option value="2" selected="selected">Paid</option></select>
1115
- * <input type="submit" name="submitButton" value="Submit" />
1116
- * </form>
1117
- * ```
1118
- *
1119
- * You could write the following to submit it:
1120
- *
1121
- * ``` php
1122
- * <?php
1123
- * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true)), 'submitButton');
1124
- *
1125
- * ```
1126
- * Note that "2" will be the submitted value for the "plan" field, as it is the selected option.
1127
- *
1128
- * You can also emulate a JavaScript submission by not specifying any buttons in the third parameter to submitForm.
1129
- *
1130
- * ```php
1131
- * <?php
1132
- * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true)));
1133
- *
1134
- * ```
1135
- *
1136
- * Pair this with seeInFormFields for quick testing magic.
1137
- *
1138
- * ``` php
1139
- * <?php
1140
- * $form = [
1141
- * 'field1' => 'value',
1142
- * 'field2' => 'another value',
1143
- * 'checkbox1' => true,
1144
- * // ...
1145
- * ];
1146
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
1147
- * // $I->amOnPage('/path/to/form-page') may be needed
1148
- * $I->seeInFormFields('//form[@id=my-form]', $form);
1149
- * ?>
1150
- * ```
1151
- *
1152
- * Parameter values can be set to arrays for multiple input fields
1153
- * of the same name, or multi-select combo boxes. For checkboxes,
1154
- * either the string value can be used, or boolean values which will
1155
- * be replaced by the checkbox's value in the DOM.
1156
- *
1157
- * ``` php
1158
- * <?php
1159
- * $I->submitForm('#my-form', [
1160
- * 'field1' => 'value',
1161
- * 'checkbox' => [
1162
- * 'value of first checkbox',
1163
- * 'value of second checkbox,
1164
- * ],
1165
- * 'otherCheckboxes' => [
1166
- * true,
1167
- * false,
1168
- * false
1169
- * ],
1170
- * 'multiselect' => [
1171
- * 'first option value',
1172
- * 'second option value'
1173
- * ]
1174
- * ]);
1175
- * ?>
1176
- * ```
1177
- *
1178
- * Mixing string and boolean values for a checkbox's value is not
1179
- * supported and may produce unexpected results.
1180
- *
1181
- * @param $selector
1182
- * @param $params
1183
- * @param $button
1184
- * @see \Codeception\Lib\InnerBrowser::submitForm()
1185
- */
1186
- public function submitForm($selector, $params, $button = null) {
1187
- return $this->scenario->runStep(new \Codeception\Step\Action('submitForm', func_get_args()));
1188
- }
1189
-
1190
-
1191
- /**
1192
- * [!] Method is generated. Documentation taken from corresponding module.
1193
- *
1194
- * Fills a text field or textarea with the given string.
1195
- *
1196
- * ``` php
1197
- * <?php
1198
- * $I->fillField("//input[@type='text']", "Hello World!");
1199
- * $I->fillField(['name' => 'email'], 'jon@mail.com');
1200
- * ?>
1201
- * ```
1202
- *
1203
- * @param $field
1204
- * @param $value
1205
- * @see \Codeception\Lib\InnerBrowser::fillField()
1206
- */
1207
- public function fillField($field, $value) {
1208
- return $this->scenario->runStep(new \Codeception\Step\Action('fillField', func_get_args()));
1209
- }
1210
-
1211
-
1212
- /**
1213
- * [!] Method is generated. Documentation taken from corresponding module.
1214
- *
1215
- * Selects an option in a select tag or in radio button group.
1216
- *
1217
- * ``` php
1218
- * <?php
1219
- * $I->selectOption('form select[name=account]', 'Premium');
1220
- * $I->selectOption('form input[name=payment]', 'Monthly');
1221
- * $I->selectOption('//form/select[@name=account]', 'Monthly');
1222
- * ?>
1223
- * ```
1224
- *
1225
- * Provide an array for the second argument to select multiple options:
1226
- *
1227
- * ``` php
1228
- * <?php
1229
- * $I->selectOption('Which OS do you use?', array('Windows','Linux'));
1230
- * ?>
1231
- * ```
1232
- *
1233
- * @param $select
1234
- * @param $option
1235
- * @see \Codeception\Lib\InnerBrowser::selectOption()
1236
- */
1237
- public function selectOption($select, $option) {
1238
- return $this->scenario->runStep(new \Codeception\Step\Action('selectOption', func_get_args()));
1239
- }
1240
-
1241
-
1242
- /**
1243
- * [!] Method is generated. Documentation taken from corresponding module.
1244
- *
1245
- * Ticks a checkbox. For radio buttons, use the `selectOption` method instead.
1246
- *
1247
- * ``` php
1248
- * <?php
1249
- * $I->checkOption('#agree');
1250
- * ?>
1251
- * ```
1252
- *
1253
- * @param $option
1254
- * @see \Codeception\Lib\InnerBrowser::checkOption()
1255
- */
1256
- public function checkOption($option) {
1257
- return $this->scenario->runStep(new \Codeception\Step\Action('checkOption', func_get_args()));
1258
- }
1259
-
1260
-
1261
- /**
1262
- * [!] Method is generated. Documentation taken from corresponding module.
1263
- *
1264
- * Unticks a checkbox.
1265
- *
1266
- * ``` php
1267
- * <?php
1268
- * $I->uncheckOption('#notify');
1269
- * ?>
1270
- * ```
1271
- *
1272
- * @param $option
1273
- * @see \Codeception\Lib\InnerBrowser::uncheckOption()
1274
- */
1275
- public function uncheckOption($option) {
1276
- return $this->scenario->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args()));
1277
- }
1278
-
1279
-
1280
- /**
1281
- * [!] Method is generated. Documentation taken from corresponding module.
1282
- *
1283
- * Attaches a file relative to the Codeception data directory to the given file upload field.
1284
- *
1285
- * ``` php
1286
- * <?php
1287
- * // file is stored in 'tests/_data/prices.xls'
1288
- * $I->attachFile('input[@type="file"]', 'prices.xls');
1289
- * ?>
1290
- * ```
1291
- *
1292
- * @param $field
1293
- * @param $filename
1294
- * @see \Codeception\Lib\InnerBrowser::attachFile()
1295
- */
1296
- public function attachFile($field, $filename) {
1297
- return $this->scenario->runStep(new \Codeception\Step\Action('attachFile', func_get_args()));
1298
- }
1299
-
1300
-
1301
- /**
1302
- * [!] Method is generated. Documentation taken from corresponding module.
1303
- *
1304
- * If your page triggers an ajax request, you can perform it manually.
1305
- * This action sends a GET ajax request with specified params.
1306
- *
1307
- * See ->sendAjaxPostRequest for examples.
1308
- *
1309
- * @param $uri
1310
- * @param $params
1311
- * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest()
1312
- */
1313
- public function sendAjaxGetRequest($uri, $params = null) {
1314
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args()));
1315
- }
1316
-
1317
-
1318
- /**
1319
- * [!] Method is generated. Documentation taken from corresponding module.
1320
- *
1321
- * If your page triggers an ajax request, you can perform it manually.
1322
- * This action sends a POST ajax request with specified params.
1323
- * Additional params can be passed as array.
1324
- *
1325
- * Example:
1326
- *
1327
- * Imagine that by clicking checkbox you trigger ajax request which updates user settings.
1328
- * We emulate that click by running this ajax request manually.
1329
- *
1330
- * ``` php
1331
- * <?php
1332
- * $I->sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST
1333
- * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET
1334
- *
1335
- * ```
1336
- *
1337
- * @param $uri
1338
- * @param $params
1339
- * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest()
1340
- */
1341
- public function sendAjaxPostRequest($uri, $params = null) {
1342
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args()));
1343
- }
1344
-
1345
-
1346
- /**
1347
- * [!] Method is generated. Documentation taken from corresponding module.
1348
- *
1349
- * If your page triggers an ajax request, you can perform it manually.
1350
- * This action sends an ajax request with specified method and params.
1351
- *
1352
- * Example:
1353
- *
1354
- * You need to perform an ajax request specifying the HTTP method.
1355
- *
1356
- * ``` php
1357
- * <?php
1358
- * $I->sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title'));
1359
- *
1360
- * ```
1361
- *
1362
- * @param $method
1363
- * @param $uri
1364
- * @param $params
1365
- * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest()
1366
- */
1367
- public function sendAjaxRequest($method, $uri, $params = null) {
1368
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args()));
1369
- }
1370
-
1371
-
1372
- /**
1373
- * [!] Method is generated. Documentation taken from corresponding module.
1374
- *
1375
- * Finds and returns the text contents of the given element.
1376
- * If a fuzzy locator is used, the element is found using CSS, XPath, and by matching the full page source by regular expression.
1377
- *
1378
- * ``` php
1379
- * <?php
1380
- * $heading = $I->grabTextFrom('h1');
1381
- * $heading = $I->grabTextFrom('descendant-or-self::h1');
1382
- * $value = $I->grabTextFrom('~<input value=(.*?)]~sgi'); // match with a regex
1383
- * ?>
1384
- * ```
1385
- *
1386
- * @param $cssOrXPathOrRegex
1387
- *
1388
- * @return mixed
1389
- * @see \Codeception\Lib\InnerBrowser::grabTextFrom()
1390
- */
1391
- public function grabTextFrom($cssOrXPathOrRegex) {
1392
- return $this->scenario->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args()));
1393
- }
1394
-
1395
-
1396
- /**
1397
- * [!] Method is generated. Documentation taken from corresponding module.
1398
- *
1399
- * Grabs the value of the given attribute value from the given element.
1400
- * Fails if element is not found.
1401
- *
1402
- * ``` php
1403
- * <?php
1404
- * $I->grabAttributeFrom('#tooltip', 'title');
1405
- * ?>
1406
- * ```
1407
- *
1408
- *
1409
- * @param $cssOrXpath
1410
- * @param $attribute
1411
- * @internal param $element
1412
- * @return mixed
1413
- * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom()
1414
- */
1415
- public function grabAttributeFrom($cssOrXpath, $attribute) {
1416
- return $this->scenario->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args()));
1417
- }
1418
-
1419
-
1420
- /**
1421
- * [!] Method is generated. Documentation taken from corresponding module.
1422
- *
1423
- * @param $field
1424
- *
1425
- * @return array|mixed|null|string
1426
- * @see \Codeception\Lib\InnerBrowser::grabValueFrom()
1427
- */
1428
- public function grabValueFrom($field) {
1429
- return $this->scenario->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args()));
1430
- }
1431
-
1432
-
1433
- /**
1434
- * [!] Method is generated. Documentation taken from corresponding module.
1435
- *
1436
- * Sets a cookie with the given name and value.
1437
- * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument.
1438
- *
1439
- * ``` php
1440
- * <?php
1441
- * $I->setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3');
1442
- * ?>
1443
- * ```
1444
- *
1445
- * @param $name
1446
- * @param $val
1447
- * @param array $params
1448
- * @internal param $cookie
1449
- * @internal param $value
1450
- *
1451
- * @return mixed
1452
- * @see \Codeception\Lib\InnerBrowser::setCookie()
1453
- */
1454
- public function setCookie($name, $val, $params = null) {
1455
- return $this->scenario->runStep(new \Codeception\Step\Action('setCookie', func_get_args()));
1456
- }
1457
-
1458
-
1459
- /**
1460
- * [!] Method is generated. Documentation taken from corresponding module.
1461
- *
1462
- * Grabs a cookie value.
1463
- * You can set additional cookie params like `domain`, `path` in array passed as last argument.
1464
- *
1465
- * @param $cookie
1466
- *
1467
- * @param array $params
1468
- * @return mixed
1469
- * @see \Codeception\Lib\InnerBrowser::grabCookie()
1470
- */
1471
- public function grabCookie($name, $params = null) {
1472
- return $this->scenario->runStep(new \Codeception\Step\Action('grabCookie', func_get_args()));
1473
- }
1474
-
1475
-
1476
- /**
1477
- * [!] Method is generated. Documentation taken from corresponding module.
1478
- *
1479
- * Checks that a cookie with the given name is set.
1480
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1481
- *
1482
- * ``` php
1483
- * <?php
1484
- * $I->seeCookie('PHPSESSID');
1485
- * ?>
1486
- * ```
1487
- *
1488
- * @param $cookie
1489
- * @param array $params
1490
- * @return mixed
1491
- * Conditional Assertion: Test won't be stopped on fail
1492
- * @see \Codeception\Lib\InnerBrowser::seeCookie()
1493
- */
1494
- public function canSeeCookie($name, $params = null) {
1495
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args()));
1496
- }
1497
- /**
1498
- * [!] Method is generated. Documentation taken from corresponding module.
1499
- *
1500
- * Checks that a cookie with the given name is set.
1501
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1502
- *
1503
- * ``` php
1504
- * <?php
1505
- * $I->seeCookie('PHPSESSID');
1506
- * ?>
1507
- * ```
1508
- *
1509
- * @param $cookie
1510
- * @param array $params
1511
- * @return mixed
1512
- * @see \Codeception\Lib\InnerBrowser::seeCookie()
1513
- */
1514
- public function seeCookie($name, $params = null) {
1515
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args()));
1516
- }
1517
-
1518
-
1519
- /**
1520
- * [!] Method is generated. Documentation taken from corresponding module.
1521
- *
1522
- * Checks that there isn't a cookie with the given name.
1523
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1524
- *
1525
- * @param $cookie
1526
- *
1527
- * @param array $params
1528
- * @return mixed
1529
- * Conditional Assertion: Test won't be stopped on fail
1530
- * @see \Codeception\Lib\InnerBrowser::dontSeeCookie()
1531
- */
1532
- public function cantSeeCookie($name, $params = null) {
1533
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args()));
1534
- }
1535
- /**
1536
- * [!] Method is generated. Documentation taken from corresponding module.
1537
- *
1538
- * Checks that there isn't a cookie with the given name.
1539
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1540
- *
1541
- * @param $cookie
1542
- *
1543
- * @param array $params
1544
- * @return mixed
1545
- * @see \Codeception\Lib\InnerBrowser::dontSeeCookie()
1546
- */
1547
- public function dontSeeCookie($name, $params = null) {
1548
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args()));
1549
- }
1550
-
1551
-
1552
- /**
1553
- * [!] Method is generated. Documentation taken from corresponding module.
1554
- *
1555
- * Unsets cookie with the given name.
1556
- * You can set additional cookie params like `domain`, `path` in array passed as last argument.
1557
- *
1558
- * @param $cookie
1559
- *
1560
- * @param array $params
1561
- * @return mixed
1562
- * @see \Codeception\Lib\InnerBrowser::resetCookie()
1563
- */
1564
- public function resetCookie($name, $params = null) {
1565
- return $this->scenario->runStep(new \Codeception\Step\Action('resetCookie', func_get_args()));
1566
- }
1567
-
1568
-
1569
- /**
1570
- * [!] Method is generated. Documentation taken from corresponding module.
1571
- *
1572
- * Checks that the given element exists on the page and is visible.
1573
- * You can also specify expected attributes of this element.
1574
- *
1575
- * ``` php
1576
- * <?php
1577
- * $I->seeElement('.error');
1578
- * $I->seeElement('//form/input[1]');
1579
- * $I->seeElement('input', ['name' => 'login']);
1580
- * $I->seeElement('input', ['value' => '123456']);
1581
- *
1582
- * // strict locator in first arg, attributes in second
1583
- * $I->seeElement(['css' => 'form input'], ['name' => 'login']);
1584
- * ?>
1585
- * ```
1586
- *
1587
- * @param $selector
1588
- * @param array $attributes
1589
- * @return
1590
- * Conditional Assertion: Test won't be stopped on fail
1591
- * @see \Codeception\Lib\InnerBrowser::seeElement()
1592
- */
1593
- public function canSeeElement($selector, $attributes = null) {
1594
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args()));
1595
- }
1596
- /**
1597
- * [!] Method is generated. Documentation taken from corresponding module.
1598
- *
1599
- * Checks that the given element exists on the page and is visible.
1600
- * You can also specify expected attributes of this element.
1601
- *
1602
- * ``` php
1603
- * <?php
1604
- * $I->seeElement('.error');
1605
- * $I->seeElement('//form/input[1]');
1606
- * $I->seeElement('input', ['name' => 'login']);
1607
- * $I->seeElement('input', ['value' => '123456']);
1608
- *
1609
- * // strict locator in first arg, attributes in second
1610
- * $I->seeElement(['css' => 'form input'], ['name' => 'login']);
1611
- * ?>
1612
- * ```
1613
- *
1614
- * @param $selector
1615
- * @param array $attributes
1616
- * @return
1617
- * @see \Codeception\Lib\InnerBrowser::seeElement()
1618
- */
1619
- public function seeElement($selector, $attributes = null) {
1620
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args()));
1621
- }
1622
-
1623
-
1624
- /**
1625
- * [!] Method is generated. Documentation taken from corresponding module.
1626
- *
1627
- * Checks that the given element is invisible or not present on the page.
1628
- * You can also specify expected attributes of this element.
1629
- *
1630
- * ``` php
1631
- * <?php
1632
- * $I->dontSeeElement('.error');
1633
- * $I->dontSeeElement('//form/input[1]');
1634
- * $I->dontSeeElement('input', ['name' => 'login']);
1635
- * $I->dontSeeElement('input', ['value' => '123456']);
1636
- * ?>
1637
- * ```
1638
- *
1639
- * @param $selector
1640
- * @param array $attributes
1641
- * Conditional Assertion: Test won't be stopped on fail
1642
- * @see \Codeception\Lib\InnerBrowser::dontSeeElement()
1643
- */
1644
- public function cantSeeElement($selector, $attributes = null) {
1645
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args()));
1646
- }
1647
- /**
1648
- * [!] Method is generated. Documentation taken from corresponding module.
1649
- *
1650
- * Checks that the given element is invisible or not present on the page.
1651
- * You can also specify expected attributes of this element.
1652
- *
1653
- * ``` php
1654
- * <?php
1655
- * $I->dontSeeElement('.error');
1656
- * $I->dontSeeElement('//form/input[1]');
1657
- * $I->dontSeeElement('input', ['name' => 'login']);
1658
- * $I->dontSeeElement('input', ['value' => '123456']);
1659
- * ?>
1660
- * ```
1661
- *
1662
- * @param $selector
1663
- * @param array $attributes
1664
- * @see \Codeception\Lib\InnerBrowser::dontSeeElement()
1665
- */
1666
- public function dontSeeElement($selector, $attributes = null) {
1667
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args()));
1668
- }
1669
-
1670
-
1671
- /**
1672
- * [!] Method is generated. Documentation taken from corresponding module.
1673
- *
1674
- * Checks that there are a certain number of elements matched by the given locator on the page.
1675
- *
1676
- * ``` php
1677
- * <?php
1678
- * $I->seeNumberOfElements('tr', 10);
1679
- * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements
1680
- * ?>
1681
- * ```
1682
- * @param $selector
1683
- * @param mixed $expected:
1684
- * - string: strict number
1685
- * - array: range of numbers [0,10]
1686
- * Conditional Assertion: Test won't be stopped on fail
1687
- * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements()
1688
- */
1689
- public function canSeeNumberOfElements($selector, $expected) {
1690
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args()));
1691
- }
1692
- /**
1693
- * [!] Method is generated. Documentation taken from corresponding module.
1694
- *
1695
- * Checks that there are a certain number of elements matched by the given locator on the page.
1696
- *
1697
- * ``` php
1698
- * <?php
1699
- * $I->seeNumberOfElements('tr', 10);
1700
- * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements
1701
- * ?>
1702
- * ```
1703
- * @param $selector
1704
- * @param mixed $expected:
1705
- * - string: strict number
1706
- * - array: range of numbers [0,10]
1707
- * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements()
1708
- */
1709
- public function seeNumberOfElements($selector, $expected) {
1710
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args()));
1711
- }
1712
-
1713
-
1714
- /**
1715
- * [!] Method is generated. Documentation taken from corresponding module.
1716
- *
1717
- * Checks that the given option is selected.
1718
- *
1719
- * ``` php
1720
- * <?php
1721
- * $I->seeOptionIsSelected('#form input[name=payment]', 'Visa');
1722
- * ?>
1723
- * ```
1724
- *
1725
- * @param $selector
1726
- * @param $optionText
1727
- *
1728
- * @return mixed
1729
- * Conditional Assertion: Test won't be stopped on fail
1730
- * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected()
1731
- */
1732
- public function canSeeOptionIsSelected($select, $optionText) {
1733
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeOptionIsSelected', func_get_args()));
1734
- }
1735
- /**
1736
- * [!] Method is generated. Documentation taken from corresponding module.
1737
- *
1738
- * Checks that the given option is selected.
1739
- *
1740
- * ``` php
1741
- * <?php
1742
- * $I->seeOptionIsSelected('#form input[name=payment]', 'Visa');
1743
- * ?>
1744
- * ```
1745
- *
1746
- * @param $selector
1747
- * @param $optionText
1748
- *
1749
- * @return mixed
1750
- * @see \Codeception\Lib\InnerBrowser::seeOptionIsSelected()
1751
- */
1752
- public function seeOptionIsSelected($select, $optionText) {
1753
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeOptionIsSelected', func_get_args()));
1754
- }
1755
-
1756
-
1757
- /**
1758
- * [!] Method is generated. Documentation taken from corresponding module.
1759
- *
1760
- * Checks that the given option is not selected.
1761
- *
1762
- * ``` php
1763
- * <?php
1764
- * $I->dontSeeOptionIsSelected('#form input[name=payment]', 'Visa');
1765
- * ?>
1766
- * ```
1767
- *
1768
- * @param $selector
1769
- * @param $optionText
1770
- *
1771
- * @return mixed
1772
- * Conditional Assertion: Test won't be stopped on fail
1773
- * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected()
1774
- */
1775
- public function cantSeeOptionIsSelected($select, $optionText) {
1776
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeOptionIsSelected', func_get_args()));
1777
- }
1778
- /**
1779
- * [!] Method is generated. Documentation taken from corresponding module.
1780
- *
1781
- * Checks that the given option is not selected.
1782
- *
1783
- * ``` php
1784
- * <?php
1785
- * $I->dontSeeOptionIsSelected('#form input[name=payment]', 'Visa');
1786
- * ?>
1787
- * ```
1788
- *
1789
- * @param $selector
1790
- * @param $optionText
1791
- *
1792
- * @return mixed
1793
- * @see \Codeception\Lib\InnerBrowser::dontSeeOptionIsSelected()
1794
- */
1795
- public function dontSeeOptionIsSelected($select, $optionText) {
1796
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeOptionIsSelected', func_get_args()));
1797
- }
1798
-
1799
-
1800
- /**
1801
- * [!] Method is generated. Documentation taken from corresponding module.
1802
- *
1803
- * Asserts that current page has 404 response status code.
1804
- * Conditional Assertion: Test won't be stopped on fail
1805
- * @see \Codeception\Lib\InnerBrowser::seePageNotFound()
1806
- */
1807
- public function canSeePageNotFound() {
1808
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seePageNotFound', func_get_args()));
1809
- }
1810
- /**
1811
- * [!] Method is generated. Documentation taken from corresponding module.
1812
- *
1813
- * Asserts that current page has 404 response status code.
1814
- * @see \Codeception\Lib\InnerBrowser::seePageNotFound()
1815
- */
1816
- public function seePageNotFound() {
1817
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seePageNotFound', func_get_args()));
1818
- }
1819
-
1820
-
1821
- /**
1822
- * [!] Method is generated. Documentation taken from corresponding module.
1823
- *
1824
- * Checks that response code is equal to value provided.
1825
- *
1826
- * @param $code
1827
- *
1828
- * @return mixed
1829
- * Conditional Assertion: Test won't be stopped on fail
1830
- * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs()
1831
- */
1832
- public function canSeeResponseCodeIs($code) {
1833
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeResponseCodeIs', func_get_args()));
1834
- }
1835
- /**
1836
- * [!] Method is generated. Documentation taken from corresponding module.
1837
- *
1838
- * Checks that response code is equal to value provided.
1839
- *
1840
- * @param $code
1841
- *
1842
- * @return mixed
1843
- * @see \Codeception\Lib\InnerBrowser::seeResponseCodeIs()
1844
- */
1845
- public function seeResponseCodeIs($code) {
1846
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeResponseCodeIs', func_get_args()));
1847
- }
1848
-
1849
-
1850
- /**
1851
- * [!] Method is generated. Documentation taken from corresponding module.
1852
- *
1853
- * Checks that the page title contains the given string.
1854
- *
1855
- * ``` php
1856
- * <?php
1857
- * $I->seeInTitle('Blog - Post #1');
1858
- * ?>
1859
- * ```
1860
- *
1861
- * @param $title
1862
- *
1863
- * @return mixed
1864
- * Conditional Assertion: Test won't be stopped on fail
1865
- * @see \Codeception\Lib\InnerBrowser::seeInTitle()
1866
- */
1867
- public function canSeeInTitle($title) {
1868
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInTitle', func_get_args()));
1869
- }
1870
- /**
1871
- * [!] Method is generated. Documentation taken from corresponding module.
1872
- *
1873
- * Checks that the page title contains the given string.
1874
- *
1875
- * ``` php
1876
- * <?php
1877
- * $I->seeInTitle('Blog - Post #1');
1878
- * ?>
1879
- * ```
1880
- *
1881
- * @param $title
1882
- *
1883
- * @return mixed
1884
- * @see \Codeception\Lib\InnerBrowser::seeInTitle()
1885
- */
1886
- public function seeInTitle($title) {
1887
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInTitle', func_get_args()));
1888
- }
1889
-
1890
-
1891
- /**
1892
- * [!] Method is generated. Documentation taken from corresponding module.
1893
- *
1894
- * Checks that the page title does not contain the given string.
1895
- *
1896
- * @param $title
1897
- *
1898
- * @return mixed
1899
- * Conditional Assertion: Test won't be stopped on fail
1900
- * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle()
1901
- */
1902
- public function cantSeeInTitle($title) {
1903
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInTitle', func_get_args()));
1904
- }
1905
- /**
1906
- * [!] Method is generated. Documentation taken from corresponding module.
1907
- *
1908
- * Checks that the page title does not contain the given string.
1909
- *
1910
- * @param $title
1911
- *
1912
- * @return mixed
1913
- * @see \Codeception\Lib\InnerBrowser::dontSeeInTitle()
1914
- */
1915
- public function dontSeeInTitle($title) {
1916
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInTitle', func_get_args()));
1917
- }
1918
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/LoginCept.php DELETED
@@ -1,48 +0,0 @@
1
- <?php
2
- /**
3
- * - login
4
- * - navigate to plugins
5
- * - verify landing pages is installed
6
- * - deactivate landing pages
7
- * - activate landing pages
8
- * - confirm welcome page shows
9
- *
10
- */
11
-
12
-
13
- $I = new AcceptanceTester($scenario);
14
-
15
-
16
- $I->wantTo('login to wp-admin');
17
- $I->amOnPage( site_url().'/wp-login.php' );
18
- $I->fillField('Username or Email', 'admin');
19
- $I->fillField('Password','admin');
20
- $I->click('Log In');
21
- $I->see('Dashboard');
22
-
23
-
24
- $I->wantTo('Navigate to plugins');
25
- $I->click( [ 'link' => 'Installed Plugins']);
26
- $I->see('Plugins');
27
- $I->see('Landing Pages');
28
- $I->see('Calls to Action');
29
- $I->see('Leads');
30
-
31
- $I->wantTo('Verify landing pages is installed');
32
-
33
- $I->click( '.active a');
34
- $I->see('Landing Pages');
35
- $I->seePluginActivated('landing-pages');
36
- $I->seePluginActivated('calls-to-action');
37
- $I->seePluginActivated('leads');
38
-
39
- $I->wantTo('Deactivate Landing Pages');
40
- $I->deactivatePlugin( 'landing-pages');
41
- $I->seePluginDeactivated('landing-pages');
42
-
43
- $I->wantTo('Reactivate Landing Pages');
44
- $I->activatePlugin( 'landing-pages');
45
-
46
- $I->wantTo('Confirm welcome page');
47
- $I->see('Welcome to WordPress Landing Pages ');
48
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/StatisticsCept.php DELETED
@@ -1,55 +0,0 @@
1
- <?php
2
- /**
3
- * This test is desnged to test the impressions/conversions systems of landing pages.
4
- * Systems tested:
5
- * [x] Login to WordPress
6
- * [x] Navigate to Landing Pages
7
- * [x] Open example landing page
8
- * [x] Check if impression/conversion UI display on landing page edit screen
9
- * [ ] Reset impressions/conversions and refresh page
10
- * [ ] Make sure stats read 0
11
- * [ ] Open landing page and make sure it does not 404
12
- * [ ] Refresh landing page and make sure variation 2 loads
13
- * [ ] Submit test conversion on variation 2
14
- * [ ] Navigate back to edit page and make sure stats read correctly
15
- */
16
-
17
- $I = new AcceptanceTester($scenario);
18
-
19
- $I->wantTo('login to wp-admin');
20
- $I->amOnPage( site_url().'/wp-login.php' );
21
- $I->fillField('Username', 'admin');
22
- $I->fillField('Password','admin');
23
- $I->click('Log In');
24
- $I->see('Dashboard');
25
-
26
- $I->wantTo('Navigate to landing pages list');
27
- $I->click('Landing Pages');
28
- $I->amOnPage( admin_url( 'edit.php?post_type=landing-page') );
29
- $I->see( 'Landing Pages');
30
-
31
- $I->wantTo('Open example landing page');
32
- $I->click( [ 'link' => 'A/B Testing Landing Page Example']);
33
- $I->wantTo('check if impressions are correct for variation a');
34
- $imp = $I->grabTextFrom('#lp-variation-A .stat-span-impressions');
35
- $I->assertContains( '30' , $imp );
36
-
37
- $I->wantTo('check check impressions for variation b');
38
- $imp = $I->grabTextFrom('#lp-variation-B .stat-span-impressions');
39
- $I->assertContains( '35' , $imp , '' );
40
-
41
- $I->wantTo('check conversions for variation a');
42
- $con = $I->grabTextFrom('#lp-variation-A .stat-span-conversions');
43
- $I->assertContains( '10' , $con , '' );
44
-
45
- $I->wantTo('check conversions for variation b');
46
- $con = $I->grabTextFrom('#lp-variation-B .stat-span-conversions');
47
- $I->assertContains( '15' , $con );
48
-
49
- $I->wantTo('check the conversion rate of variation a');
50
- $per = $I->grabTextFrom('#lp-variation-A .stat-span-conversion_rate');
51
- $I->assertContains( '33' , $per );
52
-
53
- $I->wantTo('check the conversion rate of variation b');
54
- $per = $I->grabTextFrom('#lp-variation-B .stat-span-conversion_rate');
55
- $I->assertContains( '42.86' , $per );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/WelcomeCept.php DELETED
@@ -1,7 +0,0 @@
1
- <?php
2
-
3
- $I = new AcceptanceTester($scenario);
4
- $I->wantTo('Make sure the default WordPress homepage loads.');
5
- $I->amOnPage( site_url() );
6
- $I->see('Hello world!');
7
-
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/_bootstrap.php DELETED
@@ -1,2 +0,0 @@
1
- <?php
2
- // Here you can initialize variables that will be available to your tests
 
 
assets/tests/codeception/functional/FunctionalTester.php DELETED
@@ -1,360 +0,0 @@
1
- <?php //[STAMP] fa259e6277a310881c0cdeb89563ce4c
2
-
3
- // This class was automatically generated by build task
4
- // You should not change it manually as it will be overwritten on next build
5
- // @codingStandardsIgnoreFile
6
-
7
-
8
- use Codeception\Module\Filesystem;
9
- use Codeception\Module\FunctionalHelper;
10
-
11
- /**
12
- * Inherited Methods
13
- * @method void wantToTest($text)
14
- * @method void wantTo($text)
15
- * @method void execute($callable)
16
- * @method void expectTo($prediction)
17
- * @method void expect($prediction)
18
- * @method void amGoingTo($argumentation)
19
- * @method void am($role)
20
- * @method void lookForwardTo($achieveValue)
21
- * @method void comment($description)
22
- * @method void haveFriend($name, $actorClass = null)
23
- *
24
- * @SuppressWarnings(PHPMD)
25
- */
26
- class FunctionalTester extends \Codeception\Actor
27
- {
28
-
29
- /**
30
- * [!] Method is generated. Documentation taken from corresponding module.
31
- *
32
- * Enters a directory In local filesystem.
33
- * Project root directory is used by default
34
- *
35
- * @param $path
36
- * @see \Codeception\Module\Filesystem::amInPath()
37
- */
38
- public function amInPath($path) {
39
- return $this->scenario->runStep(new \Codeception\Step\Condition('amInPath', func_get_args()));
40
- }
41
-
42
-
43
- /**
44
- * [!] Method is generated. Documentation taken from corresponding module.
45
- *
46
- * Opens a file and stores it's content.
47
- *
48
- * Usage:
49
- *
50
- * ``` php
51
- * <?php
52
- * $I->openFile('composer.json');
53
- * $I->seeInThisFile('codeception/codeception');
54
- * ?>
55
- * ```
56
- *
57
- * @param $filename
58
- * @see \Codeception\Module\Filesystem::openFile()
59
- */
60
- public function openFile($filename) {
61
- return $this->scenario->runStep(new \Codeception\Step\Action('openFile', func_get_args()));
62
- }
63
-
64
-
65
- /**
66
- * [!] Method is generated. Documentation taken from corresponding module.
67
- *
68
- * Deletes a file
69
- *
70
- * ``` php
71
- * <?php
72
- * $I->deleteFile('composer.lock');
73
- * ?>
74
- * ```
75
- *
76
- * @param $filename
77
- * @see \Codeception\Module\Filesystem::deleteFile()
78
- */
79
- public function deleteFile($filename) {
80
- return $this->scenario->runStep(new \Codeception\Step\Action('deleteFile', func_get_args()));
81
- }
82
-
83
-
84
- /**
85
- * [!] Method is generated. Documentation taken from corresponding module.
86
- *
87
- * Deletes directory with all subdirectories
88
- *
89
- * ``` php
90
- * <?php
91
- * $I->deleteDir('vendor');
92
- * ?>
93
- * ```
94
- *
95
- * @param $dirname
96
- * @see \Codeception\Module\Filesystem::deleteDir()
97
- */
98
- public function deleteDir($dirname) {
99
- return $this->scenario->runStep(new \Codeception\Step\Action('deleteDir', func_get_args()));
100
- }
101
-
102
-
103
- /**
104
- * [!] Method is generated. Documentation taken from corresponding module.
105
- *
106
- * Copies directory with all contents
107
- *
108
- * ``` php
109
- * <?php
110
- * $I->copyDir('vendor','old_vendor');
111
- * ?>
112
- * ```
113
- *
114
- * @param $src
115
- * @param $dst
116
- * @see \Codeception\Module\Filesystem::copyDir()
117
- */
118
- public function copyDir($src, $dst) {
119
- return $this->scenario->runStep(new \Codeception\Step\Action('copyDir', func_get_args()));
120
- }
121
-
122
-
123
- /**
124
- * [!] Method is generated. Documentation taken from corresponding module.
125
- *
126
- * Checks If opened file has `text` in it.
127
- *
128
- * Usage:
129
- *
130
- * ``` php
131
- * <?php
132
- * $I->openFile('composer.json');
133
- * $I->seeInThisFile('codeception/codeception');
134
- * ?>
135
- * ```
136
- *
137
- * @param $text
138
- * Conditional Assertion: Test won't be stopped on fail
139
- * @see \Codeception\Module\Filesystem::seeInThisFile()
140
- */
141
- public function canSeeInThisFile($text) {
142
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInThisFile', func_get_args()));
143
- }
144
- /**
145
- * [!] Method is generated. Documentation taken from corresponding module.
146
- *
147
- * Checks If opened file has `text` in it.
148
- *
149
- * Usage:
150
- *
151
- * ``` php
152
- * <?php
153
- * $I->openFile('composer.json');
154
- * $I->seeInThisFile('codeception/codeception');
155
- * ?>
156
- * ```
157
- *
158
- * @param $text
159
- * @see \Codeception\Module\Filesystem::seeInThisFile()
160
- */
161
- public function seeInThisFile($text) {
162
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInThisFile', func_get_args()));
163
- }
164
-
165
-
166
- /**
167
- * [!] Method is generated. Documentation taken from corresponding module.
168
- *
169
- * Checks the strict matching of file contents.
170
- * Unlike `seeInThisFile` will fail if file has something more than expected lines.
171
- * Better to use with HEREDOC strings.
172
- * Matching is done after removing "\r" chars from file content.
173
- *
174
- * ``` php
175
- * <?php
176
- * $I->openFile('process.pid');
177
- * $I->seeFileContentsEqual('3192');
178
- * ?>
179
- * ```
180
- *
181
- * @param $text
182
- * Conditional Assertion: Test won't be stopped on fail
183
- * @see \Codeception\Module\Filesystem::seeFileContentsEqual()
184
- */
185
- public function canSeeFileContentsEqual($text) {
186
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileContentsEqual', func_get_args()));
187
- }
188
- /**
189
- * [!] Method is generated. Documentation taken from corresponding module.
190
- *
191
- * Checks the strict matching of file contents.
192
- * Unlike `seeInThisFile` will fail if file has something more than expected lines.
193
- * Better to use with HEREDOC strings.
194
- * Matching is done after removing "\r" chars from file content.
195
- *
196
- * ``` php
197
- * <?php
198
- * $I->openFile('process.pid');
199
- * $I->seeFileContentsEqual('3192');
200
- * ?>
201
- * ```
202
- *
203
- * @param $text
204
- * @see \Codeception\Module\Filesystem::seeFileContentsEqual()
205
- */
206
- public function seeFileContentsEqual($text) {
207
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileContentsEqual', func_get_args()));
208
- }
209
-
210
-
211
- /**
212
- * [!] Method is generated. Documentation taken from corresponding module.
213
- *
214
- * Checks If opened file doesn't contain `text` in it
215
- *
216
- * ``` php
217
- * <?php
218
- * $I->openFile('composer.json');
219
- * $I->dontSeeInThisFile('codeception/codeception');
220
- * ?>
221
- * ```
222
- *
223
- * @param $text
224
- * Conditional Assertion: Test won't be stopped on fail
225
- * @see \Codeception\Module\Filesystem::dontSeeInThisFile()
226
- */
227
- public function cantSeeInThisFile($text) {
228
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInThisFile', func_get_args()));
229
- }
230
- /**
231
- * [!] Method is generated. Documentation taken from corresponding module.
232
- *
233
- * Checks If opened file doesn't contain `text` in it
234
- *
235
- * ``` php
236
- * <?php
237
- * $I->openFile('composer.json');
238
- * $I->dontSeeInThisFile('codeception/codeception');
239
- * ?>
240
- * ```
241
- *
242
- * @param $text
243
- * @see \Codeception\Module\Filesystem::dontSeeInThisFile()
244
- */
245
- public function dontSeeInThisFile($text) {
246
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInThisFile', func_get_args()));
247
- }
248
-
249
-
250
- /**
251
- * [!] Method is generated. Documentation taken from corresponding module.
252
- *
253
- * Deletes a file
254
- * @see \Codeception\Module\Filesystem::deleteThisFile()
255
- */
256
- public function deleteThisFile() {
257
- return $this->scenario->runStep(new \Codeception\Step\Action('deleteThisFile', func_get_args()));
258
- }
259
-
260
-
261
- /**
262
- * [!] Method is generated. Documentation taken from corresponding module.
263
- *
264
- * Checks if file exists in path.
265
- * Opens a file when it's exists
266
- *
267
- * ``` php
268
- * <?php
269
- * $I->seeFileFound('UserModel.php','app/models');
270
- * ?>
271
- * ```
272
- *
273
- * @param $filename
274
- * @param string $path
275
- * Conditional Assertion: Test won't be stopped on fail
276
- * @see \Codeception\Module\Filesystem::seeFileFound()
277
- */
278
- public function canSeeFileFound($filename, $path = null) {
279
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeFileFound', func_get_args()));
280
- }
281
- /**
282
- * [!] Method is generated. Documentation taken from corresponding module.
283
- *
284
- * Checks if file exists in path.
285
- * Opens a file when it's exists
286
- *
287
- * ``` php
288
- * <?php
289
- * $I->seeFileFound('UserModel.php','app/models');
290
- * ?>
291
- * ```
292
- *
293
- * @param $filename
294
- * @param string $path
295
- * @see \Codeception\Module\Filesystem::seeFileFound()
296
- */
297
- public function seeFileFound($filename, $path = null) {
298
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeFileFound', func_get_args()));
299
- }
300
-
301
-
302
- /**
303
- * [!] Method is generated. Documentation taken from corresponding module.
304
- *
305
- * Checks if file does not exists in path
306
- *
307
- * @param $filename
308
- * @param string $path
309
- * Conditional Assertion: Test won't be stopped on fail
310
- * @see \Codeception\Module\Filesystem::dontSeeFileFound()
311
- */
312
- public function cantSeeFileFound($filename, $path = null) {
313
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeFileFound', func_get_args()));
314
- }
315
- /**
316
- * [!] Method is generated. Documentation taken from corresponding module.
317
- *
318
- * Checks if file does not exists in path
319
- *
320
- * @param $filename
321
- * @param string $path
322
- * @see \Codeception\Module\Filesystem::dontSeeFileFound()
323
- */
324
- public function dontSeeFileFound($filename, $path = null) {
325
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeFileFound', func_get_args()));
326
- }
327
-
328
-
329
- /**
330
- * [!] Method is generated. Documentation taken from corresponding module.
331
- *
332
- * Erases directory contents
333
- *
334
- * ``` php
335
- * <?php
336
- * $I->cleanDir('logs');
337
- * ?>
338
- * ```
339
- *
340
- * @param $dirname
341
- * @see \Codeception\Module\Filesystem::cleanDir()
342
- */
343
- public function cleanDir($dirname) {
344
- return $this->scenario->runStep(new \Codeception\Step\Action('cleanDir', func_get_args()));
345
- }
346
-
347
-
348
- /**
349
- * [!] Method is generated. Documentation taken from corresponding module.
350
- *
351
- * Saves contents to file
352
- *
353
- * @param $filename
354
- * @param $contents
355
- * @see \Codeception\Module\Filesystem::writeToFile()
356
- */
357
- public function writeToFile($filename, $contents) {
358
- return $this->scenario->runStep(new \Codeception\Step\Action('writeToFile', func_get_args()));
359
- }
360
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/functional/_bootstrap.php DELETED
@@ -1,2 +0,0 @@
1
- <?php
2
- // Here you can initialize variables that will be available to your tests
 
 
assets/tests/codeception/unit/UnitTester.php DELETED
@@ -1,300 +0,0 @@
1
- <?php //[STAMP] c8568e8eab4a240544c36ae031f88bd8
2
-
3
- // This class was automatically generated by build task
4
- // You should not change it manually as it will be overwritten on next build
5
- // @codingStandardsIgnoreFile
6
-
7
-
8
- use Codeception\Module\Asserts;
9
- use Codeception\Module\UnitHelper;
10
-
11
- /**
12
- * Inherited Methods
13
- * @method void wantToTest($text)
14
- * @method void wantTo($text)
15
- * @method void execute($callable)
16
- * @method void expectTo($prediction)
17
- * @method void expect($prediction)
18
- * @method void amGoingTo($argumentation)
19
- * @method void am($role)
20
- * @method void lookForwardTo($achieveValue)
21
- * @method void comment($description)
22
- * @method void haveFriend($name, $actorClass = null)
23
- *
24
- * @SuppressWarnings(PHPMD)
25
- */
26
- class UnitTester extends \Codeception\Actor
27
- {
28
-
29
- /**
30
- * [!] Method is generated. Documentation taken from corresponding module.
31
- *
32
- * Checks that two variables are equal.
33
- *
34
- * @param $expected
35
- * @param $actual
36
- * @param string $message
37
- *
38
- * @return mixed
39
- * @see \Codeception\Module\Asserts::assertEquals()
40
- */
41
- public function assertEquals($expected, $actual, $message = null) {
42
- return $this->scenario->runStep(new \Codeception\Step\Action('assertEquals', func_get_args()));
43
- }
44
-
45
-
46
- /**
47
- * [!] Method is generated. Documentation taken from corresponding module.
48
- *
49
- * Checks that two variables are not equal
50
- *
51
- * @param $expected
52
- * @param $actual
53
- * @param string $message
54
- * @see \Codeception\Module\Asserts::assertNotEquals()
55
- */
56
- public function assertNotEquals($expected, $actual, $message = null) {
57
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args()));
58
- }
59
-
60
-
61
- /**
62
- * [!] Method is generated. Documentation taken from corresponding module.
63
- *
64
- * Checks that two variables are same
65
- *
66
- * @param $expected
67
- * @param $actual
68
- * @param string $message
69
- *
70
- * @return mixed
71
- * @see \Codeception\Module\Asserts::assertSame()
72
- */
73
- public function assertSame($expected, $actual, $message = null) {
74
- return $this->scenario->runStep(new \Codeception\Step\Action('assertSame', func_get_args()));
75
- }
76
-
77
-
78
- /**
79
- * [!] Method is generated. Documentation taken from corresponding module.
80
- *
81
- * Checks that two variables are not same
82
- *
83
- * @param $expected
84
- * @param $actual
85
- * @param string $message
86
- * @see \Codeception\Module\Asserts::assertNotSame()
87
- */
88
- public function assertNotSame($expected, $actual, $message = null) {
89
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNotSame', func_get_args()));
90
- }
91
-
92
-
93
- /**
94
- * [!] Method is generated. Documentation taken from corresponding module.
95
- *
96
- * Checks that expected is greater than actual
97
- *
98
- * @param $expected
99
- * @param $actual
100
- * @param string $message
101
- * @see \Codeception\Module\Asserts::assertGreaterThan()
102
- */
103
- public function assertGreaterThan($expected, $actual, $message = null) {
104
- return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThan', func_get_args()));
105
- }
106
-
107
-
108
- /**
109
- * [!] Method is generated. Documentation taken from corresponding module.
110
- *
111
- * @deprecated
112
- * @see \Codeception\Module\Asserts::assertGreaterThen()
113
- */
114
- public function assertGreaterThen($expected, $actual, $message = null) {
115
- return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThen', func_get_args()));
116
- }
117
-
118
-
119
- /**
120
- * [!] Method is generated. Documentation taken from corresponding module.
121
- *
122
- * Checks that expected is greater or equal than actual
123
- *
124
- * @param $expected
125
- * @param $actual
126
- * @param string $message
127
- * @see \Codeception\Module\Asserts::assertGreaterThanOrEqual()
128
- */
129
- public function assertGreaterThanOrEqual($expected, $actual, $message = null) {
130
- return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThanOrEqual', func_get_args()));
131
- }
132
-
133
-
134
- /**
135
- * [!] Method is generated. Documentation taken from corresponding module.
136
- *
137
- * @deprecated
138
- * @see \Codeception\Module\Asserts::assertGreaterThenOrEqual()
139
- */
140
- public function assertGreaterThenOrEqual($expected, $actual, $message = null) {
141
- return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThenOrEqual', func_get_args()));
142
- }
143
-
144
-
145
- /**
146
- * [!] Method is generated. Documentation taken from corresponding module.
147
- *
148
- * Checks that expected is less than actual
149
- *
150
- * @param $expected
151
- * @param $actual
152
- * @param string $message
153
- * @see \Codeception\Module\Asserts::assertLessThan()
154
- */
155
- public function assertLessThan($expected, $actual, $message = null) {
156
- return $this->scenario->runStep(new \Codeception\Step\Action('assertLessThan', func_get_args()));
157
- }
158
-
159
-
160
- /**
161
- * [!] Method is generated. Documentation taken from corresponding module.
162
- *
163
- * Checks that expected is less or equal than actual
164
- *
165
- * @param $expected
166
- * @param $actual
167
- * @param string $message
168
- * @see \Codeception\Module\Asserts::assertLessThanOrEqual()
169
- */
170
- public function assertLessThanOrEqual($expected, $actual, $message = null) {
171
- return $this->scenario->runStep(new \Codeception\Step\Action('assertLessThanOrEqual', func_get_args()));
172
- }
173
-
174
-
175
- /**
176
- * [!] Method is generated. Documentation taken from corresponding module.
177
- *
178
- * Checks that haystack contains needle
179
- *
180
- * @param $needle
181
- * @param $haystack
182
- * @param string $message
183
- * @see \Codeception\Module\Asserts::assertContains()
184
- */
185
- public function assertContains($needle, $haystack, $message = null) {
186
- return $this->scenario->runStep(new \Codeception\Step\Action('assertContains', func_get_args()));
187
- }
188
-
189
-
190
- /**
191
- * [!] Method is generated. Documentation taken from corresponding module.
192
- *
193
- * Checks that haystack doesn't contain needle.
194
- *
195
- * @param $needle
196
- * @param $haystack
197
- * @param string $message
198
- * @see \Codeception\Module\Asserts::assertNotContains()
199
- */
200
- public function assertNotContains($needle, $haystack, $message = null) {
201
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args()));
202
- }
203
-
204
-
205
- /**
206
- * [!] Method is generated. Documentation taken from corresponding module.
207
- *
208
- * Checks that variable is empty.
209
- *
210
- * @param $actual
211
- * @param string $message
212
- * @see \Codeception\Module\Asserts::assertEmpty()
213
- */
214
- public function assertEmpty($actual, $message = null) {
215
- return $this->scenario->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args()));
216
- }
217
-
218
-
219
- /**
220
- * [!] Method is generated. Documentation taken from corresponding module.
221
- *
222
- * Checks that variable is not empty.
223
- *
224
- * @param $actual
225
- * @param string $message
226
- * @see \Codeception\Module\Asserts::assertNotEmpty()
227
- */
228
- public function assertNotEmpty($actual, $message = null) {
229
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args()));
230
- }
231
-
232
-
233
- /**
234
- * [!] Method is generated. Documentation taken from corresponding module.
235
- *
236
- * Checks that variable is NULL
237
- *
238
- * @param $actual
239
- * @param string $message
240
- * @see \Codeception\Module\Asserts::assertNull()
241
- */
242
- public function assertNull($actual, $message = null) {
243
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNull', func_get_args()));
244
- }
245
-
246
-
247
- /**
248
- * [!] Method is generated. Documentation taken from corresponding module.
249
- *
250
- * Checks that variable is not NULL
251
- *
252
- * @param $actual
253
- * @param string $message
254
- * @see \Codeception\Module\Asserts::assertNotNull()
255
- */
256
- public function assertNotNull($actual, $message = null) {
257
- return $this->scenario->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args()));
258
- }
259
-
260
-
261
- /**
262
- * [!] Method is generated. Documentation taken from corresponding module.
263
- *
264
- * Checks that condition is positive.
265
- *
266
- * @param $condition
267
- * @param string $message
268
- * @see \Codeception\Module\Asserts::assertTrue()
269
- */
270
- public function assertTrue($condition, $message = null) {
271
- return $this->scenario->runStep(new \Codeception\Step\Action('assertTrue', func_get_args()));
272
- }
273
-
274
-
275
- /**
276
- * [!] Method is generated. Documentation taken from corresponding module.
277
- *
278
- * Checks that condition is negative.
279
- *
280
- * @param $condition
281
- * @param string $message
282
- * @see \Codeception\Module\Asserts::assertFalse()
283
- */
284
- public function assertFalse($condition, $message = null) {
285
- return $this->scenario->runStep(new \Codeception\Step\Action('assertFalse', func_get_args()));
286
- }
287
-
288
-
289
- /**
290
- * [!] Method is generated. Documentation taken from corresponding module.
291
- *
292
- * Fails the test with message.
293
- *
294
- * @param $message
295
- * @see \Codeception\Module\Asserts::fail()
296
- */
297
- public function fail($message) {
298
- return $this->scenario->runStep(new \Codeception\Step\Action('fail', func_get_args()));
299
- }
300
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/unit/_bootstrap.php DELETED
@@ -1,2 +0,0 @@
1
- <?php
2
- // Here you can initialize variables that will be available to your tests
 
 
assets/tests/phpunit/bootstrap.php DELETED
@@ -1,15 +0,0 @@
1
- <?php
2
- /**
3
- * Bootstrap the plugin unit testing environment.
4
- *
5
- * @package wordpress-plugin-tests
6
- */
7
-
8
- /* load wp environemnt */
9
- require '../../../wp-load.php';
10
-
11
- /* load plugins */
12
- require '../../../wp-admin/includes/plugin.php';
13
-
14
-
15
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/phpunit/test.activations.php DELETED
@@ -1,49 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Tests to test that that testing framework is testing tests. Meta, huh?
5
- *
6
- * @package wordpress-plugins-tests
7
- */
8
- class Tests_Activation extends PHPUnit_Framework_TestCase {
9
-
10
- /**
11
- * Run a simple test to ensure that the tests are running
12
- */
13
- function test_tests() {
14
- $this->assertTrue( true );
15
- }
16
-
17
- /**
18
- * Ensure landing pages is active
19
- *
20
- function test_lading_pages_activated() {
21
- $this->assertTrue( is_plugin_active( 'landing-pages/landing-pages.php' ) );
22
- }
23
-
24
- /**
25
- * Ensure that the Leads has been installed and activated.
26
- *
27
- function test_leads_activated() {
28
- $this->assertTrue( is_plugin_active( 'leads/leads.php' ) );
29
- }
30
-
31
- /**
32
- * Ensure that the Calls to Action has been installed and activated.
33
- *
34
- function test_cta_activated() {
35
- $this->assertTrue( is_plugin_active( 'cta/calls-to-action.php' ) );
36
- }
37
-
38
- /**
39
- * Run upgrade routines and check option to see if complete
40
- *
41
- function test_run_upgrade_routines() {
42
- include_once LANDINGPAGES_PATH . 'classes/class.activation.php';
43
- include_once LANDINGPAGES_PATH . 'classes/class.activation.upgrade-routines.php';
44
- Landing_Pages_Activation::run_updates();
45
- $this->assertTrue( true );
46
- }*/
47
-
48
- }
49
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/travis-ci/test.statistics.php DELETED
@@ -1,68 +0,0 @@
1
- <?php
2
-
3
- /**
4
- * Tests to test that that testing framework is testing tests. Meta, huh?
5
- *
6
- * @package wordpress-plugins-tests
7
- */
8
- class Tests_Statistics extends WP_UnitTestCase {
9
-
10
- /**
11
- * Test to see if get_post works.
12
- *
13
- * Compares a post ID ($org_post_id) with post ID
14
- * taken out of get_post ($new_post_id).
15
- * If they don't match, get_post() doesn't work, and it will
16
- * return an error.
17
- */
18
- function test_get_post() {
19
- //Create new post using method provided by WP
20
- $org_post_id = $this->factory->post->create();
21
-
22
- //get post object using the new post's ID
23
- $post_obj = get_post( $org_post_id );
24
-
25
- //Get the post ID as given to us by get_post
26
- $new_post_id = $post_obj->ID;
27
-
28
- //Use pre-defined method to test if the two ID's match
29
- $this->assertEquals( $org_post_id, $new_post_id );
30
-
31
- }
32
-
33
- /**
34
- * creates a dummy landing page for testing
35
- */
36
- function test_create_demo_lander() {
37
- /* load the class used to create the dummy landing page */
38
- include_once LANDINGPAGES_PATH . 'modules/module.install.php';
39
- $lp_id = inbound_create_default_post_type();
40
- $this->assertEquals( $lp_id , 4 );
41
- echo 1;
42
- }
43
-
44
-
45
-
46
- /**
47
- * Check if landing-page post type exists
48
- */
49
- function test_check_if_landing_page_post_type_exist() {
50
- $this->assertTrue( post_type_exists( 'landing-page' ) );
51
- }
52
-
53
-
54
-
55
- /**
56
- * Set landing page stats to zero for testing
57
- */
58
- function test_reset_landing_page_stats() {
59
- echo 2;
60
- print_r( get_option( 'lp_settings_general' ) );
61
- $landing_page = get_post( 4 );
62
- var_dump($landing_page);
63
-
64
- }
65
-
66
- }
67
-
68
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class.acf-integration.php CHANGED
@@ -1,705 +1,705 @@
1
  <?php
2
 
3
- if (!class_exists('Landing_Pages_ACF')) {
 
 
 
4
 
5
- class Landing_Pages_ACF {
6
-
7
- /**
8
- * Name under which the transient for the current tab will be saved.
9
- * @var string
10
- */
11
- static $_transient_name = 'acf_current_tab';
12
- /**
13
- * Number of minutes the transient will be saved.
14
- * @var int
15
- */
16
- static $_transient_minutes = 5;
17
-
18
 
19
- /**
20
- * Initialize Landing_Pages_ACF Class
21
- */
22
- public function __construct() {
23
- self::load_hooks();
24
- }
 
 
 
 
25
 
26
 
27
- /**
28
- * Load Hooks & Filters
29
- */
30
- public static function load_hooks() {
 
 
31
 
32
- /* Load ACF Fields On ACF powered Email Template */
33
- add_filter( 'acf/location/rule_match/template_id' , array( __CLASS__ , 'load_acf_on_template' ) , 10 , 3 );
34
 
35
- /* make sure fields are placed in the correct location */
36
- add_action( 'save_post', array( __CLASS__ , 'save_acf_fields' ) );
 
 
37
 
38
- /* make sure fields are placed in the correct location */
39
- add_action( 'wp_restore_post_revision', array( __CLASS__ , 'restore_acf_values' ) , 10 , 2 );
40
 
41
- /* Adds revision fields to the revisions screen */
 
42
 
43
- /* Adds revision fields value for Inbound Settings to the revisions screen */
44
- add_filter( '_wp_post_revision_fields', array( __CLASS__ , 'add_revision_fields' ) );
45
- add_filter( '_wp_post_revision_field_inbound_settings', array( __CLASS__ , 'add_revision_field_values' ) , 10 , 3 );
46
 
47
- /* Intercept load custom field value request and hijack it */
48
- add_filter( 'acf/load_value' , array( __CLASS__ , 'load_value' ) , 11 , 3 );
49
 
50
- /* extra field formatting
51
- add_filter( 'acf/format_value' , array( __CLASS__ , 'format_value' ) , 11 , 3 ); */
 
52
 
53
- /* make sure fields are placed in the correct location */
54
- add_action( 'admin_print_footer_scripts', array( __CLASS__ , 'reposition_acf_fields' ) );
55
 
56
- /* add new location rule to ACF Field UI */
57
- add_filter('acf/location/rule_types', array( __CLASS__ , 'define_location_rule_types' ) );
58
 
59
- /* add new location rule values to ACF Field UI */
60
- add_filter('acf/location/rule_values/template_id', array( __CLASS__ , 'define_location_rule_values' ) );
61
-
62
- add_action( 'acf/input/admin_footer', array( __CLASS__ , 'handle_tab' ) );
63
- add_action( 'wp_ajax_acf_save_current_tab', array( __CLASS__ , 'ajax_acf_save_current_tab' ) );
64
-
65
- }
66
 
67
- /**
68
- * @param $choices
69
- * @return mixed
70
- */
71
- public static function define_location_rule_types( $choices ) {
72
 
73
- if (!isset($choices['Basic']['template_id'])) {
74
- $choices['Basic']['template_id'] = __('Template ID', 'landing-page');
75
- }
76
 
77
- return $choices;
78
- }
79
 
80
- public static function define_location_rule_values( $choices ) {
81
- $template_ids = Landing_Pages_Load_Extensions::get_uploaded_template_ids();
82
 
83
- if (!isset($choices['default'])) {
84
- $choices[ 'default' ] = 'default';
85
- }
 
 
86
 
87
- if( $template_ids ) {
88
- foreach( $template_ids as $template_id ) {
 
89
 
90
- /* template ID by template name here */
91
- $choices[ $template_id ] = $template_id;
92
 
93
- }
94
- }
95
 
96
- return $choices;
 
97
  }
98
 
99
- /**
100
- * Adds javascript to make sure ACF fields load inside template container
101
- */
102
- public static function reposition_acf_fields() {
103
- global $post;
104
 
105
- if ( !defined('ACF_FREE') || ( !isset($post) || $post->post_type != 'landing-page' ) ) {
106
- return;
107
- }
108
 
109
- ?>
110
- <script type='text/javascript'>
111
- jQuery('.acf_postbox').each(function(){
112
- jQuery('#template-display-options').append(jQuery(this));
113
- });
114
- </script>
115
- <?php
116
  }
117
 
 
 
118
 
119
- /**
120
- * Compiles ACF Meta Data into a singular json pair for variation support
121
- * @param $landing_page_id
122
- */
123
- public static function save_acf_fields( $landing_page_id ) {
124
-
125
 
126
- if ( !isset($_POST['post_type']) || $_POST['post_type'] != 'landing-page' ) {
127
- return;
128
- }
129
 
130
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
131
- return;
132
- }
 
 
 
 
 
133
 
134
- /* save acf settings - uses our future data array - eventually we will migrate all post meta into this data object */
135
- $fields = (isset($_POST['fields'])) ? $_POST['fields'] : null;
136
- $fields = (isset($_POST['acf'])) ? $_POST['acf'] : $fields;
137
 
138
- if ( $fields ) {
 
 
 
 
139
 
140
- $settings = Landing_Pages_Meta::get_settings( $landing_page_id );
141
- $variation_id = (isset($_REQUEST['lp-variation-id'])) ? intval($_REQUEST['lp-variation-id']) : '0';
142
 
143
- if (!isset($settings['variations'])) {
144
- $settings['variations'] = array();
145
- }
146
 
147
- $settings['variations'][$variation_id]['acf'] = $fields;
148
- Landing_Pages_Meta::update_settings( $landing_page_id , $settings );
149
- }
150
  }
151
 
152
- /**
153
- * Restore landing page ACF values from revision
154
- * @param $post_id
155
- * @param $revision_id
156
- */
157
- public static function restore_acf_values( $post_id , $revision_id ) {
158
 
159
- $post = get_post($post_id);
160
- if($post->post_type!='landing-page' ) {
161
- return;
162
- }
163
 
164
- $revision_settings = Landing_Pages_Meta::get_settings( $revision_id );
 
165
 
166
- Landing_Pages_Meta::update_settings( $post_id , $revision_settings );
 
 
167
 
 
 
168
  }
 
 
 
 
 
 
 
 
169
 
170
- /**
171
- * Adds revision fields to revisions screen
172
- * @param $fields
173
- * @return mixed
174
- */
175
- public static function add_revision_fields( $fields ) {
176
- global $post;
177
- $fields['inbound_settings'] = __('Landing Page Settings' , 'inbound-pro');
178
- return $fields;
179
  }
180
 
181
- public static function add_revision_field_values( $value , $field , $revision) {
182
 
183
- if (!isset($revision->ID)) {
184
- return $value;
185
- }
186
 
187
- $settings = Landing_Pages_Meta::get_settings( $revision->ID );
188
 
189
- return json_encode($settings);
190
- }
 
 
 
 
 
 
 
 
191
 
192
- /**
193
- * Although unused at the moment, this method can be used for filtering the return value with ACF5 fields
194
- * @param $value
195
- * @param $post_id
196
- * @param $field
197
- * @return mixed
198
- */
199
- public static function format_value( $value, $post_id, $field ) {
200
  return $value;
201
  }
202
 
203
- /**
204
- * Finds the correct value given the variation
205
- *
206
- * @param MIXED $value contains the non-variation value
207
- * @param INT $post_id ID of landing page being loaded
208
- * @param ARRAY $field wide array of data belonging to custom field (not leveraged in this method)
209
- *
210
- * @returns MIXED $new_value value mapped to variation.
211
- */
212
- public static function load_value( $value, $post_id, $field ) {
213
- global $post;
214
 
215
- if ( !isset($post) || $post->post_type != 'landing-page' ) {
216
- return $value;
217
- }
218
 
219
- $vid = Landing_Pages_Variations::get_new_variation_reference_id( $post->ID );
 
 
 
 
 
 
 
 
 
220
 
221
- $settings = Landing_Pages_Meta::get_settings( $post->ID );
 
 
 
 
 
 
 
 
 
 
222
 
223
- $variations = ( isset($settings['variations']) ) ? $settings['variations'] : null;
 
 
224
 
225
- /* If there is no ACF data for this template attempt to pull values from the legacy postmeta values */
226
 
227
- if ( !isset( $variations[ $vid ][ 'acf' ] ) || !$variations[ $vid ][ 'acf' ]) {
228
- return self::load_legacy_value( $value, $post_id, $field );
229
- }
230
 
 
231
 
232
- if ( isset( $variations[ $vid ][ 'acf' ] ) ) {
233
- $new_value = self::search_field_array( $variations[ $vid ][ 'acf' ] , $field );
234
 
235
- /* sometimes value is an array count when new_value believes it should be an array in this case get new count */
236
- if (!is_array($value) && is_array($new_value)) {
237
- $value = count($new_value);
238
- } else if($new_value) {
239
- if ($new_value =='_empty') {
240
- $new_value = '';
241
- }
242
- $value = $new_value;
243
- }
244
 
245
- /* acf lite isn't processing return values correctly - ignore repeater subfields */
246
- if ( !is_admin() && defined('ACF_FREE') ) {
247
- $value = self::acf_free_value_formatting( $value , $field );
248
 
249
- }
 
250
 
251
- if ( !is_admin() && is_string($value) && !defined('INBOUND_DEBUG_GF_AJAX') ) {
252
- $value = do_shortcode($value);
 
 
 
 
253
  }
 
 
254
 
255
- /* handle non acf5 template return formatting */
256
- if (defined('ACF_PRO')) {
257
- $value = self::acf_check_if_acf4( $value , $field );
258
- }
259
 
260
  }
261
 
262
- return $value;
 
 
263
 
264
- }
265
- /**
266
- * Finds the correct value given the variation - uses legacy meta system
267
- *
268
- * @param MIXED $value contains the non-variation value
269
- * @param INT $post_id ID of landing page being loaded
270
- * @param ARRAY $field wide array of data belonging to custom field (not leveraged in this method)
271
- *
272
- * @returns MIXED $new_value value mapped to variation.
273
- */
274
- public static function load_legacy_value( $value, $post_id, $field ) {
275
- global $post;
276
-
277
- /* get registered field object data */
278
- $field = self::acf_get_registered_field( $field );
279
-
280
- /* if a brand new post ignore return default value */
281
- if ( $post->post_status != 'publish' ) {
282
- return ( isset($field['default_value']) ) ? do_shortcode($field['default_value']) : '' ;
283
  }
284
 
285
- $vid = Landing_Pages_Variations::get_new_variation_reference_id( $post->ID );
286
 
287
- if ( $vid ) {
288
- $value = get_post_meta( $post_id , $field['name'] . '-' . $vid , true );
289
- } else {
290
- $value = get_post_meta( $post_id , $field['name'] , true );
291
- }
292
 
 
 
 
 
 
 
 
 
 
 
 
 
293
 
294
- if ($field['type']=='image' && is_admin() ) {
295
- $value = self::get_image_id_from_url( $value );
296
- }
297
 
298
- if ($field['type']=='date_picker') {
299
- $value = str_replace('-' , '', $value);
300
- $value = explode(' ' , $value);
301
- $value = $value[0];
302
- }
303
 
304
- if ($field['type']=='color_picker') {
305
- if (!strstr( $value , '#' ) && $value ) {
306
- $value = '#'.$value;
307
- }
308
- }
309
 
310
- if (!is_array($value) && !is_admin() ) {
311
- $value = do_shortcode($value);
312
- }
 
 
313
 
314
- return $value;
315
 
 
 
316
  }
317
 
 
 
 
 
 
318
 
319
- /**
320
- * Searches ACF variation array and returns the correct field value given the field key
321
- *
322
- * @param ARRAY $array of custom field keys and values stored for variation
323
- * @param STRING $needle acf form field key
324
- *
325
- * @return $feild value
326
- */
327
- public static function search_field_array( $array , $field ) {
328
 
329
- $needle = $field['key'];
 
 
330
 
331
- foreach ($array as $key => $value ){
332
 
 
333
 
334
- if ($key === $needle && !is_array($value) ) {
335
- $value = ($value) ? $value : '_empty' ;
336
- return $value;
337
- }
338
 
339
- /* Arrays could be repeaters or any custom field with sets of multiple values */
340
- if ( is_array($value) ) {
 
 
 
 
 
 
 
341
 
342
- /* Check if this array contains a repeater field layouts. If it does then return layouts, else this array is a non-repeater value set so return it */
343
- if ( $key === $needle ) {
344
 
345
- $repeater_array = self::get_repeater_layouts( $value );
346
- if ($repeater_array) {
347
- return $repeater_array;
348
- } else {
349
- return $value;
350
- }
351
 
352
- }
353
 
354
- /* Check if array is repeater fields and determine correct value given a parsed field name with field key */
355
- $repeater_value = self::get_repeater_values( $value , $field , $needle );
 
 
 
 
 
 
 
 
356
 
357
- /* If target key is not in these repeater fields, or this array is not determined to be a repeater field then move on. */
358
- if ($repeater_value) {
359
- return $repeater_value;
 
 
360
  }
361
 
 
362
 
 
 
 
 
 
 
363
  }
364
 
 
365
  }
366
 
367
- return false;
368
  }
369
 
370
- /**
371
- * Searches an array assumed to be a repeater field dataset and returns an array of repeater field layout definitions
372
- *
373
- * @retuns ARRAY $fields this array will either be empty of contain repeater field layout definitions.
374
- */
375
- public static function get_repeater_layouts( $array ) {
 
 
 
376
 
377
- $fields = array();
378
 
379
- foreach ($array as $key => $value) {
380
- if ( isset( $value['acf_fc_layout'] ) ) {
381
- $fields[] = $value['acf_fc_layout'];
382
- }
383
  }
384
-
385
- return $fields;
386
  }
387
 
 
 
 
388
 
389
- /**
390
- * Searches an array assumed to be a repeater field dataset and returns an array of repeater field layout definitions
391
- *
392
- * @retuns ARRAY $fields this array will either be empty of contain repeater field layout definitions.
393
- */
394
- public static function get_repeater_values( $array , $field , $needle ) {
395
 
396
- /* Discover correct repeater pointer by parsing field name */
397
- preg_match('/(_\d_)/', $field['name'], $matches, 0);
398
 
399
- /* if not a repeater subfield then bail */
400
- if (!$matches) {
401
- return false;
402
- }
403
 
404
- $pointer = str_replace('_' , '' , $matches[0]);
405
- $repeater_key = self::key_search($array, $field , true ); /* returns parent flexible content field key using sub field key */
406
 
407
 
408
- /* */
409
- if ( $repeater_key && $repeater_key !== '0' && isset($array[$repeater_key][$pointer][$field['key']])){
410
- return $array[$repeater_key][$pointer][$field['key']];
411
- }
412
 
413
- /* repeater field comes after the pointer???? */
414
- if (isset($array[$pointer][$needle])){
415
- return $array[$pointer][$needle];
416
- }
417
 
418
 
419
 
420
- return '';
421
 
422
- }
423
 
424
- /**
425
- * Check if current post is a landing page using an ACF powered template
426
- *
427
- * @filter acf/location/rule_match/template_id
428
- *
429
- * @returns BOOL declaring if current page is a landing page with an ACF template loaded or not
430
- */
431
- public static function load_acf_on_template( $allow , $rule, $args ) {
432
- global $post;
433
 
434
- if ( !isset($post) || $post->post_type != 'landing-page' ) {
435
- return $allow;
436
- }
437
 
438
- $template = Landing_Pages_Variations::get_current_template( $args['post_id'] );
439
 
440
- if ($template == $rule['value']) {
441
- return true;
442
- } else {
443
- return false;
444
- }
445
  }
 
446
 
447
- /**
448
- *
449
- * @param $image_url
450
- * @return mixed
451
- */
452
- public static function get_image_id_from_url($url) {
453
- $dir = wp_upload_dir();
454
 
455
- // baseurl never has a trailing slash
456
- if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
457
- // URL points to a place outside of upload directory
458
- return false;
459
- }
460
 
461
- $file = basename( $url );
462
- $query = array(
463
- 'post_type' => 'attachment',
464
- 'fields' => 'ids',
465
- 'meta_query' => array(
466
- array(
467
- 'value' => $file,
468
- 'compare' => 'LIKE',
469
- ),
470
- )
471
- );
472
 
473
- $query['meta_query'][0]['key'] = '_wp_attached_file';
474
 
475
- // query attachments
476
- $ids = get_posts( $query );
477
 
478
- if ( ! empty( $ids ) ) {
479
 
480
- foreach ( $ids as $id ) {
481
 
482
- // first entry of returned array is the URL
483
- if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
484
- return $id;
485
- }
486
  }
 
487
 
488
- $query['meta_query'][0]['key'] = '_wp_attachment_metadata';
489
 
490
- // query attachments again
491
- $ids = get_posts( $query );
492
 
493
- if ( empty( $ids) )
494
- return false;
495
 
496
- foreach ( $ids as $id ) {
497
 
498
- $meta = wp_get_attachment_metadata( $id );
499
 
500
- foreach ( $meta['sizes'] as $size => $values ) {
501
 
502
- if ( $values['file'] === $file && $url === array_shift( wp_get_attachment_image_src( $id, $size ) ) )
503
- return $id;
504
- }
505
  }
506
-
507
- return false;
508
  }
509
 
510
- public static function acf_get_registered_field( $field ) {
511
- global $acf_register_field_group;
512
 
513
- if (!$acf_register_field_group) {
514
- return $field;
515
- }
516
 
517
- foreach ($acf_register_field_group as $key => $group) {
518
- foreach ( $group['fields'] as $this_field ) {
519
- if ( $this_field['name'] == $field['name'] ){
520
- return $this_field;
521
- }
 
 
 
522
  }
523
  }
524
  }
 
525
 
526
 
527
- /**
528
- * Correct return value formatting when Pro is NOT installed
529
- */
530
- public static function acf_free_value_formatting( $value , $field ) {
531
 
532
- if ($field['type'] == 'image' && $field['return_format'] == 'url' && !strstr($value , 'http' ) ) {
533
- $image_array = wp_get_attachment_image_src( $value , 'full' );
534
- return $image_array[0];
535
- }
536
 
537
- if ($field['type'] == 'file' && $field['return_format'] == 'url' && !strstr($value , 'http' ) ) {
538
- return wp_get_attachment_url( $value );
539
- }
540
 
541
- if ($field['type'] == 'wysiwyg') {
542
- $vaue = wpautop($value);
543
- $vaue = do_shortcode($value);
544
- }
 
 
 
545
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
546
  return $value;
547
  }
 
548
 
549
- /**
550
- * checks template data type
551
- * @param $value
552
- * @param $field
553
- * @return mixed
554
- */
555
- public static function acf_check_if_acf4( $value , $field ) {
556
- global $key, $lp_data;
557
 
558
- if (!isset($lp_data[$key])) {
559
- return $value;
560
- }
 
 
 
 
 
 
 
561
 
562
- if ( $lp_data[$key]['info']['data_type'] == 'acf4' ) {
563
- return self::acf_free_value_formatting($value , $field);
 
564
  } else {
565
- return $value;
 
 
 
566
  }
567
- }
568
-
569
-
570
- /**
571
- * This is a complicated array search method for working with ACF repeater fields.
572
- * @param $array
573
- * @param $field
574
- * @param bool|false $get_parent if get_parent is set to true to will return the parent field group key of the repeater fields
575
- * @param mixed $last_key placeholder for storing the last key...
576
- * @return bool|int|string
577
- */
578
- public static function key_search($array, $field , $get_parent = false , $last_key = false) {
579
- $value = false;
580
 
581
- foreach ($array as $key => $item) {
582
- if ($key === $field['key'] ) {
583
- $value = $item;
584
  } else {
585
- if (is_array($item)) {
586
- $last_key = ( !is_numeric($key)) ? $key : $last_key;
587
- $value = self::key_search($item, $field , $get_parent , $last_key );
588
- }
589
  }
590
 
591
- if ($value) {
592
- if (!$get_parent) {
593
- return $value;
594
- } else {
595
- return $last_key;
596
- }
597
-
598
- }
599
  }
600
-
601
- return false;
602
  }
603
-
604
- /**
605
- * Select tab that was selected in last edit session of the post.
606
- *
607
- * - If the time the same post that was last edited lies within the time the
608
- * transient exists, the last selected tab will be selected via JavaScript.
609
- * - If a new post is opened for editing, the current tab will be overwritten.
610
- */
611
- public static function handle_tab() {
612
- // Run only when post_id is present
613
- if ( ! isset( $_GET['post'] ) || ! is_numeric( $_GET['post'] ) ) {
614
- return;
615
- }
616
- $post_id = sanitize_key( $_GET['post'] );
617
- // Check for existing transient
618
- $current_tab = get_transient( self::$_transient_name );
619
- // Use value only once, delete transient right away
620
- delete_transient( self::$_transient_name );
621
- // The first tab is selected by default
622
- $tab_index = 0;
623
- // Get tab index for current post
624
- if ( $current_tab['post_id'] === $post_id ) {
625
- $tab_index = $current_tab['tab_index'];
626
- }
627
- ?>
628
- <script type="text/javascript">
629
- (function($) {
630
- /**
631
- * Global to save the current index of selected tab
632
- * @type int
633
- */
634
- window.acf_current_tab_index = null;
635
- acf.add_action('ready', function( $el ){
636
- var tabIndex = <?php echo $tab_index; ?>
637
- // Get tab element by index
638
- var $li = $('.acf-tab-group').find('li:eq(<?php echo $tab_index; ?>)');
639
- // Select tab only when it’s not the first tab, which is selected by default
640
- if (0 !== tabIndex) {
641
- $li.find('a').click();
642
- }
643
- window.acf_current_tab_index = tabIndex;
644
- });
645
- acf.add_action('refresh', function($tabGroup) {
646
- var $currentTab;
647
- var currentTabIndex = window.acf_current_tab_index;
648
- var newTabIndex;
649
- // Bail out if we have no jQuery object
650
- if (false === $tabGroup instanceof jQuery) {
651
- return;
652
- }
653
- $currentTab = $tabGroup.find('li.active');
654
- // Bail out if no active tab was found
655
- if ($currentTab.length === 0) {
656
- return;
657
- }
658
- // Get index of active tab
659
- newTabIndex = $currentTab.index();
660
- // Bail out if index is initial or previously selected tab is the same
661
- if (null === currentTabIndex || newTabIndex === currentTabIndex) {
662
- return;
663
- }
664
- window.acf_current_tab_index = newTabIndex;
665
- // Send tabIndex to backend to save transient
666
- $.ajax(ajaxurl, {
667
- method: 'post',
668
- data: {
669
- action: 'acf_save_current_tab',
670
- tab_index: newTabIndex,
671
- post_id: <?php echo $post_id; ?>
672
- }
673
- });
674
- });
675
- })(jQuery);
676
- </script>
677
- <?php
678
- }
679
- public static function ajax_acf_save_current_tab() {
680
- if ( ! isset( $_POST['tab_index'] ) || ! is_numeric( $_POST['tab_index'] ) ) {
681
- return;
682
- }
683
- $tab_index = sanitize_text_field($_POST['tab_index']);
684
- $post_id = $_POST['post_id'];
685
- $transient_value = array(
686
- 'tab_index' => $tab_index,
687
- 'post_id' => $post_id,
688
- );
689
- $result = set_transient( self::$_transient_name, $transient_value, self::$_transient_minutes * 60 );
690
- if ( $result ) {
691
- wp_send_json_success();
692
- }
693
- wp_die();
694
- }
695
 
 
696
  }
697
 
698
  /**
699
- * Initialize ACF Integrations
 
 
 
 
700
  */
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
701
 
702
- new Landing_Pages_ACF();
703
 
 
 
 
704
 
705
- }
1
  <?php
2
 
3
+ /**
4
+ * Class ifor integrating landingpage CPT with ACF4 & ACF5
5
+ * @package ACF
6
+ */
7
 
8
+ class Landing_Pages_ACF {
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ /**
11
+ * Name under which the transient for the current tab will be saved.
12
+ * @var string
13
+ */
14
+ static $_transient_name = 'acf_current_tab';
15
+ /**
16
+ * Number of minutes the transient will be saved.
17
+ * @var int
18
+ */
19
+ static $_transient_minutes = 5;
20
 
21
 
22
+ /**
23
+ * Initialize Landing_Pages_ACF Class
24
+ */
25
+ public function __construct() {
26
+ self::load_hooks();
27
+ }
28
 
 
 
29
 
30
+ /**
31
+ * Load Hooks & Filters
32
+ */
33
+ public static function load_hooks() {
34
 
35
+ /* Load ACF Fields On ACF powered Email Template */
36
+ add_filter( 'acf/location/rule_match/template_id' , array( __CLASS__ , 'load_acf_on_template' ) , 10 , 3 );
37
 
38
+ /* make sure fields are placed in the correct location */
39
+ add_action( 'save_post', array( __CLASS__ , 'save_acf_fields' ) );
40
 
41
+ /* make sure fields are placed in the correct location */
42
+ add_action( 'wp_restore_post_revision', array( __CLASS__ , 'restore_acf_values' ) , 10 , 2 );
 
43
 
44
+ /* Adds revision fields to the revisions screen */
 
45
 
46
+ /* Adds revision fields value for Inbound Settings to the revisions screen */
47
+ add_filter( '_wp_post_revision_fields', array( __CLASS__ , 'add_revision_fields' ) );
48
+ add_filter( '_wp_post_revision_field_inbound_settings', array( __CLASS__ , 'add_revision_field_values' ) , 10 , 3 );
49
 
50
+ /* Intercept load custom field value request and hijack it */
51
+ add_filter( 'acf/load_value' , array( __CLASS__ , 'load_value' ) , 11 , 3 );
52
 
53
+ /* extra field formatting
54
+ add_filter( 'acf/format_value' , array( __CLASS__ , 'format_value' ) , 11 , 3 ); */
55
 
56
+ /* make sure fields are placed in the correct location */
57
+ add_action( 'admin_print_footer_scripts', array( __CLASS__ , 'reposition_acf_fields' ) );
 
 
 
 
 
58
 
59
+ /* add new location rule to ACF Field UI */
60
+ add_filter('acf/location/rule_types', array( __CLASS__ , 'define_location_rule_types' ) );
 
 
 
61
 
62
+ /* add new location rule values to ACF Field UI */
63
+ add_filter('acf/location/rule_values/template_id', array( __CLASS__ , 'define_location_rule_values' ) );
 
64
 
65
+ add_action( 'acf/input/admin_footer', array( __CLASS__ , 'handle_tab' ) );
66
+ add_action( 'wp_ajax_acf_save_current_tab', array( __CLASS__ , 'ajax_acf_save_current_tab' ) );
67
 
68
+ }
 
69
 
70
+ /**
71
+ * @param $choices
72
+ * @return mixed
73
+ */
74
+ public static function define_location_rule_types( $choices ) {
75
 
76
+ if (!isset($choices['Basic']['template_id'])) {
77
+ $choices['Basic']['template_id'] = __('Template ID', 'landing-page');
78
+ }
79
 
80
+ return $choices;
81
+ }
82
 
83
+ public static function define_location_rule_values( $choices ) {
84
+ $template_ids = Landing_Pages_Load_Extensions::get_uploaded_template_ids();
85
 
86
+ if (!isset($choices['default'])) {
87
+ $choices[ 'default' ] = 'default';
88
  }
89
 
90
+ if( $template_ids ) {
91
+ foreach( $template_ids as $template_id ) {
 
 
 
92
 
93
+ /* template ID by template name here */
94
+ $choices[ $template_id ] = $template_id;
 
95
 
96
+ }
 
 
 
 
 
 
97
  }
98
 
99
+ return $choices;
100
+ }
101
 
102
+ /**
103
+ * Adds javascript to make sure ACF fields load inside template container
104
+ */
105
+ public static function reposition_acf_fields() {
106
+ global $post;
 
107
 
108
+ if ( !defined('ACF_FREE') || ( !isset($post) || $post->post_type != 'landing-page' ) ) {
109
+ return;
110
+ }
111
 
112
+ ?>
113
+ <script type='text/javascript'>
114
+ jQuery('.acf_postbox').each(function(){
115
+ jQuery('#template-display-options').append(jQuery(this));
116
+ });
117
+ </script>
118
+ <?php
119
+ }
120
 
 
 
 
121
 
122
+ /**
123
+ * Compiles ACF Meta Data into a singular json pair for variation support
124
+ * @param $landing_page_id
125
+ */
126
+ public static function save_acf_fields( $landing_page_id ) {
127
 
 
 
128
 
129
+ if ( !isset($_POST['post_type']) || $_POST['post_type'] != 'landing-page' ) {
130
+ return;
131
+ }
132
 
133
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ) {
134
+ return;
 
135
  }
136
 
137
+ /* save acf settings - uses our future data array - eventually we will migrate all post meta into this data object */
138
+ $fields = (isset($_POST['fields'])) ? $_POST['fields'] : null;
139
+ $fields = (isset($_POST['acf'])) ? $_POST['acf'] : $fields;
 
 
 
140
 
141
+ if ( $fields ) {
 
 
 
142
 
143
+ $settings = Landing_Pages_Meta::get_settings( $landing_page_id );
144
+ $variation_id = (isset($_REQUEST['lp-variation-id'])) ? intval($_REQUEST['lp-variation-id']) : '0';
145
 
146
+ if (!isset($settings['variations'])) {
147
+ $settings['variations'] = array();
148
+ }
149
 
150
+ $settings['variations'][$variation_id]['acf'] = $fields;
151
+ Landing_Pages_Meta::update_settings( $landing_page_id , $settings );
152
  }
153
+ }
154
+
155
+ /**
156
+ * Restore landing page ACF values from revision
157
+ * @param $post_id
158
+ * @param $revision_id
159
+ */
160
+ public static function restore_acf_values( $post_id , $revision_id ) {
161
 
162
+ $post = get_post($post_id);
163
+ if($post->post_type!='landing-page' ) {
164
+ return;
 
 
 
 
 
 
165
  }
166
 
167
+ $revision_settings = Landing_Pages_Meta::get_settings( $revision_id );
168
 
169
+ Landing_Pages_Meta::update_settings( $post_id , $revision_settings );
 
 
170
 
171
+ }
172
 
173
+ /**
174
+ * Adds revision fields to revisions screen
175
+ * @param $fields
176
+ * @return mixed
177
+ */
178
+ public static function add_revision_fields( $fields ) {
179
+ global $post;
180
+ $fields['inbound_settings'] = __('Landing Page Settings' , 'inbound-pro');
181
+ return $fields;
182
+ }
183
 
184
+ public static function add_revision_field_values( $value , $field , $revision) {
185
+
186
+ if (!isset($revision->ID)) {
 
 
 
 
 
187
  return $value;
188
  }
189
 
190
+ $settings = Landing_Pages_Meta::get_settings( $revision->ID );
 
 
 
 
 
 
 
 
 
 
191
 
192
+ return json_encode($settings);
193
+ }
 
194
 
195
+ /**
196
+ * Although unused at the moment, this method can be used for filtering the return value with ACF5 fields
197
+ * @param $value
198
+ * @param $post_id
199
+ * @param $field
200
+ * @return mixed
201
+ */
202
+ public static function format_value( $value, $post_id, $field ) {
203
+ return $value;
204
+ }
205
 
206
+ /**
207
+ * Finds the correct value given the variation
208
+ *
209
+ * @param MIXED $value contains the non-variation value
210
+ * @param INT $post_id ID of landing page being loaded
211
+ * @param ARRAY $field wide array of data belonging to custom field (not leveraged in this method)
212
+ *
213
+ * @returns MIXED $new_value value mapped to variation.
214
+ */
215
+ public static function load_value( $value, $post_id, $field ) {
216
+ global $post;
217
 
218
+ if ( !isset($post) || $post->post_type != 'landing-page' ) {
219
+ return $value;
220
+ }
221
 
222
+ $vid = Landing_Pages_Variations::get_new_variation_reference_id( $post->ID );
223
 
224
+ $settings = Landing_Pages_Meta::get_settings( $post->ID );
 
 
225
 
226
+ $variations = ( isset($settings['variations']) ) ? $settings['variations'] : null;
227
 
228
+ /* If there is no ACF data for this template attempt to pull values from the legacy postmeta values */
 
229
 
230
+ if ( !isset( $variations[ $vid ][ 'acf' ] ) || !$variations[ $vid ][ 'acf' ]) {
231
+ return self::load_legacy_value( $value, $post_id, $field );
232
+ }
 
 
 
 
 
 
233
 
 
 
 
234
 
235
+ if ( isset( $variations[ $vid ][ 'acf' ] ) ) {
236
+ $new_value = self::search_field_array( $variations[ $vid ][ 'acf' ] , $field );
237
 
238
+ /* sometimes value is an array count when new_value believes it should be an array in this case get new count */
239
+ if (!is_array($value) && is_array($new_value)) {
240
+ $value = count($new_value);
241
+ } else if($new_value) {
242
+ if ($new_value =='_empty') {
243
+ $new_value = '';
244
  }
245
+ $value = $new_value;
246
+ }
247
 
248
+ /* acf lite isn't processing return values correctly - ignore repeater subfields */
249
+ if ( !is_admin() && defined('ACF_FREE') ) {
250
+ $value = self::acf_free_value_formatting( $value , $field );
 
251
 
252
  }
253
 
254
+ if ( !is_admin() && is_string($value) && !defined('INBOUND_DEBUG_GF_AJAX') ) {
255
+ $value = do_shortcode($value);
256
+ }
257
 
258
+ /* handle non acf5 template return formatting */
259
+ if (defined('ACF_PRO')) {
260
+ $value = self::acf_check_if_acf4( $value , $field );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
261
  }
262
 
263
+ }
264
 
265
+ return $value;
 
 
 
 
266
 
267
+ }
268
+ /**
269
+ * Finds the correct value given the variation - uses legacy meta system
270
+ *
271
+ * @param MIXED $value contains the non-variation value
272
+ * @param INT $post_id ID of landing page being loaded
273
+ * @param ARRAY $field wide array of data belonging to custom field (not leveraged in this method)
274
+ *
275
+ * @returns MIXED $new_value value mapped to variation.
276
+ */
277
+ public static function load_legacy_value( $value, $post_id, $field ) {
278
+ global $post;
279
 
280
+ /* get registered field object data */
281
+ $field = self::acf_get_registered_field( $field );
 
282
 
283
+ /* if a brand new post ignore return default value */
284
+ if ( $post->post_status != 'publish' ) {
285
+ return ( isset($field['default_value']) ) ? do_shortcode($field['default_value']) : '' ;
286
+ }
 
287
 
288
+ $vid = Landing_Pages_Variations::get_new_variation_reference_id( $post->ID );
 
 
 
 
289
 
290
+ if ( $vid ) {
291
+ $value = get_post_meta( $post_id , $field['name'] . '-' . $vid , true );
292
+ } else {
293
+ $value = get_post_meta( $post_id , $field['name'] , true );
294
+ }
295
 
 
296
 
297
+ if ($field['type']=='image' && is_admin() ) {
298
+ $value = self::get_image_id_from_url( $value );
299
  }
300
 
301
+ if ($field['type']=='date_picker') {
302
+ $value = str_replace('-' , '', $value);
303
+ $value = explode(' ' , $value);
304
+ $value = $value[0];
305
+ }
306
 
307
+ if ($field['type']=='color_picker') {
308
+ if (!strstr( $value , '#' ) && $value ) {
309
+ $value = '#'.$value;
310
+ }
311
+ }
 
 
 
 
312
 
313
+ if (!is_array($value) && !is_admin() ) {
314
+ $value = do_shortcode($value);
315
+ }
316
 
317
+ return $value;
318
 
319
+ }
320
 
 
 
 
 
321
 
322
+ /**
323
+ * Searches ACF variation array and returns the correct field value given the field key
324
+ *
325
+ * @param ARRAY $array of custom field keys and values stored for variation
326
+ * @param STRING $needle acf form field key
327
+ *
328
+ * @return $feild value
329
+ */
330
+ public static function search_field_array( $array , $field ) {
331
 
332
+ $needle = $field['key'];
 
333
 
334
+ foreach ($array as $key => $value ){
 
 
 
 
 
335
 
 
336
 
337
+ if ($key === $needle && !is_array($value) ) {
338
+ $value = ($value) ? $value : '_empty' ;
339
+ return $value;
340
+ }
341
+
342
+ /* Arrays could be repeaters or any custom field with sets of multiple values */
343
+ if ( is_array($value) ) {
344
+
345
+ /* Check if this array contains a repeater field layouts. If it does then return layouts, else this array is a non-repeater value set so return it */
346
+ if ( $key === $needle ) {
347
 
348
+ $repeater_array = self::get_repeater_layouts( $value );
349
+ if ($repeater_array) {
350
+ return $repeater_array;
351
+ } else {
352
+ return $value;
353
  }
354
 
355
+ }
356
 
357
+ /* Check if array is repeater fields and determine correct value given a parsed field name with field key */
358
+ $repeater_value = self::get_repeater_values( $value , $field , $needle );
359
+
360
+ /* If target key is not in these repeater fields, or this array is not determined to be a repeater field then move on. */
361
+ if ($repeater_value) {
362
+ return $repeater_value;
363
  }
364
 
365
+
366
  }
367
 
 
368
  }
369
 
370
+ return false;
371
+ }
372
+
373
+ /**
374
+ * Searches an array assumed to be a repeater field dataset and returns an array of repeater field layout definitions
375
+ *
376
+ * @retuns ARRAY $fields this array will either be empty of contain repeater field layout definitions.
377
+ */
378
+ public static function get_repeater_layouts( $array ) {
379
 
380
+ $fields = array();
381
 
382
+ foreach ($array as $key => $value) {
383
+ if ( isset( $value['acf_fc_layout'] ) ) {
384
+ $fields[] = $value['acf_fc_layout'];
 
385
  }
 
 
386
  }
387
 
388
+ return $fields;
389
+ }
390
+
391
 
392
+ /**
393
+ * Searches an array assumed to be a repeater field dataset and returns an array of repeater field layout definitions
394
+ *
395
+ * @retuns ARRAY $fields this array will either be empty of contain repeater field layout definitions.
396
+ */
397
+ public static function get_repeater_values( $array , $field , $needle ) {
398
 
399
+ /* Discover correct repeater pointer by parsing field name */
400
+ preg_match('/(_\d_)/', $field['name'], $matches, 0);
401
 
402
+ /* if not a repeater subfield then bail */
403
+ if (!$matches) {
404
+ return false;
405
+ }
406
 
407
+ $pointer = str_replace('_' , '' , $matches[0]);
408
+ $repeater_key = self::key_search($array, $field , true ); /* returns parent flexible content field key using sub field key */
409
 
410
 
411
+ /* */
412
+ if ( $repeater_key && $repeater_key !== '0' && isset($array[$repeater_key][$pointer][$field['key']])){
413
+ return $array[$repeater_key][$pointer][$field['key']];
414
+ }
415
 
416
+ /* repeater field comes after the pointer???? */
417
+ if (isset($array[$pointer][$needle])){
418
+ return $array[$pointer][$needle];
419
+ }
420
 
421
 
422
 
423
+ return '';
424
 
425
+ }
426
 
427
+ /**
428
+ * Check if current post is a landing page using an ACF powered template
429
+ *
430
+ * @filter acf/location/rule_match/template_id
431
+ *
432
+ * @returns BOOL declaring if current page is a landing page with an ACF template loaded or not
433
+ */
434
+ public static function load_acf_on_template( $allow , $rule, $args ) {
435
+ global $post;
436
 
437
+ if ( !isset($post) || $post->post_type != 'landing-page' ) {
438
+ return $allow;
439
+ }
440
 
441
+ $template = Landing_Pages_Variations::get_current_template( $args['post_id'] );
442
 
443
+ if ($template == $rule['value']) {
444
+ return true;
445
+ } else {
446
+ return false;
 
447
  }
448
+ }
449
 
450
+ /**
451
+ *
452
+ * @param $image_url
453
+ * @return mixed
454
+ */
455
+ public static function get_image_id_from_url($url) {
456
+ $dir = wp_upload_dir();
457
 
458
+ // baseurl never has a trailing slash
459
+ if ( false === strpos( $url, $dir['baseurl'] . '/' ) ) {
460
+ // URL points to a place outside of upload directory
461
+ return false;
462
+ }
463
 
464
+ $file = basename( $url );
465
+ $query = array(
466
+ 'post_type' => 'attachment',
467
+ 'fields' => 'ids',
468
+ 'meta_query' => array(
469
+ array(
470
+ 'value' => $file,
471
+ 'compare' => 'LIKE',
472
+ ),
473
+ )
474
+ );
475
 
476
+ $query['meta_query'][0]['key'] = '_wp_attached_file';
477
 
478
+ // query attachments
479
+ $ids = get_posts( $query );
480
 
481
+ if ( ! empty( $ids ) ) {
482
 
483
+ foreach ( $ids as $id ) {
484
 
485
+ // first entry of returned array is the URL
486
+ if ( $url === array_shift( wp_get_attachment_image_src( $id, 'full' ) ) )
487
+ return $id;
 
488
  }
489
+ }
490
 
491
+ $query['meta_query'][0]['key'] = '_wp_attachment_metadata';
492
 
493
+ // query attachments again
494
+ $ids = get_posts( $query );
495
 
496
+ if ( empty( $ids) )
497
+ return false;
498
 
499
+ foreach ( $ids as $id ) {
500
 
501
+ $meta = wp_get_attachment_metadata( $id );
502
 
503
+ foreach ( $meta['sizes'] as $size => $values ) {
504
 
505
+ if ( $values['file'] === $file && $url === array_shift( wp_get_attachment_image_src( $id, $size ) ) )
506
+ return $id;
 
507
  }
 
 
508
  }
509
 
510
+ return false;
511
+ }
512
 
513
+ public static function acf_get_registered_field( $field ) {
514
+ global $acf_register_field_group;
 
515
 
516
+ if (!$acf_register_field_group) {
517
+ return $field;
518
+ }
519
+
520
+ foreach ($acf_register_field_group as $key => $group) {
521
+ foreach ( $group['fields'] as $this_field ) {
522
+ if ( $this_field['name'] == $field['name'] ){
523
+ return $this_field;
524
  }
525
  }
526
  }
527
+ }
528
 
529
 
530
+ /**
531
+ * Correct return value formatting when Pro is NOT installed
532
+ */
533
+ public static function acf_free_value_formatting( $value , $field ) {
534
 
535
+ if ($field['type'] == 'image' && $field['return_format'] == 'url' && !strstr($value , 'http' ) ) {
536
+ $image_array = wp_get_attachment_image_src( $value , 'full' );
537
+ return $image_array[0];
538
+ }
539
 
540
+ if ($field['type'] == 'file' && $field['return_format'] == 'url' && !strstr($value , 'http' ) ) {
541
+ return wp_get_attachment_url( $value );
542
+ }
543
 
544
+ if ($field['type'] == 'wysiwyg') {
545
+ $vaue = wpautop($value);
546
+ $vaue = do_shortcode($value);
547
+ }
548
+
549
+ return $value;
550
+ }
551
 
552
+ /**
553
+ * checks template data type
554
+ * @param $value
555
+ * @param $field
556
+ * @return mixed
557
+ */
558
+ public static function acf_check_if_acf4( $value , $field ) {
559
+ global $key, $lp_data;
560
+
561
+ if (!isset($lp_data[$key])) {
562
+ return $value;
563
+ }
564
+
565
+ if ( $lp_data[$key]['info']['data_type'] == 'acf4' ) {
566
+ return self::acf_free_value_formatting($value , $field);
567
+ } else {
568
  return $value;
569
  }
570
+ }
571
 
 
 
 
 
 
 
 
 
572
 
573
+ /**
574
+ * This is a complicated array search method for working with ACF repeater fields.
575
+ * @param $array
576
+ * @param $field
577
+ * @param bool|false $get_parent if get_parent is set to true to will return the parent field group key of the repeater fields
578
+ * @param mixed $last_key placeholder for storing the last key...
579
+ * @return bool|int|string
580
+ */
581
+ public static function key_search($array, $field , $get_parent = false , $last_key = false) {
582
+ $value = false;
583
 
584
+ foreach ($array as $key => $item) {
585
+ if ($key === $field['key'] ) {
586
+ $value = $item;
587
  } else {
588
+ if (is_array($item)) {
589
+ $last_key = ( !is_numeric($key)) ? $key : $last_key;
590
+ $value = self::key_search($item, $field , $get_parent , $last_key );
591
+ }
592
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
593
 
594
+ if ($value) {
595
+ if (!$get_parent) {
596
+ return $value;
597
  } else {
598
+ return $last_key;
 
 
 
599
  }
600
 
 
 
 
 
 
 
 
 
601
  }
 
 
602
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
603
 
604
+ return false;
605
  }
606
 
607
  /**
608
+ * Select tab that was selected in last edit session of the post.
609
+ *
610
+ * - If the time the same post that was last edited lies within the time the
611
+ * transient exists, the last selected tab will be selected via JavaScript.
612
+ * - If a new post is opened for editing, the current tab will be overwritten.
613
  */
614
+ public static function handle_tab() {
615
+ // Run only when post_id is present
616
+ if ( ! isset( $_GET['post'] ) || ! is_numeric( $_GET['post'] ) ) {
617
+ return;
618
+ }
619
+ $post_id = sanitize_key( $_GET['post'] );
620
+ // Check for existing transient
621
+ $current_tab = get_transient( self::$_transient_name );
622
+ // Use value only once, delete transient right away
623
+ delete_transient( self::$_transient_name );
624
+ // The first tab is selected by default
625
+ $tab_index = 0;
626
+ // Get tab index for current post
627
+ if ( $current_tab['post_id'] === $post_id ) {
628
+ $tab_index = $current_tab['tab_index'];
629
+ }
630
+ ?>
631
+ <script type="text/javascript">
632
+ (function($) {
633
+ /**
634
+ * Global to save the current index of selected tab
635
+ * @type int
636
+ */
637
+ window.acf_current_tab_index = null;
638
+ acf.add_action('ready', function( $el ){
639
+ var tabIndex = <?php echo $tab_index; ?>
640
+ // Get tab element by index
641
+ var $li = $('.acf-tab-group').find('li:eq(<?php echo $tab_index; ?>)');
642
+ // Select tab only when it’s not the first tab, which is selected by default
643
+ if (0 !== tabIndex) {
644
+ $li.find('a').click();
645
+ }
646
+ window.acf_current_tab_index = tabIndex;
647
+ });
648
+ acf.add_action('refresh', function($tabGroup) {
649
+ var $currentTab;
650
+ var currentTabIndex = window.acf_current_tab_index;
651
+ var newTabIndex;
652
+ // Bail out if we have no jQuery object
653
+ if (false === $tabGroup instanceof jQuery) {
654
+ return;
655
+ }
656
+ $currentTab = $tabGroup.find('li.active');
657
+ // Bail out if no active tab was found
658
+ if ($currentTab.length === 0) {
659
+ return;
660
+ }
661
+ // Get index of active tab
662
+ newTabIndex = $currentTab.index();
663
+ // Bail out if index is initial or previously selected tab is the same
664
+ if (null === currentTabIndex || newTabIndex === currentTabIndex) {
665
+ return;
666
+ }
667
+ window.acf_current_tab_index = newTabIndex;
668
+ // Send tabIndex to backend to save transient
669
+ $.ajax(ajaxurl, {
670
+ method: 'post',
671
+ data: {
672
+ action: 'acf_save_current_tab',
673
+ tab_index: newTabIndex,
674
+ post_id: <?php echo $post_id; ?>
675
+ }
676
+ });
677
+ });
678
+ })(jQuery);
679
+ </script>
680
+ <?php
681
+ }
682
+ public static function ajax_acf_save_current_tab() {
683
+ if ( ! isset( $_POST['tab_index'] ) || ! is_numeric( $_POST['tab_index'] ) ) {
684
+ return;
685
+ }
686
+ $tab_index = sanitize_text_field($_POST['tab_index']);
687
+ $post_id = $_POST['post_id'];
688
+ $transient_value = array(
689
+ 'tab_index' => $tab_index,
690
+ 'post_id' => $post_id,
691
+ );
692
+ $result = set_transient( self::$_transient_name, $transient_value, self::$_transient_minutes * 60 );
693
+ if ( $result ) {
694
+ wp_send_json_success();
695
+ }
696
+ wp_die();
697
+ }
698
 
699
+ }
700
 
701
+ /**
702
+ * Initialize ACF Integrations
703
+ */
704
 
705
+ new Landing_Pages_ACF();
classes/class.activation.php CHANGED
@@ -1,6 +1,10 @@
1
  <?php
2
 
3
- if ( !class_exists('Landing_Pages_Activation') ) {
 
 
 
 
4
 
5
  class Landing_Pages_Activation {
6
 
@@ -290,4 +294,3 @@ register_deactivation_hook( LANDINGPAGES_FILE , array( 'Landing_Pages_Activation
290
 
291
  new Landing_Pages_Activation;
292
 
293
- }
1
  <?php
2
 
3
+ /**
4
+ * Class for managing landing page activation routines
5
+ * @package LandingPages
6
+ * @subpackage Activation
7
+ */
8
 
9
  class Landing_Pages_Activation {
10
 
294
 
295
  new Landing_Pages_Activation;
296
 
 
classes/class.activation.upgrade-routines.php CHANGED
@@ -1,301 +1,294 @@
1
  <?php
2
 
3
- /* Public methods in this class will be run at least once during plugin activation script. */
4
- /* Updater methods fired are stored in transient to prevent repeat processing */
 
 
 
 
5
 
6
- if ( !class_exists('Landing_Pages_Activation_Update_Routines') ) {
7
 
8
- class Landing_Pages_Activation_Update_Routines {
 
 
 
 
 
9
 
 
 
10
 
11
- /*
12
- * @introduced: 1.5.7
13
- * @migration-type: Meta pair migragtion
14
- * @migration: convert meta key lp-conversion-area to template-name-conversion-area-content-{vid}
15
- */
16
- public static function migrate_legacy_conversion_area_contents() {
17
 
18
- /* ignore if not applicable */
19
- $previous_installed_version = get_transient('lp_current_version');
 
 
 
20
 
21
- if ( version_compare($previous_installed_version , "1.5.7") === 1 ) {
22
- return;
23
- }
24
 
25
- /* for all landing pages load variations */
26
- $landing_pages = get_posts( array (
27
- 'post_type' => 'landing-page',
28
- 'posts_per_page' => -1
29
- ));
30
 
31
- foreach ($landing_pages as $post) {
 
 
32
 
33
- /* for all variations loop through and migrate_data */
34
- ( get_post_meta($post->ID,'lp-ab-variations', true) ) ? $variations = get_post_meta($post->ID,'lp-ab-variations', true) : $variations = array( '0' => '0' );
35
 
36
- if (!is_array($variations) && strlen($variations) > 1 ) {
37
- $variations = explode(',',$variations);
38
- }
39
 
40
- foreach ($variations as $key=>$vid) {
41
 
42
- ($vid) ? $suffix = '-' . $vid : $suffix = '';
 
 
43
 
44
- $selected_template = get_post_meta( $post->ID , 'lp-selected-template' . $suffix , true );
 
45
 
46
- if ( !$selected_template ) {
47
- continue;
48
- }
 
 
49
 
50
- /* discover legacy main content */
51
- ( $vid ) ? $conversion_area_content = get_post_meta( $post->ID , 'conversion-area-content' . $suffix , true ) : $conversion_area_content = get_post_meta( $post->ID , 'lp-conversion-area' , true );
52
 
53
- /* Now if the new key is not already poplated, copy the content to the new key */
54
- $check = get_post_meta( $post->ID , $selected_template .'-conversion-area-content' . $suffix , true );
55
- if (!$check) {
56
- update_post_meta( $post->ID , $selected_template .'-conversion-area-content' . $suffix , $conversion_area_content );
57
- }
58
 
59
- }
60
 
61
- }
62
- }
 
 
63
 
 
 
64
 
65
- /*
66
- * @introduced: 1.5.7
67
- * @migration-type: Meta pair migragtion
68
- * @migration: mirgrates post_content and content-{vid} values to template-name-main-content-{vid}
69
 
70
- */
71
- public static function migrate_legacy_main_content() {
 
72
 
73
- /* ignore if not applicable */
74
- $previous_installed_version = get_transient('lp_current_version');
 
 
 
75
 
76
- if ( version_compare($previous_installed_version , "1.5.8") === 1 ) {
77
- return;
78
- }
79
 
80
- /* for all landing pages load variations */
81
- $landing_pages = get_posts( array (
82
- 'post_type' => 'landing-page',
83
- 'posts_per_page' => -1
84
- ));
85
 
86
- foreach ($landing_pages as $post) {
 
 
87
 
88
- /* for all variations loop through and migrate_data */
89
- ( get_post_meta($post->ID,'lp-ab-variations', true) ) ? $variations = get_post_meta($post->ID,'lp-ab-variations', true) : $variations = array( '0' => '0' );
90
 
91
- if (!is_array($variations) && strlen($variations) > 1 ) {
92
- $variations = explode(',',$variations);
93
- }
94
 
95
- foreach ($variations as $key=>$vid) {
 
 
 
96
 
97
- ($vid) ? $suffix = '-' . $vid : $suffix = '';
 
98
 
99
- $selected_template = get_post_meta( $post->ID , 'lp-selected-template' . $suffix , true );
100
- if ( !$selected_template ) {
101
- continue;
102
- }
 
103
 
104
- /* discover legacy main content */
105
- ( $vid ) ? $content = get_post_meta( $post->ID , 'content' . $suffix , true ) : $content = $post->post_content;
106
 
107
- /* Now if the new key is not already poplated, copy the content to the new key */
108
- $check = get_post_meta( $post->ID , $selected_template .'-main-content' . $suffix , true );
109
- if (!$check) {
110
- update_post_meta( $post->ID , $selected_template .'-main-content' . $suffix , $content );
111
- }
112
 
113
- }
 
 
 
 
 
 
114
 
115
- }
116
- }
117
 
118
- /*
119
- * @introduced: 1.5.8
120
- * @migration-type: template migragtion
121
- * @migration: Moves legacy templates to uploads folder
122
- *
123
- */
124
- public static function updater_move_legacy_templates() {
125
 
126
- /* ignore if not applicable */
127
- $previous_installed_version = get_transient('lp_current_version');
 
128
 
129
- if ( version_compare($previous_installed_version , "1.5.8") === 1 ) {
130
- return;
131
- }
 
 
 
132
 
133
- /* move copy of legacy core templates to the uploads folder and delete from core templates directory */
134
- $templates_to_move = array('rsvp-envelope','super-slick');
135
- chmod(LANDINGPAGES_UPLOADS_PATH, 0755);
 
136
 
137
- $template_paths = Landing_Pages_Load_Extensions::get_core_template_ids();
138
- if (count($template_paths)>0)
139
- {
140
- foreach ($template_paths as $name)
141
- {
142
- if (in_array( $name, $templates_to_move ))
143
- {
144
- $old_path = LANDINGPAGES_PATH."templates/$name/";
145
- $new_path = LANDINGPAGES_UPLOADS_PATH."$name/";
146
-
147
- /*
148
- echo "oldpath: $old_path<br>";
149
- echo "newpath: $new_path<br>";
150
- */
151
-
152
- @mkdir($new_path , 0775);
153
- chmod($old_path , 0775);
154
-
155
- self::move_files( $old_path , $new_path );
156
-
157
- rmdir($old_path);
158
- }
159
- }
160
- }
161
- }
162
-
163
- /* Private Method - Moves files from one folder the older. This is not an updater process */
164
- private static function move_files( $old_path , $new_path ) {
165
-
166
- $files = scandir($old_path);
167
-
168
- if (!$files) {
169
- return;
170
- }
171
-
172
- foreach ($files as $file) {
173
- if (in_array($file, array(".",".."))) {
174
- continue;
175
- }
176
-
177
- if ($file==".DS_Store") {
178
- unlink($old_path.$file);
179
- continue;
180
- }
181
-
182
- if (is_dir($old_path.$file)) {
183
- @mkdir($new_path.$file.'/' , 0775);
184
- chmod($old_path.$file.'/' , 0775);
185
- lp_move_template_files( $old_path.$file.'/' , $new_path.$file.'/' );
186
- rmdir($old_path.$file);
187
- continue;
188
- }
189
-
190
- /*
191
- echo "oldfile:".$old_path.$file."<br>";
192
- echo "newfile:".$new_path.$file."<br>";
193
- */
194
-
195
- if (copy($old_path.$file, $new_path.$file)) {
196
- unlink($old_path.$file);
197
- }
198
- }
199
-
200
- $delete = (isset($delete)) ? $delete : false;
201
-
202
- if (!$delete) {
203
- return;
204
- }
205
- }
206
-
207
-
208
- /*
209
- * @introduced: 1.7.5
210
- * @migration-type: Meta key rename
211
- * @migration: renames all instances of inbound_conversion_data to _inbound_conversion_data
212
-
213
- */
214
- public static function meta_key_change_conversion_object() {
215
- global $wpdb;
216
-
217
- /* ignore if not applicable */
218
- $previous_installed_version = get_transient('lp_current_version');
219
-
220
- if ( version_compare($previous_installed_version , "1.7.5") === 1 ) {
221
- return;
222
- }
223
-
224
- $wpdb->query("UPDATE $wpdb->postmeta SET `meta_key` = REPLACE (`meta_key` , 'inbound_conversion_data', '_inbound_conversion_data')");
225
- }
226
-
227
- /*
228
- * @introduced: 1.8.9
229
- * @migration-type: Meta pair migragtion
230
- * @migration: Make a meta pair copy of wp_content into 'content' meta key for variation 0 to use (refactor session)
231
- * @moredetails: Before 1.8.9 we did not source post_content from a meta pair when variation 0 was served. In a step to refactor and normalize we now pull post_content from the meta pair with key 'content'. For now (subject to further improvements in the future), variation id 0 pulls from 'content' meta key while varation 1+ pulls from 'content-{varation_id}' meta key. *
232
- */
233
- public static function prepare_content_meta_key_for_variation_0() {
234
-
235
- /* ignore if not applicable */
236
- $previous_installed_version = get_transient('lp_current_version');
237
-
238
- if ( version_compare($previous_installed_version , "1.8.9") === 1 ) {
239
- return;
240
- }
241
-
242
- /* for all landing pages load variations */
243
- $landing_pages = get_posts( array (
244
- 'post_type' => 'landing-page',
245
- 'posts_per_page' => -1
246
- ));
247
-
248
- /* loop through landing pages and copy post content into meta object */
249
- foreach ($landing_pages as $post) {
250
- $content = $post->post_content;
251
- update_post_meta( $post->ID , 'content' , $content);
252
- }
253
- }
254
 
255
- /*
256
- * @introduced: 2.0.4
257
- * @migration-type: Meta pair migragtion
258
- * @migration: mirgrates lp_ab_variation_status to lp_ab_variation_status-0
259
- * @migration: mirgrates lp-variation-notes to lp-variation-notes-0
260
-
261
- */
262
- public static function migrate_variation_status_metapair() {
263
-
264
- /* ignore if not applicable */
265
- $previous_installed_version = get_transient('lp_current_version');
266
-
267
- if ( version_compare($previous_installed_version , "2.0.4") === 1 ) {
268
- return;
269
- }
270
-
271
- /* for all landing pages load variations */
272
- $landing_pages = get_posts( array (
273
- 'post_type' => 'landing-page',
274
- 'posts_per_page' => -1
275
- ));
276
-
277
- foreach ($landing_pages as $post) {
278
-
279
- /* mirgrates lp_ab_variation_status to lp_ab_variation_status-0 */
280
- $status = get_post_meta( $post->ID , 'lp_ab_variation_status' , true );
281
- update_post_meta( $post->ID , 'lp_ab_variation_status-0' , $status );
282
-
283
- /* mirgrates lp_ab_variation_status to lp_ab_variation_status-0 */
284
- $status = get_post_meta( $post->ID , 'lp-variation-notes' , true );
285
- update_post_meta( $post->ID , 'lp-variation-notes-0' , $status );
286
-
287
- }
288
- }
289
-
290
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
 
292
  }
293
 
294
 
295
  /* Declare Helper Functions here */
296
- function lp_move_template_files( $old_path , $new_path )
297
- {
298
-
299
 
300
 
301
  }
1
  <?php
2
 
3
+ /**
4
+ * Class for defining and executing database routines. Updater methods fired are stored in transient to prevent repeat processing
5
+ * @package LandingPages
6
+ * @subpackage Activation
7
+ */
8
+ class Landing_Pages_Activation_Update_Routines {
9
 
 
10
 
11
+ /*
12
+ * @introduced: 1.5.7
13
+ * @migration-type: Meta pair migragtion
14
+ * @migration: convert meta key lp-conversion-area to template-name-conversion-area-content-{vid}
15
+ */
16
+ public static function migrate_legacy_conversion_area_contents() {
17
 
18
+ /* ignore if not applicable */
19
+ $previous_installed_version = get_transient('lp_current_version');
20
 
21
+ if (version_compare($previous_installed_version, "1.5.7") === 1) {
22
+ return;
23
+ }
 
 
 
24
 
25
+ /* for all landing pages load variations */
26
+ $landing_pages = get_posts(array(
27
+ 'post_type' => 'landing-page',
28
+ 'posts_per_page' => -1
29
+ ));
30
 
31
+ foreach ($landing_pages as $post) {
 
 
32
 
33
+ /* for all variations loop through and migrate_data */
34
+ (get_post_meta($post->ID, 'lp-ab-variations', true)) ? $variations = get_post_meta($post->ID, 'lp-ab-variations', true) : $variations = array('0' => '0');
 
 
 
35
 
36
+ if (!is_array($variations) && strlen($variations) > 1) {
37
+ $variations = explode(',', $variations);
38
+ }
39
 
40
+ foreach ($variations as $key => $vid) {
 
41
 
42
+ ($vid) ? $suffix = '-' . $vid : $suffix = '';
 
 
43
 
44
+ $selected_template = get_post_meta($post->ID, 'lp-selected-template' . $suffix, true);
45
 
46
+ if (!$selected_template) {
47
+ continue;
48
+ }
49
 
50
+ /* discover legacy main content */
51
+ ($vid) ? $conversion_area_content = get_post_meta($post->ID, 'conversion-area-content' . $suffix, true) : $conversion_area_content = get_post_meta($post->ID, 'lp-conversion-area', true);
52
 
53
+ /* Now if the new key is not already poplated, copy the content to the new key */
54
+ $check = get_post_meta($post->ID, $selected_template . '-conversion-area-content' . $suffix, true);
55
+ if (!$check) {
56
+ update_post_meta($post->ID, $selected_template . '-conversion-area-content' . $suffix, $conversion_area_content);
57
+ }
58
 
59
+ }
 
60
 
61
+ }
62
+ }
 
 
 
63
 
 
64
 
65
+ /*
66
+ * @introduced: 1.5.7
67
+ * @migration-type: Meta pair migragtion
68
+ * @migration: mirgrates post_content and content-{vid} values to template-name-main-content-{vid}
69
 
70
+ */
71
+ public static function migrate_legacy_main_content() {
72
 
73
+ /* ignore if not applicable */
74
+ $previous_installed_version = get_transient('lp_current_version');
 
 
75
 
76
+ if (version_compare($previous_installed_version, "1.5.8") === 1) {
77
+ return;
78
+ }
79
 
80
+ /* for all landing pages load variations */
81
+ $landing_pages = get_posts(array(
82
+ 'post_type' => 'landing-page',
83
+ 'posts_per_page' => -1
84
+ ));
85
 
86
+ foreach ($landing_pages as $post) {
 
 
87
 
88
+ /* for all variations loop through and migrate_data */
89
+ (get_post_meta($post->ID, 'lp-ab-variations', true)) ? $variations = get_post_meta($post->ID, 'lp-ab-variations', true) : $variations = array('0' => '0');
 
 
 
90
 
91
+ if (!is_array($variations) && strlen($variations) > 1) {
92
+ $variations = explode(',', $variations);
93
+ }
94
 
95
+ foreach ($variations as $key => $vid) {
 
96
 
97
+ ($vid) ? $suffix = '-' . $vid : $suffix = '';
 
 
98
 
99
+ $selected_template = get_post_meta($post->ID, 'lp-selected-template' . $suffix, true);
100
+ if (!$selected_template) {
101
+ continue;
102
+ }
103
 
104
+ /* discover legacy main content */
105
+ ($vid) ? $content = get_post_meta($post->ID, 'content' . $suffix, true) : $content = $post->post_content;
106
 
107
+ /* Now if the new key is not already poplated, copy the content to the new key */
108
+ $check = get_post_meta($post->ID, $selected_template . '-main-content' . $suffix, true);
109
+ if (!$check) {
110
+ update_post_meta($post->ID, $selected_template . '-main-content' . $suffix, $content);
111
+ }
112
 
113
+ }
 
114
 
115
+ }
116
+ }
 
 
 
117
 
118
+ /*
119
+ * @introduced: 1.5.8
120
+ * @migration-type: template migragtion
121
+ * @migration: Moves legacy templates to uploads folder
122
+ *
123
+ */
124
+ public static function updater_move_legacy_templates() {
125
 
126
+ /* ignore if not applicable */
127
+ $previous_installed_version = get_transient('lp_current_version');
128
 
129
+ if (version_compare($previous_installed_version, "1.5.8") === 1) {
130
+ return;
131
+ }
 
 
 
 
132
 
133
+ /* move copy of legacy core templates to the uploads folder and delete from core templates directory */
134
+ $templates_to_move = array('rsvp-envelope', 'super-slick');
135
+ chmod(LANDINGPAGES_UPLOADS_PATH, 0755);
136
 
137
+ $template_paths = Landing_Pages_Load_Extensions::get_core_template_ids();
138
+ if (count($template_paths) > 0) {
139
+ foreach ($template_paths as $name) {
140
+ if (in_array($name, $templates_to_move)) {
141
+ $old_path = LANDINGPAGES_PATH . "templates/$name/";
142
+ $new_path = LANDINGPAGES_UPLOADS_PATH . "$name/";
143
 
144
+ /*
145
+ echo "oldpath: $old_path<br>";
146
+ echo "newpath: $new_path<br>";
147
+ */
148
 
149
+ @mkdir($new_path, 0775);
150
+ chmod($old_path, 0775);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
 
152
+ self::move_files($old_path, $new_path);
153
+
154
+ rmdir($old_path);
155
+ }
156
+ }
157
+ }
158
+ }
159
+
160
+ /* Private Method - Moves files from one folder the older. This is not an updater process */
161
+ private static function move_files($old_path, $new_path) {
162
+
163
+ $files = scandir($old_path);
164
+
165
+ if (!$files) {
166
+ return;
167
+ }
168
+
169
+ foreach ($files as $file) {
170
+ if (in_array($file, array(".", ".."))) {
171
+ continue;
172
+ }
173
+
174
+ if ($file == ".DS_Store") {
175
+ unlink($old_path . $file);
176
+ continue;
177
+ }
178
+
179
+ if (is_dir($old_path . $file)) {
180
+ @mkdir($new_path . $file . '/', 0775);
181
+ chmod($old_path . $file . '/', 0775);
182
+ lp_move_template_files($old_path . $file . '/', $new_path . $file . '/');
183
+ rmdir($old_path . $file);
184
+ continue;
185
+ }
186
+
187
+ /*
188
+ echo "oldfile:".$old_path.$file."<br>";
189
+ echo "newfile:".$new_path.$file."<br>";
190
+ */
191
+
192
+ if (copy($old_path . $file, $new_path . $file)) {
193
+ unlink($old_path . $file);
194
+ }
195
+ }
196
+
197
+ $delete = (isset($delete)) ? $delete : false;
198
+
199
+ if (!$delete) {
200
+ return;
201
+ }
202
+ }
203
+
204
+
205
+ /*
206
+ * @introduced: 1.7.5
207
+ * @migration-type: Meta key rename
208
+ * @migration: renames all instances of inbound_conversion_data to _inbound_conversion_data
209
+
210
+ */
211
+ public static function meta_key_change_conversion_object() {
212
+ global $wpdb;
213
+
214
+ /* ignore if not applicable */
215
+ $previous_installed_version = get_transient('lp_current_version');
216
+
217
+ if (version_compare($previous_installed_version, "1.7.5") === 1) {
218
+ return;
219
+ }
220
+
221
+ $wpdb->query("UPDATE $wpdb->postmeta SET `meta_key` = REPLACE (`meta_key` , 'inbound_conversion_data', '_inbound_conversion_data')");
222
+ }
223
+
224
+ /*
225
+ * @introduced: 1.8.9
226
+ * @migration-type: Meta pair migragtion
227
+ * @migration: Make a meta pair copy of wp_content into 'content' meta key for variation 0 to use (refactor session)
228
+ * @moredetails: Before 1.8.9 we did not source post_content from a meta pair when variation 0 was served. In a step to refactor and normalize we now pull post_content from the meta pair with key 'content'. For now (subject to further improvements in the future), variation id 0 pulls from 'content' meta key while varation 1+ pulls from 'content-{varation_id}' meta key. *
229
+ */
230
+ public static function prepare_content_meta_key_for_variation_0() {
231
+
232
+ /* ignore if not applicable */
233
+ $previous_installed_version = get_transient('lp_current_version');
234
+
235
+ if (version_compare($previous_installed_version, "1.8.9") === 1) {
236
+ return;
237
+ }
238
+
239
+ /* for all landing pages load variations */
240
+ $landing_pages = get_posts(array(
241
+ 'post_type' => 'landing-page',
242
+ 'posts_per_page' => -1
243
+ ));
244
+
245
+ /* loop through landing pages and copy post content into meta object */
246
+ foreach ($landing_pages as $post) {
247
+ $content = $post->post_content;
248
+ update_post_meta($post->ID, 'content', $content);
249
+ }
250
+ }
251
+
252
+ /*
253
+ * @introduced: 2.0.4
254
+ * @migration-type: Meta pair migragtion
255
+ * @migration: mirgrates lp_ab_variation_status to lp_ab_variation_status-0
256
+ * @migration: mirgrates lp-variation-notes to lp-variation-notes-0
257
+
258
+ */
259
+ public static function migrate_variation_status_metapair() {
260
+
261
+ /* ignore if not applicable */
262
+ $previous_installed_version = get_transient('lp_current_version');
263
+
264
+ if (version_compare($previous_installed_version, "2.0.4") === 1) {
265
+ return;
266
+ }
267
+
268
+ /* for all landing pages load variations */
269
+ $landing_pages = get_posts(array(
270
+ 'post_type' => 'landing-page',
271
+ 'posts_per_page' => -1
272
+ ));
273
+
274
+ foreach ($landing_pages as $post) {
275
+
276
+ /* mirgrates lp_ab_variation_status to lp_ab_variation_status-0 */
277
+ $status = get_post_meta($post->ID, 'lp_ab_variation_status', true);
278
+ update_post_meta($post->ID, 'lp_ab_variation_status-0', $status);
279
+
280
+ /* mirgrates lp_ab_variation_status to lp_ab_variation_status-0 */
281
+ $status = get_post_meta($post->ID, 'lp-variation-notes', true);
282
+ update_post_meta($post->ID, 'lp-variation-notes-0', $status);
283
+
284
+ }
285
+ }
286
 
287
  }
288
 
289
 
290
  /* Declare Helper Functions here */
291
+ function lp_move_template_files($old_path, $new_path) {
 
 
292
 
293
 
294
  }
classes/class.admin-menus.php CHANGED
@@ -1,9 +1,9 @@
1
  <?php
2
-
3
  /**
4
- * Class Landing_Pages_Admin_Menus
 
 
5
  */
6
-
7
  class Landing_Pages_Admin_Menus {
8
 
9
  /**
1
  <?php
 
2
  /**
3
+ * Class for adding Landing Pages menu items to left wp-admin menu
4
+ * @package LandingPages
5
+ * @subpackage Menus
6
  */
 
7
  class Landing_Pages_Admin_Menus {
8
 
9
  /**
classes/class.admin-notices.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  class Landing_Pages_Admin_Notices {
4
 
5
  public function __construct() {
1
  <?php
2
 
3
+ /**
4
+ * Class for defining and displaying admin notices to users. Only displays notices in areas related to Landing Pages plugin.
5
+ * @package LandingPages
6
+ * @subpackage Notices
7
+ */
8
+
9
  class Landing_Pages_Admin_Notices {
10
 
11
  public function __construct() {
classes/class.click-tracking.php DELETED
@@ -1,210 +0,0 @@
1
- <?php
2
-
3
- class Landing_Pages_Click_Tracking {
4
-
5
- /**
6
- * Initiate class
7
- */
8
- public function __construct() {
9
- self::add_hooks();
10
- }
11
-
12
- public static function add_hooks() {
13
-
14
- add_action('wp_footer', array( __CLASS__ , 'build_trackable_links') );
15
- add_action('init', array( __CLASS__ , 'intecept_tracked_link' ), 11);
16
- }
17
-
18
- /**
19
- * Generate a trackable link. In the case of landing pages we are still using the legacy model
20
- */
21
- public static function build_trackable_links() {
22
- global $post;
23
-
24
- if (!isset($post) || $post->post_type != 'landing-page') {
25
- return;
26
- }
27
-
28
- $variation = (isset($_GET['lp-variation-id'])) ? $_GET['lp-variation-id'] : 0;
29
- $variation = preg_replace('/[^-a-zA-Z0-9_]/', '', $variation);
30
-
31
- ?>
32
- <script type="text/javascript">
33
- if ( typeof jQuery != 'undefined' ) {
34
- jQuery(document).ready(function($) {
35
-
36
- var lead_cpt_id = _inbound.Utils.readCookie("wp_lead_id");
37
- var lead_email = _inbound.Utils.readCookie("wp_lead_email");
38
- var lead_unique_key = _inbound.Utils.readCookie("wp_lead_uid");
39
-
40
- if (typeof (lead_cpt_id) != "undefined" && lead_cpt_id !== null) {
41
- string = "&wpl_id=" + lead_cpt_id + "&l_type=wplid";
42
- } else if (typeof (lead_email) != "undefined" && lead_email !== null && lead_email !== "") {
43
- string = "&wpl_id=" + lead_email + "&l_type=wplemail";
44
- } else if (typeof (lead_unique_key) != "undefined" && lead_unique_key !== null && lead_unique_key !== "") {
45
- string = "&wpl_id=" + lead_unique_key + "&l_type=wpluid";
46
- } else {
47
- string = "";
48
- }
49
-
50
- var external = RegExp('^((f|ht)tps?:)?//(?!' + location.host + ')');
51
- jQuery('.wpl-track-me-link a').not("#wpadminbar a").each(function () {
52
-
53
- jQuery(this).attr("data-event-id", '<?php echo $post->ID; ?>').attr("data-cta-varation", '<?php echo $variation;?>');
54
-
55
- var orignalurl = jQuery(this).attr("href");
56
-
57
- var link_is = external.test(orignalurl);
58
-
59
- if (link_is === true) {
60
- base_url = window.location.origin;
61
- } else {
62
- base_url = orignalurl;
63
- }
64
-
65
- var variation_id = "&vid=" + jQuery(this).attr("data-cta-varation");
66
- var this_id = jQuery(this).attr("data-event-id");
67
- var newurl = base_url + "?lp_redirect_" + this_id + "=" + encodeURIComponent( orignalurl ) + variation_id + string;
68
- jQuery(this).attr("href", newurl);
69
- });
70
- });
71
- }
72
- </script>
73
- <?php
74
-
75
- }
76
-
77
- /**
78
- * Intecept tracked link, process it, and redirect visitor
79
- */
80
- public static function intecept_tracked_link() {
81
- global $wpdb;
82
- if ($qs = $_SERVER['REQUEST_URI']) {
83
- parse_str($qs, $output);
84
- (isset($output['l_type'])) ? $type = $output['l_type'] : $type = "";
85
- (isset($output['wpl_id'])) ? $lead_id = $output['wpl_id'] : $lead_id = "";
86
- (isset($output['vid'])) ? $variation_id = $output['vid'] : $variation_id = null;
87
- $pos = strpos($qs, 'lp_redirect');
88
- if (!(false === $pos)) {
89
- $link = substr($qs, $pos);
90
- $link = str_replace('lp_redirect=', '', $link); /* clean url */
91
-
92
- /* Extract the ID and get the link */
93
- $pattern = '/lp_redirect_(\d+?)\=/';
94
- preg_match($pattern, $link, $matches);
95
- $link = preg_replace($pattern, '', $link);
96
- $landing_page_id = $matches[1]; /* Event ID */
97
- $lead_ID = false;
98
- $append = true;
99
- /* If lead post id exists */
100
- if ($type === 'wplid') {
101
- $lead_ID = $lead_id;
102
- }
103
- /* If lead email exists */
104
- elseif ($type === 'wplemail') {
105
- $query = $wpdb->prepare(
106
- 'SELECT ID FROM ' . $wpdb->posts . '
107
- WHERE post_title = %s
108
- AND post_type = \'wp-lead\'',
109
- $lead_id
110
- );
111
- $wpdb->query( $query );
112
- if ( $wpdb->num_rows ) {
113
- $lead_ID = $wpdb->get_var( $query );
114
- }
115
- }
116
- /* If lead wp_uid exists */
117
- elseif ($type === 'wpluid') {
118
- $query = $wpdb->prepare(
119
- 'SELECT post_id FROM ' . $wpdb->prefix . 'postmeta
120
- WHERE meta_value = %s',
121
- $lead_id
122
- );
123
- $wpdb->query( $query );
124
- if ( $wpdb->num_rows ) {
125
- $lead_ID = $wpdb->get_var( $query );
126
- } else {
127
- $lead_ID = $lead_id;
128
- $append = false;
129
- }
130
- }
131
-
132
- /* Save click! */
133
- self::store_click( $landing_page_id, $variation_id); /* Store CTA data to CTA CPT */
134
-
135
- if( $lead_ID && $append != false ) {
136
- /* Add landing page click to lead profile */
137
- self::log_lead_click($landing_page_id, $lead_ID, $variation_id);
138
- }
139
- $link = preg_replace('/(?<=wpl_id)(.*)(?=&)/s', '', $link); /* clean url */
140
- $link = preg_replace('/&wpl_id&l_type=(\D*)/', '', $link); /* clean url2 */
141
- $link = preg_replace('/&vid=(\d*)/', '', $link); /* clean url3 */
142
- $link = urldecode( $link );
143
- /* Redirect */
144
- header("HTTP/1.1 302 Temporary Redirect");
145
- header("Location:" . $link);
146
- /* I'm outta here! */
147
- exit(1);
148
- }
149
- }
150
- }
151
-
152
- /**
153
- * Stores click data
154
- */
155
- public static function store_click($landing_page_id, $variation_id){
156
- $conversions = Landing_Pages_Variations::get_conversions( $landing_page_id , $variation_id );
157
- $conversions++;
158
- Landing_Pages_Variations::set_conversions_count( $landing_page_id , $variation_id , $conversions );
159
- }
160
-
161
- /**
162
- * Log Landing Page click data into Lead Profile. Needs refactoring
163
- */
164
- public static function log_lead_click($landing_page_id, $lead_ID, $variation_id) {
165
-
166
- /* Current wordpress time from settings */
167
- $time = current_time( 'timestamp', 0 );
168
- $wordpress_date_time = date("Y-m-d G:i:s T", $time);
169
-
170
- if ( !$lead_ID ) {
171
- return;
172
- }
173
-
174
- $conversion_data = get_post_meta( $lead_ID, 'wpleads_conversion_data', TRUE );
175
- $individual_event_count = get_post_meta( $lead_ID, 'wpleads_landing_page_'.$landing_page_id, TRUE );
176
- $individual_event_count = ($individual_event_count != "") ? $individual_event_count : 0;
177
- $individual_event_count++;
178
- /* todo replace times */
179
- $meta = get_post_meta( $lead_ID, 'times', TRUE );
180
- $meta++;
181
- $conversions_count = get_post_meta($lead_ID,'wpl-lead-conversion-count', true);
182
- $conversions_count++;
183
- if ($conversion_data) {
184
-
185
- $conversion_data = json_decode($conversion_data,true);
186
- $conversion_data[$meta]['id'] = $landing_page_id;
187
- $conversion_data[$meta]['variation'] = $variation_id;
188
- $conversion_data[$meta]['datetime'] = $wordpress_date_time;
189
- $conversion_data = json_encode($conversion_data);
190
- update_post_meta( $lead_ID, 'wpleads_conversion_data', $conversion_data );
191
- update_post_meta( $lead_ID, 'wpleads_landing_page_'.$landing_page_id, $individual_event_count );
192
- } else {
193
- $conversion_data[1]['id'] = $landing_page_id;
194
- $conversion_data[1]['variation'] = $variation_id;
195
- $conversion_data[1]['datetime'] = $wordpress_date_time;
196
- $conversion_data[1]['first_time'] = 1;
197
- /* Add in exact link url clicked */
198
- $conversion_data = json_encode($conversion_data);
199
- update_post_meta( $lead_ID, 'wpleads_conversion_data', $conversion_data );
200
- update_post_meta( $lead_ID, 'wpleads_landing_page_'.$landing_page_id, $individual_event_count );
201
- /* update_post_meta( $lead_ID, 'lt_event_tracked_'.$landing_page_id, $individual_event_count ); */
202
- }
203
- update_post_meta( $lead_ID, 'times', $meta );
204
- update_post_meta( $lead_ID, 'wpl-lead-conversion-count', $meta );
205
- /* Need to call conversion paths too */
206
- }
207
-
208
- }
209
-
210
- new Landing_Pages_Click_Tracking;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
classes/class.install.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  class Landing_Pages_Install {
4
 
5
  /**
1
  <?php
2
 
3
+ /**
4
+ * Class for creating example landing page on first install and prompting Leads and CTA GPL plugin downloads when Landing Pages GPL installed.
5
+ * @package LandingPages
6
+ * @subpackage Activation
7
+ */
8
+
9
  class Landing_Pages_Install {
10
 
11
  /**
classes/class.landing-pages.php CHANGED
@@ -1,8 +1,9 @@
1
  <?php
2
 
3
  /**
4
- * Class Landing_Pages_Loader
5
- * Loads landing page elements
 
6
  */
7
 
8
  class Landing_Pages_Template_Switcher {
@@ -82,8 +83,9 @@ class Landing_Pages_Template_Switcher {
82
 
83
  $content = Landing_Pages_Variations::get_post_content( $post->ID );
84
  $content = do_shortcode( $content );
85
- $content = wpautop( $content );
86
-
 
87
  return $content;
88
  }
89
 
@@ -570,7 +572,7 @@ function lp_rebuild_attributes($content = null, $wrapper_class = null) {
570
  foreach ($matches[0] as $key => $value) {
571
  if ($key == 0) {
572
  $new_value = $value;
573
- $new_value = preg_replace('/ class=(["\'])(.*?)(["\'])/', 'class="$2 wpl-track-me-link"', $new_value);
574
 
575
 
576
  $content = str_replace($value, $new_value, $content);
1
  <?php
2
 
3
  /**
4
+ * Class for loading landing page templates when landing page is called on the frontend.
5
+ * @package LandingPages
6
+ * @subpackage Templates
7
  */
8
 
9
  class Landing_Pages_Template_Switcher {
83
 
84
  $content = Landing_Pages_Variations::get_post_content( $post->ID );
85
  $content = do_shortcode( $content );
86
+ if (!defined('LANDING_PAGES_WPAUTOP') || LANDING_PAGES_WPAUTOP === TRUE) {
87
+ $content = wpautop($content);
88
+ }
89
  return $content;
90
  }
91
 
572
  foreach ($matches[0] as $key => $value) {
573
  if ($key == 0) {
574
  $new_value = $value;
575
+ $new_value = preg_replace('/ class=(["\'])(.*?)(["\'])/', 'class="$2 inbound-track-link"', $new_value);
576
 
577
 
578
  $content = str_replace($value, $new_value, $content);
classes/class.load-templates.php CHANGED
@@ -1,4 +1,9 @@
1
  <?php
 
 
 
 
 
2
 
3
  class Landing_Pages_Load_Extensions {
4
 
1
  <?php
2
+ /**
3
+ * Class for loading extensions and templates. Mostly templates. No extensions use this class anymore.
4
+ * @package LandingPages
5
+ * @subpackage Templates
6
+ */
7
 
8
  class Landing_Pages_Load_Extensions {
9
 
classes/class.metaboxes.php CHANGED
@@ -1,5 +1,10 @@
1
  <?php
2
 
 
 
 
 
 
3
 
4
  class Landing_Pages_Metaboxes {
5
 
@@ -692,7 +697,7 @@ href='?post=<?php echo $post->ID; ?>&action=edit&action-variation-id=<?php echo
692
 
693
  /*Clear The Room! */
694
  echo "<div style='clear:both; display:block;'></div>";
695
- echo "<div style='width:100%;text-align:right;margin-top:11px;'><div class='lp_tooltip' title=\"". __('To help track conversions Landing Pages Plugin will automatically add a tracking class to forms. If you would like to track a link add this class to it' , 'landing-pages') ." class='wpl-track-me-link'\" ></div></div>";
696
 
697
  }
698
 
1
  <?php
2
 
3
+ /**
4
+ * Class for rendering and storing data related to the landing-page CPT edit screen
5
+ * @package LandingPages
6
+ * @subpackage Management
7
+ */
8
 
9
  class Landing_Pages_Metaboxes {
10
 
697
 
698
  /*Clear The Room! */
699
  echo "<div style='clear:both; display:block;'></div>";
700
+ echo "<div style='width:100%;text-align:right;margin-top:11px;'><div class='lp_tooltip' title=\"". __('To help track conversions Landing Pages Plugin will automatically add a tracking class to forms. If you would like to track a link add this class to it' , 'landing-pages') ." class='inbound-track-link'\" ></div></div>";
701
 
702
  }
703
 
classes/class.post-type.landing-page.php CHANGED
@@ -1,641 +1,645 @@
1
  <?php
2
 
3
- if ( !class_exists('Landing_Pages_Post_Type') ) {
 
 
 
 
4
 
5
- class Landing_Pages_Post_Type {
6
 
7
- function __construct() {
8
- self::load_hooks();
9
- }
10
-
11
- /**
12
- * setup hooks and filters
13
- */
14
- private function load_hooks() {
15
- add_action('init', array( __CLASS__ , 'register_post_type' ) );
16
- add_action( 'admin_init' , array( __CLASS__ , 'register_role_capabilities' ) ,999);
17
- add_action('init', array( __CLASS__ , 'register_taxonomies' ) );
18
- add_action('init', array( __CLASS__ , 'add_rewrite_rules') );
19
- add_filter('mod_rewrite_rules', array( __CLASS__ , 'filter_rewrite_rules' ) , 1);
20
-
21
- /* adds & managed collumns */
22
- add_filter("manage_edit-landing-page_columns", array( __CLASS__ , 'register_columns' ) );
23
- add_action("manage_posts_custom_column", array( __CLASS__ , "display_columns" ) );
24
- add_filter('landing-page_orderby', 'lp_column_orderby', 10, 2);
25
-
26
- /* disable SEO Filter */
27
- if ((isset($_GET['post_type']) && ($_GET['post_type'] == 'landing-page'))) {
28
- add_filter('wpseo_use_page_analysis', '__return_false');
29
- }
30
 
31
- /* adds category to landing page sorting filter */
32
- add_action('restrict_manage_posts', array( __CLASS__, 'sort_by_category' ) );
33
- add_filter('parse_query', array( __CLASS__ , 'sort_by_category_prepare_query' ));
34
-
35
- /* make columns sortable */
36
- add_filter('manage_edit-landing-page_sortable_columns', array( __CLASS__ , 'define_sortable_columns' ));
37
 
38
- /* add styling handlers to custom post states */
39
- add_filter('display_post_states', array( __CLASS__ , 'filter_custom_post_states' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
40
 
41
- /* enqueue scripts for landing page listings */
42
- add_action( 'admin_enqueue_scripts' , array(__CLASS__, 'enqueue_admin_scripts' ) );
 
43
 
 
 
44
 
45
- /* enqueue scripts for landing page listings */
46
- if (isset($_GET['dont_save'])
47
- || isset($_GET['iframe_window'])
48
- || isset($_GET['inbound-preview']) ) {
49
- add_action('wp_enqueue_scripts', array(__CLASS__, 'stop_stat_tracking') , 20);
50
- }
51
 
52
- /* load iframed preview page when preview is clicked from AB stats box */
53
- if (isset($_GET['iframe_window'])) {
54
- /*add_action('wp_head', array( __CLASS__ , 'load_preview_iframe' ) );*/
55
- add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts_iframe') );
56
- }
57
 
58
- /* Miscelanous wp_head - Should probably be refactored into enqueue - h */
59
- add_action('wp_head', array(__CLASS__, 'wp_head' ));
60
 
 
 
 
 
 
61
  }
62
 
63
- /**
64
- * register post type
65
- */
66
- public static function register_post_type() {
 
67
 
68
- $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
69
- $featured_images = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-enable-featured-image', false );
70
 
71
- $capabilities = array('title','custom-fields','editor', 'revisions');
72
 
73
- if ($featured_images) {
74
- array_push($capabilities , 'thumbnail');
75
- }
 
76
 
77
- $labels = array(
78
- 'name' => __('Landing Pages', 'inbound-pro' ),
79
- 'singular_name' => __('Landing Page', 'inbound-pro' ),
80
- 'add_new' => __('Add New', 'inbound-pro' ),
81
- 'add_new_item' => __('Add New Landing Page' , 'inbound-pro' ),
82
- 'edit_item' => __('Edit Landing Page' , 'inbound-pro' ),
83
- 'new_item' => __('New Landing Page' , 'inbound-pro' ),
84
- 'view_item' => __('View Landing Page' , 'inbound-pro' ),
85
- 'search_items' => __('Search Landing Page' , 'inbound-pro' ),
86
- 'not_found' => __('Nothing found' , 'inbound-pro' ),
87
- 'not_found_in_trash' => __('Nothing found in Trash' , 'inbound-pro' ),
88
- 'parent_item_colon' => ''
89
- );
90
 
91
- $args = array(
92
- 'labels' => $labels,
93
- 'public' => true,
94
- 'publicly_queryable' => true,
95
- 'show_ui' => true,
96
- 'query_var' => true,
97
- 'menu_icon' => '',
98
- 'rewrite' => array("slug" => "$slug",'with_front' => false),
99
- 'capability_type' => array('landing_page','landing_pages'),
100
- 'map_meta_cap' => true,
101
- 'hierarchical' => false,
102
- 'menu_position' => 32,
103
- 'supports' => $capabilities
104
- );
105
 
106
- register_post_type( 'landing-page' , $args );
 
107
  }
108
 
109
- /**
110
- * Register Role Capabilities
111
- */
112
- public static function register_role_capabilities() {
113
- // Add the roles you'd like to administer the custom post types
114
- $roles = array('inbound_marketer','administrator');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
115
 
116
- // Loop through each role and assign capabilities
117
- foreach($roles as $the_role) {
 
 
 
 
118
 
119
- $role = get_role($the_role);
120
- if (!$role) {
121
- continue;
122
- }
123
 
124
- $role->add_cap( 'read' );
125
- $role->add_cap( 'read_landing_page');
126
- $role->add_cap( 'read_private_landing_pages' );
127
- $role->add_cap( 'edit_landing_page' );
128
- $role->add_cap( 'edit_landing_pages' );
129
- $role->add_cap( 'edit_others_landing_pages' );
130
- $role->add_cap( 'edit_published_landing_pages' );
131
- $role->add_cap( 'publish_landing_pages' );
132
- $role->add_cap( 'delete_landing_pages' );
133
- $role->add_cap( 'delete_others_landing_pages' );
134
- $role->add_cap( 'delete_private_landing_pages' );
135
- $role->add_cap( 'delete_published_landing_pages' );
136
  }
137
- }
138
 
139
- /**
140
- * Register landing page taxonomies
141
- */
142
- public static function register_taxonomies() {
143
-
144
- $args = array(
145
- 'hierarchical' => true,
146
- 'label' => __("Categories", 'inbound-pro'),
147
- 'singular_label' => __("Landing Page Category",
148
- 'landing-pages'),
149
- 'show_ui' => true,
150
- 'query_var' => true,
151
- "rewrite" => true
152
- );
153
-
154
- register_taxonomy( 'landing_page_category', array('landing-page'), $args);
155
  }
 
156
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
157
 
158
 
159
- /**
160
- * Register columns
161
- *
162
- * @param $columns
163
- * @return array
164
- */
165
- public static function register_columns($columns) {
166
- $columns = array(
167
- "cb" => "<input type=\"checkbox\" />",
168
- "thumbnail-lander" => __("Preview", 'inbound-pro'),
169
- "title" => __("Landing Page Title", 'inbound-pro'),
170
- "stats" => __("Variation Testing Stats", 'inbound-pro'),
171
- "impressions" => __("Total<br>Visits", 'inbound-pro'),
172
- "actions" => __("Total<br>Conversions", 'inbound-pro'),
173
- "cr" => __("Total<br>Conversion Rate", 'inbound-pro')
174
- );
175
- return $columns;
176
- }
177
-
178
- /**
179
- * Enqueue admin scripts
180
- */
181
- public static function enqueue_admin_scripts( $hook ) {
182
- global $post;
183
- $screen = get_current_screen();
184
-
185
- if (!isset($post) ||$post->post_type != 'landing-page') {
186
- return;
187
- }
188
 
189
- wp_enqueue_style('lp-content-stats', LANDINGPAGES_URLPATH . 'assets/css/admin/content-stats.css', array() , null);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
190
 
191
- /* listing page only */
192
- if ($screen->id == 'edit-landing-page' ) {
193
- /* load stat clear handlers */
194
- wp_enqueue_script( 'lp-admin-clear-stats-ajax-request', LANDINGPAGES_URLPATH . 'assets/js/ajax.clearstats.js', array( 'jquery' ) , null );
195
- wp_localize_script( 'lp-admin-clear-stats-ajax-request', 'ajaxadmin', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'lp_clear_nonce' => wp_create_nonce('lp-clear-nonce') ) );
 
196
 
197
- wp_enqueue_script('landing-page-list', LANDINGPAGES_URLPATH . 'assets/js/admin/admin.landing-page-list.js', array() , null);
198
- wp_enqueue_style('landing-page-list-css', LANDINGPAGES_URLPATH.'assets/css/admin/landing-page-list.css', array() , null);
199
- wp_enqueue_script('jqueryui');
200
 
201
- }
202
 
 
 
 
 
 
203
 
 
 
 
204
 
205
- /* load css when landing page iframe preview is being loaded from within wp-admin */
206
- if (isset($_GET['iframe_window'])) {
207
- wp_enqueue_style('lp_ab_testing_customizer_css', LANDINGPAGES_URLPATH . 'assets/css/frontend/customizer-preview.css', array() , null);
208
- }
209
  }
210
 
211
- /**
212
- * Enqueue frontend scripts
213
- */
214
- public static function enqueue_frontend_scripts() {
215
- global $post;
216
- if ( !isset($post) && $post->post_type=='landing-page') {
217
- return;
218
- }
219
 
220
- wp_enqueue_style('inbound-wordpress-base', LANDINGPAGES_URLPATH . 'assets/css/frontend/global-landing-page-style.css', array() , null);
221
- wp_enqueue_style('inbound-shortcodes', INBOUND_FORMS.'css/frontend-render.css', array() , null);
222
 
 
 
 
 
 
223
 
 
 
 
 
 
 
 
224
  }
225
 
226
- /**
227
- * Display column data
228
- * @param $columns
229
- * @return array
230
- */
231
- public static function display_columns($column) {
232
- global $post;
233
 
234
- if ($post->post_type != 'landing-page') return;
235
 
236
- switch ($column) {
237
- case 'ID':
238
- echo $post->ID;
239
- BREAK;
240
- case 'thumbnail-lander':
241
 
242
- $template = get_post_meta($post->ID, 'lp-selected-template', true);
243
- $permalink = get_permalink($post->ID);
244
- $datetime = the_modified_date('YmjH', null, null, false);
245
- $permalink = $permalink = $permalink . '?dt=' . $datetime;
 
 
 
246
 
247
- if (in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
248
 
249
- if(file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.png')) {
250
- $thumbnail = LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.png';
251
- } else if(file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.jpg')) {
 
 
252
 
253
- $thumbnail = LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.jpg';
 
 
 
254
 
255
- } else {
256
- $thumbnail = LANDINGPAGES_URLPATH . 'templates/' . $template . '/thumbnail.png';
257
- }
 
 
 
 
258
 
259
  } else {
260
- $thumbnail = 'http://s.wordpress.com/mshots/v1/' . urlencode(esc_url($permalink)) . '?w=140';
261
  }
262
 
263
- echo "<a title='" . __('Click to Preview this variation', 'inbound-pro') . "' class='thickbox' href='" . $permalink . "?lp-variation-id=0&iframe_window=on&post_id=" . $post->ID . "&TB_iframe=true&width=640&height=703' target='_blank'><img src='" . $thumbnail . "' style='width:155px;height:110px;' title='Click to Preview'></a>";
264
- BREAK;
265
- case "stats":
266
- self::show_stats();
267
- BREAK;
268
- case "impressions" :
269
- echo self::show_aggregated_stats("impressions");
270
- BREAK;
271
- case "actions":
272
- echo self::show_aggregated_stats("actions");
273
- BREAK;
274
- case "cr":
275
- echo self::show_aggregated_stats("cr") . "%";
276
- BREAK;
277
- case "template":
278
- $template_used = Landing_Pages_Variations::get_current_template( $post->ID );
279
- echo $template_used;
280
- BREAK;
281
- }
282
- }
283
-
284
 
285
- /**
286
- * Define Sortable Columns
287
- */
288
- public static function define_sortable_columns($columns) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
289
 
290
- return array(
291
- 'title' => 'title',
292
- 'impressions' => 'impressions',
293
- 'actions' => 'actions',
294
- 'cr' => 'cr'
295
- );
296
 
297
- }
 
 
 
298
 
299
- /**
300
- * Define Row Actions
301
- */
302
- public static function filter_row_actions( $actions , $post ) {
 
 
303
 
304
- if ($post->post_type=='wp-call-to-action') {
305
- $actions['clear'] = '<a href="#clear-stats" id="wp_cta_clear_'.$post->ID.'" class="clear_stats" title="'
306
- . __( 'Clear impression and conversion records', 'cta' )
307
- . '" >' . __( 'Clear All Stats' , 'cta') . '</a>';
308
 
309
- /* show shortcode */
310
- $actions['clear'] .= '<br><span style="color:#000;">' . __( 'Shortcode:' , 'cta' ) .'</span> <input type="text" style="width: 60%; text-align: center;" class="regular-text code short-shortcode-input" readonly="readonly" id="shortcode" name="shortcode" value="[cta id=\''.$post->ID.'\']">';
311
- }
 
312
 
313
- return $actions;
 
 
 
314
 
 
 
315
  }
316
 
 
317
 
318
- /**
319
- * Needs further refactoring & documentation
320
- * @param $type_of_stat
321
- * @return float|int
322
- */
323
- public static function show_aggregated_stats($type_of_stat) {
324
- global $post;
325
 
326
- $variations = get_post_meta($post->ID, 'lp-ab-variations', true);
327
- $variations = explode(",", $variations);
328
 
329
- $impressions = 0;
330
- $conversions = 0;
 
 
 
 
 
331
 
332
- foreach ($variations as $vid) {
333
- $each_impression = get_post_meta($post->ID, 'lp-ab-variation-impressions-' . $vid, true);
334
- $each_conversion = get_post_meta($post->ID, 'lp-ab-variation-conversions-' . $vid, true);
335
- (($each_conversion === "")) ? $final_conversion = 0 : $final_conversion = $each_conversion;
336
- $impressions += get_post_meta($post->ID, 'lp-ab-variation-impressions-' . $vid, true);
337
- $conversions += get_post_meta($post->ID, 'lp-ab-variation-conversions-' . $vid, true);
338
- }
339
 
340
- if ($type_of_stat === "actions") {
341
- return $conversions;
342
- }
343
- if ($type_of_stat === "impressions") {
344
- return $impressions;
345
- }
346
- if ($type_of_stat === "cr") {
347
- if ($impressions != 0) {
348
- $conversion_rate = $conversions / $impressions;
349
- } else {
350
- $conversion_rate = 0;
351
- }
352
- $conversion_rate = round($conversion_rate, 2) * 100;
353
- return $conversion_rate;
354
- }
355
 
 
 
 
 
 
 
356
  }
357
 
358
- /**
359
- * Adds rewrite rules
360
- */
361
- public static function add_rewrite_rules() {
362
- if ( !class_exists('Inbound_Pro_Plugin')){
363
- $this_path = LANDINGPAGES_PATH;
364
- $this_path = explode('wp-content', $this_path);
365
- $this_path = "wp-content" . $this_path[1];
 
366
  } else {
367
- $this_path = INBOUND_PRO_PATH;
368
- $this_path = explode('wp-content', $this_path);
369
- $this_path = "wp-content" . $this_path[1] . "core/landing-pages/";
370
  }
 
 
 
371
 
372
- /* handles local environment */
373
- $this_path = str_replace("\\" , "/" , $this_path);
374
-
375
- $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
376
-
377
- $ab_testing = Landing_Pages_Settings::get_setting('lp-main-landing-page-disable-turn-off-ab', "0");
378
- if ($ab_testing === "0") {
379
- add_rewrite_rule("$slug/([^/]*)/([0-9]+)/", "$slug/$1?lp-variation-id=$2", 'top');
380
- add_rewrite_rule("$slug/([^/]*)?", $this_path . "modules/module.redirect-ab-testing.php?permalink_name=$1 ", 'top');
381
- add_rewrite_rule("landing-page=([^/]*)?", $this_path . 'modules/module.redirect-ab-testing.php?permalink_name=$1', 'top');
382
- }
383
 
 
 
 
 
 
 
 
 
 
 
 
 
384
  }
385
 
386
- /**
387
- * Adds conditions to rewrite rules
388
- * @param $rules
389
- * @return string
390
- */
391
- public static function filter_rewrite_rules( $rules ) {
392
- if (stristr($rules, 'RewriteCond %{QUERY_STRING} !lp-variation-id')) {
393
- return $rules;
394
- }
395
 
396
- $rules_array = preg_split('/$\R?^/m', $rules);
397
 
398
- if (count($rules_array) < 3) {
399
- $rules_array = explode("\n", $rules);
400
- $rules_array = array_filter($rules_array);
401
- }
 
 
402
 
403
- /* print_r($rules_array);exit; */
404
 
 
 
 
 
 
 
 
 
 
405
 
406
- $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
407
 
408
- $i = 0;
409
- foreach ($rules_array as $key => $val) {
 
 
410
 
411
- if (stristr($val, "RewriteRule ^{$slug}/([^/]*)? ") || stristr($val, "RewriteRule ^{$slug}/([^/]*)/([0-9]+)/ ")) {
412
- $new_val = "RewriteCond %{QUERY_STRING} !lp-variation-id";
413
- $rules_array[$i] = $new_val;
414
- $i++;
415
- $rules_array[$i] = $val;
416
- $i++;
417
- } else {
418
- $rules_array[$i] = $val;
419
- $i++;
420
- }
421
- }
422
 
423
- $rules = implode("\r\n", $rules_array);
424
 
 
425
 
426
- return $rules;
 
 
 
 
 
 
 
 
 
 
 
 
427
  }
428
 
 
429
 
430
- /**
431
- * Show stats container on Landing Page lists page
432
- */
433
- public static function show_stats() {
434
 
435
- global $post;
436
- $permalink = get_permalink($post->ID);
437
- $variations = Landing_Pages_Variations::get_variations($post->ID);
438
 
439
- echo "<span class='show-stats button'> " . __('Show Variation Stats', 'inbound-pro') . "</span>";
440
- echo "<ul class='lp-varation-stat-ul'>";
441
- $cr_array = array();
442
- $i = 0;
443
 
444
- foreach ($variations as $key => $vid) {
445
- $letter = Landing_Pages_Variations::vid_to_letter($post->ID, $key); /* convert to letter */
446
- $impressions = Landing_Pages_Variations::get_impressions($post->ID, $vid);
447
- $conversions = Landing_Pages_Variations::get_conversions($post->ID, $vid);
448
 
449
- /* get variation status */
450
- $status = Landing_Pages_Variations::get_variation_status( $post->ID, $vid ); /* Current status */
 
451
 
452
- /* Get variation notes */
453
- $each_notes = Landing_Pages_Variations::get_variation_notes( $post->ID, $vid );
 
 
454
 
455
- if ($impressions) {
456
- $conversion_rate = $conversions / $impressions;
457
- } else {
458
- $conversion_rate = 0;
459
- }
460
- $conversion_rate = round($conversion_rate, 2) * 100;
461
 
462
- $cr_array[] = $conversion_rate;
 
463
 
464
- $data_letter = "data-letter=\"" . $letter . "\"";
465
- $edit_link = admin_url('post.php?post=' . $post->ID . '&lp-variation-id=' . $vid . '&action=edit');
466
- echo "<li rel='" . $status . "' data-postid='" . $post->ID . "' data-letter='" . $letter . "' data-lp='' class='lp-stat-row-" . $vid . " " . $post->ID . '-' . $conversion_rate . " status-" . $status . "'><a class='lp-letter' title='click to edit this variation' href='" . $edit_link . "'>" . $letter . "</a><span class='lp-numbers'><span class='lp-visitors'><span class='visit-text'>" . __( 'Impressions' , 'inbound-pro' ) . "</span><span class='lp-impress-num'>" . $impressions . "</span></span> <span class='lp-conversions'> <span class='lp-conversion-txt'>" . __( 'Conversions' , 'inbound-pro' ) . "</span> <span class='lp-con-num'>" . $conversions . "</span> </span> </span><a ". $data_letter . " class='cr-number cr-empty-" . $conversion_rate . "' href='" . $edit_link . "'>" . $conversion_rate . "%</a></li>";
467
- $i++;
468
- }
469
- echo "</ul>";
470
- $winning_cr = max($cr_array); /* best conversion rate */
471
- if ($winning_cr != 0) {
472
- echo "<span class='variation-winner-is'>" . $post->ID . "-" . $winning_cr . "</span>";
473
  }
474
- /*echo "Total Visits: " . $impressions; */
475
- /*echo "Total Conversions: " . $conversions; */
 
476
 
 
 
 
 
477
  }
 
 
 
 
 
 
 
 
 
478
 
479
 
480
- /**
481
- * Show dropdown of landing page categories
482
- */
483
- public static function sort_by_category() {
484
- global $typenow;
485
 
486
- if ($typenow != "landing-page") {
487
- return;
488
- }
489
 
490
 
491
- $filters = get_object_taxonomies($typenow);
492
-
493
- foreach ($filters as $tax_slug) {
494
-
495
- $tax_obj = get_taxonomy($tax_slug);
496
- (isset($_GET[$tax_slug])) ? $current = $_GET[$tax_slug] : $current = 0;
497
- wp_dropdown_categories(
498
- array(
499
- 'show_option_all' => __('Show All ' . $tax_obj->label),
500
- 'taxonomy' => $tax_slug,
501
- 'name' => $tax_obj->name,
502
- 'orderby' => 'name',
503
- 'selected' => $current,
504
- 'hierarchical' => $tax_obj->hierarchical,
505
- 'show_count' => false,
506
- 'hide_empty' => true
507
- )
508
- );
509
- }
510
  }
 
511
 
512
- /**
513
- * Convert the category id to the taxonomy id during a query
514
- */
515
- public static function sort_by_category_prepare_query() {
516
- global $pagenow;
517
- $qv = &$query->query_vars;
518
- if ($pagenow == 'edit.php' && isset($qv['landing_page_category']) && is_numeric($qv['landing_page_category'])) {
519
- $term = get_term_by('id', $qv['landing_page_category'], 'landing_page_category');
520
- $qv['landing_page_category'] = $term->slug;
521
- }
522
  }
 
523
 
524
- /**
525
- * Add styling handlers to custom post states
526
- */
527
- public static function filter_custom_post_states($post_states) {
528
- foreach ($post_states as &$state) {
529
- $state = '<span class="' . strtolower($state) . ' states">' . str_replace(' ', '-', $state) . '</span>';
530
- }
531
- return $post_states;
532
  }
 
 
533
 
534
- /**
535
- * Loads preview iframe. Currently disabled. Plans to update @DavidWells
536
- */
537
- public static function load_preview_iframe() {
538
 
539
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
540
- $landing_page_id = $_GET['post_id'];
541
 
542
- $variations = Landing_Pages_Variations::get_variations( $landing_page_id );
543
- ?>
544
- <link rel="stylesheet" href="<?php echo LANDINGPAGES_URLPATH . 'assets/css/customizer-ab-testing.css';?>"/>
545
- <style type="text/css">
546
 
547
- #variation-list {
548
- position: absolute;
549
- top: 0px;
550
- left: 0px;
551
- padding-left: 5px;
552
- }
553
 
554
- #variation-list h3 {
555
- text-decoration: none;
556
- border-bottom: none;
557
- }
558
 
559
- #variation-list div {
560
- display: inline-block;
561
- }
562
 
563
- #current_variation_id, #current-post-id {
564
- display: none !important;
565
- }
566
 
567
- </style>
568
- <script type="text/javascript">
569
- jQuery(document).ready(function ($) {
570
- var current_page = jQuery("#current_variation_id").text();
571
- /* reload the iframe preview page (for option toggles) */
572
- jQuery('.variation-lp').on('click', function (event) {
573
- variation_is = jQuery(this).attr("id");
574
- var original_url = jQuery(parent.document).find("#TB_iframeContent").attr("src");
575
- var current_id = jQuery("#current-post-id").text();
576
- someURL = original_url;
577
-
578
- splitURL = someURL.split('?');
579
- someURL = splitURL[0];
580
- new_url = someURL + "?lp-variation-id=" + variation_is + "&iframe_window=on&post_id=" + current_id;
581
- jQuery(parent.document).find("#TB_iframeContent").attr("src", new_url);
582
- });
583
  });
584
- </script>
585
- <?php
586
- }
 
587
 
588
- /**
589
- * Load JS to disable stats from working for preview windows
590
- */
591
- public static function stop_stat_tracking() {
592
- show_admin_bar(false);
593
- wp_enqueue_script('stop-inbound-stats-js', LANDINGPAGES_URLPATH . 'assets/js/stop_page_stats.js' , array('inbound-analytics') , null );
594
- wp_enqueue_style('inbound-preview-window-css', LANDINGPAGES_URLPATH . 'assets/css/iframe-preview.css' , array() , null);
 
 
 
 
 
 
 
 
 
 
595
  }
 
 
 
 
 
 
 
 
 
 
 
 
596
 
597
- /**
598
- * Load misc wp_head
599
- */
600
- public static function wp_head() {
601
- global $post;
602
 
603
- if (isset($post) && $post->post_type !=='landing-page') {
604
- return;
605
- }
606
- /* if is tiny iframe preview window force these styles */
607
- if (isset($_GET['dont_save'])) { ?>
608
- <style type="text/css">
609
- :root:root:root #wpadminbar {
610
- display:none !important;
611
- }
612
- :root:root:root {
613
- margin-top: 0px !important;
614
- min-height: 714px !important;
615
  }
616
- </style>
617
- <?php }
618
-
619
- if (isset($_GET['lp-variation-id']) && !isset($_GET['inbound-customizer']) && !isset($_GET['iframe_window']) && !isset($_GET['live-preview-area'])) {
620
- ?>
621
-
622
- <?php
623
- if(!defined('Inbound_Now_Disable_URL_CLEAN')) {
624
- ?>
625
- <script type="text/javascript">
626
- /* Then strip params if pushstate exists */
627
- if (typeof window.history.pushState == 'function') {
628
- var cleanparams=window.location.href.split("?");
629
- var clean_url=cleanparams[0];history.replaceState({},"landing page",clean_url);
630
- }
631
- </script>
632
- <?php
633
- }
634
  }
635
-
636
  }
 
637
  }
 
638
 
639
- /* Load Post Type Pre Init */
640
- new Landing_Pages_Post_Type();
641
- }
1
  <?php
2
 
3
+ /**
4
+ * Class for registering the landing page CPT and expanding the CPT's listing page with additional data
5
+ * @package LandingPages
6
+ * @subpackage Management
7
+ */
8
 
 
9
 
10
+ class Landing_Pages_Post_Type {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ function __construct() {
13
+ self::load_hooks();
14
+ }
 
 
 
15
 
16
+ /**
17
+ * setup hooks and filters
18
+ */
19
+ private function load_hooks() {
20
+ add_action('init', array( __CLASS__ , 'register_post_type' ) );
21
+ add_action( 'admin_init' , array( __CLASS__ , 'register_role_capabilities' ) ,999);
22
+ add_action('init', array( __CLASS__ , 'register_taxonomies' ) );
23
+ add_action('init', array( __CLASS__ , 'add_rewrite_rules') );
24
+ add_filter('mod_rewrite_rules', array( __CLASS__ , 'filter_rewrite_rules' ) , 1);
25
+
26
+ /* adds & managed collumns */
27
+ add_filter("manage_edit-landing-page_columns", array( __CLASS__ , 'register_columns' ) );
28
+ add_action("manage_posts_custom_column", array( __CLASS__ , "display_columns" ) );
29
+ add_filter('landing-page_orderby', 'lp_column_orderby', 10, 2);
30
+
31
+ /* disable SEO Filter */
32
+ if ((isset($_GET['post_type']) && ($_GET['post_type'] == 'landing-page'))) {
33
+ add_filter('wpseo_use_page_analysis', '__return_false');
34
+ }
35
 
36
+ /* adds category to landing page sorting filter */
37
+ add_action('restrict_manage_posts', array( __CLASS__, 'sort_by_category' ) );
38
+ add_filter('parse_query', array( __CLASS__ , 'sort_by_category_prepare_query' ));
39
 
40
+ /* make columns sortable */
41
+ add_filter('manage_edit-landing-page_sortable_columns', array( __CLASS__ , 'define_sortable_columns' ));
42
 
43
+ /* add styling handlers to custom post states */
44
+ add_filter('display_post_states', array( __CLASS__ , 'filter_custom_post_states' ) );
 
 
 
 
45
 
46
+ /* enqueue scripts for landing page listings */
47
+ add_action( 'admin_enqueue_scripts' , array(__CLASS__, 'enqueue_admin_scripts' ) );
 
 
 
48
 
 
 
49
 
50
+ /* enqueue scripts for landing page listings */
51
+ if (isset($_GET['dont_save'])
52
+ || isset($_GET['iframe_window'])
53
+ || isset($_GET['inbound-preview']) ) {
54
+ add_action('wp_enqueue_scripts', array(__CLASS__, 'stop_stat_tracking') , 20);
55
  }
56
 
57
+ /* load iframed preview page when preview is clicked from AB stats box */
58
+ if (isset($_GET['iframe_window'])) {
59
+ /*add_action('wp_head', array( __CLASS__ , 'load_preview_iframe' ) );*/
60
+ add_action('admin_enqueue_scripts', array(__CLASS__, 'enqueue_scripts_iframe') );
61
+ }
62
 
63
+ /* Miscelanous wp_head - Should probably be refactored into enqueue - h */
64
+ add_action('wp_head', array(__CLASS__, 'wp_head' ));
65
 
66
+ }
67
 
68
+ /**
69
+ * register post type
70
+ */
71
+ public static function register_post_type() {
72
 
73
+ $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
74
+ $featured_images = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-enable-featured-image', false );
 
 
 
 
 
 
 
 
 
 
 
75
 
76
+ $capabilities = array('title','custom-fields','editor', 'revisions');
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ if ($featured_images) {
79
+ array_push($capabilities , 'thumbnail');
80
  }
81
 
82
+ $labels = array(
83
+ 'name' => __('Landing Pages', 'inbound-pro' ),
84
+ 'singular_name' => __('Landing Page', 'inbound-pro' ),
85
+ 'add_new' => __('Add New', 'inbound-pro' ),
86
+ 'add_new_item' => __('Add New Landing Page' , 'inbound-pro' ),
87
+ 'edit_item' => __('Edit Landing Page' , 'inbound-pro' ),
88
+ 'new_item' => __('New Landing Page' , 'inbound-pro' ),
89
+ 'view_item' => __('View Landing Page' , 'inbound-pro' ),
90
+ 'search_items' => __('Search Landing Page' , 'inbound-pro' ),
91
+ 'not_found' => __('Nothing found' , 'inbound-pro' ),
92
+ 'not_found_in_trash' => __('Nothing found in Trash' , 'inbound-pro' ),
93
+ 'parent_item_colon' => ''
94
+ );
95
+
96
+ $args = array(
97
+ 'labels' => $labels,
98
+ 'public' => true,
99
+ 'publicly_queryable' => true,
100
+ 'show_ui' => true,
101
+ 'query_var' => true,
102
+ 'menu_icon' => '',
103
+ 'rewrite' => array("slug" => "$slug",'with_front' => false),
104
+ 'capability_type' => array('landing_page','landing_pages'),
105
+ 'map_meta_cap' => true,
106
+ 'hierarchical' => false,
107
+ 'menu_position' => 32,
108
+ 'supports' => $capabilities
109
+ );
110
+
111
+ register_post_type( 'landing-page' , $args );
112
+ }
113
 
114
+ /**
115
+ * Register Role Capabilities
116
+ */
117
+ public static function register_role_capabilities() {
118
+ // Add the roles you'd like to administer the custom post types
119
+ $roles = array('inbound_marketer','administrator');
120
 
121
+ // Loop through each role and assign capabilities
122
+ foreach($roles as $the_role) {
 
 
123
 
124
+ $role = get_role($the_role);
125
+ if (!$role) {
126
+ continue;
 
 
 
 
 
 
 
 
 
127
  }
 
128
 
129
+ $role->add_cap( 'read' );
130
+ $role->add_cap( 'read_landing_page');
131
+ $role->add_cap( 'read_private_landing_pages' );
132
+ $role->add_cap( 'edit_landing_page' );
133
+ $role->add_cap( 'edit_landing_pages' );
134
+ $role->add_cap( 'edit_others_landing_pages' );
135
+ $role->add_cap( 'edit_published_landing_pages' );
136
+ $role->add_cap( 'publish_landing_pages' );
137
+ $role->add_cap( 'delete_landing_pages' );
138
+ $role->add_cap( 'delete_others_landing_pages' );
139
+ $role->add_cap( 'delete_private_landing_pages' );
140
+ $role->add_cap( 'delete_published_landing_pages' );
 
 
 
 
141
  }
142
+ }
143
 
144
+ /**
145
+ * Register landing page taxonomies
146
+ */
147
+ public static function register_taxonomies() {
148
+
149
+ $args = array(
150
+ 'hierarchical' => true,
151
+ 'label' => __("Categories", 'inbound-pro'),
152
+ 'singular_label' => __("Landing Page Category",
153
+ 'landing-pages'),
154
+ 'show_ui' => true,
155
+ 'query_var' => true,
156
+ "rewrite" => true
157
+ );
158
+
159
+ register_taxonomy( 'landing_page_category', array('landing-page'), $args);
160
+ }
161
 
162
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
163
 
164
+ /**
165
+ * Register columns
166
+ *
167
+ * @param $columns
168
+ * @return array
169
+ */
170
+ public static function register_columns($columns) {
171
+ $columns = array(
172
+ "cb" => "<input type=\"checkbox\" />",
173
+ "thumbnail-lander" => __("Preview", 'inbound-pro'),
174
+ "title" => __("Landing Page Title", 'inbound-pro'),
175
+ "stats" => __("Variation Testing Stats", 'inbound-pro'),
176
+ "impressions" => __("Total<br>Visits", 'inbound-pro'),
177
+ "actions" => __("Total<br>Conversions", 'inbound-pro'),
178
+ "cr" => __("Total<br>Conversion Rate", 'inbound-pro')
179
+ );
180
+ return $columns;
181
+ }
182
 
183
+ /**
184
+ * Enqueue admin scripts
185
+ */
186
+ public static function enqueue_admin_scripts( $hook ) {
187
+ global $post;
188
+ $screen = get_current_screen();
189
 
190
+ if (!isset($post) ||$post->post_type != 'landing-page') {
191
+ return;
192
+ }
193
 
194
+ wp_enqueue_style('lp-content-stats', LANDINGPAGES_URLPATH . 'assets/css/admin/content-stats.css', array() , null);
195
 
196
+ /* listing page only */
197
+ if ($screen->id == 'edit-landing-page' ) {
198
+ /* load stat clear handlers */
199
+ wp_enqueue_script( 'lp-admin-clear-stats-ajax-request', LANDINGPAGES_URLPATH . 'assets/js/ajax.clearstats.js', array( 'jquery' ) , null );
200
+ wp_localize_script( 'lp-admin-clear-stats-ajax-request', 'ajaxadmin', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'lp_clear_nonce' => wp_create_nonce('lp-clear-nonce') ) );
201
 
202
+ wp_enqueue_script('landing-page-list', LANDINGPAGES_URLPATH . 'assets/js/admin/admin.landing-page-list.js', array() , null);
203
+ wp_enqueue_style('landing-page-list-css', LANDINGPAGES_URLPATH.'assets/css/admin/landing-page-list.css', array() , null);
204
+ wp_enqueue_script('jqueryui');
205
 
 
 
 
 
206
  }
207
 
 
 
 
 
 
 
 
 
208
 
 
 
209
 
210
+ /* load css when landing page iframe preview is being loaded from within wp-admin */
211
+ if (isset($_GET['iframe_window'])) {
212
+ wp_enqueue_style('lp_ab_testing_customizer_css', LANDINGPAGES_URLPATH . 'assets/css/frontend/customizer-preview.css', array() , null);
213
+ }
214
+ }
215
 
216
+ /**
217
+ * Enqueue frontend scripts
218
+ */
219
+ public static function enqueue_frontend_scripts() {
220
+ global $post;
221
+ if ( !isset($post) && $post->post_type=='landing-page') {
222
+ return;
223
  }
224
 
225
+ wp_enqueue_style('inbound-wordpress-base', LANDINGPAGES_URLPATH . 'assets/css/frontend/global-landing-page-style.css', array() , null);
226
+ wp_enqueue_style('inbound-shortcodes', INBOUND_FORMS.'css/frontend-render.css', array() , null);
 
 
 
 
 
227
 
 
228
 
229
+ }
 
 
 
 
230
 
231
+ /**
232
+ * Display column data
233
+ * @param $columns
234
+ * @return array
235
+ */
236
+ public static function display_columns($column) {
237
+ global $post;
238
 
239
+ if ($post->post_type != 'landing-page') return;
240
 
241
+ switch ($column) {
242
+ case 'ID':
243
+ echo $post->ID;
244
+ BREAK;
245
+ case 'thumbnail-lander':
246
 
247
+ $template = get_post_meta($post->ID, 'lp-selected-template', true);
248
+ $permalink = get_permalink($post->ID);
249
+ $datetime = the_modified_date('YmjH', null, null, false);
250
+ $permalink = $permalink = $permalink . '?dt=' . $datetime;
251
 
252
+ if (in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
253
+
254
+ if(file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.png')) {
255
+ $thumbnail = LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.png';
256
+ } else if(file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.jpg')) {
257
+
258
+ $thumbnail = LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.jpg';
259
 
260
  } else {
261
+ $thumbnail = LANDINGPAGES_URLPATH . 'templates/' . $template . '/thumbnail.png';
262
  }
263
 
264
+ } else {
265
+ $thumbnail = 'http://s.wordpress.com/mshots/v1/' . urlencode(esc_url($permalink)) . '?w=140';
266
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
 
268
+ echo "<a title='" . __('Click to Preview this variation', 'inbound-pro') . "' class='thickbox' href='" . $permalink . "?lp-variation-id=0&iframe_window=on&post_id=" . $post->ID . "&TB_iframe=true&width=640&height=703' target='_blank'><img src='" . $thumbnail . "' style='width:155px;height:110px;' title='Click to Preview'></a>";
269
+ BREAK;
270
+ case "stats":
271
+ self::show_stats();
272
+ BREAK;
273
+ case "impressions" :
274
+ echo self::show_aggregated_stats("impressions");
275
+ BREAK;
276
+ case "actions":
277
+ echo self::show_aggregated_stats("actions");
278
+ BREAK;
279
+ case "cr":
280
+ echo self::show_aggregated_stats("cr") . "%";
281
+ BREAK;
282
+ case "template":
283
+ $template_used = Landing_Pages_Variations::get_current_template( $post->ID );
284
+ echo $template_used;
285
+ BREAK;
286
+ }
287
+ }
288
 
 
 
 
 
 
 
289
 
290
+ /**
291
+ * Define Sortable Columns
292
+ */
293
+ public static function define_sortable_columns($columns) {
294
 
295
+ return array(
296
+ 'title' => 'title',
297
+ 'impressions' => 'impressions',
298
+ 'actions' => 'actions',
299
+ 'cr' => 'cr'
300
+ );
301
 
302
+ }
 
 
 
303
 
304
+ /**
305
+ * Define Row Actions
306
+ */
307
+ public static function filter_row_actions( $actions , $post ) {
308
 
309
+ if ($post->post_type=='wp-call-to-action') {
310
+ $actions['clear'] = '<a href="#clear-stats" id="wp_cta_clear_'.$post->ID.'" class="clear_stats" title="'
311
+ . __( 'Clear impression and conversion records', 'cta' )
312
+ . '" >' . __( 'Clear All Stats' , 'cta') . '</a>';
313
 
314
+ /* show shortcode */
315
+ $actions['clear'] .= '<br><span style="color:#000;">' . __( 'Shortcode:' , 'cta' ) .'</span> <input type="text" style="width: 60%; text-align: center;" class="regular-text code short-shortcode-input" readonly="readonly" id="shortcode" name="shortcode" value="[cta id=\''.$post->ID.'\']">';
316
  }
317
 
318
+ return $actions;
319
 
320
+ }
 
 
 
 
 
 
321
 
 
 
322
 
323
+ /**
324
+ * Needs further refactoring & documentation
325
+ * @param $type_of_stat
326
+ * @return float|int
327
+ */
328
+ public static function show_aggregated_stats($type_of_stat) {
329
+ global $post;
330
 
331
+ $variations = get_post_meta($post->ID, 'lp-ab-variations', true);
332
+ $variations = explode(",", $variations);
 
 
 
 
 
333
 
334
+ $impressions = 0;
335
+ $conversions = 0;
 
 
 
 
 
 
 
 
 
 
 
 
 
336
 
337
+ foreach ($variations as $vid) {
338
+ $each_impression = get_post_meta($post->ID, 'lp-ab-variation-impressions-' . $vid, true);
339
+ $each_conversion = get_post_meta($post->ID, 'lp-ab-variation-conversions-' . $vid, true);
340
+ (($each_conversion === "")) ? $final_conversion = 0 : $final_conversion = $each_conversion;
341
+ $impressions += get_post_meta($post->ID, 'lp-ab-variation-impressions-' . $vid, true);
342
+ $conversions += get_post_meta($post->ID, 'lp-ab-variation-conversions-' . $vid, true);
343
  }
344
 
345
+ if ($type_of_stat === "actions") {
346
+ return $conversions;
347
+ }
348
+ if ($type_of_stat === "impressions") {
349
+ return $impressions;
350
+ }
351
+ if ($type_of_stat === "cr") {
352
+ if ($impressions != 0) {
353
+ $conversion_rate = $conversions / $impressions;
354
  } else {
355
+ $conversion_rate = 0;
 
 
356
  }
357
+ $conversion_rate = round($conversion_rate, 2) * 100;
358
+ return $conversion_rate;
359
+ }
360
 
361
+ }
 
 
 
 
 
 
 
 
 
 
362
 
363
+ /**
364
+ * Adds rewrite rules
365
+ */
366
+ public static function add_rewrite_rules() {
367
+ if ( !class_exists('Inbound_Pro_Plugin')){
368
+ $this_path = LANDINGPAGES_PATH;
369
+ $this_path = explode('wp-content', $this_path);
370
+ $this_path = "wp-content" . $this_path[1];
371
+ } else {
372
+ $this_path = INBOUND_PRO_PATH;
373
+ $this_path = explode('wp-content', $this_path);
374
+ $this_path = "wp-content" . $this_path[1] . "core/landing-pages/";
375
  }
376
 
377
+ /* handles local environment */
378
+ $this_path = str_replace("\\" , "/" , $this_path);
 
 
 
 
 
 
 
379
 
380
+ $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
381
 
382
+ $ab_testing = Landing_Pages_Settings::get_setting('lp-main-landing-page-disable-turn-off-ab', "0");
383
+ if ($ab_testing === "0") {
384
+ add_rewrite_rule("$slug/([^/]*)/([0-9]+)/", "$slug/$1?lp-variation-id=$2", 'top');
385
+ add_rewrite_rule("$slug/([^/]*)?", $this_path . "modules/module.redirect-ab-testing.php?permalink_name=$1 ", 'top');
386
+ add_rewrite_rule("landing-page=([^/]*)?", $this_path . 'modules/module.redirect-ab-testing.php?permalink_name=$1', 'top');
387
+ }
388
 
389
+ }
390
 
391
+ /**
392
+ * Adds conditions to rewrite rules
393
+ * @param $rules
394
+ * @return string
395
+ */
396
+ public static function filter_rewrite_rules( $rules ) {
397
+ if (stristr($rules, 'RewriteCond %{QUERY_STRING} !lp-variation-id')) {
398
+ return $rules;
399
+ }
400
 
401
+ $rules_array = preg_split('/$\R?^/m', $rules);
402
 
403
+ if (count($rules_array) < 3) {
404
+ $rules_array = explode("\n", $rules);
405
+ $rules_array = array_filter($rules_array);
406
+ }
407
 
408
+ /* print_r($rules_array);exit; */
 
 
 
 
 
 
 
 
 
 
409
 
 
410
 
411
+ $slug = Landing_Pages_Settings::get_setting( 'lp-main-landing-page-permalink-prefix', 'go' );
412
 
413
+ $i = 0;
414
+ foreach ($rules_array as $key => $val) {
415
+
416
+ if (stristr($val, "RewriteRule ^{$slug}/([^/]*)? ") || stristr($val, "RewriteRule ^{$slug}/([^/]*)/([0-9]+)/ ")) {
417
+ $new_val = "RewriteCond %{QUERY_STRING} !lp-variation-id";
418
+ $rules_array[$i] = $new_val;
419
+ $i++;
420
+ $rules_array[$i] = $val;
421
+ $i++;
422
+ } else {
423
+ $rules_array[$i] = $val;
424
+ $i++;
425
+ }
426
  }
427
 
428
+ $rules = implode("\r\n", $rules_array);
429
 
 
 
 
 
430
 
431
+ return $rules;
432
+ }
 
433
 
 
 
 
 
434
 
435
+ /**
436
+ * Show stats container on Landing Page lists page
437
+ */
438
+ public static function show_stats() {
439
 
440
+ global $post;
441
+ $permalink = get_permalink($post->ID);
442
+ $variations = Landing_Pages_Variations::get_variations($post->ID);
443
 
444
+ echo "<span class='show-stats button'> " . __('Show Variation Stats', 'inbound-pro') . "</span>";
445
+ echo "<ul class='lp-varation-stat-ul'>";
446
+ $cr_array = array();
447
+ $i = 0;
448
 
449
+ foreach ($variations as $key => $vid) {
450
+ $letter = Landing_Pages_Variations::vid_to_letter($post->ID, $key); /* convert to letter */
451
+ $impressions = Landing_Pages_Variations::get_impressions($post->ID, $vid);
452
+ $conversions = Landing_Pages_Variations::get_conversions($post->ID, $vid);
 
 
453
 
454
+ /* get variation status */
455
+ $status = Landing_Pages_Variations::get_variation_status( $post->ID, $vid ); /* Current status */
456
 
457
+ /* Get variation notes */
458
+ $each_notes = Landing_Pages_Variations::get_variation_notes( $post->ID, $vid );
459
+
460
+ if ($impressions) {
461
+ $conversion_rate = $conversions / $impressions;
462
+ } else {
463
+ $conversion_rate = 0;
 
 
464
  }
465
+ $conversion_rate = round($conversion_rate, 2) * 100;
466
+
467
+ $cr_array[] = $conversion_rate;
468
 
469
+ $data_letter = "data-letter=\"" . $letter . "\"";
470
+ $edit_link = admin_url('post.php?post=' . $post->ID . '&lp-variation-id=' . $vid . '&action=edit');
471
+ echo "<li rel='" . $status . "' data-postid='" . $post->ID . "' data-letter='" . $letter . "' data-lp='' class='lp-stat-row-" . $vid . " " . $post->ID . '-' . $conversion_rate . " status-" . $status . "'><a class='lp-letter' title='click to edit this variation' href='" . $edit_link . "'>" . $letter . "</a><span class='lp-numbers'><span class='lp-visitors'><span class='visit-text'>" . __( 'Impressions' , 'inbound-pro' ) . "</span><span class='lp-impress-num'>" . $impressions . "</span></span> <span class='lp-conversions'> <span class='lp-conversion-txt'>" . __( 'Conversions' , 'inbound-pro' ) . "</span> <span class='lp-con-num'>" . $conversions . "</span> </span> </span><a ". $data_letter . " class='cr-number cr-empty-" . $conversion_rate . "' href='" . $edit_link . "'>" . $conversion_rate . "%</a></li>";
472
+ $i++;
473
  }
474
+ echo "</ul>";
475
+ $winning_cr = max($cr_array); /* best conversion rate */
476
+ if ($winning_cr != 0) {
477
+ echo "<span class='variation-winner-is'>" . $post->ID . "-" . $winning_cr . "</span>";
478
+ }
479
+ /*echo "Total Visits: " . $impressions; */
480
+ /*echo "Total Conversions: " . $conversions; */
481
+
482
+ }
483
 
484
 
485
+ /**
486
+ * Show dropdown of landing page categories
487
+ */
488
+ public static function sort_by_category() {
489
+ global $typenow;
490
 
491
+ if ($typenow != "landing-page") {
492
+ return;
493
+ }
494
 
495
 
496
+ $filters = get_object_taxonomies($typenow);
497
+
498
+ foreach ($filters as $tax_slug) {
499
+
500
+ $tax_obj = get_taxonomy($tax_slug);
501
+ (isset($_GET[$tax_slug])) ? $current = $_GET[$tax_slug] : $current = 0;
502
+ wp_dropdown_categories(
503
+ array(
504
+ 'show_option_all' => __('Show All ' . $tax_obj->label),
505
+ 'taxonomy' => $tax_slug,
506
+ 'name' => $tax_obj->name,
507
+ 'orderby' => 'name',
508
+ 'selected' => $current,
509
+ 'hierarchical' => $tax_obj->hierarchical,
510
+ 'show_count' => false,
511
+ 'hide_empty' => true
512
+ )
513
+ );
 
514
  }
515
+ }
516
 
517
+ /**
518
+ * Convert the category id to the taxonomy id during a query
519
+ */
520
+ public static function sort_by_category_prepare_query() {
521
+ global $pagenow;
522
+ $qv = &$query->query_vars;
523
+ if ($pagenow == 'edit.php' && isset($qv['landing_page_category']) && is_numeric($qv['landing_page_category'])) {
524
+ $term = get_term_by('id', $qv['landing_page_category'], 'landing_page_category');
525
+ $qv['landing_page_category'] = $term->slug;
 
526
  }
527
+ }
528
 
529
+ /**
530
+ * Add styling handlers to custom post states
531
+ */
532
+ public static function filter_custom_post_states($post_states) {
533
+ foreach ($post_states as &$state) {
534
+ $state = '<span class="' . strtolower($state) . ' states">' . str_replace(' ', '-', $state) . '</span>';
 
 
535
  }
536
+ return $post_states;
537
+ }
538
 
539
+ /**
540
+ * Loads preview iframe. Currently disabled. Plans to update @DavidWells
541
+ */
542
+ public static function load_preview_iframe() {
543
 
544
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
545
+ $landing_page_id = $_GET['post_id'];
546
 
547
+ $variations = Landing_Pages_Variations::get_variations( $landing_page_id );
548
+ ?>
549
+ <link rel="stylesheet" href="<?php echo LANDINGPAGES_URLPATH . 'assets/css/customizer-ab-testing.css';?>"/>
550
+ <style type="text/css">
551
 
552
+ #variation-list {
553
+ position: absolute;
554
+ top: 0px;
555
+ left: 0px;
556
+ padding-left: 5px;
557
+ }
558
 
559
+ #variation-list h3 {
560
+ text-decoration: none;
561
+ border-bottom: none;
562
+ }
563
 
564
+ #variation-list div {
565
+ display: inline-block;
566
+ }
567
 
568
+ #current_variation_id, #current-post-id {
569
+ display: none !important;
570
+ }
571
 
572
+ </style>
573
+ <script type="text/javascript">
574
+ jQuery(document).ready(function ($) {
575
+ var current_page = jQuery("#current_variation_id").text();
576
+ /* reload the iframe preview page (for option toggles) */
577
+ jQuery('.variation-lp').on('click', function (event) {
578
+ variation_is = jQuery(this).attr("id");
579
+ var original_url = jQuery(parent.document).find("#TB_iframeContent").attr("src");
580
+ var current_id = jQuery("#current-post-id").text();
581
+ someURL = original_url;
582
+
583
+ splitURL = someURL.split('?');
584
+ someURL = splitURL[0];
585
+ new_url = someURL + "?lp-variation-id=" + variation_is + "&iframe_window=on&post_id=" + current_id;
586
+ jQuery(parent.document).find("#TB_iframeContent").attr("src", new_url);
 
587
  });
588
+ });
589
+ </script>
590
+ <?php
591
+ }
592
 
593
+ /**
594
+ * Load JS to disable stats from working for preview windows
595
+ */
596
+ public static function stop_stat_tracking() {
597
+ show_admin_bar(false);
598
+ wp_enqueue_script('stop-inbound-stats-js', LANDINGPAGES_URLPATH . 'assets/js/stop_page_stats.js' , array('inbound-analytics') , null );
599
+ wp_enqueue_style('inbound-preview-window-css', LANDINGPAGES_URLPATH . 'assets/css/iframe-preview.css' , array() , null);
600
+ }
601
+
602
+ /**
603
+ * Load misc wp_head
604
+ */
605
+ public static function wp_head() {
606
+ global $post;
607
+
608
+ if (isset($post) && $post->post_type !=='landing-page') {
609
+ return;
610
  }
611
+ /* if is tiny iframe preview window force these styles */
612
+ if (isset($_GET['dont_save'])) { ?>
613
+ <style type="text/css">
614
+ :root:root:root #wpadminbar {
615
+ display:none !important;
616
+ }
617
+ :root:root:root {
618
+ margin-top: 0px !important;
619
+ min-height: 714px !important;
620
+ }
621
+ </style>
622
+ <?php }
623
 
624
+ if (isset($_GET['lp-variation-id']) && !isset($_GET['inbound-customizer']) && !isset($_GET['iframe_window']) && !isset($_GET['live-preview-area'])) {
625
+ ?>
 
 
 
626
 
627
+ <?php
628
+ if(!defined('Inbound_Now_Disable_URL_CLEAN')) {
629
+ ?>
630
+ <script type="text/javascript">
631
+ /* Then strip params if pushstate exists */
632
+ if (typeof window.history.pushState == 'function') {
633
+ var cleanparams=window.location.href.split("?");
634
+ var clean_url=cleanparams[0];history.replaceState({},"landing page",clean_url);
 
 
 
 
635
  }
636
+ </script>
637
+ <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
638
  }
 
639
  }
640
+
641
  }
642
+ }
643
 
644
+ /* Load Post Type Pre Init */
645
+ new Landing_Pages_Post_Type();
 
classes/class.postmeta.php CHANGED
@@ -1,8 +1,11 @@
1
  <?php
2
 
3
  /**
4
- * Class Landing_Pages_Meta Array placeholder to eventually hold all landing page data
 
 
5
  */
 
6
  class Landing_Pages_Meta {
7
 
8
  /**
1
  <?php
2
 
3
  /**
4
+ * Class provides a data interface for interacting with landing page meta data. Used by Landing_Pages_ACF class
5
+ * @package LandingPages
6
+ * @subpackage DataInterfaces
7
  */
8
+
9
  class Landing_Pages_Meta {
10
 
11
  /**
classes/class.row-actions.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  class Landing_Pages_Row_Actions {
4
 
5
  /**
1
  <?php
2
 
3
+ /**
4
+ * Class adds row actions to landing-page CPT listing page. Should be moved into class.post-type.landing-pages.php
5
+ * @package LandingPages
6
+ * @subpackage Management
7
+ */
8
+
9
  class Landing_Pages_Row_Actions {
10
 
11
  /**
classes/class.settings.php CHANGED
@@ -1,5 +1,11 @@
1
  <?php
2
 
 
 
 
 
 
 
3
  class Landing_Pages_Settings {
4
 
5
  /**
1
  <?php
2
 
3
+ /**
4
+ * Class provides a data interface for retrieving and storing landing page settings into the GPL legacy setting system or the Inbound Pro settings system.
5
+ * @package LandingPages
6
+ * @subpackage DataInterfaces
7
+ */
8
+
9
  class Landing_Pages_Settings {
10
 
11
  /**
classes/class.sidebars.php CHANGED
@@ -1,6 +1,10 @@
1
  <?php
2
 
3
-
 
 
 
 
4
 
5
  class Landing_Pages_Sidebars {
6
 
1
  <?php
2
 
3
+ /**
4
+ * Class sets up landing page sidebar for holding conversion areas when 'default' template is selected.
5
+ * @package LandingPages
6
+ * @subpackage Sidebars
7
+ */
8
 
9
  class Landing_Pages_Sidebars {
10
 
classes/class.statistics.php CHANGED
@@ -1,218 +1,214 @@
1
  <?php
2
 
3
  /**
4
- * This class adds a impressions/conversions counter box to all post types that are not a landing page
5
- */
6
-
7
- if (!class_exists('Landing_Pages_Stats')) {
8
-
9
- /**
10
- * Adds impression and conversion tracking statistics to all pieces of content
11
- */
12
- class Landing_Pages_Stats {
13
-
14
- /**
15
- * Initiate class
16
- */
17
- public function __construct() {
18
- self::load_hooks();
19
- }
20
-
21
- /**
22
- * load hooks and filters
23
- */
24
- public static function load_hooks() {
25
-
26
- /* records page impression */
27
- add_action( 'lp_record_impression' , array( __CLASS__ , 'record_impression' ) , 10, 3);
28
-
29
- /* record landing page conversion */
30
- add_filter( 'inboundnow_store_lead_pre_filter_data' , array( __CLASS__ , 'record_conversion' ) ,10,1);
31
-
32
- }
33
-
34
- /**
35
- * Records landing page & non landing page impression
36
- * @param $post_id
37
- * @param string $post_type
38
- * @param int $variation_id
39
- */
40
- public static function record_impression($post_id, $post_type , $variation_id = 0) {
41
-
42
- /* ignore mshots and previews from admin area */
43
- if (strstr( $_SERVER['HTTP_REFERER'] , 'edit.php?post_type=landing-page' )) {
44
- return;
45
- }
46
- if (strstr( $_SERVER['HTTP_REFERER'] , 'post.php' )) {
47
- return;
48
- }
49
- if (strstr( $_SERVER['HTTP_REFERER'] , 'edit.php' )) {
50
- return;
51
- }
52
-
53
- /* If Landing Page Post Type */
54
- if ( $post_type == 'landing-page' ) {
55
- $impressions = Landing_Pages_Variations::get_impressions( $post_id, $variation_id );
56
- $impressions++;
57
- Landing_Pages_Variations::set_impressions_count( $post_id, $variation_id, $impressions );
58
- }
59
- /* If Non Landing Page Post Type */
60
- else {
61
- $impressions = Landing_Pages_Stats::get_impressions_count( $post_id );
62
- $impressions++;
63
- Landing_Pages_Stats::set_impressions_count( $post_id, $impressions );
64
- }
65
  }
66
 
67
- /**
68
- * Listens for new lead creation events and if the lead converted on a landing page then capture the conversion
69
- * @param $data
70
- */
71
- public static function record_conversion($data) {
72
-
73
- if (!isset( $data['page_id'] ) ) {
74
- return $data;
75
- }
76
-
77
- $post = get_post( $data['page_id'] );
78
- if ($post) {
79
- $data['post_type'] = $post->post_type;
80
- }
81
-
82
- /* this filter is used by Inbound Pro to check if visitor's ip is on a not track list */
83
- $do_not_track = apply_filters('inbound_analytics_stop_track' , false );
84
-
85
- if ( $do_not_track ) {
86
- return $data;
87
- }
88
-
89
-
90
- /* increment conversions for landing pages */
91
- if( isset($data['post_type']) && $data['post_type'] === 'landing-page' ) {
92
- $conversions = Landing_Pages_Variations::get_conversions( $data['page_id'] , $data['variation'] );
93
- $conversions++;
94
- Landing_Pages_Variations::set_conversions_count( $data['page_id'] , $data['variation'] , $conversions );
95
- }
96
- /* increment conversions for non landing pages */
97
- else {
98
- $conversions = Landing_Pages_Stats::get_conversions_count( $data['page_id'] );
99
- $conversions++;
100
- Landing_Pages_Stats::set_conversions_count( $data['page_id'] , $conversions );
101
- }
102
 
 
 
 
 
 
 
 
103
  return $data;
104
  }
105
 
106
- /**
107
- * Register Columns
108
- */
109
- public static function register_columns( $cols ) {
110
 
111
- $cols['inbound_impressions'] = __( 'Impressions' , 'inbound-email' );
112
- $cols['inbound_conversions'] = __( 'Conversions' , 'inbound-email' );
113
- $cols['inbound_conversion_rate'] = __( 'Conversion Rate' , 'inbound-email' );
114
 
115
- return $cols;
 
116
  }
117
 
118
- /**
119
- * Prepare Column Data
120
- */
121
- public static function prepare_column_data( $column , $post_id ) {
122
- global $post;
123
-
124
- switch ($column) {
125
- case "inbound_impressions":
126
- echo self::get_impressions_count( $post->ID );
127
- break;
128
- case "inbound_conversions":
129
- echo self::get_conversions_count( $post->ID );
130
- break;
131
- case "inbound_conversion_rate":
132
- echo self::get_conversion_rate( $post->ID);
133
- break;
134
- }
135
  }
136
 
 
 
137
 
138
- /**
139
- * Returns impression count for non landing pages. See Landing_Pages_Variations class for retrieving landing page statistics
140
- *
141
- * @param INT $post_id id of call to action
142
- *
143
- * @return INT impression count
144
- */
145
- public static function get_impressions_count( $post_id ) {
146
 
147
- $impressions = get_post_meta( $post_id , '_inbound_impressions_count' , true);
 
 
148
 
149
- if (!is_numeric($impressions)) {
150
- $impressions = 0;
151
- }
152
 
153
- return $impressions;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
  }
155
-
156
- /**
157
- * Returns conversion count for non landing page. See Landing_Pages_Variations class for retrieving landing page statistics
158
- *
159
- * @param INT $post_id id
160
- *
161
- * @return INT impression count
162
- */
163
- public static function get_conversions_count( $post_id ) {
164
 
165
 
166
- $conversions = get_post_meta( $post_id , '_inbound_conversions_count' , true);
 
 
 
 
 
 
 
167
 
168
- if (!is_numeric($conversions)) {
169
- $conversions = 0;
170
- }
171
 
172
- return $conversions;
 
173
  }
174
 
175
- /**
176
- * Returns conversion count for non landing page. See Landing_Pages_Variations class for retrieving landing page statistics
177
- *
178
- * @param INT $post_id id
179
- *
180
- * @return INT
181
- */
182
- public static function get_conversion_rate( $post_id ) {
 
 
 
183
 
184
- $impressions = Landing_Pages_Stats::get_impressions_count( $post_id );
185
- $conversions = Landing_Pages_Stats::get_conversions_count( $post_id );
186
-
187
- if ($impressions > 0) {
188
- $conversion_rate = $conversions / $impressions;
189
- $conversion_rate_number = $conversion_rate * 100;
190
- $conversion_rate_number = round($conversion_rate_number, 2);
191
- $conversion_rate = $conversion_rate_number;
192
- } else {
193
- $conversion_rate = 0;
194
- }
195
-
196
- return $conversion_rate;
197
- }
198
 
 
199
 
200
- /**
201
- * Set impression count
202
- */
203
- public static function set_impressions_count( $post_id , $count ) {
204
- update_post_meta( $post_id, '_inbound_impressions_count', $count );
205
  }
206
 
207
- /**
208
- * Set conversion count
209
- */
210
- public static function set_conversions_count( $post_id , $count ) {
211
- update_post_meta( $post_id, '_inbound_conversions_count', $count );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
212
  }
213
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  }
215
 
 
 
216
 
217
- new Landing_Pages_Stats;
218
- }
1
  <?php
2
 
3
  /**
4
+ * Class addsa impressions/conversions counter box to all post types that are not a landing page. This feature is disabled for Inbound Pro users.
5
+ * @package LandingPages
6
+ * @subpackage Tracking
7
+ */
8
+
9
+ class Landing_Pages_Stats {
10
+
11
+ /**
12
+ * Initiate class
13
+ */
14
+ public function __construct() {
15
+ self::load_hooks();
16
+ }
17
+
18
+ /**
19
+ * load hooks and filters
20
+ */
21
+ public static function load_hooks() {
22
+
23
+ /* records page impression */
24
+ add_action( 'lp_record_impression' , array( __CLASS__ , 'record_impression' ) , 10, 3);
25
+
26
+ /* record landing page conversion */
27
+ add_filter( 'inboundnow_store_lead_pre_filter_data' , array( __CLASS__ , 'record_conversion' ) ,10,1);
28
+
29
+ }
30
+
31
+ /**
32
+ * Records landing page & non landing page impression
33
+ * @param $post_id
34
+ * @param string $post_type
35
+ * @param int $variation_id
36
+ */
37
+ public static function record_impression($post_id, $post_type , $variation_id = 0) {
38
+
39
+ /* ignore mshots and previews from admin area */
40
+ if (strstr( $_SERVER['HTTP_REFERER'] , 'edit.php?post_type=landing-page' )) {
41
+ return;
42
+ }
43
+ if (strstr( $_SERVER['HTTP_REFERER'] , 'post.php' )) {
44
+ return;
45
+ }
46
+ if (strstr( $_SERVER['HTTP_REFERER'] , 'edit.php' )) {
47
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  }
49
 
50
+ /* If Landing Page Post Type */
51
+ if ( $post_type == 'landing-page' ) {
52
+ $impressions = Landing_Pages_Variations::get_impressions( $post_id, $variation_id );
53
+ $impressions++;
54
+ Landing_Pages_Variations::set_impressions_count( $post_id, $variation_id, $impressions );
55
+ }
56
+ /* If Non Landing Page Post Type */
57
+ else {
58
+ $impressions = Landing_Pages_Stats::get_impressions_count( $post_id );
59
+ $impressions++;
60
+ Landing_Pages_Stats::set_impressions_count( $post_id, $impressions );
61
+ }
62
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
 
64
+ /**
65
+ * Listens for new lead creation events and if the lead converted on a landing page then capture the conversion
66
+ * @param $data
67
+ */
68
+ public static function record_conversion($data) {
69
+
70
+ if (!isset( $data['page_id'] ) ) {
71
  return $data;
72
  }
73
 
74
+ $post = get_post( $data['page_id'] );
75
+ if ($post) {
76
+ $data['post_type'] = $post->post_type;
77
+ }
78
 
79
+ /* this filter is used by Inbound Pro to check if visitor's ip is on a not track list */
80
+ $do_not_track = apply_filters('inbound_analytics_stop_track' , false );
 
81
 
82
+ if ( $do_not_track ) {
83
+ return $data;
84
  }
85
 
86
+
87
+ /* increment conversions for landing pages */
88
+ if( isset($data['post_type']) && $data['post_type'] === 'landing-page' ) {
89
+ $conversions = Landing_Pages_Variations::get_conversions( $data['page_id'] , $data['variation'] );
90
+ $conversions++;
91
+ Landing_Pages_Variations::set_conversions_count( $data['page_id'] , $data['variation'] , $conversions );
92
+ }
93
+ /* increment conversions for non landing pages */
94
+ else {
95
+ $conversions = Landing_Pages_Stats::get_conversions_count( $data['page_id'] );
96
+ $conversions++;
97
+ Landing_Pages_Stats::set_conversions_count( $data['page_id'] , $conversions );
 
 
 
 
 
98
  }
99
 
100
+ return $data;
101
+ }
102
 
103
+ /**
104
+ * Register Columns
105
+ */
106
+ public static function register_columns( $cols ) {
 
 
 
 
107
 
108
+ $cols['inbound_impressions'] = __( 'Impressions' , 'inbound-email' );
109
+ $cols['inbound_conversions'] = __( 'Conversions' , 'inbound-email' );
110
+ $cols['inbound_conversion_rate'] = __( 'Conversion Rate' , 'inbound-email' );
111
 
112
+ return $cols;
113
+ }
 
114
 
115
+ /**
116
+ * Prepare Column Data
117
+ */
118
+ public static function prepare_column_data( $column , $post_id ) {
119
+ global $post;
120
+
121
+ switch ($column) {
122
+ case "inbound_impressions":
123
+ echo self::get_impressions_count( $post->ID );
124
+ break;
125
+ case "inbound_conversions":
126
+ echo self::get_conversions_count( $post->ID );
127
+ break;
128
+ case "inbound_conversion_rate":
129
+ echo self::get_conversion_rate( $post->ID);
130
+ break;
131
  }
132
+ }
 
 
 
 
 
 
 
 
133
 
134
 
135
+ /**
136
+ * Returns impression count for non landing pages. See Landing_Pages_Variations class for retrieving landing page statistics
137
+ *
138
+ * @param INT $post_id id of call to action
139
+ *
140
+ * @return INT impression count
141
+ */
142
+ public static function get_impressions_count( $post_id ) {
143
 
144
+ $impressions = get_post_meta( $post_id , '_inbound_impressions_count' , true);
 
 
145
 
146
+ if (!is_numeric($impressions)) {
147
+ $impressions = 0;
148
  }
149
 
150
+ return $impressions;
151
+ }
152
+
153
+ /**
154
+ * Returns conversion count for non landing page. See Landing_Pages_Variations class for retrieving landing page statistics
155
+ *
156
+ * @param INT $post_id id
157
+ *
158
+ * @return INT impression count
159
+ */
160
+ public static function get_conversions_count( $post_id ) {
161
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
162
 
163
+ $conversions = get_post_meta( $post_id , '_inbound_conversions_count' , true);
164
 
165
+ if (!is_numeric($conversions)) {
166
+ $conversions = 0;
 
 
 
167
  }
168
 
169
+ return $conversions;
170
+ }
171
+
172
+ /**
173
+ * Returns conversion count for non landing page. See Landing_Pages_Variations class for retrieving landing page statistics
174
+ *
175
+ * @param INT $post_id id
176
+ *
177
+ * @return INT
178
+ */
179
+ public static function get_conversion_rate( $post_id ) {
180
+
181
+ $impressions = Landing_Pages_Stats::get_impressions_count( $post_id );
182
+ $conversions = Landing_Pages_Stats::get_conversions_count( $post_id );
183
+
184
+ if ($impressions > 0) {
185
+ $conversion_rate = $conversions / $impressions;
186
+ $conversion_rate_number = $conversion_rate * 100;
187
+ $conversion_rate_number = round($conversion_rate_number, 2);
188
+ $conversion_rate = $conversion_rate_number;
189
+ } else {
190
+ $conversion_rate = 0;
191
  }
192
 
193
+ return $conversion_rate;
194
+ }
195
+
196
+
197
+ /**
198
+ * Set impression count
199
+ */
200
+ public static function set_impressions_count( $post_id , $count ) {
201
+ update_post_meta( $post_id, '_inbound_impressions_count', $count );
202
+ }
203
+
204
+ /**
205
+ * Set conversion count
206
+ */
207
+ public static function set_conversions_count( $post_id , $count ) {
208
+ update_post_meta( $post_id, '_inbound_conversions_count', $count );
209
  }
210
 
211
+ }
212
+
213
 
214
+ new Landing_Pages_Stats;
 
classes/class.store.php CHANGED
@@ -1,183 +1,33 @@
1
  <?php
 
2
  /**
3
- * Inbound Now Store
 
 
4
  */
5
- if ( ! class_exists( 'Inbound_Now_Store' ) ) {
6
-
7
- class Inbound_Now_Store {
8
-
9
- static function init() {
10
- if ( !class_exists('Inbound_Pro_Plugin') ) {
11
- self::load_hooks();
12
- }
13
- }
14
-
15
- /**
16
- * Loads hooks and filters
17
- */
18
- public static function load_hooks() {
19
- add_action( 'wp_ajax_show_store_ajax' , array( __CLASS__ , 'show_store_ajax' ) );
20
- add_action( 'admin_enqueue_scripts' , array( __CLASS__ , 'enqueue_scripts' ) );
21
- add_action( 'admin_print_footer_scripts' , array( __CLASS__ , 'print_scripts' ) );
22
- }
23
-
24
- /**
25
- * enqueues scripts and styles
26
- */
27
- public static function enqueue_scripts() {
28
- global $plugin_page;
29
-
30
- wp_enqueue_script('jquery');
31
-
32
- }
33
-
34
- public static function print_scripts() {
35
- ?>
36
- <script type="text/javascript">
37
- jQuery(document).ready(function($) {
38
- jQuery('#menu-posts-landing-page a[href*="lp_store"]').each(function() {
39
- jQuery(this).attr('target','_blank');
40
- jQuery(this).attr('href','http://www.inboundnow.com/upgrade');
41
- });
42
- });
43
- </script>
44
- <?php
45
- }
46
-
47
- public static function show_store_ajax() {
48
- if(empty($_POST) || !isset($_POST)) {
49
- return;
50
- } else {
51
- /* show store forever */
52
- $user_id = get_current_user_id();
53
- add_user_meta($user_id, 'inbound_show_store', true);
54
- die();
55
- }
56
-
57
- }
58
-
59
- /**
60
- *
61
- */
62
- public static function store_display(){
63
- global $current_user;
64
-
65
- $user_id = $current_user->ID;
66
-
67
- self::dom_output();
68
-
69
- if ( !get_user_meta($user_id, 'inbound_show_store') ) {
70
- self::inbound_store_notice();
71
- } else {
72
- /* normal display here */
73
- self::store_redirect();
74
- }
75
-
76
- }
77
-
78
- /* loads when user_meta opt in is NOT found */
79
- public static function inbound_store_notice(){
80
 
81
- echo '<div id="agreement" style="margin-top:30px;">
82
- <h1>WordPress Guidelines Compliance Agreement</h1>
83
- <h3>To ensure complaince with <a href="https://wordpress.org/plugins/about/guidelines/">WordPress.orgs Plugin Guidelines</a>, we need your express permission to load our <a target="_blank" href="http://www.inboundnow.com/market">marketplace</a>.
 
84
 
85
- <div class="details">
86
- <br>
87
- <br>
88
- <a href="#" id="accept-agreement" class="button button-primary">I accept this agreement, show me the goods!</a>
89
- </div>
90
 
91
- </div>'; ?>
92
- <script>
93
- jQuery(document).ready(function($) {
94
-
95
- jQuery("#accept-agreement").on('click', function (e) {
96
- e.preventDefault();
97
-
98
- $('#agreement').slideToggle();
99
-
100
- showInboundStore();
101
-
102
- jQuery.ajax({
103
- type: 'POST',
104
- url: ajaxadmin.ajaxurl,
105
- data: {
106
- action: 'show_store_ajax'
107
- },
108
- success: function(user_id){
109
- console.log('user meta updated');
110
- },
111
- error: function(MLHttpRequest, textStatus, errorThrown){
112
-
113
- }
114
-
115
- });
116
-
117
- });
118
- });
119
- </script>
120
-
121
- <?php }
122
 
123
- /**
124
- *
125
- */
126
- public static function store_redirect() { ?>
127
- <script>
128
 
129
- window.location = "http://www.inboundnow.com/market";
130
 
131
- </script>
132
  <?php
133
- }
134
-
135
- /* Always loads on store pages */
136
- public static function dom_output(){
137
-
138
- if (isset($_GET['inbound-store']) && $_GET['inbound-store'] === 'templates') {
139
- $url = 'http://www.inboundnow.com/products/landing-pages/templates/';
140
- } else if (isset($_GET['inbound-store']) && $_GET['inbound-store'] === 'addons') {
141
- $url = 'http://www.inboundnow.com/products/landing-pages/extensions/';
142
- } else {
143
- $url = LANDINGPAGES_STORE_URL;
144
- }
145
- ?>
146
- <style type="text/css">
147
- #setting-error-tgmpa, .updated, #wpfooter { display: none !important; }
148
- #wpwrap { background: #fff !important; }
149
- div#inbound-store-container { margin-top: 0px !important; }
150
- div#inbound-store-container iframe { width:100%; }
151
- #wpbody-content { padding-bottom: 0px !important; }
152
- </style>
153
- <script type='text/javascript'>
154
- function showInboundStore(){
155
- new easyXDM.Socket({
156
- remote: "<?php echo $url;?>",
157
- container: document.getElementById("inbound-store-container"),
158
- onMessage: function(message, origin){
159
- var height = Number(message) + 1000;
160
- this.container.getElementsByTagName("iframe")[0].scrolling="no";
161
- this.container.getElementsByTagName("iframe")[0].style.height = height + "px";
162
-
163
- },
164
- onReady: function() {
165
- socket.postMessage("Yay, it works!");
166
- /*alert('run'); */
167
- }
168
- });
169
-
170
- setTimeout(function() {
171
- jQuery("#inbound-store-container iframe").css('height', window.outerHeight + "px");
172
- }, 2000);
173
- }
174
- </script>
175
-
176
- <div id="inbound-store-container"></div>
177
- <?php }
178
-
179
  }
 
180
 
181
- Inbound_Now_Store::init();
182
 
183
- }
1
  <?php
2
+
3
  /**
4
+ * Class adds prompts to upgrade to Inbound Pro when user is using GPL Landing Pages. Also will be foundation for template installation engine once we phase out all free templates from core.
5
+ * @package LandingPages
6
+ * @subpackage NeedsAttention
7
  */
8
+ class Inbound_Now_Store {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ /**
11
+ *
12
+ */
13
+ public static function store_display() {
14
 
15
+ /* normal display here */
16
+ self::store_redirect();
 
 
 
17
 
18
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
 
20
+ /**
21
+ *
22
+ */
23
+ public static function store_redirect() { ?>
24
+ <script>
25
 
26
+ window.location = "https://www.inboundnow.com/market";
27
 
28
+ </script>
29
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  }
31
+ }
32
 
 
33
 
 
classes/class.template-management.php CHANGED
@@ -1,5 +1,10 @@
1
  <?php
2
 
 
 
 
 
 
3
 
4
  class Landing_Pages_Template_Management {
5
 
1
  <?php
2
 
3
+ /**
4
+ * Class for managing and installing landing page templates manually. This feature is redundant for Inbound Pro subscribers using 1-click-installations.
5
+ * @package LandingPages
6
+ * @subpackage Templates
7
+ */
8
 
9
  class Landing_Pages_Template_Management {
10
 
classes/class.variations.php CHANGED
@@ -1,813 +1,811 @@
1
  <?php
2
 
3
- if (!class_exists('Landing_Pages_Variations')) {
 
 
 
 
4
 
5
- class Landing_Pages_Variations {
6
 
7
- public function __construct() {
8
- self::load_hooks();
9
- }
10
 
11
- public static function load_hooks() {
12
 
13
- /* load ajax listeners */
14
- add_action('wp_ajax_lp_clear_stats_action', array( __CLASS__ , 'ajax_clear_stats' ) );
15
- add_action('wp_ajax_lp_clear_stats_single', array( __CLASS__ , 'ajax_clear_stats_single'));
16
 
17
- /* alter preview link */
18
- add_filter('post_type_link' , array( __CLASS__ , 'prepare_filter_link') , 10 , 1 );
19
- }
20
 
21
 
22
- /**
23
- * Deletes variation for a call to action
24
- *
25
- * @param INT $landing_page_id id of call to action
26
- * @param INT $variation_id id of variation to delete
27
- *
28
- */
29
- public static function delete_variation($landing_page_id, $variation_id) {
30
 
31
- $variations = Landing_Pages_Variations::get_variations($landing_page_id);
32
 
33
- /* unset variation */
34
- if (($key = array_search($variation_id, $variations)) !== false) {
35
- unset($variations[$key]);
36
- }
37
 
38
 
39
- /* set next variation to be open */
40
- $current_variation_id = current($variations);
41
- $_SESSION['lp_ab_test_open_variation'] = $current_variation_id;
42
- $_GET['lp-variation-id'] = $current_variation_id;
43
 
44
- /* update variations */
45
- Landing_Pages_Variations::update_variations($landing_page_id, $variations);
46
 
47
 
48
- if ( $variation_id > 0) {
49
- $suffix = '-' . $variation_id;
50
- $len = strlen($suffix);
51
- } else {
52
- $suffix = '';
53
- $len = strlen($suffix);
54
- }
55
 
56
- /*delete each meta value associated with variation */
57
- global $wpdb;
58
- $data = array();
59
- $wpdb->query("
60
  SELECT `meta_key`, `meta_value`
61
  FROM $wpdb->postmeta
62
  WHERE `post_id` = " . $landing_page_id . "
63
  ");
64
 
65
- foreach ($wpdb->last_result as $k => $v) {
66
- $data[$v->meta_key] = $v->meta_value;
67
- };
68
 
69
- /*echo $len;exit; */
70
- foreach ($data as $key => $value) {
71
- if (substr($key, -$len) == $suffix) {
72
- delete_post_meta($landing_page_id, $key, $value);
73
- }
74
  }
75
-
76
-
77
  }
78
 
79
- /**
80
- * Pauses variation for a call to action
81
- *
82
- * @param INT $landing_page_id id of call to action
83
- * @param INT $variation_id id of variation to delete
84
- *
85
- */
86
- public static function pause_variation($landing_page_id, $variation_id) {
87
- update_post_meta( $landing_page_id , 'lp_ab_variation_status-' . $variation_id, '0');
88
- }
89
 
90
- /**
91
- * Activations variation for a call to action
92
- *
93
- * @param INT $landing_page_id id of call to action
94
- * @param INT $variation_id id of variation to play
95
- *
96
- */
97
- public static function play_variation($landing_page_id, $variation_id) {
98
- update_post_meta( $landing_page_id , 'lp_ab_variation_status-' . $variation_id, 1 );
99
- }
100
 
 
 
 
 
 
 
 
 
 
 
101
 
102
- /**
103
- * Updates 'inbound-email-variations' meta key with json object
104
- *
105
- * @param INT $landing_page_id id of call to action
106
- * @param variations ARRAY of variation data
107
- *
108
- */
109
- public static function update_variations($landing_page_id, $variations) {
 
 
110
 
111
- if ( is_array($variations) ) {
112
- $variations = implode(',', $variations);
113
- }
114
 
115
- update_post_meta( $landing_page_id , 'lp-ab-variations', $variations);
 
 
 
 
 
 
 
116
 
 
 
117
  }
118
 
119
- /**
120
- * Increments impression count for given cta and variation id
121
- *
122
- * @param INT $landing_page_id id of call to action
123
- * @param INT $variation_id id of variation belonging to call to action
124
- *
125
- */
126
- public static function record_impression($landing_page_id, $variation_id) {
127
 
128
- $impressions = get_post_meta($landing_page_id, 'lp-ab-variation-impressions--' . $variation_id, true);
 
 
 
 
 
 
 
129
 
130
- if (!is_numeric($impressions)) {
131
- $impressions = 1;
132
- } else {
133
- $impressions++;
134
- }
135
 
136
- update_post_meta($landing_page_id, 'lp-ab-variation-impressions--' . $variation_id, $impressions);
 
 
 
137
  }
138
 
 
 
139
 
140
 
 
 
 
 
 
 
 
 
141
 
 
142
 
143
- /**
144
- * Prepare a variation id for a new variation
145
- *
146
- * @param INT $landing_page_id id of landing page
147
- *
148
- * @returns INT $vid variation id
149
- */
150
- public static function prepare_new_variation_id( $landing_page_id ) {
151
 
152
- $variations = self::get_variations( $landing_page_id );
153
 
154
- sort( $variations, SORT_NUMERIC);
 
155
 
156
- $vid = end($variations);
 
 
 
 
 
 
 
157
 
158
- return $vid + 1;
 
159
  }
160
 
161
- /* Adds variation id onto base meta key
162
- *
163
- * @param id STRING of meta key to store data into for given setting
164
- * @param INT $variation_id id of variation belonging to call to action, will attempt to autodetect if left as null
165
- *
166
- * @returns STRING of meta key appended with variation id
167
- */
168
- public static function prepare_input_id( $id , $variation_id = null , $legacy = true) {
169
 
170
- if ($variation_id === null) {
171
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
172
- }
 
 
 
173
 
174
- if ( $variation_id >0 || !$legacy ) {
175
- return $id . '-' . $variation_id;
176
- } else {
177
- return $id;
178
- }
179
  }
180
 
181
- /**
182
- * Convert permalink to the correct variation preview link
183
- * @param $link
184
- * @return mixed
185
- */
186
- public static function prepare_filter_link( $link ) {
187
 
188
- if (!is_admin() || !function_exists('get_current_screen')) {
189
- return $link;
190
- }
191
 
192
- $screen = get_current_screen();
 
 
193
 
194
- if (!isset($screen) || $screen->parent_file != 'edit.php?post_type=landing-page' || strstr($link ,'%') ){
195
- return $link;
196
- }
197
 
198
- if (strstr($link , 'lp-variation-id')) {
199
- return $link;
200
- }
201
 
 
202
 
203
- $vid = self::get_current_variation_id();
 
204
 
205
- $link = add_query_arg(array('lp-variation-id'=>$vid) , $link);
206
 
207
- return $link;
208
- }
 
 
 
 
 
 
 
209
 
 
210
 
211
- /**
212
- * Sets the variation status to a custom status
213
- *
214
- * @param INT $landing_page_id id of call to action
215
- * @param INT $variation_id id of variation to delete
216
- * @param STRING $status custom status
217
- *
218
- */
219
- public static function set_variation_status($landing_page_id, $variation_id, $status = '1') {
220
 
221
- update_post_meta( $landing_page_id , 'lp_ab_variation_status-' . $variation_id, $status );
222
 
223
- }
 
 
 
 
 
 
 
224
 
 
 
 
 
 
 
 
 
 
 
 
225
 
 
 
 
 
 
 
 
 
 
 
226
 
227
- /**
228
- * Updates variation marker (used for single sends)
229
- * @param INT $landing_page_id
230
- * @param INT $variation_marker
231
- */
232
- public static function set_variation_marker($landing_page_id, $variation_marker) {
 
 
233
 
234
- }
235
 
236
- /**
237
- * Manually sets conversion count for given cta id and variation id
238
- *
239
- * @param INT $landing_page_id id of call to action
240
- * @param INT $variation_id id of variation belonging to call to action
241
- * @param INT $count
242
- *
243
- */
244
- public static function set_impressions_count($landing_page_id, $variation_id = 0 , $count) {
245
- update_post_meta($landing_page_id, 'lp-ab-variation-impressions-' . $variation_id, $count);
246
  }
247
 
248
- /**
249
- * Manually sets conversion count for given cta id and variation id
250
- *
251
- * @param INT $landing_page_id id of landing page
252
- * @param INT $variation_id id of variation
253
- * @param INT $count
254
- */
255
- public static function set_conversions_count( $landing_page_id , $variation_id , $count) {
256
- update_post_meta($landing_page_id, 'lp-ab-variation-conversions-' . $variation_id , $count);
257
  }
258
 
259
- /**
260
- * Returns array of variation data given a landing page id
261
- *
262
- * @param INT $landing_page_id id of landing page
263
- *
264
- * @returns ARRAY of variation data
265
- */
266
- public static function get_variations($landing_page_id) {
267
-
268
- $variations = get_post_meta($landing_page_id, 'lp-ab-variations', true);
269
 
270
- if (!is_array($variations) && $variations ) {
271
- $variations = explode( ',' , $variations);
272
- }
273
 
274
- if (!is_array($variations) || !$variations ) {
275
- $variations = array( 0 => "0" );
276
- }
 
 
 
 
 
 
277
 
278
- return $variations;
 
279
  }
280
 
 
281
 
282
- /**
283
- * Returns the status of a variation given landing_page_id and vid
284
- *
285
- * @param INT $landing_page_id id of call to action
286
- * @param INT $variation_id variation id of call to action
287
- *
288
- * @returns STRING status
289
- */
290
- public static function get_variation_status($landing_page_id, $variation_id = null) {
291
 
292
- if ($variation_id === null) {
293
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
294
- }
295
 
296
- $variation_status = get_post_meta( $landing_page_id , 'lp_ab_variation_status-' . $variation_id, true);
297
 
298
- if (!is_numeric($variation_status)) {
299
- return 1;
300
- } else {
301
- return $variation_status;
302
- }
 
303
 
304
- }
305
 
 
306
 
307
- /**
308
- * Get next variation ID available
309
- * @param INT $landing_page_id
310
- * @return INT $next_variant_marker
311
- */
312
- public static function get_next_variant_marker($landing_page_id) {
313
-
 
 
314
 
 
 
315
  }
316
 
317
- /**
318
- * Returns the permalink of a variation given landing_page_id and vid
319
- *
320
- * @param INT $landing_page_id id of call to action
321
- * @param INT $variation_id variation id of call to action
322
- *
323
- * @returns STRING permalink
324
- */
325
- public static function get_variation_permalink($landing_page_id, $variation_id = null) {
326
 
327
- if ($variation_id === null) {
328
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
329
- }
330
 
331
- $permalink = get_permalink($landing_page_id);
332
 
333
- return add_query_arg(array('lp-variation-id' => $variation_id), $permalink);
 
 
 
 
 
 
 
 
 
 
334
  }
335
 
 
336
 
337
- /**
338
- * Gets the call to action variation notes
339
- *
340
- * @param INT $landing_page_id id of call to action
341
- * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
342
- *
343
- * @return STRING $notes variation notes.
344
- */
345
- public static function get_variation_notes($landing_page_id, $variation_id = null) {
346
- if ($variation_id === null) {
347
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
348
- }
349
 
350
- $variation_notes = get_post_meta( $landing_page_id , 'lp-variation-notes-' . $variation_id, true);
 
 
 
 
 
 
 
 
351
 
352
- return $variation_notes;
 
353
  }
354
 
355
- /**
356
- * Gets the call to action variation custom css
357
- *
358
- * @param INT $landing_page_id id of call to action
359
- * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
360
- *
361
- * @return STRING $custom_css.
362
- */
363
- public static function get_custom_css($landing_page_id, $variation_id = null) {
364
 
365
- if ($variation_id === null) {
366
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
367
- }
368
 
369
- return Landing_Pages_Variations::get_setting_value( 'lp-custom-css' , $landing_page_id, $variation_id , '' );
 
 
 
 
 
 
 
 
370
 
 
 
371
  }
372
 
373
- /**
374
- * Gets the call to action variation custom js
375
- *
376
- * @param INT $landing_page_id id of call to action
377
- * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
378
- *
379
- * @return STRING $custom_js.
380
- */
381
- public static function get_custom_js($landing_page_id, $variation_id = null) {
382
 
383
- if ($variation_id === null) {
384
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
385
- }
 
 
 
 
386
 
387
- return Landing_Pages_Variations::get_setting_value( 'lp-custom-js' , $landing_page_id, $variation_id , '' );
 
388
  }
389
 
390
- /*
391
- * Gets the current variation id
392
- *
393
- * @returns INT of variation id
394
- */
395
- public static function get_current_variation_id() {
396
- global $post, $current_variation_id;
397
-
398
- if (isset($_SESSION['lp_ab_test_open_variation']) && isset($_GET['ab-action']) && is_admin()) {
399
- return $_SESSION['lp_ab_test_open_variation'];
400
- }
401
-
402
- /* check to see if this has already been set during the instance */
403
- if (is_numeric($current_variation_id)) {
404
- return $current_variation_id;
405
- }
406
 
407
- if (isset($_REQUEST['lp-variation-id'])) {
408
- $_SESSION['lp_ab_test_open_variation'] = intval($_REQUEST['lp-variation-id']);
409
- $current_variation_id = intval($_REQUEST['lp-variation-id']);
410
- }
411
 
412
- if (isset($_GET['message']) && $_GET['message'] == 1 && isset($_SESSION['lp_ab_test_open_variation'])) {
413
- $current_variation_id = $_SESSION['lp_ab_test_open_variation'];
414
- }
415
 
416
- if (isset($_GET['ab-action']) && $_GET['ab-action'] == 'delete-variation') {
417
- $current_variation_id = 0;
418
- $_SESSION['lp_ab_test_open_variation'] = 0;
419
- }
420
 
421
- if (isset($_GET['new_meta_key'])) {
422
- $current_variation_id = $_GET['new_meta_key'];
423
- }
424
 
425
- if (!isset($current_variation_id)) {
426
- if (!isset($post) && isset($_GET['post'])) {
427
- $post_id = $_GET['post'];
428
- } else if (isset($post)) {
429
- $post_id = $post->ID;
430
- } else {
431
- $post_id = 0;
432
- }
433
-
434
- $variations = self::get_variations($post_id);
435
- $id = array_values($variations);
436
- $current_variation_id = array_shift($id);
437
  }
438
 
439
- $GLOBALS['current_variation_id'] = $current_variation_id;
440
-
441
- return $current_variation_id;
442
  }
443
 
444
- /*
445
- * Looks up the variation id we should use for prepopulating settings on cloned variations and new variations
446
- *
447
- * @returns INT of variation id
448
- */
449
- public static function get_new_variation_reference_id( $landing_page_id , $variation_id = null ) {
450
- global $post;
451
 
452
- /* if no variation set look for variation */
453
- if (!isset($variation_id)) {
454
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
455
- }
456
 
457
- /* listen for new variation */
458
- if (isset($_REQUEST['new-variation']) && !isset($_REQUEST['clone'])) {
459
- $variations = Landing_Pages_Variations::get_variations( $landing_page_id );
460
- $variation_id = key($variations);
461
- }
 
 
462
 
463
- /* listen for clone variation */
464
- if (isset($_REQUEST['new-variation']) && isset($_REQUEST['clone'])) {
465
- $variation_id = intval($_REQUEST['clone']);
466
- }
467
 
468
- return $variation_id;
 
 
 
469
  }
470
 
471
- /*
472
- * Gets the next available variation id
473
- *
474
- * @returns INT of variation id
475
- */
476
- public static function get_next_available_variation_id($landing_page_id) {
477
 
478
- $variations = Landing_Pages_Variations::get_variations($landing_page_id);
479
- $array_variations = $variations;
480
 
481
- end($array_variations);
 
 
 
 
 
482
 
483
- $last_variation_id = key($array_variations);
 
484
 
485
- return $last_variation_id + 1;
486
- }
487
 
488
- /*
489
- * Gets string id of template given email id
490
- *
491
- * @param INT $landing_page_id of call to action
492
- * @param INT $variation_id of variation id
493
- *
494
- * @returns STRING id of selected template
495
- */
496
- public static function get_current_template($landing_page_id, $variation_id = null) {
497
 
498
- if ( !is_numeric( $variation_id ) ) {
499
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
500
- }
501
 
502
- return Landing_Pages_Variations::get_setting_value( 'lp-selected-template' , $landing_page_id, $variation_id , 'default' );
 
 
 
 
 
 
 
 
503
 
 
 
504
  }
505
 
506
- /**
507
- * Get Screenshot URL for Call to Action preview. If local environment show template thumbnail.
508
- *
509
- * @param INT $landing_page_id id if of call to action
510
- * @param INT $variation_id id of variation belonging to call to action
511
- *
512
- * @return STRING url of preview
513
- */
514
- public static function get_screenshot_url($landing_page_id, $variation_id = null) {
515
 
516
- if ($variation_id === null) {
517
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
518
- }
 
 
 
 
 
 
 
 
519
 
520
- $template = Landing_Pages_Variations::get_current_template($landing_page_id, $variation_id);
 
 
521
 
522
- if (in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
523
 
524
- if (file_exists(INBOUND_EMAIL_UPLOADS_URLPATH . 'templates/' . $template . '/thumbnail.png')) {
525
- $screenshot = INBOUND_EMAIL_UPLOADS_URLPATH . 'templates/' . $template . '/thumbnail.png';
526
- } else {
527
- $screenshot = INBOUND_EMAIL_URLPATH . 'templates/' . $template . '/thumbnail.png';
528
- }
529
 
 
 
530
  } else {
531
- $screenshot = 'http://s.wordpress.com/mshots/v1/' . urlencode(esc_url($permalink)) . '?w=140';
532
  }
533
 
534
- return $screenshot;
 
535
  }
536
 
 
 
537
 
538
- /**
539
- * Returns impression for given cta and variation id
540
- *
541
- * @param INT $landing_page_id id of call to action
542
- * @param INT $variation_id id of variation belonging to call to action
543
- *
544
- * @return INT impression count
545
- */
546
- public static function get_impressions( $landing_page_id, $variation_id ) {
547
 
548
- if ( !is_numeric( $variation_id ) ) {
549
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
550
- }
 
 
 
 
 
 
551
 
552
- $impressions = get_post_meta( $landing_page_id , 'lp-ab-variation-impressions-' . $variation_id, true);
553
- $impressions = (is_numeric($impressions)) ? $impressions : 0;
554
-
555
- return $impressions;
556
  }
557
 
558
- /**
559
- * Returns impression for given cta and variation id
560
- *
561
- * @param INT $landing_page_id id of call to action
562
- * @param INT $variation_id id of variation belonging to call to action
563
- *
564
- * @return INT conversion count
565
- */
566
- public static function get_conversions( $landing_page_id , $variation_id ) {
567
 
568
- if ( !is_numeric( $variation_id ) ) {
569
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
570
- }
571
 
572
- $conversions = get_post_meta( $landing_page_id , 'lp-ab-variation-conversions-' . $variation_id, true);
573
- $conversions = (is_numeric($conversions)) ? $conversions : 0;
 
 
 
 
 
 
 
574
 
575
- return $conversions;
 
576
  }
577
 
578
- /**
579
- * Returns conversion rate for given cta and variation id
580
- *
581
- * @param INT $landing_page_id id of call to action
582
- * @param INT $variation_id id of variation belonging to call to action
583
- *
584
- * @return INT conversion rate
585
- */
586
- public static function get_conversion_rate($landing_page_id, $variation_id) {
587
 
588
- $impressions = Landing_Pages_Variations::get_impressions($landing_page_id, $variation_id);
589
- $conversions = Landing_Pages_Variations::get_conversions($landing_page_id, $variation_id);
590
 
591
- if ($impressions > 0) {
592
- $conversion_rate = $conversions / $impressions;
593
- $conversion_rate_number = $conversion_rate * 100;
594
- $conversion_rate_number = round($conversion_rate_number, 2);
595
- $conversion_rate = $conversion_rate_number;
596
- } else {
597
- $conversion_rate = 0;
598
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
 
600
- return $conversion_rate;
601
- }
 
 
 
602
 
603
- /**
604
- * @param $landing_page_id
605
- * @param null $variation_id
606
- */
607
- public static function get_conversion_area( $landing_page_id, $variation_id = null) {
608
 
609
- return Landing_Pages_Variations::get_setting_value( 'lp-conversion-area' , $landing_page_id, $variation_id );
610
- }
 
 
 
 
 
 
 
611
 
612
- /**
613
- * Returns conversion area placement
614
- * @param $landing_page_id
615
- * @param null $variation_id
616
- */
617
- public static function get_conversion_area_placement( $landing_page_id, $variation_id = null) {
618
- $template = Landing_Pages_Variations::get_current_template( $landing_page_id );
619
- return Landing_Pages_Variations::get_setting_value( $template . '-conversion-area-placement' , $landing_page_id, $variation_id );
620
  }
621
 
622
- /**
623
- * @param $landing_page_id
624
- * @param null $variation_id
625
- */
626
- public static function get_post_content( $landing_page_id, $variation_id = null) {
627
- if ( !is_numeric( $variation_id ) ) {
628
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
629
- }
630
 
631
- return Landing_Pages_Variations::get_setting_value( 'content' , $landing_page_id, $variation_id );
 
632
  }
633
 
634
- /**
635
- * Get main headline
636
- */
637
- public static function get_main_headline( $landing_page_id , $variation_id = null ) {
638
 
639
- if ( !is_numeric( $variation_id ) ) {
640
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
641
- }
 
642
 
643
- return Landing_Pages_Variations::get_setting_value( 'lp-main-headline' , $landing_page_id, $variation_id , '' );
 
 
 
644
  }
 
645
 
646
- /**
647
- * Gets thumbnail for selected template
648
- */
649
- public static function get_template_thumbnail( $template ) {
650
-
651
- if (file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.png')) {
652
- return LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.png';
653
- } else {
654
- return LANDINGPAGES_URLPATH . 'templates/' . $template . '/thumbnail.png';
655
- }
656
- }
657
 
 
 
 
 
 
 
 
 
 
658
 
659
- /**
660
- * Gets stored setting value
661
- * @param $key
662
- * @param $landing_page_id
663
- * @param $variation_id
664
- * @param string $default
665
- * @return string
666
- */
667
- public static function get_setting_value( $key , $landing_page_id , $variation_id = null, $default = '' ) {
668
 
669
- /* if no variation set look for variation */
670
- if (!isset($variation_id)) {
671
- $variation_id = Landing_Pages_Variations::get_current_variation_id();
672
- }
 
673
 
674
- /* listen for new variation */
675
- if (isset($_REQUEST['new-variation']) && !isset($_REQUEST['clone'])) {
676
- $variations = Landing_Pages_Variations::get_variations( $landing_page_id );
677
- $variation_id = key($variations);
678
- }
679
 
680
- /* listen for clone variation */
681
- if (isset($_REQUEST['new-variation']) && isset($_REQUEST['clone'])) {
682
- $variation_id = intval($_REQUEST['clone']);
 
 
683
  }
 
684
 
685
- if ( $variation_id > 0 ) {
686
- if (metadata_exists('post', $landing_page_id , $key .'-' . $variation_id )) {
687
- return get_post_meta( $landing_page_id , $key .'-' . $variation_id, true);
688
- } else {
689
- return $default;
690
- }
691
  } else {
692
-
693
- if (metadata_exists('post', $landing_page_id , $key )) {
694
- return get_post_meta( $landing_page_id , $key, true);
695
- } else {
696
- return $default;
697
- }
698
  }
699
-
700
  }
701
 
 
702
 
703
- /**
704
- * Increments conversion count for given landing page id and variation id
705
- *
706
- * @param INT $landing_page_id id of landing page
707
- * @param INT $variation_id id of variation belonging to call to action
708
- *
709
- */
710
- public static function record_conversion($landing_page_id, $variation_id) {
711
 
712
- $conversions = self::get_conversions( $landing_page_id , $variation_id);
 
 
 
 
 
 
 
713
 
714
- if (!is_numeric($conversions)) {
715
- $conversions = 1;
716
- } else {
717
- $conversions++;
718
- }
719
 
720
- self::set_conversions_count( $landing_page_id , $variation_id , $conversions );
 
 
 
721
  }
722
 
723
- /**
724
- * Appends current variation id onto a URL
725
- *
726
- * @param link STRING URL that param will be appended onto
727
- *
728
- *
729
- * @return STRING modified URL.
730
- */
731
- public static function append_variation_id_to_url($link) {
732
- global $post;
733
 
734
- if (!isset($post) || $post->post_type != 'landing-pages') {
735
- return $link;
736
- }
 
 
 
 
 
 
 
 
 
 
 
737
 
738
- $current_variation_id = Landing_Pages_Variations::get_current_variation_id();
739
 
740
 
741
- $link = add_query_arg(array('inbvid' => $current_variation_id), $link);
742
 
743
- return $link;
744
- }
745
 
746
- /**
747
- * Discovers which alphabetic letter should be associated with a given cta's variation id.
748
- *
749
- * @param INT $landing_page_id id of call to action
750
- * @param INT $variation_id id of variation belonging to call to action
751
- *
752
- * @return STRING alphebit letter.
753
- */
754
- public static function vid_to_letter($landing_page_id, $variation_id) {
755
- $variations = Landing_Pages_Variations::get_variations($landing_page_id);
756
 
757
- $i = 0;
758
- foreach ($variations as $key => $variation) {
759
- if ($variation_id == $key) {
760
- break;
761
- }
762
- $i++;
763
  }
 
 
764
 
765
- $alphabet = array(__('A', 'landing-pages'), __('B', 'landing-pages'), __('C', 'landing-pages'), __('D', 'landing-pages'), __('E', 'landing-pages'), __('F', 'landing-pages'), __('G', 'landing-pages'), __('H', 'landing-pages'), __('I', 'landing-pages'), __('J', 'landing-pages'), __('K', 'landing-pages'), __('L', 'landing-pages'), __('M', 'landing-pages'), __('N', 'landing-pages'), __('O', 'landing-pages'), __('P', 'landing-pages'), __('Q', 'landing-pages'), __('R', 'landing-pages'), __('S', 'landing-pages'), __('T', 'landing-pages'), __('U', 'landing-pages'), __('V', 'landing-pages'), __('W', 'landing-pages'), __('X', 'landing-pages'), __('Y', 'landing-pages'), __('Z', 'landing-pages'));
766
 
767
- if (isset($alphabet[$i])) {
768
- return $alphabet[$i];
769
- }
770
  }
 
771
 
772
 
773
- /**
774
- * Adds Ajax for Clear Stats button
775
- * clear stats for all variations
776
- */
777
- public static function ajax_clear_stats() {
778
- global $wpdb;
779
-
780
- $landing_page_id = intval($_POST['page_id']);
781
 
782
- $variations = self::get_variations( $landing_page_id );
783
 
784
- foreach ($variations as $vid) {
785
- update_post_meta( $landing_page_id , 'lp-ab-variation-impressions-' . $vid, 0 , false);
786
- update_post_meta( $landing_page_id , 'lp-ab-variation-conversions-' . $vid, 0 , false);
787
- }
788
 
789
- header('HTTP/1.1 200 OK');
 
 
790
  }
791
 
 
 
792
 
793
- /**
794
- * Adds Ajax for Clear Stats button
795
- * clear stats for single variations
796
- */
797
- public static function ajax_clear_stats_single() {
798
- global $wpdb;
799
 
800
- $landing_page_id = intval($_POST['page_id']);
801
- $vid = intval($_POST['variation']);
 
 
 
 
802
 
803
- self::set_impressions_count( $landing_page_id , $vid , 0 );
804
- self::set_conversions_count( $landing_page_id , $vid , 0 );
805
 
806
- header('HTTP/1.1 200 OK');
807
- }
808
 
 
809
  }
810
 
811
- $GLOBALS['Landing_Pages_Variations'] = new Landing_Pages_Variations();
812
 
813
- }
1
  <?php
2
 
3
+ /**
4
+ * Class covers methods handling landing page edit screen
5
+ * @package LandingPages
6
+ * @subpackage Templates
7
+ */
8
 
9
+ class Landing_Pages_Variations {
10
 
11
+ public function __construct() {
12
+ self::load_hooks();
13
+ }
14
 
15
+ public static function load_hooks() {
16
 
17
+ /* load ajax listeners */
18
+ add_action('wp_ajax_lp_clear_stats_action', array(__CLASS__, 'ajax_clear_stats'));
19
+ add_action('wp_ajax_lp_clear_stats_single', array(__CLASS__, 'ajax_clear_stats_single'));
20
 
21
+ /* alter preview link */
22
+ add_filter('post_type_link', array(__CLASS__, 'prepare_filter_link'), 10, 1);
23
+ }
24
 
25
 
26
+ /**
27
+ * Deletes variation for a call to action
28
+ *
29
+ * @param INT $landing_page_id id of call to action
30
+ * @param INT $variation_id id of variation to delete
31
+ *
32
+ */
33
+ public static function delete_variation($landing_page_id, $variation_id) {
34
 
35
+ $variations = Landing_Pages_Variations::get_variations($landing_page_id);
36
 
37
+ /* unset variation */
38
+ if (($key = array_search($variation_id, $variations)) !== false) {
39
+ unset($variations[$key]);
40
+ }
41
 
42
 
43
+ /* set next variation to be open */
44
+ $current_variation_id = current($variations);
45
+ $_SESSION['lp_ab_test_open_variation'] = $current_variation_id;
46
+ $_GET['lp-variation-id'] = $current_variation_id;
47
 
48
+ /* update variations */
49
+ Landing_Pages_Variations::update_variations($landing_page_id, $variations);
50
 
51
 
52
+ if ($variation_id > 0) {
53
+ $suffix = '-' . $variation_id;
54
+ $len = strlen($suffix);
55
+ } else {
56
+ $suffix = '';
57
+ $len = strlen($suffix);
58
+ }
59
 
60
+ /*delete each meta value associated with variation */
61
+ global $wpdb;
62
+ $data = array();
63
+ $wpdb->query("
64
  SELECT `meta_key`, `meta_value`
65
  FROM $wpdb->postmeta
66
  WHERE `post_id` = " . $landing_page_id . "
67
  ");
68
 
69
+ foreach ($wpdb->last_result as $k => $v) {
70
+ $data[$v->meta_key] = $v->meta_value;
71
+ };
72
 
73
+ /*echo $len;exit; */
74
+ foreach ($data as $key => $value) {
75
+ if (substr($key, -$len) == $suffix) {
76
+ delete_post_meta($landing_page_id, $key, $value);
 
77
  }
 
 
78
  }
79
 
 
 
 
 
 
 
 
 
 
 
80
 
81
+ }
 
 
 
 
 
 
 
 
 
82
 
83
+ /**
84
+ * Pauses variation for a call to action
85
+ *
86
+ * @param INT $landing_page_id id of call to action
87
+ * @param INT $variation_id id of variation to delete
88
+ *
89
+ */
90
+ public static function pause_variation($landing_page_id, $variation_id) {
91
+ update_post_meta($landing_page_id, 'lp_ab_variation_status-' . $variation_id, '0');
92
+ }
93
 
94
+ /**
95
+ * Activations variation for a call to action
96
+ *
97
+ * @param INT $landing_page_id id of call to action
98
+ * @param INT $variation_id id of variation to play
99
+ *
100
+ */
101
+ public static function play_variation($landing_page_id, $variation_id) {
102
+ update_post_meta($landing_page_id, 'lp_ab_variation_status-' . $variation_id, 1);
103
+ }
104
 
 
 
 
105
 
106
+ /**
107
+ * Updates 'inbound-email-variations' meta key with json object
108
+ *
109
+ * @param INT $landing_page_id id of call to action
110
+ * @param variations ARRAY of variation data
111
+ *
112
+ */
113
+ public static function update_variations($landing_page_id, $variations) {
114
 
115
+ if (is_array($variations)) {
116
+ $variations = implode(',', $variations);
117
  }
118
 
119
+ update_post_meta($landing_page_id, 'lp-ab-variations', $variations);
120
+
121
+ }
 
 
 
 
 
122
 
123
+ /**
124
+ * Increments impression count for given cta and variation id
125
+ *
126
+ * @param INT $landing_page_id id of call to action
127
+ * @param INT $variation_id id of variation belonging to call to action
128
+ *
129
+ */
130
+ public static function record_impression($landing_page_id, $variation_id) {
131
 
132
+ $impressions = get_post_meta($landing_page_id, 'lp-ab-variation-impressions--' . $variation_id, true);
 
 
 
 
133
 
134
+ if (!is_numeric($impressions)) {
135
+ $impressions = 1;
136
+ } else {
137
+ $impressions++;
138
  }
139
 
140
+ update_post_meta($landing_page_id, 'lp-ab-variation-impressions--' . $variation_id, $impressions);
141
+ }
142
 
143
 
144
+ /**
145
+ * Prepare a variation id for a new variation
146
+ *
147
+ * @param INT $landing_page_id id of landing page
148
+ *
149
+ * @returns INT $vid variation id
150
+ */
151
+ public static function prepare_new_variation_id($landing_page_id) {
152
 
153
+ $variations = self::get_variations($landing_page_id);
154
 
155
+ sort($variations, SORT_NUMERIC);
 
 
 
 
 
 
 
156
 
157
+ $vid = end($variations);
158
 
159
+ return $vid + 1;
160
+ }
161
 
162
+ /* Adds variation id onto base meta key
163
+ *
164
+ * @param id STRING of meta key to store data into for given setting
165
+ * @param INT $variation_id id of variation belonging to call to action, will attempt to autodetect if left as null
166
+ *
167
+ * @returns STRING of meta key appended with variation id
168
+ */
169
+ public static function prepare_input_id($id, $variation_id = null, $legacy = true) {
170
 
171
+ if ($variation_id === null) {
172
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
173
  }
174
 
175
+ if ($variation_id > 0 || !$legacy) {
176
+ return $id . '-' . $variation_id;
177
+ } else {
178
+ return $id;
179
+ }
180
+ }
 
 
181
 
182
+ /**
183
+ * Convert permalink to the correct variation preview link
184
+ * @param $link
185
+ * @return mixed
186
+ */
187
+ public static function prepare_filter_link($link) {
188
 
189
+ if (!is_admin() || !function_exists('get_current_screen')) {
190
+ return $link;
 
 
 
191
  }
192
 
193
+ $screen = get_current_screen();
 
 
 
 
 
194
 
195
+ if (!isset($screen) || $screen->parent_file != 'edit.php?post_type=landing-page' || strstr($link, '%')) {
196
+ return $link;
197
+ }
198
 
199
+ if (strstr($link, 'lp-variation-id')) {
200
+ return $link;
201
+ }
202
 
 
 
 
203
 
204
+ $vid = self::get_current_variation_id();
 
 
205
 
206
+ $link = add_query_arg(array('lp-variation-id' => $vid), $link);
207
 
208
+ return $link;
209
+ }
210
 
 
211
 
212
+ /**
213
+ * Sets the variation status to a custom status
214
+ *
215
+ * @param INT $landing_page_id id of call to action
216
+ * @param INT $variation_id id of variation to delete
217
+ * @param STRING $status custom status
218
+ *
219
+ */
220
+ public static function set_variation_status($landing_page_id, $variation_id, $status = '1') {
221
 
222
+ update_post_meta($landing_page_id, 'lp_ab_variation_status-' . $variation_id, $status);
223
 
224
+ }
 
 
 
 
 
 
 
 
225
 
 
226
 
227
+ /**
228
+ * Updates variation marker (used for single sends)
229
+ * @param INT $landing_page_id
230
+ * @param INT $variation_marker
231
+ */
232
+ public static function set_variation_marker($landing_page_id, $variation_marker) {
233
+
234
+ }
235
 
236
+ /**
237
+ * Manually sets conversion count for given cta id and variation id
238
+ *
239
+ * @param INT $landing_page_id id of call to action
240
+ * @param INT $variation_id id of variation belonging to call to action
241
+ * @param INT $count
242
+ *
243
+ */
244
+ public static function set_impressions_count($landing_page_id, $variation_id = 0, $count) {
245
+ update_post_meta($landing_page_id, 'lp-ab-variation-impressions-' . $variation_id, $count);
246
+ }
247
 
248
+ /**
249
+ * Manually sets conversion count for given cta id and variation id
250
+ *
251
+ * @param INT $landing_page_id id of landing page
252
+ * @param INT $variation_id id of variation
253
+ * @param INT $count
254
+ */
255
+ public static function set_conversions_count($landing_page_id, $variation_id, $count) {
256
+ update_post_meta($landing_page_id, 'lp-ab-variation-conversions-' . $variation_id, $count);
257
+ }
258
 
259
+ /**
260
+ * Returns array of variation data given a landing page id
261
+ *
262
+ * @param INT $landing_page_id id of landing page
263
+ *
264
+ * @returns ARRAY of variation data
265
+ */
266
+ public static function get_variations($landing_page_id) {
267
 
268
+ $variations = get_post_meta($landing_page_id, 'lp-ab-variations', true);
269
 
270
+ if (!is_array($variations) && $variations) {
271
+ $variations = explode(',', $variations);
 
 
 
 
 
 
 
 
272
  }
273
 
274
+ if (!is_array($variations) || !$variations) {
275
+ $variations = array(0 => "0");
 
 
 
 
 
 
 
276
  }
277
 
278
+ return $variations;
279
+ }
 
 
 
 
 
 
 
 
280
 
 
 
 
281
 
282
+ /**
283
+ * Returns the status of a variation given landing_page_id and vid
284
+ *
285
+ * @param INT $landing_page_id id of call to action
286
+ * @param INT $variation_id variation id of call to action
287
+ *
288
+ * @returns STRING status
289
+ */
290
+ public static function get_variation_status($landing_page_id, $variation_id = null) {
291
 
292
+ if ($variation_id === null) {
293
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
294
  }
295
 
296
+ $variation_status = get_post_meta($landing_page_id, 'lp_ab_variation_status-' . $variation_id, true);
297
 
298
+ if (!is_numeric($variation_status)) {
299
+ return 1;
300
+ } else {
301
+ return $variation_status;
302
+ }
 
 
 
 
303
 
304
+ }
 
 
305
 
 
306
 
307
+ /**
308
+ * Get next variation ID available
309
+ * @param INT $landing_page_id
310
+ * @return INT $next_variant_marker
311
+ */
312
+ public static function get_next_variant_marker($landing_page_id) {
313
 
 
314
 
315
+ }
316
 
317
+ /**
318
+ * Returns the permalink of a variation given landing_page_id and vid
319
+ *
320
+ * @param INT $landing_page_id id of call to action
321
+ * @param INT $variation_id variation id of call to action
322
+ *
323
+ * @returns STRING permalink
324
+ */
325
+ public static function get_variation_permalink($landing_page_id, $variation_id = null) {
326
 
327
+ if ($variation_id === null) {
328
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
329
  }
330
 
331
+ $permalink = get_permalink($landing_page_id);
 
 
 
 
 
 
 
 
332
 
333
+ return add_query_arg(array('lp-variation-id' => $variation_id), $permalink);
334
+ }
 
335
 
 
336
 
337
+ /**
338
+ * Gets the call to action variation notes
339
+ *
340
+ * @param INT $landing_page_id id of call to action
341
+ * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
342
+ *
343
+ * @return STRING $notes variation notes.
344
+ */
345
+ public static function get_variation_notes($landing_page_id, $variation_id = null) {
346
+ if ($variation_id === null) {
347
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
348
  }
349
 
350
+ $variation_notes = get_post_meta($landing_page_id, 'lp-variation-notes-' . $variation_id, true);
351
 
352
+ return $variation_notes;
353
+ }
 
 
 
 
 
 
 
 
 
 
354
 
355
+ /**
356
+ * Gets the call to action variation custom css
357
+ *
358
+ * @param INT $landing_page_id id of call to action
359
+ * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
360
+ *
361
+ * @return STRING $custom_css.
362
+ */
363
+ public static function get_custom_css($landing_page_id, $variation_id = null) {
364
 
365
+ if ($variation_id === null) {
366
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
367
  }
368
 
369
+ return Landing_Pages_Variations::get_setting_value('lp-custom-css', $landing_page_id, $variation_id, '');
 
 
 
 
 
 
 
 
370
 
371
+ }
 
 
372
 
373
+ /**
374
+ * Gets the call to action variation custom js
375
+ *
376
+ * @param INT $landing_page_id id of call to action
377
+ * @param INT $variation_id variation id of call to action variation, will attempt to autodetect if left as null
378
+ *
379
+ * @return STRING $custom_js.
380
+ */
381
+ public static function get_custom_js($landing_page_id, $variation_id = null) {
382
 
383
+ if ($variation_id === null) {
384
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
385
  }
386
 
387
+ return Landing_Pages_Variations::get_setting_value('lp-custom-js', $landing_page_id, $variation_id, '');
388
+ }
 
 
 
 
 
 
 
389
 
390
+ /*
391
+ * Gets the current variation id
392
+ *
393
+ * @returns INT of variation id
394
+ */
395
+ public static function get_current_variation_id() {
396
+ global $post, $current_variation_id;
397
 
398
+ if (isset($_SESSION['lp_ab_test_open_variation']) && isset($_GET['ab-action']) && is_admin()) {
399
+ return $_SESSION['lp_ab_test_open_variation'];
400
  }
401
 
402
+ /* check to see if this has already been set during the instance */
403
+ if (is_numeric($current_variation_id)) {
404
+ return $current_variation_id;
405
+ }
 
 
 
 
 
 
 
 
 
 
 
 
406
 
407
+ if (isset($_REQUEST['lp-variation-id'])) {
408
+ $_SESSION['lp_ab_test_open_variation'] = intval($_REQUEST['lp-variation-id']);
409
+ $current_variation_id = intval($_REQUEST['lp-variation-id']);
410
+ }
411
 
412
+ if (isset($_GET['message']) && $_GET['message'] == 1 && isset($_SESSION['lp_ab_test_open_variation'])) {
413
+ $current_variation_id = $_SESSION['lp_ab_test_open_variation'];
414
+ }
415
 
416
+ if (isset($_GET['ab-action']) && $_GET['ab-action'] == 'delete-variation') {
417
+ $current_variation_id = 0;
418
+ $_SESSION['lp_ab_test_open_variation'] = 0;
419
+ }
420
 
421
+ if (isset($_GET['new_meta_key'])) {
422
+ $current_variation_id = $_GET['new_meta_key'];
423
+ }
424
 
425
+ if (!isset($current_variation_id)) {
426
+ if (!isset($post) && isset($_GET['post'])) {
427
+ $post_id = $_GET['post'];
428
+ } else if (isset($post)) {
429
+ $post_id = $post->ID;
430
+ } else {
431
+ $post_id = 0;
 
 
 
 
 
432
  }
433
 
434
+ $variations = self::get_variations($post_id);
435
+ $id = array_values($variations);
436
+ $current_variation_id = array_shift($id);
437
  }
438
 
439
+ $GLOBALS['current_variation_id'] = $current_variation_id;
 
 
 
 
 
 
440
 
441
+ return $current_variation_id;
442
+ }
 
 
443
 
444
+ /*
445
+ * Looks up the variation id we should use for prepopulating settings on cloned variations and new variations
446
+ *
447
+ * @returns INT of variation id
448
+ */
449
+ public static function get_new_variation_reference_id($landing_page_id, $variation_id = null) {
450
+ global $post;
451
 
452
+ /* if no variation set look for variation */
453
+ if (!isset($variation_id)) {
454
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
455
+ }
456
 
457
+ /* listen for new variation */
458
+ if (isset($_REQUEST['new-variation']) && !isset($_REQUEST['clone'])) {
459
+ $variations = Landing_Pages_Variations::get_variations($landing_page_id);
460
+ $variation_id = key($variations);
461
  }
462
 
463
+ /* listen for clone variation */
464
+ if (isset($_REQUEST['new-variation']) && isset($_REQUEST['clone'])) {
465
+ $variation_id = intval($_REQUEST['clone']);
466
+ }
 
 
467
 
468
+ return $variation_id;
469
+ }
470
 
471
+ /*
472
+ * Gets the next available variation id
473
+ *
474
+ * @returns INT of variation id
475
+ */
476
+ public static function get_next_available_variation_id($landing_page_id) {
477
 
478
+ $variations = Landing_Pages_Variations::get_variations($landing_page_id);
479
+ $array_variations = $variations;
480
 
481
+ end($array_variations);
 
482
 
483
+ $last_variation_id = key($array_variations);
 
 
 
 
 
 
 
 
484
 
485
+ return $last_variation_id + 1;
486
+ }
 
487
 
488
+ /*
489
+ * Gets string id of template given email id
490
+ *
491
+ * @param INT $landing_page_id of call to action
492
+ * @param INT $variation_id of variation id
493
+ *
494
+ * @returns STRING id of selected template
495
+ */
496
+ public static function get_current_template($landing_page_id, $variation_id = null) {
497
 
498
+ if (!is_numeric($variation_id)) {
499
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
500
  }
501
 
502
+ return Landing_Pages_Variations::get_setting_value('lp-selected-template', $landing_page_id, $variation_id, 'default');
 
 
 
 
 
 
 
 
503
 
504
+ }
505
+
506
+ /**
507
+ * Get Screenshot URL for Call to Action preview. If local environment show template thumbnail.
508
+ *
509
+ * @param INT $landing_page_id id if of call to action
510
+ * @param INT $variation_id id of variation belonging to call to action
511
+ *
512
+ * @return STRING url of preview
513
+ */
514
+ public static function get_screenshot_url($landing_page_id, $variation_id = null) {
515
 
516
+ if ($variation_id === null) {
517
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
518
+ }
519
 
520
+ $template = Landing_Pages_Variations::get_current_template($landing_page_id, $variation_id);
521
 
522
+ if (in_array($_SERVER['REMOTE_ADDR'], array('127.0.0.1', '::1'))) {
 
 
 
 
523
 
524
+ if (file_exists(INBOUND_EMAIL_UPLOADS_URLPATH . 'templates/' . $template . '/thumbnail.png')) {
525
+ $screenshot = INBOUND_EMAIL_UPLOADS_URLPATH . 'templates/' . $template . '/thumbnail.png';
526
  } else {
527
+ $screenshot = INBOUND_EMAIL_URLPATH . 'templates/' . $template . '/thumbnail.png';
528
  }
529
 
530
+ } else {
531
+ $screenshot = 'http://s.wordpress.com/mshots/v1/' . urlencode(esc_url($permalink)) . '?w=140';
532
  }
533
 
534
+ return $screenshot;
535
+ }
536
 
 
 
 
 
 
 
 
 
 
537
 
538
+ /**
539
+ * Returns impression for given cta and variation id
540
+ *
541
+ * @param INT $landing_page_id id of call to action
542
+ * @param INT $variation_id id of variation belonging to call to action
543
+ *
544
+ * @return INT impression count
545
+ */
546
+ public static function get_impressions($landing_page_id, $variation_id) {
547
 
548
+ if (!is_numeric($variation_id)) {
549
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
 
 
550
  }
551
 
552
+ $impressions = get_post_meta($landing_page_id, 'lp-ab-variation-impressions-' . $variation_id, true);
553
+ $impressions = (is_numeric($impressions)) ? $impressions : 0;
 
 
 
 
 
 
 
554
 
555
+ return $impressions;
556
+ }
 
557
 
558
+ /**
559
+ * Returns impression for given cta and variation id
560
+ *
561
+ * @param INT $landing_page_id id of call to action
562
+ * @param INT $variation_id id of variation belonging to call to action
563
+ *
564
+ * @return INT conversion count
565
+ */
566
+ public static function get_conversions($landing_page_id, $variation_id) {
567
 
568
+ if (!is_numeric($variation_id)) {
569
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
570
  }
571
 
572
+ $conversions = get_post_meta($landing_page_id, 'lp-ab-variation-conversions-' . $variation_id, true);
573
+ $conversions = (is_numeric($conversions)) ? $conversions : 0;
 
 
 
 
 
 
 
574
 
575
+ return $conversions;
576
+ }
577
 
578
+ /**
579
+ * Returns conversion rate for given cta and variation id
580
+ *
581
+ * @param INT $landing_page_id id of call to action
582
+ * @param INT $variation_id id of variation belonging to call to action
583
+ *
584
+ * @return INT conversion rate
585
+ */
586
+ public static function get_conversion_rate($landing_page_id, $variation_id) {
587
+
588
+ $impressions = Landing_Pages_Variations::get_impressions($landing_page_id, $variation_id);
589
+ $conversions = Landing_Pages_Variations::get_conversions($landing_page_id, $variation_id);
590
+
591
+ if ($impressions > 0) {
592
+ $conversion_rate = $conversions / $impressions;
593
+ $conversion_rate_number = $conversion_rate * 100;
594
+ $conversion_rate_number = round($conversion_rate_number, 2);
595
+ $conversion_rate = $conversion_rate_number;
596
+ } else {
597
+ $conversion_rate = 0;
598
+ }
599
+
600
+ return $conversion_rate;
601
+ }
602
 
603
+ /**
604
+ * @param $landing_page_id
605
+ * @param null $variation_id
606
+ */
607
+ public static function get_conversion_area($landing_page_id, $variation_id = null) {
608
 
609
+ return Landing_Pages_Variations::get_setting_value('lp-conversion-area', $landing_page_id, $variation_id);
610
+ }
 
 
 
611
 
612
+ /**
613
+ * Returns conversion area placement
614
+ * @param $landing_page_id
615
+ * @param null $variation_id
616
+ */
617
+ public static function get_conversion_area_placement($landing_page_id, $variation_id = null) {
618
+ $template = Landing_Pages_Variations::get_current_template($landing_page_id);
619
+ return Landing_Pages_Variations::get_setting_value($template . '-conversion-area-placement', $landing_page_id, $variation_id);
620
+ }
621
 
622
+ /**
623
+ * @param $landing_page_id
624
+ * @param null $variation_id
625
+ */
626
+ public static function get_post_content($landing_page_id, $variation_id = null) {
627
+ if (!is_numeric($variation_id)) {
628
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
 
629
  }
630
 
631
+ return Landing_Pages_Variations::get_setting_value('content', $landing_page_id, $variation_id);
632
+ }
633
+
634
+ /**
635
+ * Get main headline
636
+ */
637
+ public static function get_main_headline($landing_page_id, $variation_id = null) {
 
638
 
639
+ if (!is_numeric($variation_id)) {
640
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
641
  }
642
 
643
+ return Landing_Pages_Variations::get_setting_value('lp-main-headline', $landing_page_id, $variation_id, '');
644
+ }
 
 
645
 
646
+ /**
647
+ * Gets thumbnail for selected template
648
+ */
649
+ public static function get_template_thumbnail($template) {
650
 
651
+ if (file_exists(LANDINGPAGES_UPLOADS_PATH . $template . '/thumbnail.png')) {
652
+ return LANDINGPAGES_UPLOADS_URLPATH . $template . '/thumbnail.png';
653
+ } else {
654
+ return LANDINGPAGES_URLPATH . 'templates/' . $template . '/thumbnail.png';
655
  }
656
+ }
657
 
 
 
 
 
 
 
 
 
 
 
 
658
 
659
+ /**
660
+ * Gets stored setting value
661
+ * @param $key
662
+ * @param $landing_page_id
663
+ * @param $variation_id
664
+ * @param string $default
665
+ * @return string
666
+ */
667
+ public static function get_setting_value($key, $landing_page_id, $variation_id = null, $default = '') {
668
 
669
+ /* if no variation set look for variation */
670
+ if (!isset($variation_id)) {
671
+ $variation_id = Landing_Pages_Variations::get_current_variation_id();
672
+ }
 
 
 
 
 
673
 
674
+ /* listen for new variation */
675
+ if (isset($_REQUEST['new-variation']) && !isset($_REQUEST['clone'])) {
676
+ $variations = Landing_Pages_Variations::get_variations($landing_page_id);
677
+ $variation_id = key($variations);
678
+ }
679
 
680
+ /* listen for clone variation */
681
+ if (isset($_REQUEST['new-variation']) && isset($_REQUEST['clone'])) {
682
+ $variation_id = intval($_REQUEST['clone']);
683
+ }
 
684
 
685
+ if ($variation_id > 0) {
686
+ if (metadata_exists('post', $landing_page_id, $key . '-' . $variation_id)) {
687
+ return get_post_meta($landing_page_id, $key . '-' . $variation_id, true);
688
+ } else {
689
+ return $default;
690
  }
691
+ } else {
692
 
693
+ if (metadata_exists('post', $landing_page_id, $key)) {
694
+ return get_post_meta($landing_page_id, $key, true);
 
 
 
 
695
  } else {
696
+ return $default;
 
 
 
 
 
697
  }
 
698
  }
699
 
700
+ }
701
 
 
 
 
 
 
 
 
 
702
 
703
+ /**
704
+ * Increments conversion count for given landing page id and variation id
705
+ *
706
+ * @param INT $landing_page_id id of landing page
707
+ * @param INT $variation_id id of variation belonging to call to action
708
+ *
709
+ */
710
+ public static function record_conversion($landing_page_id, $variation_id) {
711
 
712
+ $conversions = self::get_conversions($landing_page_id, $variation_id);
 
 
 
 
713
 
714
+ if (!is_numeric($conversions)) {
715
+ $conversions = 1;
716
+ } else {
717
+ $conversions++;
718
  }
719
 
720
+ self::set_conversions_count($landing_page_id, $variation_id, $conversions);
721
+ }
 
 
 
 
 
 
 
 
722
 
723
+ /**
724
+ * Appends current variation id onto a URL
725
+ *
726
+ * @param link STRING URL that param will be appended onto
727
+ *
728
+ *
729
+ * @return STRING modified URL.
730
+ */
731
+ public static function append_variation_id_to_url($link) {
732
+ global $post;
733
+
734
+ if (!isset($post) || $post->post_type != 'landing-pages') {
735
+ return $link;
736
+ }
737
 
738
+ $current_variation_id = Landing_Pages_Variations::get_current_variation_id();
739
 
740
 
741
+ $link = add_query_arg(array('inbvid' => $current_variation_id), $link);
742
 
743
+ return $link;
744
+ }
745
 
746
+ /**
747
+ * Discovers which alphabetic letter should be associated with a given cta's variation id.
748
+ *
749
+ * @param INT $landing_page_id id of call to action
750
+ * @param INT $variation_id id of variation belonging to call to action
751
+ *
752
+ * @return STRING alphebit letter.
753
+ */
754
+ public static function vid_to_letter($landing_page_id, $variation_id) {
755
+ $variations = Landing_Pages_Variations::get_variations($landing_page_id);
756
 
757
+ $i = 0;
758
+ foreach ($variations as $key => $variation) {
759
+ if ($variation_id == $key) {
760
+ break;
 
 
761
  }
762
+ $i++;
763
+ }
764
 
765
+ $alphabet = array(__('A', 'landing-pages'), __('B', 'landing-pages'), __('C', 'landing-pages'), __('D', 'landing-pages'), __('E', 'landing-pages'), __('F', 'landing-pages'), __('G', 'landing-pages'), __('H', 'landing-pages'), __('I', 'landing-pages'), __('J', 'landing-pages'), __('K', 'landing-pages'), __('L', 'landing-pages'), __('M', 'landing-pages'), __('N', 'landing-pages'), __('O', 'landing-pages'), __('P', 'landing-pages'), __('Q', 'landing-pages'), __('R', 'landing-pages'), __('S', 'landing-pages'), __('T', 'landing-pages'), __('U', 'landing-pages'), __('V', 'landing-pages'), __('W', 'landing-pages'), __('X', 'landing-pages'), __('Y', 'landing-pages'), __('Z', 'landing-pages'));
766
 
767
+ if (isset($alphabet[$i])) {
768
+ return $alphabet[$i];
 
769
  }
770
+ }
771
 
772
 
773
+ /**
774
+ * Adds Ajax for Clear Stats button
775
+ * clear stats for all variations
776
+ */
777
+ public static function ajax_clear_stats() {
778
+ global $wpdb;
 
 
779
 
780
+ $landing_page_id = intval($_POST['page_id']);
781
 
782
+ $variations = self::get_variations($landing_page_id);
 
 
 
783
 
784
+ foreach ($variations as $vid) {
785
+ update_post_meta($landing_page_id, 'lp-ab-variation-impressions-' . $vid, 0, false);
786
+ update_post_meta($landing_page_id, 'lp-ab-variation-conversions-' . $vid, 0, false);
787
  }
788
 
789
+ header('HTTP/1.1 200 OK');
790
+ }
791
 
 
 
 
 
 
 
792
 
793
+ /**
794
+ * Adds Ajax for Clear Stats button
795
+ * clear stats for single variations
796
+ */
797
+ public static function ajax_clear_stats_single() {
798
+ global $wpdb;
799
 
800
+ $landing_page_id = intval($_POST['page_id']);
801
+ $vid = intval($_POST['variation']);
802
 
803
+ self::set_impressions_count($landing_page_id, $vid, 0);
804
+ self::set_conversions_count($landing_page_id, $vid, 0);
805
 
806
+ header('HTTP/1.1 200 OK');
807
  }
808
 
809
+ }
810
 
811
+ new Landing_Pages_Variations();
classes/class.welcome.php CHANGED
@@ -2,12 +2,8 @@
2
  /**
3
  * Weclome Page Class
4
  *
5
- * @package Landing Pages
6
- * @subpackage Admin/Welcome
7
- * @copyright Copyright (c) 2013, Pippin Williamson
8
- * @license http://opensource.org/licenses/gpl-2.0.php GNU Public License
9
- * @since 1.4
10
- * Forked from pippin's WordPress Landing Pages! https://easydigitaldownloads.com/
11
  */
12
 
13
  /* Exit if accessed directly */
2
  /**
3
  * Weclome Page Class
4
  *
5
+ * @package LandingPages
6
+ * @subpackage WelcomeScreen
 
 
 
 
7
  */
8
 
9
  /* Exit if accessed directly */
classes/class.widgets.php CHANGED
@@ -1,5 +1,13 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
3
  add_action('widgets_init', 'lp_load_widgets');
4
 
5
  function lp_load_widgets() {
1
  <?php
2
 
3
+ /**
4
+ * Class adds the 'conversion area widget' designed to compliment landing pages set to use the default template.
5
+ *
6
+ * @package LandingPages
7
+ * @subpackage Widgets
8
+ */
9
+
10
+
11
  add_action('widgets_init', 'lp_load_widgets');
12
 
13
  function lp_load_widgets() {
classes/class.wp-list-table.templates.php CHANGED
@@ -1,5 +1,12 @@
1
  <?php
2
 
 
 
 
 
 
 
 
3
  if (!class_exists('WP_List_Table')) {
4
  require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
5
  }
1
  <?php
2
 
3
+ /**
4
+ * Class displays activated landing page templates
5
+ *
6
+ * @package LandingPages
7
+ * @subpackage Templates
8
+ */
9
+
10
  if (!class_exists('WP_List_Table')) {
11
  require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
12
  }
landing-pages.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Landing Pages
4
  Plugin URI: http://www.inboundnow.com/landing-pages/
5
  Description: Landing page template framework with variant testing and lead capturing through cooperation with Inbound Now's Leads plugin. This is the stand alone version served through WordPress.org.
6
- Version: 2.5.7
7
  Author: Inbound Now
8
  Author URI: http://www.inboundnow.com/
9
 
@@ -11,6 +11,10 @@ Author URI: http://www.inboundnow.com/
11
 
12
  if (!class_exists('Inbound_Landing_Pages_Plugin')) {
13
 
 
 
 
 
14
  final class Inbound_Landing_Pages_Plugin {
15
 
16
  /**
@@ -37,7 +41,7 @@ if (!class_exists('Inbound_Landing_Pages_Plugin')) {
37
  */
38
  private static function load_constants() {
39
 
40
- define('LANDINGPAGES_CURRENT_VERSION', '2.5.7' );
41
  define('LANDINGPAGES_URLPATH', plugins_url( '/' , __FILE__ ) );
42
  define('LANDINGPAGES_PATH', WP_PLUGIN_DIR.'/'.plugin_basename( dirname(__FILE__) ).'/' );
43
  define('LANDINGPAGES_PLUGIN_SLUG', 'landing-pages' );
@@ -93,7 +97,6 @@ if (!class_exists('Inbound_Landing_Pages_Plugin')) {
93
  include_once( LANDINGPAGES_PATH . 'classes/class.acf-integration.php');
94
  include_once( LANDINGPAGES_PATH . 'classes/class.postmeta.php');
95
  include_once( LANDINGPAGES_PATH . 'classes/class.statistics.php');
96
- include_once( LANDINGPAGES_PATH . 'classes/class.click-tracking.php');
97
  include_once( LANDINGPAGES_PATH . 'classes/class.post-type.landing-page.php');
98
  include_once( LANDINGPAGES_PATH . 'modules/module.utils.php');
99
  include_once( LANDINGPAGES_PATH . 'classes/class.sidebars.php');
3
  Plugin Name: Landing Pages
4
  Plugin URI: http://www.inboundnow.com/landing-pages/
5
  Description: Landing page template framework with variant testing and lead capturing through cooperation with Inbound Now's Leads plugin. This is the stand alone version served through WordPress.org.
6
+ Version: 2.5.8
7
  Author: Inbound Now
8
  Author URI: http://www.inboundnow.com/
9
 
11
 
12
  if (!class_exists('Inbound_Landing_Pages_Plugin')) {
13
 
14
+ /**
15
+ * Class Inbound_Landing_Pages_Plugin loads Landing Pages plugin
16
+ * @package Leads
17
+ */
18
  final class Inbound_Landing_Pages_Plugin {
19
 
20
  /**
41
  */
42
  private static function load_constants() {
43
 
44
+ define('LANDINGPAGES_CURRENT_VERSION', '2.5.8' );
45
  define('LANDINGPAGES_URLPATH', plugins_url( '/' , __FILE__ ) );
46
  define('LANDINGPAGES_PATH', WP_PLUGIN_DIR.'/'.plugin_basename( dirname(__FILE__) ).'/' );
47
  define('LANDINGPAGES_PLUGIN_SLUG', 'landing-pages' );
97
  include_once( LANDINGPAGES_PATH . 'classes/class.acf-integration.php');
98
  include_once( LANDINGPAGES_PATH . 'classes/class.postmeta.php');
99
  include_once( LANDINGPAGES_PATH . 'classes/class.statistics.php');
 
100
  include_once( LANDINGPAGES_PATH . 'classes/class.post-type.landing-page.php');
101
  include_once( LANDINGPAGES_PATH . 'modules/module.utils.php');
102
  include_once( LANDINGPAGES_PATH . 'classes/class.sidebars.php');
modules/module.redirect-ab-testing.php CHANGED
@@ -15,6 +15,12 @@ if (file_exists('./../../../../wp-load.php')) {
15
  include_once('./../../../../../../../wp-load.php');
16
  }
17
 
 
 
 
 
 
 
18
  class LP_Variation_Rotation {
19
 
20
  static $permalink_name;
@@ -205,4 +211,4 @@ class LP_Variation_Rotation {
205
  }
206
  }
207
 
208
- $VariationRoation = new LP_Variation_Rotation;
15
  include_once('./../../../../../../../wp-load.php');
16
  }
17
 
18
+ /**
19
+ * Class LP_Variation_Rotation provides an external WP instance set of classes for controlling landing page rotation memory
20
+ * @package Landing Pages
21
+ * @subpackage Variations
22
+ */
23
+
24
  class LP_Variation_Rotation {
25
 
26
  static $permalink_name;
211
  }
212
  }
213
 
214
+ new LP_Variation_Rotation;
modules/module.utils.php CHANGED
@@ -37,6 +37,15 @@ if (!function_exists('inbound_qtrans_disable')) {
37
  * Add namespaces for legacy classes to try and prevent fatals
38
  */
39
  if (!class_exists('LP_EXTENSION_UPDATER') ){
 
 
 
 
40
  class LP_EXTENSION_UPDATER { };
 
 
 
 
 
41
  class LP_EXTENSION_LICENSENING { };
42
  }
37
  * Add namespaces for legacy classes to try and prevent fatals
38
  */
39
  if (!class_exists('LP_EXTENSION_UPDATER') ){
40
+ /**
41
+ * Class LP_EXTENSION_UPDATER depreciated class name
42
+ * @package xDepreciated
43
+ */
44
  class LP_EXTENSION_UPDATER { };
45
+
46
+ /**
47
+ * Class LP_EXTENSION_LICENSENING depreciated class name
48
+ * @package xDepreciated
49
+ */
50
  class LP_EXTENSION_LICENSENING { };
51
  }
readme.txt CHANGED
@@ -7,7 +7,7 @@ License URI: http://www.gnu.org/licenses/gpl-2.0.html
7
  Tags: landing pages, inbound marketing, conversion pages, split testing, a b test, a b testing, a/b test, a/b testing, coming soon page, email list, landing page, list building, maintenance page, squeeze page, inbound now, landing-pages, splash pages, cpa, click tracking, goal tracking, analytics, free landing page templates
8
  Requires at least: 3.8
9
  Tested up to: 4.8
10
- Stable Tag: 2.5.7
11
 
12
 
13
  Create landing pages for your WordPress site. Monitor and improve conversion rates, run A/B split tests, customize your own templates and more.
@@ -85,6 +85,10 @@ We also offer a guide for using <a href="https://github.com/inboundnow/landing-p
85
 
86
  == Changelog ==
87
 
 
 
 
 
88
  = 2.5.7 =
89
  * Compatible with WordPress 4.8
90
  * Updating shared files
7
  Tags: landing pages, inbound marketing, conversion pages, split testing, a b test, a b testing, a/b test, a/b testing, coming soon page, email list, landing page, list building, maintenance page, squeeze page, inbound now, landing-pages, splash pages, cpa, click tracking, goal tracking, analytics, free landing page templates
8
  Requires at least: 3.8
9
  Tested up to: 4.8
10
+ Stable Tag: 2.5.8
11
 
12
 
13
  Create landing pages for your WordPress site. Monitor and improve conversion rates, run A/B split tests, customize your own templates and more.
85
 
86
  == Changelog ==
87
 
88
+ = 2.5.8 =
89
+ * Updating docblocks for APIGen
90
+ * [Restored] Impression tracking was failing in select theme environments.
91
+
92
  = 2.5.7 =
93
  * Compatible with WordPress 4.8
94
  * Updating shared files
shared/assets/js/admin/marketing-button.js CHANGED
@@ -1,243 +1,243 @@
1
- /**
2
- * Marketing Button JS
3
- */
4
- var MarketingButton = (function () {
5
-
6
- var _privateMethod = function () {};
7
-
8
- var inbound_buttons_loaded = false;
9
- var Public = {
10
- init: function () {
11
- // add listeners to iframes
12
- this.waitForEditorLoad();
13
- this.attachHandlers();
14
- },
15
- attachHandlers: function(){
16
- this.closePopupHandler();
17
- this.triggerPopupHandler();
18
- this.insertShortcodeHandler();
19
- this.launchShortcodeWindow();
20
- this.backButtonHandler();
21
- },
22
- triggerPopupHandler: function() {
23
- var that = this;
24
- jQuery("body").on('click', '.open-marketing-button-popup', function (e) {
25
- e.preventDefault();
26
- var id = jQuery(this).attr('data-editor');
27
- jQuery('#iframe-target').attr('current-editor', id);
28
-
29
- if(document.getElementById(id) && document.getElementById(id).parentNode.parentNode.parentNode.style.display !== "none") {
30
- console.log('iframe display');
31
- } else {
32
- id = id.replace("_ifr", "");
33
- jQuery('#iframe-target').attr('current-editor', id);
34
- console.log('textarea display');
35
- }
36
-
37
- console.log('editor id', id);
38
- /*var pos = that.getCursorPosition(iframeTarget);*/
39
-
40
- /* Run popup here */
41
- jQuery.magnificPopup.open({
42
- items: {
43
- src: '#inbound-marketing-popup', // can be a HTML string, jQuery object, or CSS selector
44
- type: 'inline',
45
- callbacks: {
46
- open: function() {
47
- jQuery('.inbound-short-list').show();
48
- },
49
- close: function() {
50
- jQuery("#iframe-target").html('');
51
- }
52
- }
53
- }
54
- });
55
-
56
- });
57
- // on marketing button click, grab ID to insert to
58
- },
59
- waitForEditorLoad: function() {
60
- var that = this;
61
- jQuery(".acf_postbox .field_type-wysiwyg iframe")
62
- .waitUntilExists(function(){
63
- console.log('wait');
64
- if(!inbound_buttons_loaded) {
65
- // do stuff with editor
66
-
67
- that.addButtonsToACFNormal();
68
-
69
- inbound_buttons_loaded = true;
70
- }
71
- });
72
- },
73
- launchShortcodeWindow: function(){
74
- jQuery(".launch-marketing-shortcode").on('click', function (e) {
75
- var $this = jQuery(this);
76
- var type = $this.attr('data-launch-sc');
77
- var url = inbound_load.image_dir + 'popup.php?popup=' + type + '&width=' + 900 + "&path=" + encodeURIComponent(inbound_load.image_dir);
78
- //$('shortcode-frame').attr('href', url);
79
- // hide list
80
- jQuery('.inbound-short-list').hide();
81
- var iframe = "<iframe src="+url+">";
82
- //$('#iframe-target').html('');
83
- //$('#iframe-target').html(iframe);
84
- jQuery.ajax({
85
- url: url,
86
- success:function(data){
87
- jQuery('#iframe-target').html(data);
88
- }
89
- });
90
- });
91
- },
92
- /* Add buttons to normal ACF */
93
- addButtonsToACFNormal: function(){
94
- console.log('add buttons');
95
- jQuery('.acf_postbox .field_type-wysiwyg').each(function(){
96
- var $this = jQuery(this);
97
- var label = $this.find('label');
98
- var iframeID = $this.find('iframe').attr('id');
99
- //console.log('iframe', iframeID);
100
- var marButton = '<a data-editor="'+iframeID+'" href="#inbound-marketing-popup" class="button inbound-marketing-button open-marketing-button-popup" title="Marketing"><span class="wp-media-buttons-icon" id="inboundnow-media-button"></span>Marketing</a>';
101
- jQuery(marButton).appendTo(label);
102
- });
103
-
104
- },
105
-
106
- insertShortcodeHandler: function() {
107
- var that = this;
108
- jQuery("body").on('click', '#marketing-insert-shortcode', function () {
109
- console.log('Insert the shortcode dudddee');
110
- // insert into content
111
- var shortcode = jQuery('#_inbound_shortcodes_newoutput').html();
112
- var id = jQuery('#iframe-target').attr('current-editor');
113
-
114
- if(id.indexOf("_ifr") > -1) {
115
- var iframeTarget = document.getElementById(id).contentWindow.document.body;
116
- var type = "iframe";
117
- } else {
118
- var iframeTarget = jQuery("#" + id);
119
- var type = "textarea";
120
- }
121
-
122
- setTimeout(function() {
123
-
124
- if(type === "iframe") {
125
- that.insertContent(shortcode, iframeTarget);
126
- } else {
127
- that.insertTextAreaContent(shortcode, iframeTarget);
128
- }
129
-
130
- jQuery.magnificPopup.close();
131
-
132
- }, 300);
133
- });
134
- },
135
- closePopupHandler: function() {
136
- var that = this;
137
- jQuery("body").on('click', '#cancel_marketing_button', function () {
138
- jQuery.magnificPopup.close();
139
- jQuery("#iframe-target").html('');
140
- jQuery('.inbound-short-list').show();
141
- });
142
- },
143
- insertTextAreaContent: function(text, selector) {
144
- var cursorPos = selector.prop('selectionStart');
145
- var v = selector.val();
146
- var textBefore = v.substring(0, cursorPos );
147
- var textAfter = v.substring( cursorPos, v.length );
148
- selector.val( textBefore + text + textAfter );
149
- },
150
- insertContent: function(text, iframe) {
151
- var sel, range, html;
152
- var doc = iframe.ownerDocument || iframe.document;
153
- var win = doc.defaultView || doc.parentWindow;
154
- sel = win.getSelection();
155
- if (sel && sel.rangeCount > 0) {
156
- console.log('Content inserted!');
157
- range = sel.getRangeAt(0);
158
- range.deleteContents();
159
- var textNode = document.createTextNode(text);
160
- range.insertNode(textNode);
161
- range.setStartAfter(textNode);
162
- sel.removeAllRanges();
163
- sel.addRange(range);
164
- } else {
165
- console.log('havent clicked in box yet');
166
- /* focus for the user to insert the content */
167
- iframe.focus();
168
- console.log('run this again');
169
- this.insertContent(text, iframe);
170
- }
171
- },
172
- backButtonHandler: function() {
173
- jQuery("body").on('click', '.marketing-back-button', function () {
174
- // toggle display
175
- jQuery("#iframe-target").html('');
176
- jQuery('.select2-drop').remove();
177
- jQuery('.inbound-short-list').show();
178
- });
179
- },
180
- getCursorPosition: function (iframe) {
181
- var caretOffset = 0,
182
- doc = iframe.ownerDocument || iframe.document,
183
- win = doc.defaultView || doc.parentWindow,
184
- sel;
185
-
186
- if (typeof win.getSelection != "undefined") {
187
- sel = win.getSelection();
188
- if (sel.rangeCount > 0) {
189
- var range = win.getSelection().getRangeAt(0);
190
- var preCaretRange = range.cloneRange();
191
- preCaretRange.selectNodeContents(iframe);
192
- preCaretRange.setEnd(range.endContainer, range.endOffset);
193
- caretOffset = preCaretRange.toString().length;
194
- }
195
- } else if ( (sel = doc.selection) && sel.type != "Control") {
196
- var textRange = sel.createRange();
197
- var preCaretTextRange = doc.body.createTextRange();
198
- preCaretTextRange.moveToElementText(iframe);
199
- preCaretTextRange.setEndPoint("EndToEnd", textRange);
200
- caretOffset = preCaretTextRange.text.length;
201
- }
202
-
203
- return caretOffset;
204
- }
205
- };
206
-
207
- return Public;
208
-
209
- })();
210
-
211
- jQuery(document).ready(function($) {
212
- MarketingButton.init();
213
-
214
- });
215
-
216
- (function ($) {
217
-
218
- /**
219
- * @function
220
- * @property {object} jQuery plugin which runs handler function once specified element is inserted into the DOM
221
- * @param {function} handler A function to execute at the time when the element is inserted
222
- * @param {bool} shouldRunHandlerOnce Optional: if true, handler is unbound after its first invocation
223
- * @example $(selector).waitUntilExists(function);
224
- */
225
-
226
- $.fn.waitUntilExists = function (handler, shouldRunHandlerOnce, isChild) {
227
- var found = 'found';
228
- var $this = $(this.selector);
229
- var $elements = $this.not(function () { return $(this).data(found); }).each(handler).data(found, true);
230
-
231
- if (!isChild) {
232
- (window.wait_until_exists = window.wait_until_exists || {})[this.selector] = window.setInterval(function () {
233
- $this.waitUntilExists(handler, shouldRunHandlerOnce, true);
234
- }, 500)
235
- ;
236
- } else if (shouldRunHandlerOnce && $elements.length) {
237
- window.clearInterval(window.wait_until_exists[this.selector]);
238
- }
239
-
240
- return $this;
241
- }
242
-
243
- }(jQuery));
1
+ /**
2
+ * Marketing Button JS
3
+ */
4
+ var MarketingButton = (function () {
5
+
6
+ var _privateMethod = function () {};
7
+
8
+ var inbound_buttons_loaded = false;
9
+ var Public = {
10
+ init: function () {
11
+ // add listeners to iframes
12
+ this.waitForEditorLoad();
13
+ this.attachHandlers();
14
+ },
15
+ attachHandlers: function(){
16
+ this.closePopupHandler();
17
+ this.triggerPopupHandler();
18
+ this.insertShortcodeHandler();
19
+ this.launchShortcodeWindow();
20
+ this.backButtonHandler();
21
+ },
22
+ triggerPopupHandler: function() {
23
+ var that = this;
24
+ jQuery("body").on('click', '.open-marketing-button-popup', function (e) {
25
+ e.preventDefault();
26
+ var id = jQuery(this).attr('data-editor');
27
+ jQuery('#iframe-target').attr('current-editor', id);
28
+
29
+ if(document.getElementById(id) && document.getElementById(id).parentNode.parentNode.parentNode.style.display !== "none") {
30
+ console.log('iframe display');
31
+ } else {
32
+ id = id.replace("_ifr", "");
33
+ jQuery('#iframe-target').attr('current-editor', id);
34
+ console.log('textarea display');
35
+ }
36
+
37
+ console.log('editor id', id);
38
+ /*var pos = that.getCursorPosition(iframeTarget);*/
39
+
40
+ /* Run popup here */
41
+ jQuery.magnificPopup.open({
42
+ items: {
43
+ src: '#inbound-marketing-popup', // can be a HTML string, jQuery object, or CSS selector
44
+ type: 'inline',
45
+ callbacks: {
46
+ open: function() {
47
+ jQuery('.inbound-short-list').show();
48
+ },
49
+ close: function() {
50
+ jQuery("#iframe-target").html('');
51
+ }
52
+ }
53
+ }
54
+ });
55
+
56
+ });
57
+ // on marketing button click, grab ID to insert to
58
+ },
59
+ waitForEditorLoad: function() {
60
+ var that = this;
61
+ jQuery(".acf_postbox .field_type-wysiwyg iframe")
62
+ .waitUntilExists(function(){
63
+ console.log('wait');
64
+ if(!inbound_buttons_loaded) {
65
+ // do stuff with editor
66
+
67
+ that.addButtonsToACFNormal();
68
+
69
+ inbound_buttons_loaded = true;
70
+ }
71
+ });
72
+ },
73
+ launchShortcodeWindow: function(){
74
+ jQuery(".launch-marketing-shortcode").on('click', function (e) {
75
+ var $this = jQuery(this);
76
+ var type = $this.attr('data-launch-sc');
77
+ var url = inbound_load.image_dir + 'popup.php?popup=' + type + '&width=' + 900 + "&path=" + encodeURIComponent(inbound_load.image_dir);
78
+ //$('shortcode-frame').attr('href', url);
79
+ // hide list
80
+ jQuery('.inbound-short-list').hide();
81
+ var iframe = "<iframe src="+url+">";
82
+ //$('#iframe-target').html('');
83
+ //$('#iframe-target').html(iframe);
84
+ jQuery.ajax({
85
+ url: url,
86
+ success:function(data){
87
+ jQuery('#iframe-target').html(data);
88
+ }
89
+ });
90
+ });
91
+ },
92
+ /* Add buttons to normal ACF */
93
+ addButtonsToACFNormal: function(){
94
+ console.log('add buttons');
95
+ jQuery('.acf_postbox .field_type-wysiwyg').each(function(){
96
+ var $this = jQuery(this);
97
+ var label = $this.find('label');
98
+ var iframeID = $this.find('iframe').attr('id');
99
+ //console.log('iframe', iframeID);
100
+ var marButton = '<a data-editor="'+iframeID+'" href="#inbound-marketing-popup" class="button inbound-marketing-button open-marketing-button-popup" title="Marketing"><span class="wp-media-buttons-icon" id="inboundnow-media-button"></span>Marketing</a>';
101
+ jQuery(marButton).appendTo(label);
102
+ });
103
+
104
+ },
105
+
106
+ insertShortcodeHandler: function() {
107
+ var that = this;
108
+ jQuery("body").on('click', '#marketing-insert-shortcode', function () {
109
+ console.log('Insert the shortcode dudddee');
110
+ // insert into content
111
+ var shortcode = jQuery('#_inbound_shortcodes_newoutput').html();
112
+ var id = jQuery('#iframe-target').attr('current-editor');
113
+
114
+ if(id.indexOf("_ifr") > -1) {
115
+ var iframeTarget = document.getElementById(id).contentWindow.document.body;
116
+ var type = "iframe";
117
+ } else {
118
+ var iframeTarget = jQuery("#" + id);
119
+ var type = "textarea";
120
+ }
121
+
122
+ setTimeout(function() {
123
+
124
+ if(type === "iframe") {
125
+ that.insertContent(shortcode, iframeTarget);
126
+ } else {
127
+ that.insertTextAreaContent(shortcode, iframeTarget);
128
+ }
129
+
130
+ jQuery.magnificPopup.close();
131
+
132
+ }, 300);
133
+ });
134
+ },
135
+ closePopupHandler: function() {
136
+ var that = this;
137
+ jQuery("body").on('click', '#cancel_marketing_button', function () {
138
+ jQuery.magnificPopup.close();
139
+ jQuery("#iframe-target").html('');
140
+ jQuery('.inbound-short-list').show();
141
+ });
142
+ },
143
+ insertTextAreaContent: function(text, selector) {
144
+ var cursorPos = selector.prop('selectionStart');
145
+ var v = selector.val();
146
+ var textBefore = v.substring(0, cursorPos );
147
+ var textAfter = v.substring( cursorPos, v.length );
148
+ selector.val( textBefore + text + textAfter );
149
+ },
150
+ insertContent: function(text, iframe) {
151
+ var sel, range, html;
152
+ var doc = iframe.ownerDocument || iframe.document;
153
+ var win = doc.defaultView || doc.parentWindow;
154
+ sel = win.getSelection();
155
+ if (sel && sel.rangeCount > 0) {
156
+ console.log('Content inserted!');
157
+ range = sel.getRangeAt(0);
158
+ range.deleteContents();
159
+ var textNode = document.createTextNode(text);
160
+ range.insertNode(textNode);
161
+ range.setStartAfter(textNode);
162
+ sel.removeAllRanges();
163
+ sel.addRange(range);
164
+ } else {
165
+ console.log('havent clicked in box yet');
166
+ /* focus for the user to insert the content */
167
+ iframe.focus();
168
+ console.log('run this again');
169
+ this.insertContent(text, iframe);
170
+ }
171
+ },
172
+ backButtonHandler: function() {
173
+ jQuery("body").on('click', '.marketing-back-button', function () {
174
+ // toggle display
175
+ jQuery("#iframe-target").html('');
176
+ jQuery('.select2-drop').remove();
177
+ jQuery('.inbound-short-list').show();
178
+ });
179
+ },
180
+ getCursorPosition: function (iframe) {
181
+ var caretOffset = 0,
182
+ doc = iframe.ownerDocument || iframe.document,
183
+ win = doc.defaultView || doc.parentWindow,
184
+ sel;
185
+
186
+ if (typeof win.getSelection != "undefined") {
187
+ sel = win.getSelection();
188
+ if (sel.rangeCount > 0) {
189
+ var range = win.getSelection().getRangeAt(0);
190
+ var preCaretRange = range.cloneRange();
191
+ preCaretRange.selectNodeContents(iframe);
192
+ preCaretRange.setEnd(range.endContainer, range.endOffset);
193
+ caretOffset = preCaretRange.toString().length;
194
+ }
195
+ } else if ( (sel = doc.selection) && sel.type != "Control") {
196
+ var textRange = sel.createRange();
197
+ var preCaretTextRange = doc.body.createTextRange();
198
+ preCaretTextRange.moveToElementText(iframe);
199
+ preCaretTextRange.setEndPoint("EndToEnd", textRange);
200
+ caretOffset = preCaretTextRange.text.length;
201
+ }
202
+
203
+ return caretOffset;
204
+ }
205
+ };
206
+
207
+ return Public;
208
+
209
+ })();
210
+
211
+ jQuery(document).ready(function($) {
212
+ MarketingButton.init();
213
+
214
+ });
215
+
216
+ (function ($) {
217
+
218
+ /**
219
+ * @function
220
+ * @property {object} jQuery plugin which runs handler function once specified element is inserted into the DOM
221
+ * @param {function} handler A function to execute at the time when the element is inserted
222
+ * @param {bool} shouldRunHandlerOnce Optional: if true, handler is unbound after its first invocation
223
+ * @example $(selector).waitUntilExists(function);
224
+ */
225
+
226
+ $.fn.waitUntilExists = function (handler, shouldRunHandlerOnce, isChild) {
227
+ var found = 'found';
228
+ var $this = $(this.selector);
229
+ var $elements = $this.not(function () { return $(this).data(found); }).each(handler).data(found, true);
230
+
231
+ if (!isChild) {
232
+ (window.wait_until_exists = window.wait_until_exists || {})[this.selector] = window.setInterval(function () {
233
+ $this.waitUntilExists(handler, shouldRunHandlerOnce, true);
234
+ }, 500)
235
+ ;
236
+ } else if (shouldRunHandlerOnce && $elements.length) {
237
+ window.clearInterval(window.wait_until_exists[this.selector]);
238
+ }
239
+
240
+ return $this;
241
+ }
242
+
243
+ }(jQuery));
shared/assets/js/frontend/analytics-src/analytics.events.js CHANGED
@@ -1,609 +1,609 @@
1
- /**
2
- * # Analytics Events
3
- *
4
- * Events are triggered throughout the visitors journey through the site. See more on [Inbound Now][in]
5
- *
6
- * @author David Wells <david@inboundnow.com>
7
- * @author Hudson Atwell <hudson@inboundnow.com>
8
- * @version 0.0.2
9
- *
10
- * [in]: http://www.inboundnow.com/
11
- */
12
-
13
- // Add object to _inbound
14
- var _inboundEvents = (function(_inbound) {
15
-
16
-
17
- _inbound.trigger = function(trigger, data) {
18
- _inbound.Events[trigger](data);
19
-
20
- };
21
-
22
- /*!
23
- *
24
- * Private Function that Fires & Emits Events
25
- *
26
- * There are three options for firing events and they trigger in this order:
27
- *
28
- * 1. Vanilla JS dispatch event
29
- * 2. `_inbound.add_action('namespace', callback, priority)`
30
- * 3. jQuery Trigger `jQuery.trigger('namespace', callback);`
31
- *
32
- * The Event `data` can be filtered before events are triggered
33
- * with filters. Example: filter_ + "namespace"
34
- *
35
- * ```js
36
- * // Filter Form Data before submissionsz
37
- * _inbound.add_filter( 'filter_form_before_submission', event_filter_data_example, 10);
38
- *
39
- * function event_filter_data_example(data) {
40
- * var data = data || {};
41
- * // Do something with data
42
- * return data;
43
- * }
44
- * ```
45
- *
46
- * @param {string} eventName Name of the event
47
- * @param {object} data Data passed to external functions/triggers
48
- * @param {object} options Options for configuring events
49
- * @return {null} Nothing returned
50
- */
51
- function fireEvent(eventName, data, options) {
52
- var data = data || {};
53
- options = options || {};
54
-
55
- /*! defaults for JS dispatch event */
56
- options.bubbles = options.bubbles || true,
57
- options.cancelable = options.cancelable || true;
58
-
59
- /*! Customize Data via filter_ + "namespace" */
60
- data = _inbound.apply_filters('filter_' + eventName, data);
61
-
62
- var is_IE_11 = !(window.ActiveXObject) && "ActiveXObject" in window;
63
-
64
- if( typeof CustomEvent === 'function') {
65
-
66
- var TriggerEvent = new CustomEvent(eventName, {
67
- detail: data,
68
- bubbles: options.bubbles,
69
- cancelable: options.cancelable
70
- });
71
-
72
- } else {
73
- var TriggerEvent = document.createEvent("Event");
74
- TriggerEvent.initEvent(eventName, true, true);
75
- }
76
-
77
- /*! 1. Trigger Pure Javascript Event See: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events for example on creating events */
78
- window.dispatchEvent(TriggerEvent);
79
- /*! 2. Trigger _inbound action */
80
- _inbound.do_action(eventName, data);
81
- /*! 3. jQuery trigger */
82
- triggerJQueryEvent(eventName, data);
83
-
84
- // console.log('Action:' + eventName + " ran on ->", data);
85
-
86
- }
87
-
88
- function triggerJQueryEvent(eventName, data) {
89
- if (window.jQuery) {
90
- var data = data || {};
91
- /*! try catch here */
92
- jQuery(document).trigger(eventName, data);
93
- }
94
- };
95
-
96
- var universalGA,
97
- classicGA,
98
- googleTagManager;
99
-
100
- _inbound.Events = {
101
-
102
- /**
103
- * # Event Usage
104
- *
105
- * Events are triggered throughout the visitors path through the site.
106
- * You can hook into these custom actions and filters much like WordPress Core
107
- *
108
- * See below for examples
109
- */
110
-
111
- /**
112
- * Adding Custom Actions
113
- * ------------------
114
- * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
115
- *
116
- * `
117
- * _inbound.add_action( 'action_name', callback, priority );
118
- * `
119
- *
120
- * ```js
121
- * // example:
122
- *
123
- * // Add custom function to `page_visit` event
124
- * _inbound.add_action( 'page_visit', callback, 10 );
125
- *
126
- * // add custom callback to trigger when `page_visit` fires
127
- * function callback(pageData){
128
- * var pageData = pageData || {};
129
- * // run callback on 'page_visit' trigger
130
- * alert(pageData.title);
131
- * }
132
- * ```
133
- *
134
- * @param {string} action_name Name of the event trigger
135
- * @param {function} callback function to trigger when event happens
136
- * @param {int} priority Order to trigger the event in
137
- *
138
- */
139
-
140
- /**
141
- * Removing Custom Actions
142
- * ------------------
143
- * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
144
- *
145
- * `
146
- * _inbound.remove_action( 'action_name');
147
- * `
148
- *
149
- * ```js
150
- * // example:
151
- *
152
- * _inbound.remove_action( 'page_visit');
153
- * // all 'page_visit' actions have been deregistered
154
- * ```
155
- *
156
- * @param {string} action_name Name of the event trigger
157
- *
158
- */
159
-
160
- /**
161
- * # Event List
162
- *
163
- * Events are triggered throughout the visitors journey through the site
164
- */
165
-
166
- /**
167
- * Triggers when analyics has finished loading
168
- */
169
- analytics_ready: function() {
170
- var ops = {
171
- 'opt1': true
172
- };
173
- var data = {
174
- 'data': 'xyxy'
175
- };
176
- fireEvent('analytics_ready', data, ops);
177
- },
178
- /**
179
- * Triggers when the browser url params are parsed. You can perform custom actions
180
- * if specific url params exist.
181
- *
182
- * ```js
183
- * // Usage:
184
- *
185
- * // Add function to 'url_parameters' event
186
- * _inbound.add_action( 'url_parameters', url_parameters_func_example, 10);
187
- *
188
- * function url_parameters_func_example(urlParams) {
189
- * var urlParams = urlParams || {};
190
- * for( var param in urlParams ) {
191
- * var key = param;
192
- * var value = urlParams[param];
193
- * }
194
- * // All URL Params
195
- * alert(JSON.stringify(urlParams));
196
- *
197
- * // Check if URL parameter `utm_source` exists and matches value
198
- * if(urlParams.utm_source === "twitter") {
199
- * alert('This person is from twitter!');
200
- * }
201
- * }
202
- * ```
203
- */
204
- url_parameters: function(data) {
205
- fireEvent('url_parameters', data);
206
- },
207
- /**
208
- * Triggers when session starts
209
- *
210
- * ```js
211
- * // Usage:
212
- *
213
- * // Add function to 'session_start' event
214
- * _inbound.add_action( 'session_start', session_start_func_example, 10);
215
- *
216
- * function session_start_func_example(data) {
217
- * var data = data || {};
218
- * // session start. Do something for new visitor
219
- * }
220
- * ```
221
- */
222
- session_start: function() {
223
- console.log('');
224
- fireEvent('session_start');
225
- },
226
- /**
227
- * Triggers when visitor session goes idle for more than 30 minutes.
228
- *
229
- * ```js
230
- * // Usage:
231
- *
232
- * // Add function to 'session_end' event
233
- * _inbound.add_action( 'session_end', session_end_func_example, 10);
234
- *
235
- * function session_end_func_example(data) {
236
- * var data = data || {};
237
- * // Do something when session ends
238
- * alert("Hey! It's been 30 minutes... where did you go?");
239
- * }
240
- * ```
241
- */
242
- session_end: function(clockTime) {
243
- fireEvent('session_end', clockTime);
244
- console.log('Session End');
245
- },
246
- /**
247
- * Triggers if active session is detected
248
- *
249
- * ```js
250
- * // Usage:
251
- *
252
- * // Add function to 'session_active' event
253
- * _inbound.add_action( 'session_active', session_active_func_example, 10);
254
- *
255
- * function session_active_func_example(data) {
256
- * var data = data || {};
257
- * // session active
258
- * }
259
- * ```
260
- */
261
- session_active: function() {
262
- fireEvent('session_active');
263
- },
264
- /**
265
- * Triggers when visitor session goes idle. Idling occurs after 60 seconds of
266
- * inactivity or when the visitor switches browser tabs
267
- *
268
- * ```js
269
- * // Usage:
270
- *
271
- * // Add function to 'session_idle' event
272
- * _inbound.add_action( 'session_idle', session_idle_func_example, 10);
273
- *
274
- * function session_idle_func_example(data) {
275
- * var data = data || {};
276
- * // Do something when session idles
277
- * alert('Here is a special offer for you!');
278
- * }
279
- * ```
280
- */
281
- session_idle: function(clockTime) {
282
- fireEvent('session_idle', clockTime);
283
- },
284
- /**
285
- * Triggers when session is already active and gets resumed
286
- *
287
- * ```js
288
- * // Usage:
289
- *
290
- * // Add function to 'session_resume' event
291
- * _inbound.add_action( 'session_resume', session_resume_func_example, 10);
292
- *
293
- * function session_resume_func_example(data) {
294
- * var data = data || {};
295
- * // Session exists and is being resumed
296
- * }
297
- * ```
298
- */
299
- session_resume: function() {
300
- fireEvent('session_resume');
301
- },
302
- /**
303
- * Session emitter. Runs every 10 seconds. This is a useful function for
304
- * pinging third party services
305
- *
306
- * ```js
307
- * // Usage:
308
- *
309
- * // Add session_heartbeat_func_example function to 'session_heartbeat' event
310
- * _inbound.add_action( 'session_heartbeat', session_heartbeat_func_example, 10);
311
- *
312
- * function session_heartbeat_func_example(data) {
313
- * var data = data || {};
314
- * // Do something with every 10 seconds
315
- * }
316
- * ```
317
- */
318
- session_heartbeat: function(clockTime) {
319
- var data = {
320
- 'clock': clockTime,
321
- 'leadData': InboundLeadData
322
- };
323
- fireEvent('session_heartbeat', data);
324
- },
325
- /**
326
- * Triggers Every Page View
327
- *
328
- * ```js
329
- * // Usage:
330
- *
331
- * // Add function to 'page_visit' event
332
- * _inbound.add_action( 'page_visit', page_visit_func_example, 10);
333
- *
334
- * function session_idle_func_example(pageData) {
335
- * var pageData = pageData || {};
336
- * if( pageData.view_count > 8 ){
337
- * alert('Wow you have been to this page more than 8 times.');
338
- * }
339
- * }
340
- * ```
341
- */
342
- page_visit: function(pageData) {
343
- fireEvent('page_view', pageData);
344
- },
345
- /**
346
- * Triggers If the visitor has never seen the page before
347
- *
348
- * ```js
349
- * // Usage:
350
- *
351
- * // Add function to 'page_first_visit' event
352
- * _inbound.add_action( 'page_first_visit', page_first_visit_func_example, 10);
353
- *
354
- * function page_first_visit_func_example(pageData) {
355
- * var pageData = pageData || {};
356
- * alert('Welcome to this page! Its the first time you have seen it')
357
- * }
358
- * ```
359
- */
360
- page_first_visit: function(pageData) {
361
- fireEvent('page_first_visit');
362
- _inbound.deBugger('pages', 'First Ever Page View of this Page');
363
- },
364
- /**
365
- * Triggers If the visitor has seen the page before
366
- *
367
- * ```js
368
- * // Usage:
369
- *
370
- * // Add function to 'page_revisit' event
371
- * _inbound.add_action( 'page_revisit', page_revisit_func_example, 10);
372
- *
373
- * function page_revisit_func_example(pageData) {
374
- * var pageData = pageData || {};
375
- * alert('Welcome back to this page!');
376
- * // Show visitor special content/offer
377
- * }
378
- * ```
379
- */
380
- page_revisit: function(pageData) {
381
-
382
- fireEvent('page_revisit', pageData);
383
-
384
- var logger = function() {
385
- console.log('pageData', pageData);
386
- console.log('Page Revisit viewed ' + pageData + " times");
387
- }
388
- _inbound.deBugger('pages', status, logger);
389
- },
390
-
391
- /**
392
- * `tab_hidden` is triggered when the visitor switches browser tabs
393
- *
394
- * ```js
395
- * // Usage:
396
- *
397
- * // Adding the callback
398
- * function tab_hidden_function( data ) {
399
- * alert('The Tab is Hidden');
400
- * };
401
- *
402
- * // Hook the function up the the `tab_hidden` event
403
- * _inbound.add_action( 'tab_hidden', tab_hidden_function, 10 );
404
- * ```
405
- */
406
- tab_hidden: function(data) {
407
- _inbound.deBugger('pages', 'Tab Hidden');
408
- fireEvent('tab_hidden');
409
- },
410
- /**
411
- * `tab_visible` is triggered when the visitor switches back to the sites tab
412
- *
413
- * ```js
414
- * // Usage:
415
- *
416
- * // Adding the callback
417
- * function tab_visible_function( data ) {
418
- * alert('Welcome back to this tab!');
419
- * // trigger popup or offer special discount etc.
420
- * };
421
- *
422
- * // Hook the function up the the `tab_visible` event
423
- * _inbound.add_action( 'tab_visible', tab_visible_function, 10 );
424
- * ```
425
- */
426
- tab_visible: function(data) {
427
- _inbound.deBugger('pages', 'Tab Visible');
428
- fireEvent('tab_visible');
429
- },
430
- /**
431
- * `tab_mouseout` is triggered when the visitor mouses out of the browser window.
432
- * This is especially useful for exit popups
433
- *
434
- * ```js
435
- * // Usage:
436
- *
437
- * // Adding the callback
438
- * function tab_mouseout_function( data ) {
439
- * alert("Wait don't Go");
440
- * // trigger popup or offer special discount etc.
441
- * };
442
- *
443
- * // Hook the function up the the `tab_mouseout` event
444
- * _inbound.add_action( 'tab_mouseout', tab_mouseout_function, 10 );
445
- * ```
446
- */
447
- tab_mouseout: function(data) {
448
- _inbound.deBugger('pages', 'Tab Mouseout');
449
- fireEvent('tab_mouseout');
450
- },
451
- /**
452
- * `form_input_change` is triggered when tracked form inputs change
453
- * You can use this to add additional validation or set conditional triggers
454
- *
455
- * ```js
456
- * // Usage:
457
- *
458
- * ```
459
- */
460
- form_input_change: function(inputData) {
461
- var logger = function() {
462
- console.log(inputData);
463
- //console.log('Page Revisit viewed ' + pageData + " times");
464
- }
465
- _inbound.deBugger('forms', 'inputData change. Data=', logger);
466
- fireEvent('form_input_change', inputData);
467
- },
468
- /**
469
- * `form_before_submission` is triggered before the form is submitted to the server.
470
- * You can filter the data here or send it to third party services
471
- *
472
- * ```js
473
- * // Usage:
474
- *
475
- * // Adding the callback
476
- * function form_before_submission_function( data ) {
477
- * var data = data || {};
478
- * // filter form data
479
- * };
480
- *
481
- * // Hook the function up the the `form_before_submission` event
482
- * _inbound.add_action( 'form_before_submission', form_before_submission_function, 10 );
483
- * ```
484
- */
485
- form_before_submission: function(formData) {
486
- fireEvent('form_before_submission', formData);
487
- },
488
- /**
489
- * `form_after_submission` is triggered after the form is submitted to the server.
490
- * You can filter the data here or send it to third party services
491
- *
492
- * ```js
493
- * // Usage:
494
- *
495
- * // Adding the callback
496
- * function form_after_submission_function( data ) {
497
- * var data = data || {};
498
- * // filter form data
499
- * };
500
- *
501
- * // Hook the function up the the `form_after_submission` event
502
- * _inbound.add_action( 'form_after_submission', form_after_submission_function, 10 );
503
- * ```
504
- */
505
- form_after_submission: function(formData) {
506
-
507
- fireEvent('form_after_submission', formData);
508
-
509
- },
510
- /**
511
- * `search_before_caching` is triggered before the search is stored in the user's browser.
512
- * If a lead ID is set, the search data will be saved to the server when the next page loads.
513
- * You can filter the data here or send it to third party services
514
- *
515
- * ```js
516
- * // Usage:
517
- *
518
- * // Adding the callback
519
- * function search_before_caching_function( data ) {
520
- * var data = data || {};
521
- * // filter search data
522
- * };
523
- *
524
- * // Hook the function up the the `search_before_caching` event
525
- * _inbound.add_action( 'search_before_caching', search_before_caching_function, 10 );
526
- * ```
527
- */
528
- search_before_caching: function(searchData) {
529
- fireEvent('search_before_caching', searchData);
530
- },
531
- /*! Scrol depth https://github.com/robflaherty/jquery-scrolldepth/blob/master/jquery.scrolldepth.js */
532
-
533
- analyticsError: function(MLHttpRequest, textStatus, errorThrown) {
534
- var error = new CustomEvent("inbound_analytics_error", {
535
- detail: {
536
- MLHttpRequest: MLHttpRequest,
537
- textStatus: textStatus,
538
- errorThrown: errorThrown
539
- }
540
- });
541
- window.dispatchEvent(error);
542
- console.log('Page Save Error');
543
- }
544
-
545
- };
546
-
547
- return _inbound;
548
-
549
- })(_inbound || {});
550
-
551
-
552
- function inboundFormNoRedirect(){
553
- /*button == the button that was clicked, form == the form that button belongs to, formRedirectUrl == the link that the form redirects to, if set*/
554
-
555
- /*Get the button...*/
556
- /*If not an iframe*/
557
- if(window.frames.frameElement == null){
558
- var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
559
- }
560
- /*If it is an iframe*/
561
- else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
562
- var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
563
- }
564
-
565
- if ( typeof button == 'undefined' ) {
566
- return;
567
- }
568
-
569
- var form = button.form,
570
- formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
571
-
572
- /*If the redirect link is not set, or there is a single space in it, the form isn't supposed to redirect. So set the action for void*/
573
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
574
- form.action = 'javascript:void(0)';
575
- }
576
- }
577
-
578
- _inbound.add_action( 'form_before_submission', inboundFormNoRedirect, 10 );
579
-
580
- function inboundFormNoRedirectContent(){
581
-
582
- /*If not an iframe*/
583
- if(window.frames.frameElement == null){
584
- var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
585
- }
586
- /*If it is an iframe*/
587
- else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
588
- var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
589
- }
590
-
591
-
592
- if ( typeof button == 'undefined' ) {
593
- return;
594
- }
595
-
596
- var form = button.form,
597
- formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),
598
- btnBackground = jQuery(button).css('background'),
599
- btnFontColor = jQuery(button).css('color'),
600
- btnHeight = jQuery(button).css('height'),
601
- spinner = button.getElementsByClassName('inbound-form-spinner');
602
-
603
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
604
- jQuery(spinner).remove();
605
- jQuery(button).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+ btnBackground +'; color='+ btnFontColor +'; font-size:calc('+ btnHeight +' * .42);"></i></div>');
606
- }
607
- }
608
-
609
- _inbound.add_action( 'form_after_submission', inboundFormNoRedirectContent, 10 );
1
+ /**
2
+ * # Analytics Events
3
+ *
4
+ * Events are triggered throughout the visitors journey through the site. See more on [Inbound Now][in]
5
+ *
6
+ * @contributor David Wells <david@inboundnow.com>
7
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
8
+ * @version 0.0.2
9
+ *
10
+ * [in]: http://www.inboundnow.com/
11
+ */
12
+
13
+ // Add object to _inbound
14
+ var _inboundEvents = (function(_inbound) {
15
+
16
+
17
+ _inbound.trigger = function(trigger, data) {
18
+ _inbound.Events[trigger](data);
19
+
20
+ };
21
+
22
+ /*!
23
+ *
24
+ * Private Function that Fires & Emits Events
25
+ *
26
+ * There are three options for firing events and they trigger in this order:
27
+ *
28
+ * 1. Vanilla JS dispatch event
29
+ * 2. `_inbound.add_action('namespace', callback, priority)`
30
+ * 3. jQuery Trigger `jQuery.trigger('namespace', callback);`
31
+ *
32
+ * The Event `data` can be filtered before events are triggered
33
+ * with filters. Example: filter_ + "namespace"
34
+ *
35
+ * ```js
36
+ * // Filter Form Data before submissionsz
37
+ * _inbound.add_filter( 'filter_form_before_submission', event_filter_data_example, 10);
38
+ *
39
+ * function event_filter_data_example(data) {
40
+ * var data = data || {};
41
+ * // Do something with data
42
+ * return data;
43
+ * }
44
+ * ```
45
+ *
46
+ * @param {string} eventName Name of the event
47
+ * @param {object} data Data passed to external functions/triggers
48
+ * @param {object} options Options for configuring events
49
+ * @return {null} Nothing returned
50
+ */
51
+ function fireEvent(eventName, data, options) {
52
+ var data = data || {};
53
+ options = options || {};
54
+
55
+ /*! defaults for JS dispatch event */
56
+ options.bubbles = options.bubbles || true,
57
+ options.cancelable = options.cancelable || true;
58
+
59
+ /*! Customize Data via filter_ + "namespace" */
60
+ data = _inbound.apply_filters('filter_' + eventName, data);
61
+
62
+ var is_IE_11 = !(window.ActiveXObject) && "ActiveXObject" in window;
63
+
64
+ if( typeof CustomEvent === 'function') {
65
+
66
+ var TriggerEvent = new CustomEvent(eventName, {
67
+ detail: data,
68
+ bubbles: options.bubbles,
69
+ cancelable: options.cancelable
70
+ });
71
+
72
+ } else {
73
+ var TriggerEvent = document.createEvent("Event");
74
+ TriggerEvent.initEvent(eventName, true, true);
75
+ }
76
+
77
+ /*! 1. Trigger Pure Javascript Event See: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events for example on creating events */
78
+ window.dispatchEvent(TriggerEvent);
79
+ /*! 2. Trigger _inbound action */
80
+ _inbound.do_action(eventName, data);
81
+ /*! 3. jQuery trigger */
82
+ triggerJQueryEvent(eventName, data);
83
+
84
+ // console.log('Action:' + eventName + " ran on ->", data);
85
+
86
+ }
87
+
88
+ function triggerJQueryEvent(eventName, data) {
89
+ if (window.jQuery) {
90
+ var data = data || {};
91
+ /*! try catch here */
92
+ jQuery(document).trigger(eventName, data);
93
+ }
94
+ };
95
+
96
+ var universalGA,
97
+ classicGA,
98
+ googleTagManager;
99
+
100
+ _inbound.Events = {
101
+
102
+ /**
103
+ * # Event Usage
104
+ *
105
+ * Events are triggered throughout the visitors path through the site.
106
+ * You can hook into these custom actions and filters much like WordPress Core
107
+ *
108
+ * See below for examples
109
+ */
110
+
111
+ /**
112
+ * Adding Custom Actions
113
+ * ------------------
114
+ * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
115
+ *
116
+ * `
117
+ * _inbound.add_action( 'action_name', callback, priority );
118
+ * `
119
+ *
120
+ * ```js
121
+ * // example:
122
+ *
123
+ * // Add custom function to `page_visit` event
124
+ * _inbound.add_action( 'page_visit', callback, 10 );
125
+ *
126
+ * // add custom callback to trigger when `page_visit` fires
127
+ * function callback(pageData){
128
+ * var pageData = pageData || {};
129
+ * // run callback on 'page_visit' trigger
130
+ * alert(pageData.title);
131
+ * }
132
+ * ```
133
+ *
134
+ * @param {string} action_name Name of the event trigger
135
+ * @param {function} callback function to trigger when event happens
136
+ * @param {int} priority Order to trigger the event in
137
+ *
138
+ */
139
+
140
+ /**
141
+ * Removing Custom Actions
142
+ * ------------------
143
+ * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
144
+ *
145
+ * `
146
+ * _inbound.remove_action( 'action_name');
147
+ * `
148
+ *
149
+ * ```js
150
+ * // example:
151
+ *
152
+ * _inbound.remove_action( 'page_visit');
153
+ * // all 'page_visit' actions have been deregistered
154
+ * ```
155
+ *
156
+ * @param {string} action_name Name of the event trigger
157
+ *
158
+ */
159
+
160
+ /**
161
+ * # Event List
162
+ *
163
+ * Events are triggered throughout the visitors journey through the site
164
+ */
165
+
166
+ /**
167
+ * Triggers when analyics has finished loading
168
+ */
169
+ analytics_ready: function() {
170
+ var ops = {
171
+ 'opt1': true
172
+ };
173
+ var data = {
174
+ 'data': 'xyxy'
175
+ };
176
+ fireEvent('analytics_ready', data, ops);
177
+ },
178
+ /**
179
+ * Triggers when the browser url params are parsed. You can perform custom actions
180
+ * if specific url params exist.
181
+ *
182
+ * ```js
183
+ * // Usage:
184
+ *
185
+ * // Add function to 'url_parameters' event
186
+ * _inbound.add_action( 'url_parameters', url_parameters_func_example, 10);
187
+ *
188
+ * function url_parameters_func_example(urlParams) {
189
+ * var urlParams = urlParams || {};
190
+ * for( var param in urlParams ) {
191
+ * var key = param;
192
+ * var value = urlParams[param];
193
+ * }
194
+ * // All URL Params
195
+ * alert(JSON.stringify(urlParams));
196
+ *
197
+ * // Check if URL parameter `utm_source` exists and matches value
198
+ * if(urlParams.utm_source === "twitter") {
199
+ * alert('This person is from twitter!');
200
+ * }
201
+ * }
202
+ * ```
203
+ */
204
+ url_parameters: function(data) {
205
+ fireEvent('url_parameters', data);
206
+ },
207
+ /**
208
+ * Triggers when session starts
209
+ *
210
+ * ```js
211
+ * // Usage:
212
+ *
213
+ * // Add function to 'session_start' event
214
+ * _inbound.add_action( 'session_start', session_start_func_example, 10);
215
+ *
216
+ * function session_start_func_example(data) {
217
+ * var data = data || {};
218
+ * // session start. Do something for new visitor
219
+ * }
220
+ * ```
221
+ */
222
+ session_start: function() {
223
+ console.log('');
224
+ fireEvent('session_start');
225
+ },
226
+ /**
227
+ * Triggers when visitor session goes idle for more than 30 minutes.
228
+ *
229
+ * ```js
230
+ * // Usage:
231
+ *
232
+ * // Add function to 'session_end' event
233
+ * _inbound.add_action( 'session_end', session_end_func_example, 10);
234
+ *
235
+ * function session_end_func_example(data) {
236
+ * var data = data || {};
237
+ * // Do something when session ends
238
+ * alert("Hey! It's been 30 minutes... where did you go?");
239
+ * }
240
+ * ```
241
+ */
242
+ session_end: function(clockTime) {
243
+ fireEvent('session_end', clockTime);
244
+ console.log('Session End');
245
+ },
246
+ /**
247
+ * Triggers if active session is detected
248
+ *
249
+ * ```js
250
+ * // Usage:
251
+ *
252
+ * // Add function to 'session_active' event
253
+ * _inbound.add_action( 'session_active', session_active_func_example, 10);
254
+ *
255
+ * function session_active_func_example(data) {
256
+ * var data = data || {};
257
+ * // session active
258
+ * }
259
+ * ```
260
+ */
261
+ session_active: function() {
262
+ fireEvent('session_active');
263
+ },
264
+ /**
265
+ * Triggers when visitor session goes idle. Idling occurs after 60 seconds of
266
+ * inactivity or when the visitor switches browser tabs
267
+ *
268
+ * ```js
269
+ * // Usage:
270
+ *
271
+ * // Add function to 'session_idle' event
272
+ * _inbound.add_action( 'session_idle', session_idle_func_example, 10);
273
+ *
274
+ * function session_idle_func_example(data) {
275
+ * var data = data || {};
276
+ * // Do something when session idles
277
+ * alert('Here is a special offer for you!');
278
+ * }
279
+ * ```
280
+ */
281
+ session_idle: function(clockTime) {
282
+ fireEvent('session_idle', clockTime);
283
+ },
284
+ /**
285
+ * Triggers when session is already active and gets resumed
286
+ *
287
+ * ```js
288
+ * // Usage:
289
+ *
290
+ * // Add function to 'session_resume' event
291
+ * _inbound.add_action( 'session_resume', session_resume_func_example, 10);
292
+ *
293
+ * function session_resume_func_example(data) {
294
+ * var data = data || {};
295
+ * // Session exists and is being resumed
296
+ * }
297
+ * ```
298
+ */
299
+ session_resume: function() {
300
+ fireEvent('session_resume');
301
+ },
302
+ /**
303
+ * Session emitter. Runs every 10 seconds. This is a useful function for
304
+ * pinging third party services
305
+ *
306
+ * ```js
307
+ * // Usage:
308
+ *
309
+ * // Add session_heartbeat_func_example function to 'session_heartbeat' event
310
+ * _inbound.add_action( 'session_heartbeat', session_heartbeat_func_example, 10);
311
+ *
312
+ * function session_heartbeat_func_example(data) {
313
+ * var data = data || {};
314
+ * // Do something with every 10 seconds
315
+ * }
316
+ * ```
317
+ */
318
+ session_heartbeat: function(clockTime) {
319
+ var data = {
320
+ 'clock': clockTime,
321
+ 'leadData': InboundLeadData
322
+ };
323
+ fireEvent('session_heartbeat', data);
324
+ },
325
+ /**
326
+ * Triggers Every Page View
327
+ *
328
+ * ```js
329
+ * // Usage:
330
+ *
331
+ * // Add function to 'page_visit' event
332
+ * _inbound.add_action( 'page_visit', page_visit_func_example, 10);
333
+ *
334
+ * function session_idle_func_example(pageData) {
335
+ * var pageData = pageData || {};
336
+ * if( pageData.view_count > 8 ){
337
+ * alert('Wow you have been to this page more than 8 times.');
338
+ * }
339
+ * }
340
+ * ```
341
+ */
342
+ page_visit: function(pageData) {
343
+ fireEvent('page_view', pageData);
344
+ },
345
+ /**
346
+ * Triggers If the visitor has never seen the page before
347
+ *
348
+ * ```js
349
+ * // Usage:
350
+ *
351
+ * // Add function to 'page_first_visit' event
352
+ * _inbound.add_action( 'page_first_visit', page_first_visit_func_example, 10);
353
+ *
354
+ * function page_first_visit_func_example(pageData) {
355
+ * var pageData = pageData || {};
356
+ * alert('Welcome to this page! Its the first time you have seen it')
357
+ * }
358
+ * ```
359
+ */
360
+ page_first_visit: function(pageData) {
361
+ fireEvent('page_first_visit');
362
+ _inbound.deBugger('pages', 'First Ever Page View of this Page');
363
+ },
364
+ /**
365
+ * Triggers If the visitor has seen the page before
366
+ *
367
+ * ```js
368
+ * // Usage:
369
+ *
370
+ * // Add function to 'page_revisit' event
371
+ * _inbound.add_action( 'page_revisit', page_revisit_func_example, 10);
372
+ *
373
+ * function page_revisit_func_example(pageData) {
374
+ * var pageData = pageData || {};
375
+ * alert('Welcome back to this page!');
376
+ * // Show visitor special content/offer
377
+ * }
378
+ * ```
379
+ */
380
+ page_revisit: function(pageData) {
381
+
382
+ fireEvent('page_revisit', pageData);
383
+
384
+ var logger = function() {
385
+ console.log('pageData', pageData);
386
+ console.log('Page Revisit viewed ' + pageData + " times");
387
+ }
388
+ _inbound.deBugger('pages', status, logger);
389
+ },
390
+
391
+ /**
392
+ * `tab_hidden` is triggered when the visitor switches browser tabs
393
+ *
394
+ * ```js
395
+ * // Usage:
396
+ *
397
+ * // Adding the callback
398
+ * function tab_hidden_function( data ) {
399
+ * alert('The Tab is Hidden');
400
+ * };
401
+ *
402
+ * // Hook the function up the the `tab_hidden` event
403
+ * _inbound.add_action( 'tab_hidden', tab_hidden_function, 10 );
404
+ * ```
405
+ */
406
+ tab_hidden: function(data) {
407
+ _inbound.deBugger('pages', 'Tab Hidden');
408
+ fireEvent('tab_hidden');
409
+ },
410
+ /**
411
+ * `tab_visible` is triggered when the visitor switches back to the sites tab
412
+ *
413
+ * ```js
414
+ * // Usage:
415
+ *
416
+ * // Adding the callback
417
+ * function tab_visible_function( data ) {
418
+ * alert('Welcome back to this tab!');
419
+ * // trigger popup or offer special discount etc.
420
+ * };
421
+ *
422
+ * // Hook the function up the the `tab_visible` event
423
+ * _inbound.add_action( 'tab_visible', tab_visible_function, 10 );
424
+ * ```
425
+ */
426
+ tab_visible: function(data) {
427
+ _inbound.deBugger('pages', 'Tab Visible');
428
+ fireEvent('tab_visible');
429
+ },
430
+ /**
431
+ * `tab_mouseout` is triggered when the visitor mouses out of the browser window.
432
+ * This is especially useful for exit popups
433
+ *
434
+ * ```js
435
+ * // Usage:
436
+ *
437
+ * // Adding the callback
438
+ * function tab_mouseout_function( data ) {
439
+ * alert("Wait don't Go");
440
+ * // trigger popup or offer special discount etc.
441
+ * };
442
+ *
443
+ * // Hook the function up the the `tab_mouseout` event
444
+ * _inbound.add_action( 'tab_mouseout', tab_mouseout_function, 10 );
445
+ * ```
446
+ */
447
+ tab_mouseout: function(data) {
448
+ _inbound.deBugger('pages', 'Tab Mouseout');
449
+ fireEvent('tab_mouseout');
450
+ },
451
+ /**
452
+ * `form_input_change` is triggered when tracked form inputs change
453
+ * You can use this to add additional validation or set conditional triggers
454
+ *
455
+ * ```js
456
+ * // Usage:
457
+ *
458
+ * ```
459
+ */
460
+ form_input_change: function(inputData) {
461
+ var logger = function() {
462
+ console.log(inputData);
463
+ //console.log('Page Revisit viewed ' + pageData + " times");
464
+ }
465
+ _inbound.deBugger('forms', 'inputData change. Data=', logger);
466
+ fireEvent('form_input_change', inputData);
467
+ },
468
+ /**
469
+ * `form_before_submission` is triggered before the form is submitted to the server.
470
+ * You can filter the data here or send it to third party services
471
+ *
472
+ * ```js
473
+ * // Usage:
474
+ *
475
+ * // Adding the callback
476
+ * function form_before_submission_function( data ) {
477
+ * var data = data || {};
478
+ * // filter form data
479
+ * };
480
+ *
481
+ * // Hook the function up the the `form_before_submission` event
482
+ * _inbound.add_action( 'form_before_submission', form_before_submission_function, 10 );
483
+ * ```
484
+ */
485
+ form_before_submission: function(formData) {
486
+ fireEvent('form_before_submission', formData);
487
+ },
488
+ /**
489
+ * `form_after_submission` is triggered after the form is submitted to the server.
490
+ * You can filter the data here or send it to third party services
491
+ *
492
+ * ```js
493
+ * // Usage:
494
+ *
495
+ * // Adding the callback
496
+ * function form_after_submission_function( data ) {
497
+ * var data = data || {};
498
+ * // filter form data
499
+ * };
500
+ *
501
+ * // Hook the function up the the `form_after_submission` event
502
+ * _inbound.add_action( 'form_after_submission', form_after_submission_function, 10 );
503
+ * ```
504
+ */
505
+ form_after_submission: function(formData) {
506
+
507
+ fireEvent('form_after_submission', formData);
508
+
509
+ },
510
+ /**
511
+ * `search_before_caching` is triggered before the search is stored in the user's browser.
512
+ * If a lead ID is set, the search data will be saved to the server when the next page loads.
513
+ * You can filter the data here or send it to third party services
514
+ *
515
+ * ```js
516
+ * // Usage:
517
+ *
518
+ * // Adding the callback
519
+ * function search_before_caching_function( data ) {
520
+ * var data = data || {};
521
+ * // filter search data
522
+ * };
523
+ *
524
+ * // Hook the function up the the `search_before_caching` event
525
+ * _inbound.add_action( 'search_before_caching', search_before_caching_function, 10 );
526
+ * ```
527
+ */
528
+ search_before_caching: function(searchData) {
529
+ fireEvent('search_before_caching', searchData);
530
+ },
531
+ /*! Scrol depth https://github.com/robflaherty/jquery-scrolldepth/blob/master/jquery.scrolldepth.js */
532
+
533
+ analyticsError: function(MLHttpRequest, textStatus, errorThrown) {
534
+ var error = new CustomEvent("inbound_analytics_error", {
535
+ detail: {
536
+ MLHttpRequest: MLHttpRequest,
537
+ textStatus: textStatus,
538
+ errorThrown: errorThrown
539
+ }
540
+ });
541
+ window.dispatchEvent(error);
542
+ console.log('Page Save Error');
543
+ }
544
+
545
+ };
546
+
547
+ return _inbound;
548
+
549
+ })(_inbound || {});
550
+
551
+
552
+ function inboundFormNoRedirect(){
553
+ /*button == the button that was clicked, form == the form that button belongs to, formRedirectUrl == the link that the form redirects to, if set*/
554
+
555
+ /*Get the button...*/
556
+ /*If not an iframe*/
557
+ if(window.frames.frameElement == null){
558
+ var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
559
+ }
560
+ /*If it is an iframe*/
561
+ else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
562
+ var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
563
+ }
564
+
565
+ if ( typeof button == 'undefined' ) {
566
+ return;
567
+ }
568
+
569
+ var form = button.form,
570
+ formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
571
+
572
+ /*If the redirect link is not set, or there is a single space in it, the form isn't supposed to redirect. So set the action for void*/
573
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
574
+ form.action = 'javascript:void(0)';
575
+ }
576
+ }
577
+
578
+ _inbound.add_action( 'form_before_submission', inboundFormNoRedirect, 10 );
579
+
580
+ function inboundFormNoRedirectContent(){
581
+
582
+ /*If not an iframe*/
583
+ if(window.frames.frameElement == null){
584
+ var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
585
+ }
586
+ /*If it is an iframe*/
587
+ else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
588
+ var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
589
+ }
590
+
591
+
592
+ if ( typeof button == 'undefined' ) {
593
+ return;
594
+ }
595
+
596
+ var form = button.form,
597
+ formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),
598
+ btnBackground = jQuery(button).css('background'),
599
+ btnFontColor = jQuery(button).css('color'),
600
+ btnHeight = jQuery(button).css('height'),
601
+ spinner = button.getElementsByClassName('inbound-form-spinner');
602
+
603
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
604
+ jQuery(spinner).remove();
605
+ jQuery(button).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+ btnBackground +'; color='+ btnFontColor +'; font-size:calc('+ btnHeight +' * .42);"></i></div>');
606
+ }
607
+ }
608
+
609
+ _inbound.add_action( 'form_after_submission', inboundFormNoRedirectContent, 10 );
shared/assets/js/frontend/analytics-src/analytics.forms.js CHANGED
@@ -1,1207 +1,1207 @@
1
- /**
2
- * # Inbound Forms
3
- *
4
- * This file contains all of the form functions of the main _inbound object.
5
- * Filters and actions are described below
6
- *
7
- * @author David Wells <david@inboundnow.com>
8
- * @author Hudson Atwell <hudson@inboundnow.com>
9
- * @version 0.0.2
10
- */
11
- /* Finish Exclusions for CC */
12
-
13
- /* Launches form class */
14
- var InboundForms = (function(_inbound) {
15
-
16
- var debugMode = false,
17
- utils = _inbound.Utils,
18
- no_match = [],
19
- rawParams = [],
20
- mappedParams = [],
21
- callTracker = {},
22
- settings = _inbound.Settings;
23
-
24
- var FieldMapArray = [
25
- "first name",
26
- "last name",
27
- "name",
28
- "email",
29
- "e-mail",
30
- "phone",
31
- "website",
32
- "job title",
33
- "your favorite food",
34
- "company",
35
- "tele",
36
- "address",
37
- "comment"
38
- /* Adding values here maps them */
39
- ];
40
-
41
- _inbound.Forms = {
42
-
43
- // Init Form functions
44
- init: function() {
45
- _inbound.Forms.runFieldMappingFilters();
46
- _inbound.Forms.formTrackInit();
47
- _inbound.Forms.searchTrackInit();
48
- },
49
- /**
50
- * This triggers the forms.field_map filter on the mapping array.
51
- * This will allow you to add or remore Items from the mapping lookup
52
- *
53
- * ### Example inbound.form_map_before filter
54
- *
55
- * This is an example of how form mapping can be filtered and
56
- * additional fields can be mapped via javascript
57
- *
58
- * ```js
59
- * // Adding the filter function
60
- * function Inbound_Add_Filter_Example( FieldMapArray ) {
61
- * var map = FieldMapArray || [];
62
- * map.push('new lookup value');
63
- *
64
- * return map;
65
- * };
66
- *
67
- * // Adding the filter on dom ready
68
- * _inbound.hooks.addFilter( 'inbound.form_map_before', Inbound_Add_Filter_Example, 10 );
69
- * ```
70
- *
71
- * @return {[type]} [description]
72
- */
73
- runFieldMappingFilters: function() {
74
- FieldMapArray = _inbound.hooks.applyFilters('forms.field_map', FieldMapArray);
75
- //alert(FieldMapArray);
76
- },
77
- debug: function(msg, callback) {
78
- //if app not in debug mode, exit immediately
79
- if (!debugMode || !console) {
80
- return;
81
- }
82
-
83
- var msg = msg || false;
84
- //console.log the message
85
- if (msg && (typeof msg === 'string')) {
86
- console.log(msg);
87
- }
88
-
89
- //execute the callback if one was passed-in
90
- if (callback && (callback instanceof Function)) {
91
- callback();
92
- }
93
- },
94
- formTrackInit: function() {
95
-
96
- for (var i = 0; i < window.document.forms.length; i++) {
97
- var trackForm = false;
98
- var form = window.document.forms[i];
99
- /* process forms only once */
100
- if (!form.dataset.formProcessed) {
101
- form.dataset.formProcessed = true;
102
- trackForm = this.checkTrackStatus(form);
103
- if (trackForm) {
104
- this.attachFormSubmitEvent(form); /* attach form listener */
105
- this.initFormMapping(form);
106
- }
107
- }
108
- }
109
- },
110
- searchTrackInit: function(){
111
-
112
- /* exit if searches aren't supposed to be tracked, or this function has already been called */
113
- if(inbound_settings.search_tracking == 'off' || callTracker['searchTrackInit']){
114
- return;
115
- }
116
-
117
- for (var i = 0; i < window.document.forms.length; i++) {
118
- var trackForm = false;
119
- var form = window.document.forms[i];
120
- /* process forms only once */
121
- if (!form.dataset.searchChecked) {
122
- form.dataset.searchChecked = true;
123
- trackForm = this.checkSearchTrackStatus(form);
124
- if (trackForm) {
125
- this.attachSearchFormSubmitEvent(form); /* attach form listener */
126
- }
127
- }
128
- }
129
-
130
- /* store the search data on init */
131
- utils.storeSearchData();
132
-
133
- /* log that this function has been called */
134
- callTracker['searchTrackInit'] = true;
135
- },
136
- checkTrackStatus: function(form) {
137
- var ClassIs = form.getAttribute('class');
138
- if (ClassIs !== "" && ClassIs !== null) {
139
- if (ClassIs.toLowerCase().indexOf("wpl-track-me") > -1) {
140
- return true;
141
- } else if (ClassIs.toLowerCase().indexOf("inbound-track") > -1) {
142
- return true;
143
- } else {
144
- cb = function() { console.log(form); };
145
- _inbound.deBugger('forms', "This form not tracked. Please assign on in settings...", cb);
146
- return false;
147
- }
148
- }
149
- },
150
- checkSearchTrackStatus: function(form) {
151
- var ClassIs = form.getAttribute('class'),
152
- IdIs = form.getAttribute('id');
153
- if (ClassIs !== "" && ClassIs !== null) {
154
- if (ClassIs.toLowerCase().indexOf("search") > -1) {
155
- return true;
156
- }
157
- }
158
- if (IdIs !== "" && IdIs !== null) {
159
- if (IdIs.toLowerCase().indexOf("search") > -1) {
160
- return true;
161
- }
162
- }else{
163
- cb = function() { console.log(form); };
164
- _inbound.deBugger('searches', "This search form is not tracked. Please assign on in settings...", cb);
165
- return false;
166
- }
167
- },
168
- /* Loop through include/exclude items for tracking */
169
- loopClassSelectors: function(selectors, action) {
170
- for (var i = selectors.length - 1; i >= 0; i--) {
171
-
172
- var selector = utils.trim(selectors[i])
173
- if (selector.indexOf("#") === -1 && selector.indexOf(".") === -1) {
174
- // assign ID as default
175
- selector = "#" + selector;
176
- }
177
- //if(selectors[i] match . or # )
178
- selector = document.querySelector(selector);
179
- //console.log("SELECTOR", selector);
180
- if (selector) {
181
- if (action === 'add') {
182
- _inbound.Utils.addClass('wpl-track-me', selector);
183
- _inbound.Utils.addClass('inbound-track', selector);
184
- } else {
185
- _inbound.Utils.removeClass('wpl-track-me', selector);
186
- _inbound.Utils.removeClass('inbound-track', selector);
187
- }
188
- }
189
- }
190
- },
191
- /* Map field fields on load */
192
- initFormMapping: function(form) {
193
- var hiddenInputs = [];
194
-
195
- for (var i = 0; i < form.elements.length; i++) {
196
- formInput = form.elements[i];
197
-
198
- if (formInput.type === 'hidden') {
199
- hiddenInputs.push(formInput);
200
- continue;
201
- }
202
-
203
- //this.ignoreFields(formInput);
204
- /* Map form fields */
205
- this.mapField(formInput);
206
- /* Remember visible inputs */
207
- this.rememberInputValues(formInput);
208
- /* Fill visible inputs */
209
- if (settings.formAutoPopulation && !_inbound.Utils.hasClass( "nopopulate", form ) ) {
210
- this.fillInputValues(formInput);
211
- }
212
-
213
- }
214
-
215
- /* loop hidden inputs */
216
- for (var n = hiddenInputs.length - 1; n >= 0; n--) {
217
- formInput = hiddenInputs[n];
218
- this.mapField(formInput);
219
- }
220
-
221
- //console.log('mapping on load completed');
222
- },
223
- /* Maps data attributes to fields on page load */
224
- mapField: function(input) {
225
-
226
- var input_id = input.id || false;
227
- var input_name = input.name || false;
228
- var label = this.getInputLabel(input);
229
-
230
- if(label){
231
- //console.log(label[0].innerText);
232
- var ignoreField = this.ignoreFieldByLabel(label[0].innerText);
233
- if(ignoreField){
234
- input.dataset.ignoreFormField = true;
235
- return false;
236
- }
237
- }
238
-
239
- /* Loop through all match possiblities */
240
- for (i = 0; i < FieldMapArray.length; i++) {
241
- //for (var i = FieldMapArray.length - 1; i >= 0; i--) {
242
- var found = false;
243
- var match = FieldMapArray[i];
244
- var lookingFor = utils.trim(match);
245
- var nice_name = lookingFor.replace(/ /g, '_');
246
-
247
-
248
- //console.log("NICE NAME", nice_name);
249
- //console.log('looking for match on ' + lookingFor);
250
- //_inbound.deBugger('forms', 'looking for match on ' + lookingFor + " nice_name= " + nice_name);
251
-
252
- // Check if input has an attached lable using for= tag
253
- //var $laxbel = $("label[for='" + $element.attr('id') + "']").text();
254
- //var labxel = 'label[for="' + input_id + '"]';
255
-
256
- /* look for name attribute match */
257
- if (input_name && input_name.toLowerCase().indexOf(lookingFor) > -1) {
258
- found = true;
259
- _inbound.deBugger('forms', 'Found matching name attribute for -> ' + lookingFor);
260
-
261
- /* look for id match */
262
- } else if (input_id && input_id.toLowerCase().indexOf(lookingFor) > -1) {
263
-
264
- found = true;
265
- _inbound.deBugger('forms', 'Found matching ID attribute for ->' + lookingFor);
266
-
267
- /* Check siblings for label */
268
- } else if (label) {
269
- //var label = (label.length > 1 ? label[0] : label);
270
- //console.log('label', label);
271
- if (label[0].innerText.toLowerCase().indexOf(lookingFor) > -1) {
272
-
273
- found = true;
274
- _inbound.deBugger('forms', 'Found matching sibling label for -> ' + lookingFor);
275
-
276
- }
277
-
278
- } else {
279
- /* no match found */
280
- //_inbound.deBugger('forms', 'NO Match on ' + lookingFor + " in " + input_name);
281
- no_match.push(lookingFor);
282
-
283
- }
284
-
285
- /* Map the field */
286
- if (found) {
287
- this.addDataAttr(input, nice_name);
288
- this.removeArrayItem(FieldMapArray, lookingFor);
289
- i--; //decrement count
290
- }
291
-
292
- }
293
-
294
- return inbound_data;
295
-
296
- },
297
- /* prevent default submission temporarily */
298
- formListener: function(event) {
299
- //console.log(event);
300
- event.preventDefault();
301
- _inbound.Forms.saveFormData(event.target);
302
- document.body.style.cursor = "wait";
303
- },
304
- /* prevent default submission temporarily */
305
- searchFormListener: function(event) {
306
- //console.log(event);
307
- event.preventDefault();
308
- _inbound.Forms.saveSearchData(event.target);
309
- //document.body.style.cursor = "wait";
310
- },
311
- /* attach form listeners */
312
- attachFormSubmitEvent: function(form) {
313
- utils.addListener(form, 'submit', this.formListener);
314
- var email_input = document.querySelector('.inbound-email');
315
- /* utils.addListener(email_input, 'blur', this.mailCheck); */
316
- },
317
- /* attach search form listener */
318
- attachSearchFormSubmitEvent: function(form) {
319
- utils.addListener(form, 'submit', this.searchFormListener);
320
- },
321
- /* Ignore CC data */
322
- ignoreFieldByLabel: function(label) {
323
- var ignore_field = false;
324
-
325
- if(!label){ return false; }
326
-
327
- // Ignore any fields with labels that indicate a credit card field
328
- if (label.toLowerCase().indexOf('credit card') != -1 || label.toLowerCase().indexOf('card number') != -1) {
329
- ignore_field = true;
330
- }
331
-
332
- if (label.toLowerCase().indexOf('expiration') != -1 || label.toLowerCase().indexOf('expiry') != -1) {
333
- ignore_field = true;
334
- }
335
-
336
- if (label.toLowerCase() == 'month' || label.toLowerCase() == 'mm' || label.toLowerCase() == 'yy' || label.toLowerCase() == 'yyyy' || label.toLowerCase() == 'year') {
337
- ignore_field = true;
338
- }
339
-
340
- if (label.toLowerCase().indexOf('cvv') != -1 || label.toLowerCase().indexOf('cvc') != -1 || label.toLowerCase().indexOf('secure code') != -1 || label.toLowerCase().indexOf('security code') != -1) {
341
- ignore_field = true;
342
- }
343
-
344
- if(ignore_field){
345
- _inbound.deBugger('forms', 'ignore ' + label);
346
- }
347
-
348
- return ignore_field;
349
-
350
- },
351
- /* not implemented yet */
352
- ignoreFieldByValue: function(value){
353
- var ignore_field = false;
354
-
355
- if(!value){ return false; }
356
-
357
- if (value.toLowerCase() == 'visa' || value.toLowerCase() == 'mastercard' || value.toLowerCase() == 'american express' || value.toLowerCase() == 'amex' || value.toLowerCase() == 'discover') {
358
- ignore_field = true;
359
- }
360
-
361
- // Check if value has integers, strip out spaces, then ignore anything with a credit card length (>16) or an expiration/cvv length (<5)
362
- var int_regex = new RegExp("/^[0-9]+$/");
363
- if (int_regex.test(value)) {
364
- var value_no_spaces = value.replace(' ', '');
365
-
366
- if (this.isInt(value_no_spaces) && value_no_spaces.length >= 16) {
367
- ignore_field = true;
368
- }
369
-
370
- }
371
-
372
- return ignore_field;
373
-
374
- },
375
- isInt: function(n) {
376
- return typeof n == "number" && isFinite(n) && n % 1 === 0;
377
- },
378
- releaseFormSubmit: function(form) {
379
- //console.log('remove form listener event');
380
- document.body.style.cursor = "default";
381
- utils.removeClass('wpl-track-me', form);
382
- utils.removeListener(form, 'submit', this.formListener);
383
- var formClass = form.getAttribute('class');
384
- if (formClass !== "" && formClass !== null) {
385
- /* If contact form 7 do this */
386
- if (formClass.toLowerCase().indexOf("wpcf7-form") != -1) {
387
- //alert('release')
388
- setTimeout(function() {
389
- document.body.style.cursor = "default";
390
- }, 300);
391
- return true;
392
- }
393
- }
394
-
395
- form.submit();
396
- /* fallback if submit name="submit" */
397
- setTimeout(function() {
398
- for (var i = 0; i < form.elements.length; i++) {
399
- formInput = form.elements[i];
400
- type = formInput.type || false;
401
- if (type === "submit" && formInput.name === "submit") {
402
- form.elements[i].click();
403
- }
404
- }
405
- }, 2000);
406
-
407
- },
408
- saveFormData: function(form) {
409
- var inputsObject = inputsObject || {};
410
- for (var i = 0; i < form.elements.length; i++) {
411
-
412
- // console.log(inputsObject);
413
-
414
- formInput = form.elements[i];
415
- multiple = false;
416
-
417
- if (formInput.name) {
418
-
419
- if (formInput.dataset.ignoreFormField) {
420
- _inbound.deBugger('forms', 'ignore ' + formInput.name);
421
- continue;
422
- }
423
-
424
- inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
425
- //inputName = inputName.replace(/-/g, "_");
426
- if (!inputsObject[inputName]) {
427
- inputsObject[inputName] = {};
428
- }
429
- if (formInput.type) {
430
- inputsObject[inputName]['type'] = formInput.type;
431
- }
432
- if (!inputsObject[inputName]['name']) {
433
- inputsObject[inputName]['name'] = formInput.name;
434
- }
435
- if (formInput.dataset.mapFormField) {
436
- inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
437
- }
438
-
439
-
440
- switch (formInput.nodeName) {
441
-
442
- case 'INPUT':
443
- value = this.getInputValue(formInput);
444
-
445
-
446
- if (value === false) {
447
- continue;
448
- }
449
- break;
450
-
451
- case 'TEXTAREA':
452
- value = formInput.value;
453
- break;
454
-
455
- case 'SELECT':
456
- if (formInput.multiple) {
457
- values = [];
458
- multiple = true;
459
-
460
- for (var j = 0; j < formInput.length; j++) {
461
- if (formInput[j].selected) {
462
- values.push(encodeURIComponent(formInput[j].value));
463
- }
464
- }
465
-
466
- } else {
467
- value = (formInput.value);
468
- }
469
-
470
- break;
471
- }
472
-
473
- _inbound.deBugger('forms', 'Input Value = ' + value);
474
-
475
-
476
- if (value) {
477
- /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
478
- if (!inputsObject[inputName]['value']) {
479
- inputsObject[inputName]['value'] = [];
480
- }
481
- inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
482
- var value = multiple ? values.join(',') : encodeURIComponent(value);
483
-
484
- }
485
-
486
- }
487
- }
488
- _inbound.deBugger('forms', inputsObject);
489
-
490
- //console.log('These are the raw values', inputsObject);
491
- //_inbound.totalStorage('the_key', inputsObject);
492
- //var inputsObject = sortInputs(inputsObject);
493
-
494
- var matchCommon = /name|first name|last name|email|e-mail|phone|website|job title|company|tele|address|comment/;
495
-
496
- for (var input in inputsObject) {
497
- //console.log(input);
498
-
499
- var inputValue = inputsObject[input]['value'];
500
- var inputMappedField = inputsObject[input]['map'];
501
- //if (matchCommon.test(input) !== false) {
502
- //console.log(input + " Matches Regex run mapping test");
503
- //var map = inputsObject[input];
504
- //console.log("MAPP", map);
505
- //mappedParams.push( input + '=' + inputsObject[input]['value'].join(',') );
506
- //}
507
-
508
- /* Add custom hook here to look for additional values */
509
- if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
510
- rawParams.push(input + '=' + inputsObject[input]['value'].join(','));
511
- }
512
-
513
- if (typeof(inputMappedField) != "undefined" && inputMappedField != null && inputsObject[input]['value']) {
514
- //console.log('Data ATTR', formInput.dataset.mapFormField);
515
- mappedParams.push(inputMappedField + "=" + inputsObject[input]['value'].join(','));
516
- if (input === 'email') {
517
- var email = inputsObject[input]['value'].join(',');
518
- //alert(email);
519
-
520
- }
521
- }
522
- }
523
-
524
- var raw_params = rawParams.join('&');
525
- _inbound.deBugger('forms', "Stringified Raw Form PARAMS: " + raw_params);
526
-
527
- var mapped_params = mappedParams.join('&');
528
- _inbound.deBugger('forms', "Stringified Mapped PARAMS" + mapped_params);
529
-
530
- /* Check Use form Email or Cookie */
531
- var email = utils.getParameterVal('email', mapped_params) || utils.readCookie('wp_lead_email');
532
-
533
- /* Legacy Email map */
534
- if (!email) {
535
- email = utils.getParameterVal('wpleads_email_address', mapped_params);
536
- }
537
-
538
- var fullName = utils.getParameterVal('name', mapped_params);
539
- var fName = utils.getParameterVal('first_name', mapped_params);
540
- var lName = utils.getParameterVal('last_name', mapped_params);
541
-
542
- // Fallbacks for empty values
543
- if (!lName && fName) {
544
- var parts = decodeURI(fName).split(" ");
545
- if (parts.length > 0) {
546
- fName = parts[0];
547
- lName = parts[1];
548
- }
549
- }
550
-
551
- if (fullName && !lName && !fName) {
552
- var parts = decodeURI(fullName).split(" ");
553
- if (parts.length > 0) {
554
- fName = parts[0];
555
- lName = parts[1];
556
- }
557
- }
558
-
559
- fullName = (fName && lName) ? fName + " " + lName : fullName;
560
-
561
- if(!fName) { fName = ""; }
562
- if(!lName) { lName = ""; }
563
-
564
- _inbound.deBugger('forms', "fName = " + fName);
565
- _inbound.deBugger('forms', "lName = " + lName);
566
- _inbound.deBugger('forms', "fullName = " + fullName);
567
-
568
- //return false;
569
- var page_views = _inbound.totalStorage('page_views') || {};
570
- var urlParams = _inbound.totalStorage('inbound_url_params') || {};
571
-
572
- /* check if redirect url is empty */
573
- var formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
574
- var inbound_form_is_ajax = false;
575
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
576
- var inbound_form_is_ajax = true;
577
- }
578
-
579
- /* get form id */
580
- var inbound_form_id = form.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');
581
- if(inbound_form_id.length > 0 ){
582
- inbound_form_id = inbound_form_id[0]['value'];
583
- } else {
584
- inbound_form_id = 0;
585
- }
586
-
587
- var inboundDATA = {
588
- 'email': email
589
- };
590
-
591
- /* Get Variation ID */
592
- if (typeof(landing_path_info) != "undefined") {
593
- var variation = landing_path_info.variation;
594
- } else if (typeof(cta_path_info) != "undefined") {
595
- var variation = cta_path_info.variation;
596
- } else {
597
- var variation = inbound_settings.variation_id;
598
- }
599
- var post_type = inbound_settings.post_type || 'page';
600
- var page_id = inbound_settings.post_id || 0;
601
- // data['wp_lead_uid'] = jQuery.cookie("wp_lead_uid") || null;
602
- // data['search_data'] = JSON.stringify(jQuery.totalStorage('inbound_search')) || {};
603
- search_data = {};
604
- /* Filter here for raw */
605
- formData = {
606
- 'action': 'inbound_lead_store',
607
- 'email': email,
608
- "full_name": fullName,
609
- "first_name": fName,
610
- "last_name": lName,
611
- 'raw_params': raw_params,
612
- 'mapped_params': mapped_params,
613
- 'url_params': JSON.stringify(urlParams),
614
- 'search_data': 'test',
615
- 'page_views': JSON.stringify(page_views),
616
- 'post_type': post_type,
617
- 'page_id': page_id,
618
- 'variation': variation,
619
- 'source': utils.readCookie("inbound_referral_site"),
620
- 'inbound_submitted': inbound_form_is_ajax,
621
- 'inbound_form_id': inbound_form_id,
622
- 'inbound_nonce': inbound_settings.ajax_nonce,
623
- 'event': form
624
- };
625
-
626
- callback = function(leadID) {
627
- /* Action Example */
628
-
629
- _inbound.deBugger('forms', 'Lead Created with ID: ' + leadID);
630
- leadID = parseInt(leadID, 10);
631
- formData.leadID = leadID;
632
- /* Set Lead cookie ID */
633
- if (leadID) {
634
- utils.createCookie("wp_lead_id", leadID);
635
- _inbound.totalStorage.deleteItem('page_views'); // remove pageviews
636
- _inbound.totalStorage.deleteItem('tracking_events'); // remove events
637
- }
638
-
639
- _inbound.trigger('form_after_submission', formData);
640
-
641
- /* Resume normal form functionality */
642
- _inbound.Forms.releaseFormSubmit(form);
643
-
644
- }
645
-
646
- _inbound.trigger('form_before_submission', formData);
647
-
648
- utils.ajaxPost(inbound_settings.admin_url, formData, callback);
649
- },
650
- saveSearchData: function(form) {
651
- var inputsObject = inputsObject || {};
652
- for (var i = 0; i < form.elements.length; i++) {
653
-
654
- //console.log(inputsObject);
655
-
656
- formInput = form.elements[i];
657
- multiple = false;
658
-
659
- if (formInput.name) {
660
-
661
- if (formInput.dataset.ignoreFormField) {
662
- _inbound.deBugger('searches', 'ignore ' + formInput.name);
663
- continue;
664
- }
665
-
666
- inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
667
- //inputName = inputName.replace(/-/g, "_");
668
- if (!inputsObject[inputName]) {
669
- inputsObject[inputName] = {};
670
- }
671
- if (formInput.type) {
672
- inputsObject[inputName]['type'] = formInput.type;
673
-
674
- }
675
- if (!inputsObject[inputName]['name']) {
676
- inputsObject[inputName]['name'] = formInput.name;
677
- }
678
- if (formInput.dataset.mapFormField) {
679
- inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
680
- }
681
-
682
-
683
- switch (formInput.nodeName) {
684
-
685
- case 'INPUT':
686
- value = this.getInputValue(formInput);
687
-
688
-
689
- if (value === false) {
690
- continue;
691
- }
692
- break;
693
-
694
- case 'TEXTAREA':
695
- value = formInput.value;
696
- break;
697
-
698
- case 'SELECT':
699
- if (formInput.multiple) {
700
- values = [];
701
- multiple = true;
702
-
703
- for (var j = 0; j < formInput.length; j++) {
704
- if (formInput[j].selected) {
705
- values.push(encodeURIComponent(formInput[j].value));
706
- }
707
- }
708
-
709
- } else {
710
- value = (formInput.value);
711
- }
712
-
713
- break;
714
- }
715
-
716
- _inbound.deBugger('searches', 'Input Value = ' + value);
717
-
718
-
719
- if (value) {
720
- /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
721
- if (!inputsObject[inputName]['value']) {
722
- inputsObject[inputName]['value'] = [];
723
- }
724
- inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
725
- var value = multiple ? values.join(',') : encodeURIComponent(value);
726
-
727
- }
728
-
729
- }
730
- }
731
-
732
- _inbound.deBugger('searches', inputsObject);
733
-
734
- /* create an array of search fields //(not fully implemented) at the moment, it only maps the text in the "search" input types*/
735
- var searchQuery = [];
736
- for (var input in inputsObject) {
737
- var inputValue = inputsObject[input]['value'];
738
- var inputType = inputsObject[input]['type'];
739
-
740
- /* Add custom hook here to look for additional values */
741
- if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
742
- // This is for mapping all fields of a search form. The resulting string is processed
743
- // in inbound-pro\classes\admin\report-templates\report.lead-searches-and-comments.php
744
- // In the function print_action_popup()
745
- // searchQuery.push(input + '|value|' + inputsObject[input]['value'].join(','));
746
-
747
- // get the search input value
748
- if(inputType == 'search'){
749
- searchQuery.push('search_text' + '|value|' + inputsObject[input]['value']);
750
- }
751
- }
752
- }
753
- /* exit if there isn't a search query */
754
- if(!searchQuery[0]){
755
- return;
756
- }
757
-
758
- var searchString = searchQuery.join('|field|');
759
- _inbound.deBugger('searches', "Stringified Search Form PARAMS: " + searchString);
760
-
761
- /* Get Variation ID */
762
- if (typeof(landing_path_info) != "undefined") {
763
- var variation = landing_path_info.variation;
764
- } else if (typeof(cta_path_info) != "undefined") {
765
- var variation = cta_path_info.variation;
766
- } else {
767
- var variation = inbound_settings.variation_id;
768
- }
769
- var post_type = inbound_settings.post_type || 'page';
770
- var page_id = inbound_settings.post_id || 0;
771
-
772
- var user_UID = utils.readCookie("wp_lead_uid");
773
-
774
- /* get the user's email address if possible */
775
- if(inbound_settings.wp_lead_data.lead_email){
776
- email = inbound_settings.wp_lead_data.lead_email;
777
- }else if(utils.readCookie('inbound_wpleads_email_address')){
778
- email = utils.readCookie('inbound_wpleads_email_address');
779
- }else{
780
- email = '';
781
- }
782
-
783
- /* Filter here for raw */
784
- searchData = {
785
- 'email': email,
786
- 'search_data': searchString,
787
- 'user_UID': user_UID,
788
- 'post_type': post_type,
789
- 'page_id': page_id,
790
- 'variation': variation,
791
- 'source': utils.readCookie("inbound_referral_site"),
792
- 'ip_address': inbound_settings.ip_address,
793
- 'timestamp': Math.floor((new Date).getTime()/1000),
794
- };
795
-
796
- /* filter data before caching it in the user's browser */
797
- _inbound.trigger('search_before_caching', searchData);
798
-
799
- /* cache search data */
800
- if(inbound_settings.wp_lead_data.lead_id){
801
- searchData['lead_id'] = inbound_settings.wp_lead_data.lead_id;
802
- utils.cacheSearchData(searchData, form)
803
- }else{
804
- utils.cacheSearchData(searchData, form);
805
- }
806
-
807
-
808
- },
809
- rememberInputValues: function(input) {
810
- var name = (input.name) ? "inbound_" + input.name : '';
811
- var type = (input.type) ? input.type : 'text';
812
- if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password" || input.dataset.ignoreFormField) {
813
- return false;
814
- }
815
-
816
- utils.addListener(input, 'change', function(e) {
817
- if (e.target.name) {
818
- /* Check for input type */
819
- if (type !== "checkbox") {
820
- var value = e.target.value;
821
- } else {
822
- var values = [];
823
- var checkboxes = document.querySelectorAll('input[name="' + e.target.name + '"]');
824
- for (var i = 0; i < checkboxes.length; i++) {
825
- var checked = checkboxes[i].checked;
826
- if (checked) {
827
- values.push(checkboxes[i].value);
828
- }
829
- value = values.join(',');
830
- };
831
- }
832
- //console.log(e.target.nodeName);
833
- //console.log('change ' + e.target.name + " " + encodeURIComponent(value));
834
-
835
- inputData = {
836
- name: e.target.name,
837
- node: e.target.nodeName.toLowerCase(),
838
- type: type,
839
- value: value,
840
- mapping: e.target.dataset.mapFormField
841
- };
842
-
843
- _inbound.trigger('form_input_change', inputData);
844
- /* Set Field Input Cookies */
845
- utils.createCookie("inbound_" + e.target.name, encodeURIComponent(value));
846
- // _inbound.totalStorage('the_key', FormStore);
847
- /* Push to 'unsubmitted form object' */
848
- }
849
-
850
- });
851
- },
852
- fillInputValues: function(input) {
853
- var name = (input.name) ? "inbound_" + input.name : '';
854
- var type = (input.type) ? input.type : 'text';
855
- if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password") {
856
- return false;
857
- }
858
- if (utils.readCookie(name) && name != 'comment') {
859
-
860
- value = decodeURIComponent(utils.readCookie(name));
861
- if (type === 'checkbox' || type === 'radio') {
862
- var checkbox_vals = value.split(',');
863
- for (var i = 0; i < checkbox_vals.length; i++) {
864
- if (input.value.indexOf(checkbox_vals[i]) > -1) {
865
- input.checked = true;
866
- }
867
- }
868
- } else {
869
- if (value !== "undefined") {
870
- input.value = value;
871
- }
872
- }
873
- }
874
- },
875
- getInputLabel: function(input){
876
- var label;
877
- if(label = this.siblingsIsLabel(input)){
878
- return label;
879
- } else if (label = this.CheckParentForLabel(input)) {
880
- return label;
881
- } else {
882
- //console.log("no label nf", input);
883
- return false;
884
- }
885
- },
886
- /* Get correct input values */
887
- getInputValue: function(input) {
888
- var value = false;
889
-
890
- switch (input.type) {
891
- case 'radio':
892
- case 'checkbox':
893
- if (input.checked) {
894
- value = input.value;
895
- //console.log("CHECKBOX VAL", value)
896
- }
897
- break;
898
-
899
- case 'text':
900
- case 'hidden':
901
- default:
902
- value = input.value;
903
- break;
904
-
905
- }
906
-
907
- return value;
908
- },
909
- /* Add data-map-form-field attr to input */
910
- addDataAttr: function(formInput, match) {
911
-
912
- var getAllInputs = document.getElementsByName(formInput.name);
913
- for (var i = getAllInputs.length - 1; i >= 0; i--) {
914
- if (!formInput.dataset.mapFormField) {
915
- getAllInputs[i].dataset.mapFormField = match;
916
- }
917
- };
918
- },
919
- /* Optimize FieldMapArray array for fewer lookups */
920
- removeArrayItem: function(array, item) {
921
- if (array.indexOf) {
922
- index = array.indexOf(item);
923
- } else {
924
- for (index = array.length - 1; index >= 0; --index) {
925
- if (array[index] === item) {
926
- break;
927
- }
928
- }
929
- }
930
- if (index >= 0) {
931
- array.splice(index, 1);
932
- }
933
- //_inbound.deBugger('forms', 'removed ' + item + " from array");
934
- //console.log('removed ' + item + " from array");
935
- return;
936
- },
937
- /* Look for siblings that are form labels */
938
- siblingsIsLabel: function(input) {
939
- var siblings = this.getSiblings(input);
940
- var labels = [];
941
- for (var i = siblings.length - 1; i >= 0; i--) {
942
- if (siblings[i].nodeName.toLowerCase() === 'label') {
943
- labels.push(siblings[i]);
944
- }
945
- };
946
- /* if only 1 label */
947
- if (labels.length > 0 && labels.length < 2) {
948
- return labels;
949
- }
950
-
951
- return false;
952
- },
953
- getChildren: function(n, skipMe) {
954
- var r = [];
955
- var elem = null;
956
- for (; n; n = n.nextSibling)
957
- if (n.nodeType == 1 && n != skipMe)
958
- r.push(n);
959
- return r;
960
- },
961
- getSiblings: function(n) {
962
- return this.getChildren(n.parentNode.firstChild, n);
963
- },
964
- /* Check parent elements inside form for labels */
965
- CheckParentForLabel: function(element) {
966
- if (element.nodeName === 'FORM') {
967
- return null;
968
- }
969
- do {
970
- var labels = element.getElementsByTagName("label");
971
- if (labels.length > 0 && labels.length < 2) {
972
- return element.getElementsByTagName("label");
973
- }
974
-
975
- } while (element = element.parentNode);
976
-
977
- return null;
978
- },
979
- /* Validate Common Email addresses */
980
- mailCheck: function() {
981
- var email_input = document.querySelector('.inbound-email');
982
- if (email_input) {
983
- //
984
- utils.addListener(email_input, 'blur', this.mailCheck);
985
-
986
- Mailcheck.run({
987
- email: document.querySelector('.inbound-email').value,
988
- suggested: function(suggestion) {
989
- // callback code
990
-
991
- var suggest = document.querySelector('.email_suggestion');
992
- if (suggest) {
993
- utils.removeElement(suggest);
994
- }
995
- var el = document.createElement("span");
996
- el.innerHTML = "<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">" + suggestion.full + "</b></i>?</span>";
997
- email_input.parentNode.insertBefore(el, email_input.nextSibling);
998
- var update = document.getElementById('email_correction');
999
- utils.addListener(update, 'click', function() {
1000
- email_input.value = update.innerHTML;
1001
- update.parentNode.parentNode.innerHTML = "Fixed!";
1002
- });
1003
- },
1004
- empty: function() {
1005
- //$(".email_suggestion").html("No Suggestions :(");
1006
- }
1007
- });
1008
- }
1009
- }
1010
-
1011
- };
1012
- /* Mailcheck */
1013
- if (typeof Mailcheck === "undefined") {
1014
- var Mailcheck = {
1015
- domainThreshold: 1,
1016
- topLevelThreshold: 3,
1017
-
1018
- defaultDomains: ["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
1019
- "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
1020
- "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "outlook.com", "icloud.com"
1021
- ],
1022
-
1023
- defaultTopLevelDomains: ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca", "de"],
1024
-
1025
- run: function(opts) {
1026
- opts.domains = opts.domains || Mailcheck.defaultDomains;
1027
- opts.topLevelDomains = opts.topLevelDomains || Mailcheck.defaultTopLevelDomains;
1028
- opts.distanceFunction = opts.distanceFunction || Mailcheck.sift3Distance;
1029
-
1030
- var defaultCallback = function(result) {
1031
- return result;
1032
- };
1033
- var suggestedCallback = opts.suggested || defaultCallback;
1034
- var emptyCallback = opts.empty || defaultCallback;
1035
-
1036
- var result = Mailcheck.suggest(Mailcheck.encodeEmail(opts.email), opts.domains, opts.topLevelDomains, opts.distanceFunction);
1037
-
1038
- return result ? suggestedCallback(result) : emptyCallback();
1039
- },
1040
-
1041
- suggest: function(email, domains, topLevelDomains, distanceFunction) {
1042
- email = email.toLowerCase();
1043
-
1044
- var emailParts = this.splitEmail(email);
1045
-
1046
- var closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction, this.domainThreshold);
1047
-
1048
- if (closestDomain) {
1049
- if (closestDomain != emailParts.domain) {
1050
- // The email address closely matches one of the supplied domains; return a suggestion
1051
- return {
1052
- address: emailParts.address,
1053
- domain: closestDomain,
1054
- full: emailParts.address + "@" + closestDomain
1055
- };
1056
- }
1057
- } else {
1058
- // The email address does not closely match one of the supplied domains
1059
- var closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains, distanceFunction, this.topLevelThreshold);
1060
- if (emailParts.domain && closestTopLevelDomain && closestTopLevelDomain != emailParts.topLevelDomain) {
1061
- // The email address may have a mispelled top-level domain; return a suggestion
1062
- var domain = emailParts.domain;
1063
- closestDomain = domain.substring(0, domain.lastIndexOf(emailParts.topLevelDomain)) + closestTopLevelDomain;
1064
- return {
1065
- address: emailParts.address,
1066
- domain: closestDomain,
1067
- full: emailParts.address + "@" + closestDomain
1068
- };
1069
- }
1070
- }
1071
- /* The email address exactly matches one of the supplied domains, does not closely
1072
- * match any domain and does not appear to simply have a mispelled top-level domain,
1073
- * or is an invalid email address; do not return a suggestion.
1074
- */
1075
- return false;
1076
- },
1077
-
1078
- findClosestDomain: function(domain, domains, distanceFunction, threshold) {
1079
- threshold = threshold || this.topLevelThreshold;
1080
- var dist;
1081
- var minDist = 99;
1082
- var closestDomain = null;
1083
-
1084
- if (!domain || !domains) {
1085
- return false;
1086
- }
1087
- if (!distanceFunction) {
1088
- distanceFunction = this.sift3Distance;
1089
- }
1090
-
1091
- for (var i = 0; i < domains.length; i++) {
1092
- if (domain === domains[i]) {
1093
- return domain;
1094
- }
1095
- dist = distanceFunction(domain, domains[i]);
1096
- if (dist < minDist) {
1097
- minDist = dist;
1098
- closestDomain = domains[i];
1099
- }
1100
- }
1101
-
1102
- if (minDist <= threshold && closestDomain !== null) {
1103
- return closestDomain;
1104
- } else {
1105
- return false;
1106
- }
1107
- },
1108
-
1109
- sift3Distance: function(s1, s2) {
1110
- // sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
1111
- if (s1 === null || s1.length === 0) {
1112
- if (s2 === null || s2.length === 0) {
1113
- return 0;
1114
- } else {
1115
- return s2.length;
1116
- }
1117
- }
1118
-
1119
- if (s2 === null || s2.length === 0) {
1120
- return s1.length;
1121
- }
1122
-
1123
- var c = 0;
1124
- var offset1 = 0;
1125
- var offset2 = 0;
1126
- var lcs = 0;
1127
- var maxOffset = 5;
1128
-
1129
- while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
1130
- if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
1131
- lcs++;
1132
- } else {
1133
- offset1 = 0;
1134
- offset2 = 0;
1135
- for (var i = 0; i < maxOffset; i++) {
1136
- if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
1137
- offset1 = i;
1138
- break;
1139
- }
1140
- if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
1141
- offset2 = i;
1142
- break;
1143
- }
1144
- }
1145
- }
1146
- c++;
1147
- }
1148
- return (s1.length + s2.length) / 2 - lcs;
1149
- },
1150
-
1151
- splitEmail: function(email) {
1152
- var parts = email.trim().split("@");
1153
-
1154
- if (parts.length < 2) {
1155
- return false;
1156
- }
1157
-
1158
- for (var i = 0; i < parts.length; i++) {
1159
- if (parts[i] === "") {
1160
- return false;
1161
- }
1162
- }
1163
-
1164
- var domain = parts.pop();
1165
- var domainParts = domain.split(".");
1166
- var tld = "";
1167
-
1168
- if (domainParts.length === 0) {
1169
- // The address does not have a top-level domain
1170
- return false;
1171
- } else if (domainParts.length == 1) {
1172
- // The address has only a top-level domain (valid under RFC)
1173
- tld = domainParts[0];
1174
- } else {
1175
- // The address has a domain and a top-level domain
1176
- for (var i = 1; i < domainParts.length; i++) {
1177
- tld += domainParts[i] + ".";
1178
- }
1179
- if (domainParts.length >= 2) {
1180
- tld = tld.substring(0, tld.length - 1);
1181
- }
1182
- }
1183
-
1184
- return {
1185
- topLevelDomain: tld,
1186
- domain: domain,
1187
- address: parts.join("@")
1188
- };
1189
- },
1190
-
1191
- // Encode the email address to prevent XSS but leave in valid
1192
- // characters, following this official spec:
1193
- // http://en.wikipedia.org/wiki/Email_address#Syntax
1194
- encodeEmail: function(email) {
1195
- var result = encodeURI(email);
1196
- result = result.replace("%20", " ").replace("%25", "%").replace("%5E", "^")
1197
- .replace("%60", "`").replace("%7B", "{").replace("%7C", "|")
1198
- .replace("%7D", "}");
1199
- return result;
1200
- }
1201
- };
1202
- } // End Mailcheck
1203
-
1204
-
1205
- return _inbound;
1206
-
1207
- })(_inbound || {});
1
+ /**
2
+ * # Inbound Forms
3
+ *
4
+ * This file contains all of the form functions of the main _inbound object.
5
+ * Filters and actions are described below
6
+ *
7
+ * @contributor David Wells <david@inboundnow.com>
8
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
9
+ * @version 0.0.2
10
+ */
11
+ /* Finish Exclusions for CC */
12
+
13
+ /* Launches form class */
14
+ var InboundForms = (function(_inbound) {
15
+
16
+ var debugMode = false,
17
+ utils = _inbound.Utils,
18
+ no_match = [],
19
+ rawParams = [],
20
+ mappedParams = [],
21
+ callTracker = {},
22
+ settings = _inbound.Settings;
23
+
24
+ var FieldMapArray = [
25
+ "first name",
26
+ "last name",
27
+ "name",
28
+ "email",
29
+ "e-mail",
30
+ "phone",
31
+ "website",
32
+ "job title",
33
+ "your favorite food",
34
+ "company",
35
+ "tele",
36
+ "address",
37
+ "comment"
38
+ /* Adding values here maps them */
39
+ ];
40
+
41
+ _inbound.Forms = {
42
+
43
+ // Init Form functions
44
+ init: function() {
45
+ _inbound.Forms.runFieldMappingFilters();
46
+ _inbound.Forms.formTrackInit();
47
+ _inbound.Forms.searchTrackInit();
48
+ },
49
+ /**
50
+ * This triggers the forms.field_map filter on the mapping array.
51
+ * This will allow you to add or remore Items from the mapping lookup
52
+ *
53
+ * ### Example inbound.form_map_before filter
54
+ *
55
+ * This is an example of how form mapping can be filtered and
56
+ * additional fields can be mapped via javascript
57
+ *
58
+ * ```js
59
+ * // Adding the filter function
60
+ * function Inbound_Add_Filter_Example( FieldMapArray ) {
61
+ * var map = FieldMapArray || [];
62
+ * map.push('new lookup value');
63
+ *
64
+ * return map;
65
+ * };
66
+ *
67
+ * // Adding the filter on dom ready
68
+ * _inbound.hooks.addFilter( 'inbound.form_map_before', Inbound_Add_Filter_Example, 10 );
69
+ * ```
70
+ *
71
+ * @return {[type]} [description]
72
+ */
73
+ runFieldMappingFilters: function() {
74
+ FieldMapArray = _inbound.hooks.applyFilters('forms.field_map', FieldMapArray);
75
+ //alert(FieldMapArray);
76
+ },
77
+ debug: function(msg, callback) {
78
+ //if app not in debug mode, exit immediately
79
+ if (!debugMode || !console) {
80
+ return;
81
+ }
82
+
83
+ var msg = msg || false;
84
+ //console.log the message
85
+ if (msg && (typeof msg === 'string')) {
86
+ console.log(msg);
87
+ }
88
+
89
+ //execute the callback if one was passed-in
90
+ if (callback && (callback instanceof Function)) {
91
+ callback();
92
+ }
93
+ },
94
+ formTrackInit: function() {
95
+
96
+ for (var i = 0; i < window.document.forms.length; i++) {
97
+ var trackForm = false;
98
+ var form = window.document.forms[i];
99
+ /* process forms only once */
100
+ if (!form.dataset.formProcessed) {
101
+ form.dataset.formProcessed = true;
102
+ trackForm = this.checkTrackStatus(form);
103
+ if (trackForm) {
104
+ this.attachFormSubmitEvent(form); /* attach form listener */
105
+ this.initFormMapping(form);
106
+ }
107
+ }
108
+ }
109
+ },
110
+ searchTrackInit: function(){
111
+
112
+ /* exit if searches aren't supposed to be tracked, or this function has already been called */
113
+ if(inbound_settings.search_tracking == 'off' || callTracker['searchTrackInit']){
114
+ return;
115
+ }
116
+
117
+ for (var i = 0; i < window.document.forms.length; i++) {
118
+ var trackForm = false;
119
+ var form = window.document.forms[i];
120
+ /* process forms only once */
121
+ if (!form.dataset.searchChecked) {
122
+ form.dataset.searchChecked = true;
123
+ trackForm = this.checkSearchTrackStatus(form);
124
+ if (trackForm) {
125
+ this.attachSearchFormSubmitEvent(form); /* attach form listener */
126
+ }
127
+ }
128
+ }
129
+
130
+ /* store the search data on init */
131
+ utils.storeSearchData();
132
+
133
+ /* log that this function has been called */
134
+ callTracker['searchTrackInit'] = true;
135
+ },
136
+ checkTrackStatus: function(form) {
137
+ var ClassIs = form.getAttribute('class');
138
+ if (ClassIs !== "" && ClassIs !== null) {
139
+ if (ClassIs.toLowerCase().indexOf("wpl-track-me") > -1) {
140
+ return true;
141
+ } else if (ClassIs.toLowerCase().indexOf("inbound-track") > -1) {
142
+ return true;
143
+ } else {
144
+ cb = function() { console.log(form); };
145
+ _inbound.deBugger('forms', "This form not tracked. Please assign on in settings...", cb);
146
+ return false;
147
+ }
148
+ }
149
+ },
150
+ checkSearchTrackStatus: function(form) {
151
+ var ClassIs = form.getAttribute('class'),
152
+ IdIs = form.getAttribute('id');
153
+ if (ClassIs !== "" && ClassIs !== null) {
154
+ if (ClassIs.toLowerCase().indexOf("search") > -1) {
155
+ return true;
156
+ }
157
+ }
158
+ if (IdIs !== "" && IdIs !== null) {
159
+ if (IdIs.toLowerCase().indexOf("search") > -1) {
160
+ return true;
161
+ }
162
+ }else{
163
+ cb = function() { console.log(form); };
164
+ _inbound.deBugger('searches', "This search form is not tracked. Please assign on in settings...", cb);
165
+ return false;
166
+ }
167
+ },
168
+ /* Loop through include/exclude items for tracking */
169
+ loopClassSelectors: function(selectors, action) {
170
+ for (var i = selectors.length - 1; i >= 0; i--) {
171
+
172
+ var selector = utils.trim(selectors[i])
173
+ if (selector.indexOf("#") === -1 && selector.indexOf(".") === -1) {
174
+ // assign ID as default
175
+ selector = "#" + selector;
176
+ }
177
+ //if(selectors[i] match . or # )
178
+ selector = document.querySelector(selector);
179
+ //console.log("SELECTOR", selector);
180
+ if (selector) {
181
+ if (action === 'add') {
182
+ _inbound.Utils.addClass('wpl-track-me', selector);
183
+ _inbound.Utils.addClass('inbound-track', selector);
184
+ } else {
185
+ _inbound.Utils.removeClass('wpl-track-me', selector);
186
+ _inbound.Utils.removeClass('inbound-track', selector);
187
+ }
188
+ }
189
+ }
190
+ },
191
+ /* Map field fields on load */
192
+ initFormMapping: function(form) {
193
+ var hiddenInputs = [];
194
+
195
+ for (var i = 0; i < form.elements.length; i++) {
196
+ formInput = form.elements[i];
197
+
198
+ if (formInput.type === 'hidden') {
199
+ hiddenInputs.push(formInput);
200
+ continue;
201
+ }
202
+
203
+ //this.ignoreFields(formInput);
204
+ /* Map form fields */
205
+ this.mapField(formInput);
206
+ /* Remember visible inputs */
207
+ this.rememberInputValues(formInput);
208
+ /* Fill visible inputs */
209
+ if (settings.formAutoPopulation && !_inbound.Utils.hasClass( "nopopulate", form ) ) {
210
+ this.fillInputValues(formInput);
211
+ }
212
+
213
+ }
214
+
215
+ /* loop hidden inputs */
216
+ for (var n = hiddenInputs.length - 1; n >= 0; n--) {
217
+ formInput = hiddenInputs[n];
218
+ this.mapField(formInput);
219
+ }
220
+
221
+ //console.log('mapping on load completed');
222
+ },
223
+ /* Maps data attributes to fields on page load */
224
+ mapField: function(input) {
225
+
226
+ var input_id = input.id || false;
227
+ var input_name = input.name || false;
228
+ var label = this.getInputLabel(input);
229
+
230
+ if(label){
231
+ //console.log(label[0].innerText);
232
+ var ignoreField = this.ignoreFieldByLabel(label[0].innerText);
233
+ if(ignoreField){
234
+ input.dataset.ignoreFormField = true;
235
+ return false;
236
+ }
237
+ }
238
+
239
+ /* Loop through all match possiblities */
240
+ for (i = 0; i < FieldMapArray.length; i++) {
241
+ //for (var i = FieldMapArray.length - 1; i >= 0; i--) {
242
+ var found = false;
243
+ var match = FieldMapArray[i];
244
+ var lookingFor = utils.trim(match);
245
+ var nice_name = lookingFor.replace(/ /g, '_');
246
+
247
+
248
+ //console.log("NICE NAME", nice_name);
249
+ //console.log('looking for match on ' + lookingFor);
250
+ //_inbound.deBugger('forms', 'looking for match on ' + lookingFor + " nice_name= " + nice_name);
251
+
252
+ // Check if input has an attached lable using for= tag
253
+ //var $laxbel = $("label[for='" + $element.attr('id') + "']").text();
254
+ //var labxel = 'label[for="' + input_id + '"]';
255
+
256
+ /* look for name attribute match */
257
+ if (input_name && input_name.toLowerCase().indexOf(lookingFor) > -1) {
258
+ found = true;
259
+ _inbound.deBugger('forms', 'Found matching name attribute for -> ' + lookingFor);
260
+
261
+ /* look for id match */
262
+ } else if (input_id && input_id.toLowerCase().indexOf(lookingFor) > -1) {
263
+
264
+ found = true;
265
+ _inbound.deBugger('forms', 'Found matching ID attribute for ->' + lookingFor);
266
+
267
+ /* Check siblings for label */
268
+ } else if (label) {
269
+ //var label = (label.length > 1 ? label[0] : label);
270
+ //console.log('label', label);
271
+ if (label[0].innerText.toLowerCase().indexOf(lookingFor) > -1) {
272
+
273
+ found = true;
274
+ _inbound.deBugger('forms', 'Found matching sibling label for -> ' + lookingFor);
275
+
276
+ }
277
+
278
+ } else {
279
+ /* no match found */
280
+ //_inbound.deBugger('forms', 'NO Match on ' + lookingFor + " in " + input_name);
281
+ no_match.push(lookingFor);
282
+
283
+ }
284
+
285
+ /* Map the field */
286
+ if (found) {
287
+ this.addDataAttr(input, nice_name);
288
+ this.removeArrayItem(FieldMapArray, lookingFor);
289
+ i--; //decrement count
290
+ }
291
+
292
+ }
293
+
294
+ return inbound_data;
295
+
296
+ },
297
+ /* prevent default submission temporarily */
298
+ formListener: function(event) {
299
+ //console.log(event);
300
+ event.preventDefault();
301
+ _inbound.Forms.saveFormData(event.target);
302
+ document.body.style.cursor = "wait";
303
+ },
304
+ /* prevent default submission temporarily */
305
+ searchFormListener: function(event) {
306
+ //console.log(event);
307
+ event.preventDefault();
308
+ _inbound.Forms.saveSearchData(event.target);
309
+ //document.body.style.cursor = "wait";
310
+ },
311
+ /* attach form listeners */
312
+ attachFormSubmitEvent: function(form) {
313
+ utils.addListener(form, 'submit', this.formListener);
314
+ var email_input = document.querySelector('.inbound-email');
315
+ /* utils.addListener(email_input, 'blur', this.mailCheck); */
316
+ },
317
+ /* attach search form listener */
318
+ attachSearchFormSubmitEvent: function(form) {
319
+ utils.addListener(form, 'submit', this.searchFormListener);
320
+ },
321
+ /* Ignore CC data */
322
+ ignoreFieldByLabel: function(label) {
323
+ var ignore_field = false;
324
+
325
+ if(!label){ return false; }
326
+
327
+ // Ignore any fields with labels that indicate a credit card field
328
+ if (label.toLowerCase().indexOf('credit card') != -1 || label.toLowerCase().indexOf('card number') != -1) {
329
+ ignore_field = true;
330
+ }
331
+
332
+ if (label.toLowerCase().indexOf('expiration') != -1 || label.toLowerCase().indexOf('expiry') != -1) {
333
+ ignore_field = true;
334
+ }
335
+
336
+ if (label.toLowerCase() == 'month' || label.toLowerCase() == 'mm' || label.toLowerCase() == 'yy' || label.toLowerCase() == 'yyyy' || label.toLowerCase() == 'year') {
337
+ ignore_field = true;
338
+ }
339
+
340
+ if (label.toLowerCase().indexOf('cvv') != -1 || label.toLowerCase().indexOf('cvc') != -1 || label.toLowerCase().indexOf('secure code') != -1 || label.toLowerCase().indexOf('security code') != -1) {
341
+ ignore_field = true;
342
+ }
343
+
344
+ if(ignore_field){
345
+ _inbound.deBugger('forms', 'ignore ' + label);
346
+ }
347
+
348
+ return ignore_field;
349
+
350
+ },
351
+ /* not implemented yet */
352
+ ignoreFieldByValue: function(value){
353
+ var ignore_field = false;
354
+
355
+ if(!value){ return false; }
356
+
357
+ if (value.toLowerCase() == 'visa' || value.toLowerCase() == 'mastercard' || value.toLowerCase() == 'american express' || value.toLowerCase() == 'amex' || value.toLowerCase() == 'discover') {
358
+ ignore_field = true;
359
+ }
360
+
361
+ // Check if value has integers, strip out spaces, then ignore anything with a credit card length (>16) or an expiration/cvv length (<5)
362
+ var int_regex = new RegExp("/^[0-9]+$/");
363
+ if (int_regex.test(value)) {
364
+ var value_no_spaces = value.replace(' ', '');
365
+
366
+ if (this.isInt(value_no_spaces) && value_no_spaces.length >= 16) {
367
+ ignore_field = true;
368
+ }
369
+
370
+ }
371
+
372
+ return ignore_field;
373
+
374
+ },
375
+ isInt: function(n) {
376
+ return typeof n == "number" && isFinite(n) && n % 1 === 0;
377
+ },
378
+ releaseFormSubmit: function(form) {
379
+ //console.log('remove form listener event');
380
+ document.body.style.cursor = "default";
381
+ utils.removeClass('wpl-track-me', form);
382
+ utils.removeListener(form, 'submit', this.formListener);
383
+ var formClass = form.getAttribute('class');
384
+ if (formClass !== "" && formClass !== null) {
385
+ /* If contact form 7 do this */
386
+ if (formClass.toLowerCase().indexOf("wpcf7-form") != -1) {
387
+ //alert('release')
388
+ setTimeout(function() {
389
+ document.body.style.cursor = "default";
390
+ }, 300);
391
+ return true;
392
+ }
393
+ }
394
+
395
+ form.submit();
396
+ /* fallback if submit name="submit" */
397
+ setTimeout(function() {
398
+ for (var i = 0; i < form.elements.length; i++) {
399
+ formInput = form.elements[i];
400
+ type = formInput.type || false;
401
+ if (type === "submit" && formInput.name === "submit") {
402
+ form.elements[i].click();
403
+ }
404
+ }
405
+ }, 2000);
406
+
407
+ },
408
+ saveFormData: function(form) {
409
+ var inputsObject = inputsObject || {};
410
+ for (var i = 0; i < form.elements.length; i++) {
411
+
412
+ // console.log(inputsObject);
413
+
414
+ formInput = form.elements[i];
415
+ multiple = false;
416
+
417
+ if (formInput.name) {
418
+
419
+ if (formInput.dataset.ignoreFormField) {
420
+ _inbound.deBugger('forms', 'ignore ' + formInput.name);
421
+ continue;
422
+ }
423
+
424
+ inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
425
+ //inputName = inputName.replace(/-/g, "_");
426
+ if (!inputsObject[inputName]) {
427
+ inputsObject[inputName] = {};
428
+ }
429
+ if (formInput.type) {
430
+ inputsObject[inputName]['type'] = formInput.type;
431
+ }
432
+ if (!inputsObject[inputName]['name']) {
433
+ inputsObject[inputName]['name'] = formInput.name;
434
+ }
435
+ if (formInput.dataset.mapFormField) {
436
+ inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
437
+ }
438
+
439
+
440
+ switch (formInput.nodeName) {
441
+
442
+ case 'INPUT':
443
+ value = this.getInputValue(formInput);
444
+
445
+
446
+ if (value === false) {
447
+ continue;
448
+ }
449
+ break;
450
+
451
+ case 'TEXTAREA':
452
+ value = formInput.value;
453
+ break;
454
+
455
+ case 'SELECT':
456
+ if (formInput.multiple) {
457
+ values = [];
458
+ multiple = true;
459
+
460
+ for (var j = 0; j < formInput.length; j++) {
461
+ if (formInput[j].selected) {
462
+ values.push(encodeURIComponent(formInput[j].value));
463
+ }
464
+ }
465
+
466
+ } else {
467
+ value = (formInput.value);
468
+ }
469
+
470
+ break;
471
+ }
472
+
473
+ _inbound.deBugger('forms', 'Input Value = ' + value);
474
+
475
+
476
+ if (value) {
477
+ /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
478
+ if (!inputsObject[inputName]['value']) {
479
+ inputsObject[inputName]['value'] = [];
480
+ }
481
+ inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
482
+ var value = multiple ? values.join(',') : encodeURIComponent(value);
483
+
484
+ }
485
+
486
+ }
487
+ }
488
+ _inbound.deBugger('forms', inputsObject);
489
+
490
+ //console.log('These are the raw values', inputsObject);
491
+ //_inbound.totalStorage('the_key', inputsObject);
492
+ //var inputsObject = sortInputs(inputsObject);
493
+
494
+ var matchCommon = /name|first name|last name|email|e-mail|phone|website|job title|company|tele|address|comment/;
495
+
496
+ for (var input in inputsObject) {
497
+ //console.log(input);
498
+
499
+ var inputValue = inputsObject[input]['value'];
500
+ var inputMappedField = inputsObject[input]['map'];
501
+ //if (matchCommon.test(input) !== false) {
502
+ //console.log(input + " Matches Regex run mapping test");
503
+ //var map = inputsObject[input];
504
+ //console.log("MAPP", map);
505
+ //mappedParams.push( input + '=' + inputsObject[input]['value'].join(',') );
506
+ //}
507
+
508
+ /* Add custom hook here to look for additional values */
509
+ if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
510
+ rawParams.push(input + '=' + inputsObject[input]['value'].join(','));
511
+ }
512
+
513
+ if (typeof(inputMappedField) != "undefined" && inputMappedField != null && inputsObject[input]['value']) {
514
+ //console.log('Data ATTR', formInput.dataset.mapFormField);
515
+ mappedParams.push(inputMappedField + "=" + inputsObject[input]['value'].join(','));
516
+ if (input === 'email') {
517
+ var email = inputsObject[input]['value'].join(',');
518
+ //alert(email);
519
+
520
+ }
521
+ }
522
+ }
523
+
524
+ var raw_params = rawParams.join('&');
525
+ _inbound.deBugger('forms', "Stringified Raw Form PARAMS: " + raw_params);
526
+
527
+ var mapped_params = mappedParams.join('&');
528
+ _inbound.deBugger('forms', "Stringified Mapped PARAMS" + mapped_params);
529
+
530
+ /* Check Use form Email or Cookie */
531
+ var email = utils.getParameterVal('email', mapped_params) || utils.readCookie('wp_lead_email');
532
+
533
+ /* Legacy Email map */
534
+ if (!email) {
535
+ email = utils.getParameterVal('wpleads_email_address', mapped_params);
536
+ }
537
+
538
+ var fullName = utils.getParameterVal('name', mapped_params);
539
+ var fName = utils.getParameterVal('first_name', mapped_params);
540
+ var lName = utils.getParameterVal('last_name', mapped_params);
541
+
542
+ // Fallbacks for empty values
543
+ if (!lName && fName) {
544
+ var parts = decodeURI(fName).split(" ");
545
+ if (parts.length > 0) {
546
+ fName = parts[0];
547
+ lName = parts[1];
548
+ }
549
+ }
550
+
551
+ if (fullName && !lName && !fName) {
552
+ var parts = decodeURI(fullName).split(" ");
553
+ if (parts.length > 0) {
554
+ fName = parts[0];
555
+ lName = parts[1];
556
+ }
557
+ }
558
+
559
+ fullName = (fName && lName) ? fName + " " + lName : fullName;
560
+
561
+ if(!fName) { fName = ""; }
562
+ if(!lName) { lName = ""; }
563
+
564
+ _inbound.deBugger('forms', "fName = " + fName);
565
+ _inbound.deBugger('forms', "lName = " + lName);
566
+ _inbound.deBugger('forms', "fullName = " + fullName);
567
+
568
+ //return false;
569
+ var page_views = _inbound.totalStorage('page_views') || {};
570
+ var urlParams = _inbound.totalStorage('inbound_url_params') || {};
571
+
572
+ /* check if redirect url is empty */
573
+ var formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
574
+ var inbound_form_is_ajax = false;
575
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
576
+ var inbound_form_is_ajax = true;
577
+ }
578
+
579
+ /* get form id */
580
+ var inbound_form_id = form.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');
581
+ if(inbound_form_id.length > 0 ){
582
+ inbound_form_id = inbound_form_id[0]['value'];
583
+ } else {
584
+ inbound_form_id = 0;
585
+ }
586
+
587
+ var inboundDATA = {
588
+ 'email': email
589
+ };
590
+
591
+ /* Get Variation ID */
592
+ if (typeof(landing_path_info) != "undefined") {
593
+ var variation = landing_path_info.variation;
594
+ } else if (typeof(cta_path_info) != "undefined") {
595
+ var variation = cta_path_info.variation;
596
+ } else {
597
+ var variation = inbound_settings.variation_id;
598
+ }
599
+ var post_type = inbound_settings.post_type || 'page';
600
+ var page_id = inbound_settings.post_id || 0;
601
+ // data['wp_lead_uid'] = jQuery.cookie("wp_lead_uid") || null;
602
+ // data['search_data'] = JSON.stringify(jQuery.totalStorage('inbound_search')) || {};
603
+ search_data = {};
604
+ /* Filter here for raw */
605
+ formData = {
606
+ 'action': 'inbound_lead_store',
607
+ 'email': email,
608
+ "full_name": fullName,
609
+ "first_name": fName,
610
+ "last_name": lName,
611
+ 'raw_params': raw_params,
612
+ 'mapped_params': mapped_params,
613
+ 'url_params': JSON.stringify(urlParams),
614
+ 'search_data': 'test',
615
+ 'page_views': JSON.stringify(page_views),
616
+ 'post_type': post_type,
617
+ 'page_id': page_id,
618
+ 'variation': variation,
619
+ 'source': utils.readCookie("inbound_referral_site"),
620
+ 'inbound_submitted': inbound_form_is_ajax,
621
+ 'inbound_form_id': inbound_form_id,
622
+ 'inbound_nonce': inbound_settings.ajax_nonce,
623
+ 'event': form
624
+ };
625
+
626
+ callback = function(leadID) {
627
+ /* Action Example */
628
+
629
+ _inbound.deBugger('forms', 'Lead Created with ID: ' + leadID);
630
+ leadID = parseInt(leadID, 10);
631
+ formData.leadID = leadID;
632
+ /* Set Lead cookie ID */
633
+ if (leadID) {
634
+ utils.createCookie("wp_lead_id", leadID);
635
+ _inbound.totalStorage.deleteItem('page_views'); // remove pageviews
636
+ _inbound.totalStorage.deleteItem('tracking_events'); // remove events
637
+ }
638
+
639
+ _inbound.trigger('form_after_submission', formData);
640
+
641
+ /* Resume normal form functionality */
642
+ _inbound.Forms.releaseFormSubmit(form);
643
+
644
+ }
645
+
646
+ _inbound.trigger('form_before_submission', formData);
647
+
648
+ utils.ajaxPost(inbound_settings.admin_url, formData, callback);
649
+ },
650
+ saveSearchData: function(form) {
651
+ var inputsObject = inputsObject || {};
652
+ for (var i = 0; i < form.elements.length; i++) {
653
+
654
+ //console.log(inputsObject);
655
+
656
+ formInput = form.elements[i];
657
+ multiple = false;
658
+
659
+ if (formInput.name) {
660
+
661
+ if (formInput.dataset.ignoreFormField) {
662
+ _inbound.deBugger('searches', 'ignore ' + formInput.name);
663
+ continue;
664
+ }
665
+
666
+ inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
667
+ //inputName = inputName.replace(/-/g, "_");
668
+ if (!inputsObject[inputName]) {
669
+ inputsObject[inputName] = {};
670
+ }
671
+ if (formInput.type) {
672
+ inputsObject[inputName]['type'] = formInput.type;
673
+
674
+ }
675
+ if (!inputsObject[inputName]['name']) {
676
+ inputsObject[inputName]['name'] = formInput.name;
677
+ }
678
+ if (formInput.dataset.mapFormField) {
679
+ inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
680
+ }
681
+
682
+
683
+ switch (formInput.nodeName) {
684
+
685
+ case 'INPUT':
686
+ value = this.getInputValue(formInput);
687
+
688
+
689
+ if (value === false) {
690
+ continue;
691
+ }
692
+ break;
693
+
694
+ case 'TEXTAREA':
695
+ value = formInput.value;
696
+ break;
697
+
698
+ case 'SELECT':
699
+ if (formInput.multiple) {
700
+ values = [];
701
+ multiple = true;
702
+
703
+ for (var j = 0; j < formInput.length; j++) {
704
+ if (formInput[j].selected) {
705
+ values.push(encodeURIComponent(formInput[j].value));
706
+ }
707
+ }
708
+
709
+ } else {
710
+ value = (formInput.value);
711
+ }
712
+
713
+ break;
714
+ }
715
+
716
+ _inbound.deBugger('searches', 'Input Value = ' + value);
717
+
718
+
719
+ if (value) {
720
+ /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
721
+ if (!inputsObject[inputName]['value']) {
722
+ inputsObject[inputName]['value'] = [];
723
+ }
724
+ inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
725
+ var value = multiple ? values.join(',') : encodeURIComponent(value);
726
+
727
+ }
728
+
729
+ }
730
+ }
731
+
732
+ _inbound.deBugger('searches', inputsObject);
733
+
734
+ /* create an array of search fields //(not fully implemented) at the moment, it only maps the text in the "search" input types*/
735
+ var searchQuery = [];
736
+ for (var input in inputsObject) {
737
+ var inputValue = inputsObject[input]['value'];
738
+ var inputType = inputsObject[input]['type'];
739
+
740
+ /* Add custom hook here to look for additional values */
741
+ if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
742
+ // This is for mapping all fields of a search form. The resulting string is processed
743
+ // in inbound-pro\classes\admin\report-templates\report.lead-searches-and-comments.php
744
+ // In the function print_action_popup()
745
+ // searchQuery.push(input + '|value|' + inputsObject[input]['value'].join(','));
746
+
747
+ // get the search input value
748
+ if(inputType == 'search'){
749
+ searchQuery.push('search_text' + '|value|' + inputsObject[input]['value']);
750
+ }
751
+ }
752
+ }
753
+ /* exit if there isn't a search query */
754
+ if(!searchQuery[0]){
755
+ return;
756
+ }
757
+
758
+ var searchString = searchQuery.join('|field|');
759
+ _inbound.deBugger('searches', "Stringified Search Form PARAMS: " + searchString);
760
+
761
+ /* Get Variation ID */
762
+ if (typeof(landing_path_info) != "undefined") {
763
+ var variation = landing_path_info.variation;
764
+ } else if (typeof(cta_path_info) != "undefined") {
765
+ var variation = cta_path_info.variation;
766
+ } else {
767
+ var variation = inbound_settings.variation_id;
768
+ }
769
+ var post_type = inbound_settings.post_type || 'page';
770
+ var page_id = inbound_settings.post_id || 0;
771
+
772
+ var user_UID = utils.readCookie("wp_lead_uid");
773
+
774
+ /* get the user's email address if possible */
775
+ if(inbound_settings.wp_lead_data.lead_email){
776
+ email = inbound_settings.wp_lead_data.lead_email;
777
+ }else if(utils.readCookie('inbound_wpleads_email_address')){
778
+ email = utils.readCookie('inbound_wpleads_email_address');
779
+ }else{
780
+ email = '';
781
+ }
782
+
783
+ /* Filter here for raw */
784
+ searchData = {
785
+ 'email': email,
786
+ 'search_data': searchString,
787
+ 'user_UID': user_UID,
788
+ 'post_type': post_type,
789
+ 'page_id': page_id,
790
+ 'variation': variation,
791
+ 'source': utils.readCookie("inbound_referral_site"),
792
+ 'ip_address': inbound_settings.ip_address,
793
+ 'timestamp': Math.floor((new Date).getTime()/1000),
794
+ };
795
+
796
+ /* filter data before caching it in the user's browser */
797
+ _inbound.trigger('search_before_caching', searchData);
798
+
799
+ /* cache search data */
800
+ if(inbound_settings.wp_lead_data.lead_id){
801
+ searchData['lead_id'] = inbound_settings.wp_lead_data.lead_id;
802
+ utils.cacheSearchData(searchData, form)
803
+ }else{
804
+ utils.cacheSearchData(searchData, form);
805
+ }
806
+
807
+
808
+ },
809
+ rememberInputValues: function(input) {
810
+ var name = (input.name) ? "inbound_" + input.name : '';
811
+ var type = (input.type) ? input.type : 'text';
812
+ if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password" || input.dataset.ignoreFormField) {
813
+ return false;
814
+ }
815
+
816
+ utils.addListener(input, 'change', function(e) {
817
+ if (e.target.name) {
818
+ /* Check for input type */
819
+ if (type !== "checkbox") {
820
+ var value = e.target.value;
821
+ } else {
822
+ var values = [];
823
+ var checkboxes = document.querySelectorAll('input[name="' + e.target.name + '"]');
824
+ for (var i = 0; i < checkboxes.length; i++) {
825
+ var checked = checkboxes[i].checked;
826
+ if (checked) {
827
+ values.push(checkboxes[i].value);
828
+ }
829
+ value = values.join(',');
830
+ };
831
+ }
832
+ //console.log(e.target.nodeName);
833
+ //console.log('change ' + e.target.name + " " + encodeURIComponent(value));
834
+
835
+ inputData = {
836
+ name: e.target.name,
837
+ node: e.target.nodeName.toLowerCase(),
838
+ type: type,
839
+ value: value,
840
+ mapping: e.target.dataset.mapFormField
841
+ };
842
+
843
+ _inbound.trigger('form_input_change', inputData);
844
+ /* Set Field Input Cookies */
845
+ utils.createCookie("inbound_" + e.target.name, encodeURIComponent(value));
846
+ // _inbound.totalStorage('the_key', FormStore);
847
+ /* Push to 'unsubmitted form object' */
848
+ }
849
+
850
+ });
851
+ },
852
+ fillInputValues: function(input) {
853
+ var name = (input.name) ? "inbound_" + input.name : '';
854
+ var type = (input.type) ? input.type : 'text';
855
+ if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password") {
856
+ return false;
857
+ }
858
+ if (utils.readCookie(name) && name != 'comment') {
859
+
860
+ value = decodeURIComponent(utils.readCookie(name));
861
+ if (type === 'checkbox' || type === 'radio') {
862
+ var checkbox_vals = value.split(',');
863
+ for (var i = 0; i < checkbox_vals.length; i++) {
864
+ if (input.value.indexOf(checkbox_vals[i]) > -1) {
865
+ input.checked = true;
866
+ }
867
+ }
868
+ } else {
869
+ if (value !== "undefined") {
870
+ input.value = value;
871
+ }
872
+ }
873
+ }
874
+ },
875
+ getInputLabel: function(input){
876
+ var label;
877
+ if(label = this.siblingsIsLabel(input)){
878
+ return label;
879
+ } else if (label = this.CheckParentForLabel(input)) {
880
+ return label;
881
+ } else {
882
+ //console.log("no label nf", input);
883
+ return false;
884
+ }
885
+ },
886
+ /* Get correct input values */
887
+ getInputValue: function(input) {
888
+ var value = false;
889
+
890
+ switch (input.type) {
891
+ case 'radio':
892
+ case 'checkbox':
893
+ if (input.checked) {
894
+ value = input.value;
895
+ //console.log("CHECKBOX VAL", value)
896
+ }
897
+ break;
898
+
899
+ case 'text':
900
+ case 'hidden':
901
+ default:
902
+ value = input.value;
903
+ break;
904
+
905
+ }
906
+
907
+ return value;
908
+ },
909
+ /* Add data-map-form-field attr to input */
910
+ addDataAttr: function(formInput, match) {
911
+
912
+ var getAllInputs = document.getElementsByName(formInput.name);
913
+ for (var i = getAllInputs.length - 1; i >= 0; i--) {
914
+ if (!formInput.dataset.mapFormField) {
915
+ getAllInputs[i].dataset.mapFormField = match;
916
+ }
917
+ };
918
+ },
919
+ /* Optimize FieldMapArray array for fewer lookups */
920
+ removeArrayItem: function(array, item) {
921
+ if (array.indexOf) {
922
+ index = array.indexOf(item);
923
+ } else {
924
+ for (index = array.length - 1; index >= 0; --index) {
925
+ if (array[index] === item) {
926
+ break;
927
+ }
928
+ }
929
+ }
930
+ if (index >= 0) {
931
+ array.splice(index, 1);
932
+ }
933
+ //_inbound.deBugger('forms', 'removed ' + item + " from array");
934
+ //console.log('removed ' + item + " from array");
935
+ return;
936
+ },
937
+ /* Look for siblings that are form labels */
938
+ siblingsIsLabel: function(input) {
939
+ var siblings = this.getSiblings(input);
940
+ var labels = [];
941
+ for (var i = siblings.length - 1; i >= 0; i--) {
942
+ if (siblings[i].nodeName.toLowerCase() === 'label') {
943
+ labels.push(siblings[i]);
944
+ }
945
+ };
946
+ /* if only 1 label */
947
+ if (labels.length > 0 && labels.length < 2) {
948
+ return labels;
949
+ }
950
+
951
+ return false;
952
+ },
953
+ getChildren: function(n, skipMe) {
954
+ var r = [];
955
+ var elem = null;
956
+ for (; n; n = n.nextSibling)
957
+ if (n.nodeType == 1 && n != skipMe)
958
+ r.push(n);
959
+ return r;
960
+ },
961
+ getSiblings: function(n) {
962
+ return this.getChildren(n.parentNode.firstChild, n);
963
+ },
964
+ /* Check parent elements inside form for labels */
965
+ CheckParentForLabel: function(element) {
966
+ if (element.nodeName === 'FORM') {
967
+ return null;
968
+ }
969
+ do {
970
+ var labels = element.getElementsByTagName("label");
971
+ if (labels.length > 0 && labels.length < 2) {
972
+ return element.getElementsByTagName("label");
973
+ }
974
+
975
+ } while (element = element.parentNode);
976
+
977
+ return null;
978
+ },
979
+ /* Validate Common Email addresses */
980
+ mailCheck: function() {
981
+ var email_input = document.querySelector('.inbound-email');
982
+ if (email_input) {
983
+ //
984
+ utils.addListener(email_input, 'blur', this.mailCheck);
985
+
986
+ Mailcheck.run({
987
+ email: document.querySelector('.inbound-email').value,
988
+ suggested: function(suggestion) {
989
+ // callback code
990
+
991
+ var suggest = document.querySelector('.email_suggestion');
992
+ if (suggest) {
993
+ utils.removeElement(suggest);
994
+ }
995
+ var el = document.createElement("span");
996
+ el.innerHTML = "<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">" + suggestion.full + "</b></i>?</span>";
997
+ email_input.parentNode.insertBefore(el, email_input.nextSibling);
998
+ var update = document.getElementById('email_correction');
999
+ utils.addListener(update, 'click', function() {
1000
+ email_input.value = update.innerHTML;
1001
+ update.parentNode.parentNode.innerHTML = "Fixed!";
1002
+ });
1003
+ },
1004
+ empty: function() {
1005
+ //$(".email_suggestion").html("No Suggestions :(");
1006
+ }
1007
+ });
1008
+ }
1009
+ }
1010
+
1011
+ };
1012
+ /* Mailcheck */
1013
+ if (typeof Mailcheck === "undefined") {
1014
+ var Mailcheck = {
1015
+ domainThreshold: 1,
1016
+ topLevelThreshold: 3,
1017
+
1018
+ defaultDomains: ["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
1019
+ "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
1020
+ "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "outlook.com", "icloud.com"
1021
+ ],
1022
+
1023
+ defaultTopLevelDomains: ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca", "de"],
1024
+
1025
+ run: function(opts) {
1026
+ opts.domains = opts.domains || Mailcheck.defaultDomains;
1027
+ opts.topLevelDomains = opts.topLevelDomains || Mailcheck.defaultTopLevelDomains;
1028
+ opts.distanceFunction = opts.distanceFunction || Mailcheck.sift3Distance;
1029
+
1030
+ var defaultCallback = function(result) {
1031
+ return result;
1032
+ };
1033
+ var suggestedCallback = opts.suggested || defaultCallback;
1034
+ var emptyCallback = opts.empty || defaultCallback;
1035
+
1036
+ var result = Mailcheck.suggest(Mailcheck.encodeEmail(opts.email), opts.domains, opts.topLevelDomains, opts.distanceFunction);
1037
+
1038
+ return result ? suggestedCallback(result) : emptyCallback();
1039
+ },
1040
+
1041
+ suggest: function(email, domains, topLevelDomains, distanceFunction) {
1042
+ email = email.toLowerCase();
1043
+
1044
+ var emailParts = this.splitEmail(email);
1045
+
1046
+ var closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction, this.domainThreshold);
1047
+
1048
+ if (closestDomain) {
1049
+ if (closestDomain != emailParts.domain) {
1050
+ // The email address closely matches one of the supplied domains; return a suggestion
1051
+ return {
1052
+ address: emailParts.address,
1053
+ domain: closestDomain,
1054
+ full: emailParts.address + "@" + closestDomain
1055
+ };
1056
+ }
1057
+ } else {
1058
+ // The email address does not closely match one of the supplied domains
1059
+ var closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains, distanceFunction, this.topLevelThreshold);
1060
+ if (emailParts.domain && closestTopLevelDomain && closestTopLevelDomain != emailParts.topLevelDomain) {
1061
+ // The email address may have a mispelled top-level domain; return a suggestion
1062
+ var domain = emailParts.domain;
1063
+ closestDomain = domain.substring(0, domain.lastIndexOf(emailParts.topLevelDomain)) + closestTopLevelDomain;
1064
+ return {
1065
+ address: emailParts.address,
1066
+ domain: closestDomain,
1067
+ full: emailParts.address + "@" + closestDomain
1068
+ };
1069
+ }
1070
+ }
1071
+ /* The email address exactly matches one of the supplied domains, does not closely
1072
+ * match any domain and does not appear to simply have a mispelled top-level domain,
1073
+ * or is an invalid email address; do not return a suggestion.
1074
+ */
1075
+ return false;
1076
+ },
1077
+
1078
+ findClosestDomain: function(domain, domains, distanceFunction, threshold) {
1079
+ threshold = threshold || this.topLevelThreshold;
1080
+ var dist;
1081
+ var minDist = 99;
1082
+ var closestDomain = null;
1083
+
1084
+ if (!domain || !domains) {
1085
+ return false;
1086
+ }
1087
+ if (!distanceFunction) {
1088
+ distanceFunction = this.sift3Distance;
1089
+ }
1090
+
1091
+ for (var i = 0; i < domains.length; i++) {
1092
+ if (domain === domains[i]) {
1093
+ return domain;
1094
+ }
1095
+ dist = distanceFunction(domain, domains[i]);
1096
+ if (dist < minDist) {
1097
+ minDist = dist;
1098
+ closestDomain = domains[i];
1099
+ }
1100
+ }
1101
+
1102
+ if (minDist <= threshold && closestDomain !== null) {
1103
+ return closestDomain;
1104
+ } else {
1105
+ return false;
1106
+ }
1107
+ },
1108
+
1109
+ sift3Distance: function(s1, s2) {
1110
+ // sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
1111
+ if (s1 === null || s1.length === 0) {
1112
+ if (s2 === null || s2.length === 0) {
1113
+ return 0;
1114
+ } else {
1115
+ return s2.length;
1116
+ }
1117
+ }
1118
+
1119
+ if (s2 === null || s2.length === 0) {
1120
+ return s1.length;
1121
+ }
1122
+
1123
+ var c = 0;
1124
+ var offset1 = 0;
1125
+ var offset2 = 0;
1126
+ var lcs = 0;
1127
+ var maxOffset = 5;
1128
+
1129
+ while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
1130
+ if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
1131
+ lcs++;
1132
+ } else {
1133
+ offset1 = 0;
1134
+ offset2 = 0;
1135
+ for (var i = 0; i < maxOffset; i++) {
1136
+ if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
1137
+ offset1 = i;
1138
+ break;
1139
+ }
1140
+ if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
1141
+ offset2 = i;
1142
+ break;
1143
+ }
1144
+ }
1145
+ }
1146
+ c++;
1147
+ }
1148
+ return (s1.length + s2.length) / 2 - lcs;
1149
+ },
1150
+
1151
+ splitEmail: function(email) {
1152
+ var parts = email.trim().split("@");
1153
+
1154
+ if (parts.length < 2) {
1155
+ return false;
1156
+ }
1157
+
1158
+ for (var i = 0; i < parts.length; i++) {
1159
+ if (parts[i] === "") {
1160
+ return false;
1161
+ }
1162
+ }
1163
+
1164
+ var domain = parts.pop();
1165
+ var domainParts = domain.split(".");
1166
+ var tld = "";
1167
+
1168
+ if (domainParts.length === 0) {
1169
+ // The address does not have a top-level domain
1170
+ return false;
1171
+ } else if (domainParts.length == 1) {
1172
+ // The address has only a top-level domain (valid under RFC)
1173
+ tld = domainParts[0];
1174
+ } else {
1175
+ // The address has a domain and a top-level domain
1176
+ for (var i = 1; i < domainParts.length; i++) {
1177
+ tld += domainParts[i] + ".";
1178
+ }
1179
+ if (domainParts.length >= 2) {
1180
+ tld = tld.substring(0, tld.length - 1);
1181
+ }
1182
+ }
1183
+
1184
+ return {
1185
+ topLevelDomain: tld,
1186
+ domain: domain,
1187
+ address: parts.join("@")
1188
+ };
1189
+ },
1190
+
1191
+ // Encode the email address to prevent XSS but leave in valid
1192
+ // characters, following this official spec:
1193
+ // http://en.wikipedia.org/wiki/Email_address#Syntax
1194
+ encodeEmail: function(email) {
1195
+ var result = encodeURI(email);
1196
+ result = result.replace("%20", " ").replace("%25", "%").replace("%5E", "^")
1197
+ .replace("%60", "`").replace("%7B", "{").replace("%7C", "|")
1198
+ .replace("%7D", "}");
1199
+ return result;
1200
+ }
1201
+ };
1202
+ } // End Mailcheck
1203
+
1204
+
1205
+ return _inbound;
1206
+
1207
+ })(_inbound || {});
shared/assets/js/frontend/analytics-src/analytics.hooks.js CHANGED
@@ -1,405 +1,405 @@
1
- /**
2
- * # Hooks & Filters
3
- *
4
- * This file contains all of the form functions of the main _inbound object.
5
- * Filters and actions are described below
6
- *
7
- * Forked from https://github.com/carldanley/WP-JS-Hooks/blob/master/src/event-manager.js
8
- *
9
- * @author David Wells <david@inboundnow.com>
10
- * @contributors Hudson Atwell <hudson@inboundnow.com>
11
- * @version 0.0.2
12
- */
13
-
14
- var _inboundHooks = (function (_inbound) {
15
-
16
- /**
17
- * # EventManager
18
- *
19
- * Actions and filters List
20
- * addAction( 'namespace.identifier', callback, priority )
21
- * addFilter( 'namespace.identifier', callback, priority )
22
- * removeAction( 'namespace.identifier' )
23
- * removeFilter( 'namespace.identifier' )
24
- * doAction( 'namespace.identifier', arg1, arg2, moreArgs, finalArg )
25
- * applyFilters( 'namespace.identifier', content )
26
- * @return {[type]} [description]
27
- */
28
-
29
- /**
30
- * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
31
- * that, lowest priority hooks are fired first.
32
- */
33
- var EventManager = function() {
34
- /**
35
- * Maintain a reference to the object scope so our public methods never get confusing.
36
- */
37
- var MethodsAvailable = {
38
- removeFilter : removeFilter,
39
- applyFilters : applyFilters,
40
- addFilter : addFilter,
41
- removeAction : removeAction,
42
- doAction : doAction,
43
- addAction : addAction
44
- };
45
-
46
- /**
47
- * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
48
- * object literal such that looking up the hook utilizes the native object literal hash.
49
- */
50
- var STORAGE = {
51
- actions : {},
52
- filters : {}
53
- };
54
-
55
- /**
56
- * Adds an action to the event manager.
57
- *
58
- * @param action Must contain namespace.identifier
59
- * @param callback Must be a valid callback function before this action is added
60
- * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
61
- * @param [context] Supply a value to be used for this
62
- */
63
- function addAction( action, callback, priority, context ) {
64
- if( typeof action === 'string' && typeof callback === 'function' ) {
65
- priority = parseInt( ( priority || 10 ), 10 );
66
- _addHook( 'actions', action, callback, priority, context );
67
- }
68
-
69
- return MethodsAvailable;
70
- }
71
-
72
- /**
73
- * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
74
- * that the first argument must always be the action.
75
- */
76
- function doAction( /* action, arg1, arg2, ... */ ) {
77
- var args = Array.prototype.slice.call( arguments );
78
- var action = args.shift();
79
-
80
- if( typeof action === 'string' ) {
81
- _runHook( 'actions', action, args );
82
- }
83
-
84
- return MethodsAvailable;
85
- }
86
-
87
- /**
88
- * Removes the specified action if it contains a namespace.identifier & exists.
89
- *
90
- * @param action The action to remove
91
- * @param [callback] Callback function to remove
92
- */
93
- function removeAction( action, callback ) {
94
- if( typeof action === 'string' ) {
95
- _removeHook( 'actions', action, callback );
96
- }
97
-
98
- return MethodsAvailable;
99
- }
100
-
101
- /**
102
- * Adds a filter to the event manager.
103
- *
104
- * @param filter Must contain namespace.identifier
105
- * @param callback Must be a valid callback function before this action is added
106
- * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
107
- * @param [context] Supply a value to be used for this
108
- */
109
- function addFilter( filter, callback, priority, context ) {
110
- if( typeof filter === 'string' && typeof callback === 'function' ) {
111
- //console.log('add filter', filter);
112
- priority = parseInt( ( priority || 10 ), 10 );
113
- _addHook( 'filters', filter, callback, priority );
114
- }
115
-
116
- return MethodsAvailable;
117
- }
118
-
119
- /**
120
- * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
121
- * the first argument must always be the filter.
122
- */
123
- function applyFilters( /* filter, filtered arg, arg2, ... */ ) {
124
- var args = Array.prototype.slice.call( arguments );
125
- var filter = args.shift();
126
-
127
- if( typeof filter === 'string' ) {
128
- return _runHook( 'filters', filter, args );
129
- }
130
-
131
- return MethodsAvailable;
132
- }
133
-
134
- /**
135
- * Removes the specified filter if it contains a namespace.identifier & exists.
136
- *
137
- * @param filter The action to remove
138
- * @param [callback] Callback function to remove
139
- */
140
- function removeFilter( filter, callback ) {
141
- if( typeof filter === 'string') {
142
- _removeHook( 'filters', filter, callback );
143
- }
144
-
145
- return MethodsAvailable;
146
- }
147
-
148
- /**
149
- * Removes the specified hook by resetting the value of it.
150
- *
151
- * @param type Type of hook, either 'actions' or 'filters'
152
- * @param hook The hook (namespace.identifier) to remove
153
- * @private
154
- */
155
- function _removeHook( type, hook, callback, context ) {
156
- if ( !STORAGE[ type ][ hook ] ) {
157
- return;
158
- }
159
- if ( !callback ) {
160
- STORAGE[ type ][ hook ] = [];
161
- } else {
162
- var handlers = STORAGE[ type ][ hook ];
163
- var i;
164
- if ( !context ) {
165
- for ( i = handlers.length; i--; ) {
166
- if ( handlers[i].callback === callback ) {
167
- handlers.splice( i, 1 );
168
- }
169
- }
170
- }
171
- else {
172
- for ( i = handlers.length; i--; ) {
173
- var handler = handlers[i];
174
- if ( handler.callback === callback && handler.context === context) {
175
- handlers.splice( i, 1 );
176
- }
177
- }
178
- }
179
- }
180
- }
181
-
182
- /**
183
- * Adds the hook to the appropriate storage container
184
- *
185
- * @param type 'actions' or 'filters'
186
- * @param hook The hook (namespace.identifier) to add to our event manager
187
- * @param callback The function that will be called when the hook is executed.
188
- * @param priority The priority of this hook. Must be an integer.
189
- * @param [context] A value to be used for this
190
- * @private
191
- */
192
- function _addHook( type, hook, callback, priority, context ) {
193
- var hookObject = {
194
- callback : callback,
195
- priority : priority,
196
- context : context
197
- };
198
-
199
- // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
200
- var hooks = STORAGE[ type ][ hook ];
201
- if( hooks ) {
202
- hooks.push( hookObject );
203
- hooks = _hookInsertSort( hooks );
204
- }
205
- else {
206
- hooks = [ hookObject ];
207
- }
208
-
209
- STORAGE[ type ][ hook ] = hooks;
210
- }
211
-
212
- /**
213
- * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
214
- * than bubble sort, etc: http://jsperf.com/javascript-sort
215
- *
216
- * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
217
- * @private
218
- */
219
- function _hookInsertSort( hooks ) {
220
- var tmpHook, j, prevHook;
221
- for( var i = 1, len = hooks.length; i < len; i++ ) {
222
- tmpHook = hooks[ i ];
223
- j = i;
224
- while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
225
- hooks[ j ] = hooks[ j - 1 ];
226
- --j;
227
- }
228
- hooks[ j ] = tmpHook;
229
- }
230
-
231
- return hooks;
232
- }
233
-
234
- /**
235
- * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
236
- *
237
- * @param type 'actions' or 'filters'
238
- * @param hook The hook ( namespace.identifier ) to be ran.
239
- * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
240
- * @private
241
- */
242
- function _runHook( type, hook, args ) {
243
- var handlers = STORAGE[ type ][ hook ];
244
-
245
- if ( !handlers ) {
246
- return (type === 'filters') ? args[0] : false;
247
- }
248
-
249
- var i = 0, len = handlers.length;
250
- if ( type === 'filters' ) {
251
- for ( ; i < len; i++ ) {
252
- args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
253
- }
254
- } else {
255
- for ( ; i < len; i++ ) {
256
- handlers[ i ].callback.apply( handlers[ i ].context, args );
257
- }
258
- }
259
-
260
- return ( type === 'filters' ) ? args[ 0 ] : true;
261
- }
262
-
263
- // return all of the publicly available methods
264
- return MethodsAvailable;
265
-
266
- };
267
-
268
- _inbound.hooks = new EventManager();
269
-
270
-
271
- /**
272
- * Event Hooks and Filters public methods
273
- */
274
- /*
275
- * add_action
276
- *
277
- * This function uses _inbound.hooks to mimics WP add_action
278
- *
279
- * ```js
280
- * function Inbound_Add_Action_Example(data) {
281
- * // Do stuff here.
282
- * };
283
- * // Add action to the hook
284
- * _inbound.add_action( 'name_of_action', Inbound_Add_Action_Example, 10 );
285
- * ```
286
- */
287
- _inbound.add_action = function() {
288
- // allow multiple action parameters such as 'ready append'
289
- var actions = arguments[0].split(' ');
290
-
291
- for( k in actions ) {
292
-
293
- // prefix action
294
- arguments[0] = 'inbound.' + actions[ k ];
295
-
296
- _inbound.hooks.addAction.apply(this, arguments);
297
- }
298
-
299
- return this;
300
-
301
- };
302
- /*
303
- * remove_action
304
- *
305
- * This function uses _inbound.hooks to mimics WP remove_action
306
- *
307
- * ```js
308
- * // Add remove action 'name_of_action'
309
- * _inbound.remove_action( 'name_of_action');
310
- * ```
311
- *
312
- */
313
- _inbound.remove_action = function() {
314
- // prefix action
315
- arguments[0] = 'inbound.' + arguments[0];
316
- _inbound.hooks.removeAction.apply(this, arguments);
317
-
318
- return this;
319
-
320
- };
321
- /*
322
- * do_action
323
- *
324
- * This function uses _inbound.hooks to mimics WP do_action
325
- * This is used if you want to allow for third party JS plugins to act on your functions
326
- *
327
- */
328
- _inbound.do_action = function() {
329
- // prefix action
330
- arguments[0] = 'inbound.' + arguments[0];
331
- _inbound.hooks.doAction.apply(this, arguments);
332
-
333
- return this;
334
-
335
- };
336
- /*
337
- * add_filter
338
- *
339
- * This function uses _inbound.hooks to mimics WP add_filter
340
- *
341
- * ```js
342
- * _inbound.add_filter( 'urlParamFilter', URL_Param_Filter, 10 );
343
- * function URL_Param_Filter(urlParams) {
344
- *
345
- * var params = urlParams || {};
346
- * // check for item in object
347
- * if(params.utm_source !== "undefined"){
348
- * //alert('url param "utm_source" is here');
349
- * }
350
- *
351
- * // delete item from object
352
- * delete params.utm_source;
353
- *
354
- * return params;
355
- *
356
- * }
357
- * ```
358
- */
359
- _inbound.add_filter = function() {
360
- // prefix action
361
- arguments[0] = 'inbound.' + arguments[0];
362
- _inbound.hooks.addFilter.apply(this, arguments);
363
-
364
- return this;
365
-
366
- };
367
- /*
368
- * remove_filter
369
- *
370
- * This function uses _inbound.hooks to mimics WP remove_filter
371
- *
372
- * ```js
373
- * // Add remove filter 'urlParamFilter'
374
- * _inbound.remove_action( 'urlParamFilter');
375
- * ```
376
- *
377
- */
378
- _inbound.remove_filter = function() {
379
- // prefix action
380
- arguments[0] = 'inbound.' + arguments[0];
381
-
382
- _inbound.hooks.removeFilter.apply(this, arguments);
383
-
384
- return this;
385
-
386
- };
387
- /*
388
- * apply_filters
389
- *
390
- * This function uses _inbound.hooks to mimics WP apply_filters
391
- *
392
- */
393
- _inbound.apply_filters = function() {
394
- //console.log('Filter:' + arguments[0] + " ran on ->", arguments[1]);
395
- // prefix action
396
- arguments[0] = 'inbound.' + arguments[0];
397
-
398
- return _inbound.hooks.applyFilters.apply(this, arguments);
399
-
400
- };
401
-
402
-
403
- return _inbound;
404
-
405
  })(_inbound || {});
1
+ /**
2
+ * # Hooks & Filters
3
+ *
4
+ * This file contains all of the form functions of the main _inbound object.
5
+ * Filters and actions are described below
6
+ *
7
+ * Forked from https://github.com/carldanley/WP-JS-Hooks/blob/master/src/event-manager.js
8
+ *
9
+ * @contributor David Wells <david@inboundnow.com>
10
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
11
+ * @version 0.0.2
12
+ */
13
+
14
+ var _inboundHooks = (function (_inbound) {
15
+
16
+ /**
17
+ * # EventManager
18
+ *
19
+ * Actions and filters List
20
+ * addAction( 'namespace.identifier', callback, priority )
21
+ * addFilter( 'namespace.identifier', callback, priority )
22
+ * removeAction( 'namespace.identifier' )
23
+ * removeFilter( 'namespace.identifier' )
24
+ * doAction( 'namespace.identifier', arg1, arg2, moreArgs, finalArg )
25
+ * applyFilters( 'namespace.identifier', content )
26
+ * @return {[type]} [description]
27
+ */
28
+
29
+ /**
30
+ * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
31
+ * that, lowest priority hooks are fired first.
32
+ */
33
+ var EventManager = function() {
34
+ /**
35
+ * Maintain a reference to the object scope so our public methods never get confusing.
36
+ */
37
+ var MethodsAvailable = {
38
+ removeFilter : removeFilter,
39
+ applyFilters : applyFilters,
40
+ addFilter : addFilter,
41
+ removeAction : removeAction,
42
+ doAction : doAction,
43
+ addAction : addAction
44
+ };
45
+
46
+ /**
47
+ * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
48
+ * object literal such that looking up the hook utilizes the native object literal hash.
49
+ */
50
+ var STORAGE = {
51
+ actions : {},
52
+ filters : {}
53
+ };
54
+
55
+ /**
56
+ * Adds an action to the event manager.
57
+ *
58
+ * @param action Must contain namespace.identifier
59
+ * @param callback Must be a valid callback function before this action is added
60
+ * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
61
+ * @param [context] Supply a value to be used for this
62
+ */
63
+ function addAction( action, callback, priority, context ) {
64
+ if( typeof action === 'string' && typeof callback === 'function' ) {
65
+ priority = parseInt( ( priority || 10 ), 10 );
66
+ _addHook( 'actions', action, callback, priority, context );
67
+ }
68
+
69
+ return MethodsAvailable;
70
+ }
71
+
72
+ /**
73
+ * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
74
+ * that the first argument must always be the action.
75
+ */
76
+ function doAction( /* action, arg1, arg2, ... */ ) {
77
+ var args = Array.prototype.slice.call( arguments );
78
+ var action = args.shift();
79
+
80
+ if( typeof action === 'string' ) {
81
+ _runHook( 'actions', action, args );
82
+ }
83
+
84
+ return MethodsAvailable;
85
+ }
86
+
87
+ /**
88
+ * Removes the specified action if it contains a namespace.identifier & exists.
89
+ *
90
+ * @param action The action to remove
91
+ * @param [callback] Callback function to remove
92
+ */
93
+ function removeAction( action, callback ) {
94
+ if( typeof action === 'string' ) {
95
+ _removeHook( 'actions', action, callback );
96
+ }
97
+
98
+ return MethodsAvailable;
99
+ }
100
+
101
+ /**
102
+ * Adds a filter to the event manager.
103
+ *
104
+ * @param filter Must contain namespace.identifier
105
+ * @param callback Must be a valid callback function before this action is added
106
+ * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
107
+ * @param [context] Supply a value to be used for this
108
+ */
109
+ function addFilter( filter, callback, priority, context ) {
110
+ if( typeof filter === 'string' && typeof callback === 'function' ) {
111
+ //console.log('add filter', filter);
112
+ priority = parseInt( ( priority || 10 ), 10 );
113
+ _addHook( 'filters', filter, callback, priority );
114
+ }
115
+
116
+ return MethodsAvailable;
117
+ }
118
+
119
+ /**
120
+ * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
121
+ * the first argument must always be the filter.
122
+ */
123
+ function applyFilters( /* filter, filtered arg, arg2, ... */ ) {
124
+ var args = Array.prototype.slice.call( arguments );
125
+ var filter = args.shift();
126
+
127
+ if( typeof filter === 'string' ) {
128
+ return _runHook( 'filters', filter, args );
129
+ }
130
+
131
+ return MethodsAvailable;
132
+ }
133
+
134
+ /**
135
+ * Removes the specified filter if it contains a namespace.identifier & exists.
136
+ *
137
+ * @param filter The action to remove
138
+ * @param [callback] Callback function to remove
139
+ */
140
+ function removeFilter( filter, callback ) {
141
+ if( typeof filter === 'string') {
142
+ _removeHook( 'filters', filter, callback );
143
+ }
144
+
145
+ return MethodsAvailable;
146
+ }
147
+
148
+ /**
149
+ * Removes the specified hook by resetting the value of it.
150
+ *
151
+ * @param type Type of hook, either 'actions' or 'filters'
152
+ * @param hook The hook (namespace.identifier) to remove
153
+ * @private
154
+ */
155
+ function _removeHook( type, hook, callback, context ) {
156
+ if ( !STORAGE[ type ][ hook ] ) {
157
+ return;
158
+ }
159
+ if ( !callback ) {
160
+ STORAGE[ type ][ hook ] = [];
161
+ } else {
162
+ var handlers = STORAGE[ type ][ hook ];
163
+ var i;
164
+ if ( !context ) {
165
+ for ( i = handlers.length; i--; ) {
166
+ if ( handlers[i].callback === callback ) {
167
+ handlers.splice( i, 1 );
168
+ }
169
+ }
170
+ }
171
+ else {
172
+ for ( i = handlers.length; i--; ) {
173
+ var handler = handlers[i];
174
+ if ( handler.callback === callback && handler.context === context) {
175
+ handlers.splice( i, 1 );
176
+ }
177
+ }
178
+ }
179
+ }
180
+ }
181
+
182
+ /**
183
+ * Adds the hook to the appropriate storage container
184
+ *
185
+ * @param type 'actions' or 'filters'
186
+ * @param hook The hook (namespace.identifier) to add to our event manager
187
+ * @param callback The function that will be called when the hook is executed.
188
+ * @param priority The priority of this hook. Must be an integer.
189
+ * @param [context] A value to be used for this
190
+ * @private
191
+ */
192
+ function _addHook( type, hook, callback, priority, context ) {
193
+ var hookObject = {
194
+ callback : callback,
195
+ priority : priority,
196
+ context : context
197
+ };
198
+
199
+ // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
200
+ var hooks = STORAGE[ type ][ hook ];
201
+ if( hooks ) {
202
+ hooks.push( hookObject );
203
+ hooks = _hookInsertSort( hooks );
204
+ }
205
+ else {
206
+ hooks = [ hookObject ];
207
+ }
208
+
209
+ STORAGE[ type ][ hook ] = hooks;
210
+ }
211
+
212
+ /**
213
+ * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
214
+ * than bubble sort, etc: http://jsperf.com/javascript-sort
215
+ *
216
+ * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
217
+ * @private
218
+ */
219
+ function _hookInsertSort( hooks ) {
220
+ var tmpHook, j, prevHook;
221
+ for( var i = 1, len = hooks.length; i < len; i++ ) {
222
+ tmpHook = hooks[ i ];
223
+ j = i;
224
+ while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
225
+ hooks[ j ] = hooks[ j - 1 ];
226
+ --j;
227
+ }
228
+ hooks[ j ] = tmpHook;
229
+ }
230
+
231
+ return hooks;
232
+ }
233
+
234
+ /**
235
+ * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
236
+ *
237
+ * @param type 'actions' or 'filters'
238
+ * @param hook The hook ( namespace.identifier ) to be ran.
239
+ * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
240
+ * @private
241
+ */
242
+ function _runHook( type, hook, args ) {
243
+ var handlers = STORAGE[ type ][ hook ];
244
+
245
+ if ( !handlers ) {
246
+ return (type === 'filters') ? args[0] : false;
247
+ }
248
+
249
+ var i = 0, len = handlers.length;
250
+ if ( type === 'filters' ) {
251
+ for ( ; i < len; i++ ) {
252
+ args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
253
+ }
254
+ } else {
255
+ for ( ; i < len; i++ ) {
256
+ handlers[ i ].callback.apply( handlers[ i ].context, args );
257
+ }
258
+ }
259
+
260
+ return ( type === 'filters' ) ? args[ 0 ] : true;
261
+ }
262
+
263
+ // return all of the publicly available methods
264
+ return MethodsAvailable;
265
+
266
+ };
267
+
268
+ _inbound.hooks = new EventManager();
269
+
270
+
271
+ /**
272
+ * Event Hooks and Filters public methods
273
+ */
274
+ /*
275
+ * add_action
276
+ *
277
+ * This function uses _inbound.hooks to mimics WP add_action
278
+ *
279
+ * ```js
280
+ * function Inbound_Add_Action_Example(data) {
281
+ * // Do stuff here.
282
+ * };
283
+ * // Add action to the hook
284
+ * _inbound.add_action( 'name_of_action', Inbound_Add_Action_Example, 10 );
285
+ * ```
286
+ */
287
+ _inbound.add_action = function() {
288
+ // allow multiple action parameters such as 'ready append'
289
+ var actions = arguments[0].split(' ');
290
+
291
+ for( k in actions ) {
292
+
293
+ // prefix action
294
+ arguments[0] = 'inbound.' + actions[ k ];
295
+
296
+ _inbound.hooks.addAction.apply(this, arguments);
297
+ }
298
+
299
+ return this;
300
+
301
+ };
302
+ /*
303
+ * remove_action
304
+ *
305
+ * This function uses _inbound.hooks to mimics WP remove_action
306
+ *
307
+ * ```js
308
+ * // Add remove action 'name_of_action'
309
+ * _inbound.remove_action( 'name_of_action');
310
+ * ```
311
+ *
312
+ */
313
+ _inbound.remove_action = function() {
314
+ // prefix action
315
+ arguments[0] = 'inbound.' + arguments[0];
316
+ _inbound.hooks.removeAction.apply(this, arguments);
317
+
318
+ return this;
319
+
320
+ };
321
+ /*
322
+ * do_action
323
+ *
324
+ * This function uses _inbound.hooks to mimics WP do_action
325
+ * This is used if you want to allow for third party JS plugins to act on your functions
326
+ *
327
+ */
328
+ _inbound.do_action = function() {
329
+ // prefix action
330
+ arguments[0] = 'inbound.' + arguments[0];
331
+ _inbound.hooks.doAction.apply(this, arguments);
332
+
333
+ return this;
334
+
335
+ };
336
+ /*
337
+ * add_filter
338
+ *
339
+ * This function uses _inbound.hooks to mimics WP add_filter
340
+ *
341
+ * ```js
342
+ * _inbound.add_filter( 'urlParamFilter', URL_Param_Filter, 10 );
343
+ * function URL_Param_Filter(urlParams) {
344
+ *
345
+ * var params = urlParams || {};
346
+ * // check for item in object
347
+ * if(params.utm_source !== "undefined"){
348
+ * //alert('url param "utm_source" is here');
349
+ * }
350
+ *
351
+ * // delete item from object
352
+ * delete params.utm_source;
353
+ *
354
+ * return params;
355
+ *
356
+ * }
357
+ * ```
358
+ */
359
+ _inbound.add_filter = function() {
360
+ // prefix action
361
+ arguments[0] = 'inbound.' + arguments[0];
362
+ _inbound.hooks.addFilter.apply(this, arguments);
363
+
364
+ return this;
365
+
366
+ };
367
+ /*
368
+ * remove_filter
369
+ *
370
+ * This function uses _inbound.hooks to mimics WP remove_filter
371
+ *
372
+ * ```js
373
+ * // Add remove filter 'urlParamFilter'
374
+ * _inbound.remove_action( 'urlParamFilter');
375
+ * ```
376
+ *
377
+ */
378
+ _inbound.remove_filter = function() {
379
+ // prefix action
380
+ arguments[0] = 'inbound.' + arguments[0];
381
+
382
+ _inbound.hooks.removeFilter.apply(this, arguments);
383
+
384
+ return this;
385
+
386
+ };
387
+ /*
388
+ * apply_filters
389
+ *
390
+ * This function uses _inbound.hooks to mimics WP apply_filters
391
+ *
392
+ */
393
+ _inbound.apply_filters = function() {
394
+ //console.log('Filter:' + arguments[0] + " ran on ->", arguments[1]);
395
+ // prefix action
396
+ arguments[0] = 'inbound.' + arguments[0];
397
+
398
+ return _inbound.hooks.applyFilters.apply(this, arguments);
399
+
400
+ };
401
+
402
+
403
+ return _inbound;
404
+
405
  })(_inbound || {});
shared/assets/js/frontend/analytics-src/analytics.init.js CHANGED
@@ -1,131 +1,131 @@
1
- /**
2
- * # _inbound
3
- *
4
- * This main the _inbound class
5
- *
6
- * @author David Wells <david@inboundnow.com>
7
- * @contributors Hudson Atwell <hudson@inboundnow.com>
8
- * @version 0.0.2
9
- */
10
-
11
- var inbound_data = inbound_data || {};
12
- var _inboundOptions = _inboundOptions || {};
13
- /* Ensure global _gaq Google Analytics queue has been initialized. */
14
- var _gaq = _gaq || [];
15
-
16
- var _inbound = (function(options) {
17
-
18
- /* Constants */
19
- var defaults = {
20
- timeout: ( inbound_settings.is_admin ? 500 : 10000 ),
21
- formAutoTracking: true,
22
- formAutoPopulation: true
23
- };
24
-
25
- var Analytics = {
26
- /* Initialize individual modules */
27
- init: function() {
28
- _inbound.Utils.init();
29
-
30
- _inbound.Utils.domReady(window, function() {
31
- /* On Load Analytics Events */
32
- _inbound.DomLoaded();
33
-
34
- });
35
- },
36
- DomLoaded: function() {
37
- _inbound.PageTracking.init();
38
- /* run form mapping */
39
- _inbound.Forms.init();
40
- /* set URL params */
41
- _inbound.Utils.setUrlParams();
42
- _inbound.LeadsAPI.init();
43
- /* run form mapping for dynamically generated forms */
44
- setTimeout(function() {
45
- _inbound.Forms.init();
46
- }, 2000);
47
-
48
- _inbound.trigger('analytics_ready');
49
-
50
- },
51
- /**
52
- * Merge script defaults with user options
53
- * @private
54
- * @param {Object} defaults Default settings
55
- * @param {Object} options User options
56
- * @returns {Object} Merged values of defaults and options
57
- */
58
- extend: function(defaults, options) {
59
- var extended = {};
60
- var prop;
61
- for (prop in defaults) {
62
- if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
63
- extended[prop] = defaults[prop];
64
- }
65
- }
66
- for (prop in options) {
67
- if (Object.prototype.hasOwnProperty.call(options, prop)) {
68
- extended[prop] = options[prop];
69
- }
70
- }
71
- return extended;
72
- },
73
- /* Debugger Function toggled by var debugMode */
74
- debug: function(msg, callback) {
75
- /* legacy */
76
- },
77
- deBugger: function(context, msg, callback) {
78
-
79
- if (!console) {
80
- return;
81
- }
82
- //if app not in debug mode, exit immediately
83
- // check for hash
84
- var hash = (document.location.hash) ? document.location.hash : '',
85
- debugHash = hash.indexOf("#debug") > -1,
86
- msg = msg || false,
87
- logCookie,
88
- logAllMessages,
89
- hashcontext;
90
-
91
- if (hash && hash.match(/debug/)) {
92
- hash = hash.split('-');
93
- hashcontext = hash[1];
94
- }
95
-
96
-
97
- logAllMessages = (_inbound.Utils.readCookie("inbound_debug") === "true") ? true : false;
98
- logCookie = (_inbound.Utils.readCookie("inbound_debug_" + context) === "true") ? true : false;
99
-
100
- if (!logCookie && !debugHash && !logAllMessages) {
101
- // no logger set. exit.
102
- return;
103
- };
104
-
105
- //console.log the message
106
- if (msg && (typeof msg === 'string')) {
107
-
108
- if (logAllMessages || hashcontext === 'all') {
109
- console.log('logAll "' + context + '" =>', msg)
110
- } else if (logCookie) {
111
- console.log('log "' + context + '" =>', msg)
112
- } else if (context === hashcontext) {
113
- console.log('#log "' + context + '" =>', msg)
114
- }
115
-
116
- };
117
-
118
- //execute the callback if one was passed-in
119
- if (callback && (callback instanceof Function)) {
120
- callback();
121
- };
122
- }
123
- };
124
-
125
- var settings = Analytics.extend(defaults, options);
126
- /* Set globals */
127
- Analytics.Settings = settings || {};
128
-
129
- return Analytics;
130
-
131
  })(_inboundOptions);
1
+ /**
2
+ * # _inbound
3
+ *
4
+ * This main the _inbound class
5
+ *
6
+ * @contributor David Wells <david@inboundnow.com>
7
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
8
+ * @version 0.0.2
9
+ */
10
+
11
+ var inbound_data = inbound_data || {};
12
+ var _inboundOptions = _inboundOptions || {};
13
+ /* Ensure global _gaq Google Analytics queue has been initialized. */
14
+ var _gaq = _gaq || [];
15
+
16
+ var _inbound = (function(options) {
17
+
18
+ /* Constants */
19
+ var defaults = {
20
+ timeout: ( inbound_settings.is_admin ? 500 : 10000 ),
21
+ formAutoTracking: true,
22
+ formAutoPopulation: true
23
+ };
24
+
25
+ var Analytics = {
26
+ /* Initialize individual modules */
27
+ init: function() {
28
+ _inbound.Utils.init();
29
+
30
+ _inbound.Utils.domReady(window, function() {
31
+ /* On Load Analytics Events */
32
+ _inbound.DomLoaded();
33
+
34
+ });
35
+ },
36
+ DomLoaded: function() {
37
+ _inbound.PageTracking.init();
38
+ /* run form mapping */
39
+ _inbound.Forms.init();
40
+ /* set URL params */
41
+ _inbound.Utils.setUrlParams();
42
+ _inbound.LeadsAPI.init();
43
+ /* run form mapping for dynamically generated forms */
44
+ setTimeout(function() {
45
+ _inbound.Forms.init();
46
+ }, 2000);
47
+
48
+ _inbound.trigger('analytics_ready');
49
+
50
+ },
51
+ /**
52
+ * Merge script defaults with user options
53
+ * @private
54
+ * @param {Object} defaults Default settings
55
+ * @param {Object} options User options
56
+ * @returns {Object} Merged values of defaults and options
57
+ */
58
+ extend: function(defaults, options) {
59
+ var extended = {};
60
+ var prop;
61
+ for (prop in defaults) {
62
+ if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
63
+ extended[prop] = defaults[prop];
64
+ }
65
+ }
66
+ for (prop in options) {
67
+ if (Object.prototype.hasOwnProperty.call(options, prop)) {
68
+ extended[prop] = options[prop];
69
+ }
70
+ }
71
+ return extended;
72
+ },
73
+ /* Debugger Function toggled by var debugMode */
74
+ debug: function(msg, callback) {
75
+ /* legacy */
76
+ },
77
+ deBugger: function(context, msg, callback) {
78
+
79
+ if (!console) {
80
+ return;
81
+ }
82
+ //if app not in debug mode, exit immediately
83
+ // check for hash
84
+ var hash = (document.location.hash) ? document.location.hash : '',
85
+ debugHash = hash.indexOf("#debug") > -1,
86
+ msg = msg || false,
87
+ logCookie,
88
+ logAllMessages,
89
+ hashcontext;
90
+
91
+ if (hash && hash.match(/debug/)) {
92
+ hash = hash.split('-');
93
+ hashcontext = hash[1];
94
+ }
95
+
96
+
97
+ logAllMessages = (_inbound.Utils.readCookie("inbound_debug") === "true") ? true : false;
98
+ logCookie = (_inbound.Utils.readCookie("inbound_debug_" + context) === "true") ? true : false;
99
+
100
+ if (!logCookie && !debugHash && !logAllMessages) {
101
+ // no logger set. exit.
102
+ return;
103
+ };
104
+
105
+ //console.log the message
106
+ if (msg && (typeof msg === 'string')) {
107
+
108
+ if (logAllMessages || hashcontext === 'all') {
109
+ console.log('logAll "' + context + '" =>', msg)
110
+ } else if (logCookie) {
111
+ console.log('log "' + context + '" =>', msg)
112
+ } else if (context === hashcontext) {
113
+ console.log('#log "' + context + '" =>', msg)
114
+ }
115
+
116
+ };
117
+
118
+ //execute the callback if one was passed-in
119
+ if (callback && (callback instanceof Function)) {
120
+ callback();
121
+ };
122
+ }
123
+ };
124
+
125
+ var settings = Analytics.extend(defaults, options);
126
+ /* Set globals */
127
+ Analytics.Settings = settings || {};
128
+
129
+ return Analytics;
130
+
131
  })(_inboundOptions);
shared/assets/js/frontend/analytics-src/analytics.page.js CHANGED
@@ -1,394 +1,395 @@
1
- /**
2
- * # Page View Tracking
3
- *
4
- * Page view tracking
5
- *
6
- * @author David Wells <david@inboundnow.com>
7
- * @author Hudson Atwell <hudson@inboundnow.com>
8
- * @version 0.0.2
9
- */
10
- /* Launches view tracking */
11
- var _inboundPageTracking = (function(_inbound) {
12
-
13
- var started = false,
14
- stopped = false,
15
- turnedOff = false,
16
- clockTime = parseInt(_inbound.Utils.readCookie("lead_session"), 10) || 0,
17
- inactiveClockTime = 0,
18
- startTime = new Date(),
19
- clockTimer = null,
20
- inactiveClockTimer = null,
21
- idleTimer = null,
22
- reportInterval,
23
- idleTimeout,
24
- utils = _inbound.Utils,
25
- timeNow = _inbound.Utils.GetDate(),
26
- lsType = 'page_views',
27
- Pages = _inbound.totalStorage(lsType) || {},
28
- /*!
29
- Todo: Use UTC offset
30
- var x = new Date();
31
- var currentTime = x.getTimezoneOffset() / 60;
32
- console.log(currentTime) // gets UTC offset
33
- */
34
- id = inbound_settings.post_id || window.location.pathname,
35
- analyticsTimeout = _inbound.Settings.timeout || 10000;
36
-
37
- _inbound.PageTracking = {
38
-
39
- init: function(options) {
40
-
41
- if(lsType !== 'page_views') {
42
- return false; // in admin
43
- }
44
-
45
- this.CheckTimeOut();
46
- // Set up options and defaults
47
- options = options || {};
48
- reportInterval = parseInt(options.reportInterval, 10) || 10;
49
- idleTimeout = parseInt(options.idleTimeout, 10) || 3;
50
-
51
- // Basic activity event listeners
52
- utils.addListener(document, 'keydown', utils.throttle(_inbound.PageTracking.pingSession, 1000));
53
- utils.addListener(document, 'click', utils.throttle(_inbound.PageTracking.pingSession, 1000));
54
- utils.addListener(window, 'mousemove', utils.throttle(_inbound.PageTracking.pingSession, 1000));
55
- //utils.addListener(window, 'scroll', utils.throttle(_inbound.PageTracking.pingSession, 1000));
56
-
57
- // Page visibility listeners
58
- _inbound.PageTracking.checkVisibility();
59
-
60
- /* Start Session on page load */
61
- this.startSession();
62
-
63
- },
64
-
65
- setIdle: function(reason) {
66
- var reason = reason || "No Movement",
67
- msg = 'Session IDLE. Activity Timeout due to ' + reason;
68
-
69
- _inbound.deBugger('pages', msg);
70
-
71
- clearTimeout(_inbound.PageTracking.idleTimer);
72
- _inbound.PageTracking.stopClock();
73
- _inbound.trigger('session_idle');
74
-
75
- },
76
-
77
- checkVisibility: function() {
78
- var hidden, visibilityState, visibilityChange;
79
-
80
- if (typeof document.hidden !== "undefined") {
81
- hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
82
- } else if (typeof document.mozHidden !== "undefined") {
83
- hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
84
- } else if (typeof document.msHidden !== "undefined") {
85
- hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
86
- } else if (typeof document.webkitHidden !== "undefined") {
87
- hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
88
- }
89
-
90
- var document_hidden = document[hidden];
91
-
92
- _inbound.Utils.addListener(document, visibilityChange, function(e) {
93
- /*! Listen for visibility changes */
94
- if (document_hidden != document[hidden]) {
95
- if (document[hidden]) {
96
- // Document hidden
97
- _inbound.trigger('tab_hidden');
98
- _inbound.PageTracking.setIdle('browser tab switch');
99
- } else {
100
- // Document shown
101
- _inbound.trigger('tab_visible');
102
- _inbound.PageTracking.pingSession();
103
- }
104
-
105
- document_hidden = document[hidden];
106
- }
107
- });
108
- },
109
- clock: function() {
110
- clockTime += 1;
111
- var niceTime = clockTime / 60;
112
- var msg = 'Total time spent on Page in this Session: ' + niceTime.toFixed(2) + " min";
113
- _inbound.deBugger('pages', msg);
114
- if (clockTime > 0 && (clockTime % reportInterval === 0)) {
115
-
116
- var d = new Date();
117
- d.setTime(d.getTime() + 30 * 60 * 1000);
118
- utils.createCookie("lead_session", clockTime, d); // Set cookie on page load
119
-
120
- /*! every 10 seconds run this */
121
- //console.log('Session Heartbeat every ' + reportInterval + ' secs');
122
- _inbound.trigger('session_heartbeat', clockTime);
123
-
124
- }
125
-
126
- },
127
- inactiveClock: function() {
128
- inactiveClockTime += 1;
129
- var TimeUntilTimeOut = (1800 - inactiveClockTime) / 60;
130
- var msg = 'Time until Session Timeout: ' + TimeUntilTimeOut.toFixed(2) + " min";
131
- _inbound.deBugger('pages', msg);
132
- //console.log('Time until Session Timeout: ', TimeUntilTimeOut.toFixed(2) + " min");
133
- /* Session timeout after 30min */
134
- if (inactiveClockTime > 1800) {
135
-
136
- // sendEvent(clockTime);
137
- /*! End session after 30min timeout */
138
- _inbound.trigger('session_end', InboundLeadData);
139
- _inbound.Utils.eraseCookie("lead_session");
140
- /* todo maybe? remove session Cookie */
141
- inactiveClockTime = 0;
142
- clearTimeout(inactiveClockTimer);
143
- }
144
-
145
-
146
- },
147
- stopClock: function() {
148
- stopped = true;
149
- clearTimeout(clockTimer);
150
- clearTimeout(inactiveClockTimer);
151
- inactiveClockTimer = setInterval(_inbound.PageTracking.inactiveClock, 1000);
152
- },
153
-
154
- restartClock: function() {
155
- stopped = false;
156
-
157
-
158
- _inbound.trigger('session_resume');
159
- _inbound.deBugger('pages', 'Activity resumed. Session Active');
160
- /* todo add session_resume */
161
- clearTimeout(clockTimer);
162
- inactiveClockTime = 0;
163
- clearTimeout(inactiveClockTimer);
164
- clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
165
- },
166
-
167
- turnOff: function() {
168
- _inbound.PageTracking.setIdle();
169
- turnedOff = true;
170
- },
171
-
172
- turnOn: function() {
173
- turnedOff = false;
174
- },
175
- /* This start only runs once */
176
- startSession: function() {
177
- /* todo add session Cookie */
178
- // Calculate seconds from start to first interaction
179
- var currentTime = new Date();
180
- var diff = currentTime - startTime;
181
-
182
-
183
- started = true; // Set global
184
-
185
- // Send User Timing Event
186
- /* Todo session start here */
187
-
188
- // Start clock
189
- clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
190
- //utils.eraseCookie("lead_session");
191
- var session = utils.readCookie("lead_session");
192
-
193
- if (!session) {
194
- _inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
195
- var d = new Date();
196
- d.setTime(d.getTime() + 30 * 60 * 1000);
197
- _inbound.Utils.createCookie("lead_session", 1, d); // Set cookie on page load
198
- } else {
199
- _inbound.trigger('session_active');
200
- //console.log("count of secs " + session);
201
- //_inbound.trigger('session_active'); // trigger 'inbound_analytics_session_active'
202
- }
203
-
204
- this.pingSession();
205
-
206
-
207
- },
208
- resetInactiveFunc: function() {
209
- inactiveClockTime = 0;
210
- clearTimeout(inactiveClockTimer);
211
- },
212
- /* Ping Session to keep active */
213
- pingSession: function(e) {
214
-
215
-
216
- if (turnedOff) {
217
- return;
218
- }
219
-
220
- if (!started) {
221
- _inbound.PageTracking.startSession();
222
- }
223
-
224
- if (stopped) {
225
- _inbound.PageTracking.restartClock();
226
- }
227
-
228
- clearTimeout(idleTimer);
229
-
230
- idleTimer = setTimeout(_inbound.PageTracking.setIdle, idleTimeout * 1000 + 100);
231
-
232
- if (typeof(e) != "undefined") {
233
- if (e.type === "mousemove") {
234
- _inbound.PageTracking.mouseEvents(e);
235
- }
236
- }
237
-
238
- },
239
- mouseEvents: function(e) {
240
-
241
- if (e.pageY <= 5) {
242
- _inbound.trigger('tab_mouseout');
243
- }
244
-
245
- },
246
- /**
247
- * Returns the pages viewed by the site visitor
248
- *
249
- * ```js
250
- * var pageViews = _inbound.PageTracking.getPageViews();
251
- * // returns page view object
252
- * ```
253
- *
254
- * @return {object} page view object with page ID as key and timestamp
255
- */
256
- getPageViews: function() {
257
- var local_store = _inbound.Utils.checkLocalStorage();
258
- if (local_store) {
259
- var page_views = localStorage.getItem(lsType),
260
- local_object = JSON.parse(page_views);
261
- if (typeof local_object == 'object' && local_object) {
262
- //this.triggerPageView();
263
- }
264
- return local_object;
265
- }
266
- },
267
- isRevisit: function(Pages) {
268
- var revisitCheck = false;
269
- var Pages = Pages || {};
270
- var pageSeen = Pages[id];
271
- if (typeof(pageSeen) != "undefined" && pageSeen !== null) {
272
- revisitCheck = true;
273
- }
274
- return revisitCheck;
275
- },
276
- triggerPageView: function(pageRevisit) {
277
-
278
- var pageData = {
279
- title: document.title,
280
- url: document.location.href,
281
- path: document.location.pathname,
282
- count: 1 // default
283
- };
284
-
285
- if (pageRevisit) {
286
- /* Page Revisit Trigger */
287
- Pages[id].push(timeNow);
288
- pageData.count = Pages[id].length;
289
- _inbound.trigger('page_revisit', pageData);
290
-
291
- } else {
292
- /* Page First Seen Trigger */
293
- Pages[id] = [];
294
- Pages[id].push(timeNow);
295
- _inbound.trigger('page_first_visit', pageData);
296
- }
297
-
298
- _inbound.trigger('page_visit', pageData);
299
-
300
- _inbound.totalStorage(lsType, Pages);
301
-
302
- this.storePageView();
303
-
304
- },
305
- CheckTimeOut: function() {
306
-
307
- var pageRevisit = this.isRevisit(Pages),
308
- status,
309
- timeout;
310
-
311
- /* Default */
312
- if (pageRevisit) {
313
-
314
- var prev = Pages[id].length - 1,
315
- lastView = Pages[id][prev],
316
- timeDiff = Math.abs(new Date(lastView).getTime() - new Date(timeNow).getTime());
317
-
318
- timeout = timeDiff > analyticsTimeout;
319
-
320
- if (timeout) {
321
- status = 'Timeout Happened. Page view fired';
322
- this.triggerPageView(pageRevisit);
323
- } else {
324
- time_left = Math.abs((analyticsTimeout - timeDiff)) * 0.001;
325
- status = analyticsTimeout / 1000 + ' sec timeout not done: ' + time_left + " seconds left";
326
- }
327
-
328
- } else {
329
- /*! Page never seen before save view */
330
- this.triggerPageView(pageRevisit);
331
- }
332
-
333
- _inbound.deBugger('pages', status);
334
- },
335
- storePageView: function() {
336
-
337
- /* ignore if page tracking off and page is not a landing page */
338
- if ( inbound_settings.page_tracking == 'off' && inbound_settings.post_type != 'landing-page' ) {
339
- return;
340
- }
341
-
342
- /* Let's try and fire this last - also defines what constitutes a bounce - */
343
- document.addEventListener("DOMContentLoaded", function() {
344
- setTimeout(function(){
345
- var leadID = ( _inbound.Utils.readCookie('wp_lead_id') ) ? _inbound.Utils.readCookie('wp_lead_id') : '';
346
- var lead_uid = ( _inbound.Utils.readCookie('wp_lead_uid') ) ? _inbound.Utils.readCookie('wp_lead_uid') : '';
347
- var ctas_loaded = _inbound.totalStorage('wp_cta_loaded');
348
- var ctas_impressions = _inbound.totalStorage('wp_cta_impressions');
349
-
350
- /* now reset impressions */
351
- _inbound.totalStorage('wp_cta_impressions' , {} );
352
-
353
- var data = {
354
- action: 'inbound_track_lead',
355
- wp_lead_uid: lead_uid,
356
- wp_lead_id: leadID,
357
- page_id: inbound_settings.post_id,
358
- variation_id: inbound_settings.variation_id,
359
- post_type: inbound_settings.post_type,
360
- current_url: window.location.href,
361
- page_views: JSON.stringify(_inbound.PageTracking.getPageViews()),
362
- cta_impressions : JSON.stringify(ctas_impressions),
363
- cta_history : JSON.stringify(ctas_loaded),
364
- json: '0'
365
- };
366
-
367
- var firePageCallback = function(leadID) {
368
- //_inbound.Events.page_view_saved(leadID);
369
- };
370
- //_inbound.Utils.doAjax(data, firePageCallback);
371
-
372
- _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, firePageCallback);
373
-
374
- } , 200 );
375
-
376
-
377
- });
378
-
379
-
380
- }
381
- /*! GA functions
382
- function log_event(category, action, label) {
383
- _gaq.push(['_trackEvent', category, action, label]);
384
- }
385
-
386
- function log_click(category, link) {
387
- log_event(category, 'Click', $(link).text());
388
- }
389
- */
390
- };
391
-
392
- return _inbound;
393
-
 
394
  })(_inbound || {});
1
+ /**
2
+ * # Page View Tracking
3
+ *
4
+ * Page view tracking
5
+ *
6
+ * @contributor David Wells <david@inboundnow.com>
7
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
8
+ * @version 0.0.2
9
+ */
10
+ /* Launches view tracking */
11
+ var _inboundPageTracking = (function(_inbound) {
12
+
13
+ var started = false,
14
+ stopped = false,
15
+ turnedOff = false,
16
+ clockTime = parseInt(_inbound.Utils.readCookie("lead_session"), 10) || 0,
17
+ inactiveClockTime = 0,
18
+ startTime = new Date(),
19
+ clockTimer = null,
20
+ inactiveClockTimer = null,
21
+ idleTimer = null,
22
+ reportInterval,
23
+ idleTimeout,
24
+ utils = _inbound.Utils,
25
+ timeNow = _inbound.Utils.GetDate(),
26
+ lsType = 'page_views',
27
+ Pages = _inbound.totalStorage(lsType) || {},
28
+ /*!
29
+ Todo: Use UTC offset
30
+ var x = new Date();
31
+ var currentTime = x.getTimezoneOffset() / 60;
32
+ console.log(currentTime) // gets UTC offset
33
+ */
34
+ id = inbound_settings.post_id || window.location.pathname,
35
+ analyticsTimeout = _inbound.Settings.timeout || 10000;
36
+
37
+ _inbound.PageTracking = {
38
+
39
+ init: function(options) {
40
+
41
+ if(lsType !== 'page_views') {
42
+ return false; // in admin
43
+ }
44
+
45
+ this.CheckTimeOut();
46
+ // Set up options and defaults
47
+ options = options || {};
48
+ reportInterval = parseInt(options.reportInterval, 10) || 10;
49
+ idleTimeout = parseInt(options.idleTimeout, 10) || 3;
50
+
51
+ // Basic activity event listeners
52
+ utils.addListener(document, 'keydown', utils.throttle(_inbound.PageTracking.pingSession, 1000));
53
+ utils.addListener(document, 'click', utils.throttle(_inbound.PageTracking.pingSession, 1000));
54
+ utils.addListener(window, 'mousemove', utils.throttle(_inbound.PageTracking.pingSession, 1000));
55
+ //utils.addListener(window, 'scroll', utils.throttle(_inbound.PageTracking.pingSession, 1000));
56
+
57
+ // Page visibility listeners
58
+ _inbound.PageTracking.checkVisibility();
59
+
60
+ /* Start Session on page load */
61
+ this.startSession();
62
+
63
+ },
64
+
65
+ setIdle: function(reason) {
66
+ var reason = reason || "No Movement",
67
+ msg = 'Session IDLE. Activity Timeout due to ' + reason;
68
+
69
+ _inbound.deBugger('pages', msg);
70
+
71
+ clearTimeout(_inbound.PageTracking.idleTimer);
72
+ _inbound.PageTracking.stopClock();
73
+ _inbound.trigger('session_idle');
74
+
75
+ },
76
+
77
+ checkVisibility: function() {
78
+ var hidden, visibilityState, visibilityChange;
79
+
80
+ if (typeof document.hidden !== "undefined") {
81
+ hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
82
+ } else if (typeof document.mozHidden !== "undefined") {
83
+ hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
84
+ } else if (typeof document.msHidden !== "undefined") {
85
+ hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
86
+ } else if (typeof document.webkitHidden !== "undefined") {
87
+ hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
88
+ }
89
+
90
+ var document_hidden = document[hidden];
91
+
92
+ _inbound.Utils.addListener(document, visibilityChange, function(e) {
93
+ /*! Listen for visibility changes */
94
+ if (document_hidden != document[hidden]) {
95
+ if (document[hidden]) {
96
+ // Document hidden
97
+ _inbound.trigger('tab_hidden');
98
+ _inbound.PageTracking.setIdle('browser tab switch');
99
+ } else {
100
+ // Document shown
101
+ _inbound.trigger('tab_visible');
102
+ _inbound.PageTracking.pingSession();
103
+ }
104
+
105
+ document_hidden = document[hidden];
106
+ }
107
+ });
108
+ },
109
+ clock: function() {
110
+ clockTime += 1;
111
+ var niceTime = clockTime / 60;
112
+ var msg = 'Total time spent on Page in this Session: ' + niceTime.toFixed(2) + " min";
113
+ _inbound.deBugger('pages', msg);
114
+ if (clockTime > 0 && (clockTime % reportInterval === 0)) {
115
+
116
+ var d = new Date();
117
+ d.setTime(d.getTime() + 30 * 60 * 1000);
118
+ utils.createCookie("lead_session", clockTime, d); // Set cookie on page load
119
+
120
+ /*! every 10 seconds run this */
121
+ //console.log('Session Heartbeat every ' + reportInterval + ' secs');
122
+ _inbound.trigger('session_heartbeat', clockTime);
123
+
124
+ }
125
+
126
+ },
127
+ inactiveClock: function() {
128
+ inactiveClockTime += 1;
129
+ var TimeUntilTimeOut = (1800 - inactiveClockTime) / 60;
130
+ var msg = 'Time until Session Timeout: ' + TimeUntilTimeOut.toFixed(2) + " min";
131
+ _inbound.deBugger('pages', msg);
132
+ //console.log('Time until Session Timeout: ', TimeUntilTimeOut.toFixed(2) + " min");
133
+ /* Session timeout after 30min */
134
+ if (inactiveClockTime > 1800) {
135
+
136
+ // sendEvent(clockTime);
137
+ /*! End session after 30min timeout */
138
+ _inbound.trigger('session_end', InboundLeadData);
139
+ _inbound.Utils.eraseCookie("lead_session");
140
+ /* todo maybe? remove session Cookie */
141
+ inactiveClockTime = 0;
142
+ clearTimeout(inactiveClockTimer);
143
+ }
144
+
145
+
146
+ },
147
+ stopClock: function() {
148
+ stopped = true;
149
+ clearTimeout(clockTimer);
150
+ clearTimeout(inactiveClockTimer);
151
+ inactiveClockTimer = setInterval(_inbound.PageTracking.inactiveClock, 1000);
152
+ },
153
+
154
+ restartClock: function() {
155
+ stopped = false;
156
+
157
+
158
+ _inbound.trigger('session_resume');
159
+ _inbound.deBugger('pages', 'Activity resumed. Session Active');
160
+ /* todo add session_resume */
161
+ clearTimeout(clockTimer);
162
+ inactiveClockTime = 0;
163
+ clearTimeout(inactiveClockTimer);
164
+ clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
165
+ },
166
+
167
+ turnOff: function() {
168
+ _inbound.PageTracking.setIdle();
169
+ turnedOff = true;
170
+ },
171
+
172
+ turnOn: function() {
173
+ turnedOff = false;
174
+ },
175
+ /* This start only runs once */
176
+ startSession: function() {
177
+ /* todo add session Cookie */
178
+ // Calculate seconds from start to first interaction
179
+ var currentTime = new Date();
180
+ var diff = currentTime - startTime;
181
+
182
+
183
+ started = true; // Set global
184
+
185
+ // Send User Timing Event
186
+ /* Todo session start here */
187
+
188
+ // Start clock
189
+ clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
190
+ //utils.eraseCookie("lead_session");
191
+ var session = utils.readCookie("lead_session");
192
+
193
+ if (!session) {
194
+ _inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
195
+ var d = new Date();
196
+ d.setTime(d.getTime() + 30 * 60 * 1000);
197
+ _inbound.Utils.createCookie("lead_session", 1, d); // Set cookie on page load
198
+ } else {
199
+ _inbound.trigger('session_active');
200
+ //console.log("count of secs " + session);
201
+ //_inbound.trigger('session_active'); // trigger 'inbound_analytics_session_active'
202
+ }
203
+
204
+ this.pingSession();
205
+
206
+
207
+ },
208
+ resetInactiveFunc: function() {
209
+ inactiveClockTime = 0;
210
+ clearTimeout(inactiveClockTimer);
211
+ },
212
+ /* Ping Session to keep active */
213
+ pingSession: function(e) {
214
+
215
+
216
+ if (turnedOff) {
217
+ return;
218
+ }
219
+
220
+ if (!started) {
221
+ _inbound.PageTracking.startSession();
222
+ }
223
+
224
+ if (stopped) {
225
+ _inbound.PageTracking.restartClock();
226
+ }
227
+
228
+ clearTimeout(idleTimer);
229
+
230
+ idleTimer = setTimeout(_inbound.PageTracking.setIdle, idleTimeout * 1000 + 100);
231
+
232
+ if (typeof(e) != "undefined") {
233
+ if (e.type === "mousemove") {
234
+ _inbound.PageTracking.mouseEvents(e);
235
+ }
236
+ }
237
+
238
+ },
239
+ mouseEvents: function(e) {
240
+
241
+ if (e.pageY <= 5) {
242
+ _inbound.trigger('tab_mouseout');
243
+ }
244
+
245
+ },
246
+ /**
247
+ * Returns the pages viewed by the site visitor
248
+ *
249
+ * ```js
250
+ * var pageViews = _inbound.PageTracking.getPageViews();
251
+ * // returns page view object
252
+ * ```
253
+ *
254
+ * @return {object} page view object with page ID as key and timestamp
255
+ */
256
+ getPageViews: function() {
257
+ var local_store = _inbound.Utils.checkLocalStorage();
258
+ if (local_store) {
259
+ var page_views = localStorage.getItem(lsType),
260
+ local_object = JSON.parse(page_views);
261
+ if (typeof local_object == 'object' && local_object) {
262
+ //this.triggerPageView();
263
+ }
264
+ return local_object;
265
+ }
266
+ },
267
+ isRevisit: function(Pages) {
268
+ var revisitCheck = false;
269
+ var Pages = Pages || {};
270
+ var pageSeen = Pages[id];
271
+ if (typeof(pageSeen) != "undefined" && pageSeen !== null) {
272
+ revisitCheck = true;
273
+ }
274
+ return revisitCheck;
275
+ },
276
+ triggerPageView: function(pageRevisit) {
277
+
278
+ var pageData = {
279
+ title: document.title,
280
+ url: document.location.href,
281
+ path: document.location.pathname,
282
+ count: 1 // default
283
+ };
284
+
285
+ if (pageRevisit) {
286
+ /* Page Revisit Trigger */
287
+ Pages[id].push(timeNow);
288
+ pageData.count = Pages[id].length;
289
+ _inbound.trigger('page_revisit', pageData);
290
+
291
+ } else {
292
+ /* Page First Seen Trigger */
293
+ Pages[id] = [];
294
+ Pages[id].push(timeNow);
295
+ _inbound.trigger('page_first_visit', pageData);
296
+ }
297
+
298
+ _inbound.trigger('page_visit', pageData);
299
+
300
+ _inbound.totalStorage(lsType, Pages);
301
+
302
+ this.storePageView();
303
+
304
+ },
305
+ CheckTimeOut: function() {
306
+
307
+ var pageRevisit = this.isRevisit(Pages),
308
+ status,
309
+ timeout;
310
+
311
+ /* Default */
312
+ if (pageRevisit) {
313
+
314
+ var prev = Pages[id].length - 1,
315
+ lastView = Pages[id][prev],
316
+ timeDiff = Math.abs(new Date(lastView).getTime() - new Date(timeNow).getTime());
317
+
318
+ timeout = timeDiff > analyticsTimeout;
319
+
320
+ if (timeout) {
321
+ status = 'Timeout Happened. Page view fired';
322
+ this.triggerPageView(pageRevisit);
323
+ } else {
324
+ time_left = Math.abs((analyticsTimeout - timeDiff)) * 0.001;
325
+ status = analyticsTimeout / 1000 + ' sec timeout not done: ' + time_left + " seconds left";
326
+ }
327
+
328
+ } else {
329
+ /*! Page never seen before save view */
330
+ this.triggerPageView(pageRevisit);
331
+ }
332
+
333
+ _inbound.deBugger('pages', status);
334
+ },
335
+ storePageView: function() {
336
+ var stored = false;
337
+
338
+ /* ignore if page tracking off and page is not a landing page */
339
+ if ( inbound_settings.page_tracking == 'off' && inbound_settings.post_type != 'landing-page' ) {
340
+ return;
341
+ }
342
+
343
+ /* Let's try and fire this last - also defines what constitutes a bounce - */
344
+ document.onreadystatechange = function(){
345
+
346
+ if(document.readyState !== 'loading' && stored === false){
347
+ setTimeout(function(){
348
+ var leadID = ( _inbound.Utils.readCookie('wp_lead_id') ) ? _inbound.Utils.readCookie('wp_lead_id') : '';
349
+ var lead_uid = ( _inbound.Utils.readCookie('wp_lead_uid') ) ? _inbound.Utils.readCookie('wp_lead_uid') : '';
350
+ var ctas_loaded = _inbound.totalStorage('wp_cta_loaded');
351
+ var ctas_impressions = _inbound.totalStorage('wp_cta_impressions');
352
+ stored = true;
353
+
354
+ /* now reset impressions */
355
+ _inbound.totalStorage('wp_cta_impressions' , {} );
356
+
357
+ var data = {
358
+ action: 'inbound_track_lead',
359
+ wp_lead_uid: lead_uid,
360
+ wp_lead_id: leadID,
361
+ page_id: inbound_settings.post_id,
362
+ variation_id: inbound_settings.variation_id,
363
+ post_type: inbound_settings.post_type,
364
+ current_url: window.location.href,
365
+ page_views: JSON.stringify(_inbound.PageTracking.getPageViews()),
366
+ cta_impressions : JSON.stringify(ctas_impressions),
367
+ cta_history : JSON.stringify(ctas_loaded),
368
+ json: '0'
369
+ };
370
+
371
+ var firePageCallback = function(leadID) {
372
+ //_inbound.Events.page_view_saved(leadID);
373
+ };
374
+ //_inbound.Utils.doAjax(data, firePageCallback);
375
+
376
+ _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, firePageCallback);
377
+
378
+ } , 200 );
379
+ }
380
+ }
381
+ }
382
+ /*! GA functions
383
+ function log_event(category, action, label) {
384
+ _gaq.push(['_trackEvent', category, action, label]);
385
+ }
386
+
387
+ function log_click(category, link) {
388
+ log_event(category, 'Click', $(link).text());
389
+ }
390
+ */
391
+ };
392
+
393
+ return _inbound;
394
+
395
  })(_inbound || {});
shared/assets/js/frontend/analytics-src/analytics.start.js CHANGED
@@ -1,17 +1,17 @@
1
- /**
2
- * # Start
3
- *
4
- * Runs init functions
5
- *
6
- * @author David Wells <david@inboundnow.com>
7
- * @author Hudson Atwell <hudson@inboundnow.com>
8
- * @version 0.0.2
9
- */
10
-
11
-
12
- /* Initialize _inbound */
13
- _inbound.init();
14
-
15
- /* Set Global Lead Data */
16
- InboundLeadData = _inbound.totalStorage('inbound_lead_data') || null;
17
-
1
+ /**
2
+ * # Start
3
+ *
4
+ * Runs init functions
5
+ *
6
+ * @contributor David Wells <david@inboundnow.com>
7
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
8
+ * @version 0.0.2
9
+ */
10
+
11
+
12
+ /* Initialize _inbound */
13
+ _inbound.init();
14
+
15
+ /* Set Global Lead Data */
16
+ InboundLeadData = _inbound.totalStorage('inbound_lead_data') || null;
17
+
shared/assets/js/frontend/analytics-src/analytics.utils.js CHANGED
@@ -1,858 +1,858 @@
1
- /**
2
- * # _inbound UTILS
3
- *
4
- * This file contains all of the utility functions used by analytics
5
- *
6
- * @author David Wells <david@inboundnow.com>
7
- * @author Hudson Atwell <hudson@inboundnow.com>
8
- * @version 0.0.2
9
- */
10
-
11
- var _inboundUtils = (function(_inbound) {
12
-
13
- var storageSupported,
14
- corsEnabled = window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest(),
15
- toString = Object.prototype.toString,
16
- currentPage = ('https:' == location.protocol ? 'https://' : 'http://') + location.hostname + location.pathname.replace(/\/$/, "");
17
-
18
- var settings = {
19
- api_host: currentPage,
20
- track_pageview: true,
21
- track_links_timeout: 300,
22
- cookie_name: '_sp',
23
- cookie_expiration: 365,
24
- cookie_domain: (host = location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i)) ? host[0] : ''
25
- };
26
-
27
- _inbound.Utils = {
28
- init: function() {
29
-
30
- this.polyFills();
31
- this.checkLocalStorage();
32
- this.SetUID();
33
- this.storeReferralData();
34
-
35
- },
36
- /*! http://stackoverflow.com/questions/951791/javascript-global-error-handling */
37
- /* Polyfills for missing browser functionality */
38
- polyFills: function() {
39
- /* Console.log fix for old browsers */
40
- if (!window.console) {
41
- window.console = {};
42
- }
43
- var m = [
44
- "log", "info", "warn", "error", "debug", "trace", "dir", "group",
45
- "groupCollapsed", "groupEnd", "time", "timeEnd", "profile", "profileEnd",
46
- "dirxml", "assert", "count", "markTimeline", "timeStamp", "clear"
47
- ];
48
- // define undefined methods as noops to prevent errors
49
- for (var i = 0; i < m.length; i++) {
50
- if (!window.console[m[i]]) {
51
- window.console[m[i]] = function() {};
52
- }
53
- }
54
- /* Event trigger polyfill for IE9 and 10
55
- (function() {
56
- function CustomEvent(event, params) {
57
- params = params || {
58
- bubbles: false,
59
- cancelable: false,
60
- detail: undefined
61
- };
62
- var evt = document.createEvent('CustomEvent');
63
- evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
64
- return evt;
65
- }
66
-
67
- CustomEvent.prototype = window.Event.prototype;
68
-
69
- window.CustomEvent = CustomEvent;
70
- })();*/
71
-
72
- /*\
73
- |*| Polyfill Date.toISOString
74
- \*/
75
- if (!Date.prototype.toISOString) {
76
- (function() {
77
- /**
78
- * @param {number} text
79
- * @returns {?}
80
- */
81
- function pad(text) {
82
- /** @type {string} */
83
- var code = String(text);
84
- return 1 === code.length && (code = '0' + code), code;
85
- }
86
- /**
87
- * @returns {string}
88
- */
89
- Date.prototype.toISOString = function() {
90
- return this.getUTCFullYear() + '-' + pad(this.getUTCMonth() + 1) + '-' + pad(this.getUTCDate()) + 'T' + pad(this.getUTCHours()) + ':' + pad(this.getUTCMinutes()) + ':' + pad(this.getUTCSeconds()) + '.' + String((this.getUTCMilliseconds() / 1E3).toFixed(3)).slice(2, 5) + 'Z';
91
- };
92
- })();
93
- }
94
-
95
- /* custom event for ie8+ https://gist.github.com/WebReflection/6693661 */
96
- try {
97
- new CustomEvent('?');
98
- } catch (o_O) {
99
- /*!(C) Andrea Giammarchi -- WTFPL License*/
100
- this.CustomEvent = function(
101
- eventName,
102
- defaultInitDict
103
- ) {
104
-
105
- // the infamous substitute
106
- function CustomEvent(type, eventInitDict) {
107
- var event = document.createEvent(eventName);
108
- if (type !== null) {
109
- initCustomEvent.call(
110
- event,
111
- type, (eventInitDict || (
112
- // if falsy we can just use defaults
113
- eventInitDict = defaultInitDict
114
- )).bubbles,
115
- eventInitDict.cancelable,
116
- eventInitDict.detail
117
- );
118
- } else {
119
- // no need to put the expando property otherwise
120
- // since an event cannot be initialized twice
121
- // previous case is the most common one anyway
122
- // but if we end up here ... there it goes
123
- event.initCustomEvent = initCustomEvent;
124
- }
125
- return event;
126
- }
127
-
128
- // borrowed or attached at runtime
129
- function initCustomEvent(
130
- type, bubbles, cancelable, detail
131
- ) {
132
- this['init' + eventName](type, bubbles, cancelable, detail);
133
- 'detail' in this || (this.detail = detail);
134
- }
135
-
136
- // that's it
137
- return CustomEvent;
138
- }(
139
- // is this IE9 or IE10 ?
140
- // where CustomEvent is there
141
- // but not usable as construtor ?
142
- this.CustomEvent ?
143
- // use the CustomEvent interface in such case
144
- 'CustomEvent' : 'Event',
145
- // otherwise the common compatible one
146
- {
147
- bubbles: false,
148
- cancelable: false,
149
- detail: null
150
- }
151
- );
152
- }
153
- /* querySelectorAll polyfill for ie7+ */
154
- if (!document.querySelectorAll) {
155
- document.querySelectorAll = function(selectors) {
156
- var style = document.createElement('style'),
157
- elements = [],
158
- element;
159
- document.documentElement.firstChild.appendChild(style);
160
- document._qsa = [];
161
-
162
- style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
163
- window.scrollBy(0, 0);
164
- style.parentNode.removeChild(style);
165
-
166
- while (document._qsa.length) {
167
- element = document._qsa.shift();
168
- element.style.removeAttribute('x-qsa');
169
- elements.push(element);
170
- }
171
- document._qsa = null;
172
- return elements;
173
- };
174
- }
175
-
176
- if (!document.querySelector) {
177
- document.querySelector = function(selectors) {
178
- var elements = document.querySelectorAll(selectors);
179
- return (elements.length) ? elements[0] : null;
180
- };
181
- }
182
- /* Innertext shim for firefox https://github.com/duckinator/innerText-polyfill/blob/master/innertext.js */
183
- if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
184
- HTMLElement.prototype.__defineGetter__("innerText", function() {
185
- var selection = window.getSelection(),
186
- ranges = [],
187
- str;
188
-
189
- // Save existing selections.
190
- for (var i = 0; i < selection.rangeCount; i++) {
191
- ranges[i] = selection.getRangeAt(i);
192
- }
193
-
194
- // Deselect everything.
195
- selection.removeAllRanges();
196
-
197
- // Select `el` and all child nodes.
198
- // 'this' is the element .innerText got called on
199
- selection.selectAllChildren(this);
200
-
201
- // Get the string representation of the selected nodes.
202
- str = selection.toString();
203
-
204
- // Deselect everything. Again.
205
- selection.removeAllRanges();
206
-
207
- // Restore all formerly existing selections.
208
- for (var i = 0; i < ranges.length; i++) {
209
- selection.addRange(ranges[i]);
210
- }
211
-
212
- // Oh look, this is what we wanted.
213
- // String representation of the element, close to as rendered.
214
- return str;
215
- })
216
- }
217
- },
218
- /**
219
- * Create cookie
220
- *
221
- * ```js
222
- * // Creates cookie for 10 days
223
- * _inbound.Utils.createCookie( 'cookie_name', 'value', 10 );
224
- * ```
225
- *
226
- * @param {string} name Name of cookie
227
- * @param {string} value Value of cookie
228
- * @param {string} days Length of storage
229
- */
230
- createCookie: function(name, value, days) {
231
- var expires = "";
232
- if (days) {
233
- var date = new Date();
234
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
235
- expires = "; expires=" + date.toGMTString();
236
- }
237
- document.cookie = name + "=" + value + expires + "; path=/";
238
- },
239
- /**
240
- * Read cookie value
241
- *
242
- * ```js
243
- * var cookie = _inbound.Utils.readCookie( 'cookie_name' );
244
- * console.log(cookie); // cookie value
245
- * ```
246
- * @param {string} name name of cookie
247
- * @return {string} value of cookie
248
- */
249
- readCookie: function(name) {
250
- var nameEQ = name + "=";
251
- var ca = document.cookie.split(';');
252
- for (var i = 0; i < ca.length; i++) {
253
- var c = ca[i];
254
- while (c.charAt(0) === ' ') {
255
- c = c.substring(1, c.length);
256
- }
257
- if (c.indexOf(nameEQ) === 0) {
258
- return c.substring(nameEQ.length, c.length);
259
- }
260
- }
261
- return null;
262
- },
263
- /**
264
- * Erase cookie
265
- *
266
- * ```js
267
- * // usage:
268
- * _inbound.Utils.eraseCookie( 'cookie_name' );
269
- * // deletes 'cookie_name' value
270
- * ```
271
- * @param {string} name name of cookie
272
- * @return {string} value of cookie
273
- */
274
- eraseCookie: function(name) {
275
- this.createCookie(name, "", -1);
276
- },
277
- /* Get All Cookies */
278
- getAllCookies: function() {
279
- var cookies = {};
280
- if (document.cookie && document.cookie !== '') {
281
- var split = document.cookie.split(';');
282
- for (var i = 0; i < split.length; i++) {
283
- var name_value = split[i].split("=");
284
- name_value[0] = name_value[0].replace(/^ /, '');
285
- cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
286
- }
287
- }
288
- _inbound.totalStorage('inbound_cookies', cookies); // store cookie data
289
- return cookies;
290
- },
291
- /* Grab URL params and save */
292
- setUrlParams: function() {
293
- var urlParams = {};
294
-
295
- (function() {
296
- var e,
297
- d = function(s) {
298
- return decodeURIComponent(s).replace(/\+/g, " ");
299
- },
300
- q = window.location.search.substring(1),
301
- r = /([^&=]+)=?([^&]*)/g;
302
-
303
- while (e = r.exec(q)) {
304
- if (e[1].indexOf("[") == "-1")
305
- urlParams[d(e[1])] = d(e[2]);
306
- else {
307
- var b1 = e[1].indexOf("["),
308
- aN = e[1].slice(b1 + 1, e[1].indexOf("]", b1)),
309
- pN = d(e[1].slice(0, b1));
310
-
311
- if (typeof urlParams[pN] != "object")
312
- urlParams[d(pN)] = {},
313
- urlParams[d(pN)].length = 0;
314
-
315
- if (aN)
316
- urlParams[d(pN)][d(aN)] = d(e[2]);
317
- else
318
- Array.prototype.push.call(urlParams[d(pN)], d(e[2]));
319
-
320
- }
321
- }
322
- })();
323
-
324
- /* Set Param Cookies */
325
- for (var k in urlParams) {
326
- /* account for wordpress media uploader bug */
327
- if (k == 'action') {
328
- continue;
329
- }
330
-
331
- if (typeof urlParams[k] == "object") {
332
- for (var k2 in urlParams[k])
333
- this.createCookie(k2, urlParams[k][k2], 30);
334
- } else {
335
- this.createCookie(k, urlParams[k], 30);
336
- }
337
- }
338
- /* Set Param LocalStorage */
339
- if (storageSupported) {
340
- var pastParams = _inbound.totalStorage('inbound_url_params') || {};
341
- var params = this.mergeObjs(pastParams, urlParams);
342
- _inbound.totalStorage('inbound_url_params', params); // store cookie data
343
- }
344
-
345
- var options = {
346
- 'option1': 'yo',
347
- 'option2': 'woooo'
348
- };
349
-
350
- _inbound.trigger('url_parameters', urlParams, options);
351
-
352
- },
353
- getAllUrlParams: function() {
354
- var get_params = {};
355
- if (storageSupported) {
356
- var get_params = _inbound.totalStorage('inbound_url_params');
357
- }
358
- return get_params;
359
- },
360
- /* Get url param */
361
- getParameterVal: function(name, string) {
362
- return (RegExp(name + '=' + '(.+?)(&|$)').exec(string) || [, false])[1];
363
- },
364
- // Check local storage
365
- // provate browsing safari fix https://github.com/marcuswestin/store.js/issues/42#issuecomment-25274685
366
- checkLocalStorage: function() {
367
- if ('localStorage' in window) {
368
- try {
369
- ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
370
- if (typeof ls == 'undefined' || typeof window.JSON == 'undefined') {
371
- storageSupported = false;
372
- } else {
373
- storageSupported = true;
374
- }
375
-
376
- } catch (err) {
377
- storageSupported = false;
378
- }
379
- }
380
- return storageSupported;
381
- /* http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
382
- var hasStorage;
383
- hasStorage = function() {
384
- var mod, result;
385
- try {
386
- mod = new Date;
387
- localStorage.setItem(mod, mod.toString());
388
- result = localStorage.getItem(mod) === mod.toString();
389
- localStorage.removeItem(mod);
390
- return result;
391
- } catch (_error) {}
392
- };
393
- */
394
- },
395
- // http://stackoverflow.com/questions/4391575/how-to-find-the-size-of-localstorage
396
- showLocalStorageSize: function() {
397
- function stringSizeBytes(str) {
398
- return str.length * 2;
399
- }
400
-
401
- function toMB(bytes) {
402
- return bytes / 1024 / 1024;
403
- }
404
-
405
- function toSize(key) {
406
- return {
407
- name: key,
408
- size: stringSizeBytes(localStorage[key])
409
- };
410
- }
411
-
412
- function toSizeMB(info) {
413
- info.size = toMB(info.size).toFixed(2) + ' MB';
414
- return info;
415
- }
416
-
417
- var sizes = Object.keys(localStorage).map(toSize).map(toSizeMB);
418
-
419
- console.table(sizes);
420
- },
421
- /* Add days to datetime */
422
- addDays: function(myDate, days) {
423
- return new Date(myDate.getTime() + days * 24 * 60 * 60 * 1000);
424
- },
425
- GetDate: function() {
426
- var timeNow = new Date(),
427
- d = timeNow.getDate(),
428
- dPre = (d < 10) ? "0" : "",
429
- y = timeNow.getFullYear(),
430
- h = timeNow.getHours(),
431
- hPre = (h < 10) ? "0" : "",
432
- min = timeNow.getMinutes(),
433
- minPre = (min < 10) ? "0" : "",
434
- sec = timeNow.getSeconds(),
435
- secPre = (sec < 10) ? "0" : "",
436
- m = timeNow.getMonth() + 1,
437
- mPre = (m < 10) ? "0" : "";
438
-
439
- var datetime = y + '/' + mPre + m + "/" + dPre + d + " " + hPre + h + ":" + minPre + min + ":" + secPre + sec;
440
- /* format 2014/11/13 18:22:02 */
441
- return datetime;
442
- },
443
- /* Set Expiration Date of Session Logging. LEGACY Not in Use */
444
- SetSessionTimeout: function() {
445
- var session = this.readCookie("lead_session_expire");
446
- //console.log(session_check);
447
- if (!session) {
448
- //_inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
449
- } else {
450
- //_inbound.trigger('session_resume'); // trigger 'inbound_analytics_session_active'
451
- }
452
- var d = new Date();
453
- d.setTime(d.getTime() + 30 * 60 * 1000);
454
-
455
- this.createCookie("lead_session_expire", true, d); // Set cookie on page load
456
-
457
- },
458
- storeReferralData: function() {
459
- //console.log(expire_time);
460
- var d = new Date(),
461
- referrer = document.referrer || "Direct Traffic",
462
- referrer_cookie = _inbound.Utils.readCookie("inbound_referral_site"),
463
- original_src = _inbound.totalStorage('inbound_original_referral');
464
-
465
- d.setTime(d.getTime() + 30 * 60 * 1000);
466
-
467
- if (!referrer_cookie) {
468
- this.createCookie("inbound_referral_site", referrer, d);
469
- }
470
- if (!original_src) {
471
- _inbound.totalStorage('inbound_original_referral', original_src);
472
- }
473
- },
474
- CreateUID: function(length) {
475
- var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split(''),
476
- str = '';
477
- if (!length) {
478
- length = Math.floor(Math.random() * chars.length);
479
- }
480
- for (var i = 0; i < length; i++) {
481
- str += chars[Math.floor(Math.random() * chars.length)];
482
- }
483
- return str;
484
- },
485
- generateGUID: function(a) {
486
- return a ? (a ^ 16 * Math.random() >> a / 4).toString(16) : ([1E7] + -1E3 + -4E3 + -8E3 + -1E11).replace(/[018]/g, guid);
487
- },
488
- SetUID: function(leadUID) {
489
- /* Set Lead UID */
490
- if (!this.readCookie("wp_lead_uid")) {
491
- var wp_lead_uid = leadUID || this.CreateUID(35);
492
- this.createCookie("wp_lead_uid", wp_lead_uid);
493
- }
494
- },
495
- /* Count number of session visits */
496
- countProperties: function(obj) {
497
- var count = 0;
498
- for (var prop in obj) {
499
- if (obj.hasOwnProperty(prop)) {
500
- ++count;
501
- }
502
- }
503
- return count;
504
- },
505
- mergeObjs: function(obj1, obj2) {
506
- var obj3 = {};
507
- for (var attrname in obj1) {
508
- obj3[attrname] = obj1[attrname];
509
- }
510
- for (var attrname in obj2) {
511
- obj3[attrname] = obj2[attrname];
512
- }
513
- return obj3;
514
- },
515
- hasClass: function(className, el) {
516
- var hasClass;
517
- if ('classList' in document.documentElement) {
518
- var hasClass = el.classList.contains(className);
519
- } else {
520
- var hasClass = new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); /* IE Polyfill */
521
- }
522
- return hasClass;
523
- },
524
- addClass: function(className, el) {
525
- if ('classList' in document.documentElement) {
526
- el.classList.add(className);
527
- } else {
528
- if (!this.hasClass(el, className)) {
529
- el.className += (el.className ? ' ' : '') + className;
530
- }
531
- }
532
- },
533
- removeClass: function(className, el) {
534
- if ('classList' in document.documentElement) {
535
- el.classList.remove(className);
536
- } else {
537
- if (this.hasClass(el, className)) {
538
- el.className = el.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), '');
539
- }
540
- }
541
- },
542
- removeElement: function(el) {
543
- el.parentNode.removeChild(el);
544
- },
545
- trim: function(s) {
546
- s = s.replace(/(^\s*)|(\s*$)/gi, "");
547
- s = s.replace(/[ ]{2,}/gi, " ");
548
- s = s.replace(/\n /, "\n");
549
- return s;
550
- },
551
- ajaxPolyFill: function() {
552
- if (typeof XMLHttpRequest !== 'undefined') {
553
- return new XMLHttpRequest();
554
- }
555
- var versions = [
556
- "MSXML2.XmlHttp.5.0",
557
- "MSXML2.XmlHttp.4.0",
558
- "MSXML2.XmlHttp.3.0",
559
- "MSXML2.XmlHttp.2.0",
560
- "Microsoft.XmlHttp"
561
- ];
562
-
563
- var xhr;
564
- for (var i = 0; i < versions.length; i++) {
565
- try {
566
- xhr = new ActiveXObject(versions[i]);
567
- break;
568
- } catch (e) {}
569
- }
570
- return xhr;
571
- },
572
- ajaxSendData: function(url, callback, method, data, sync) {
573
- var x = this.ajaxPolyFill();
574
- /* timeout for safari idiocy */
575
- setTimeout(function() {
576
- x.open(method, url, true);
577
- x.onreadystatechange = function() {
578
- if (x.readyState == 4) {
579
- callback(x.responseText)
580
- }
581
- };
582
- if (method == 'POST') {
583
- x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
584
- }
585
- x.send(data);
586
- }, 100);
587
- },
588
- ajaxGet: function(url, data, callback, sync) {
589
- var query = [];
590
- for (var key in data) {
591
- query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
592
- }
593
- this.ajaxSendData(url + '?' + query.join('&'), callback, 'GET', null, sync)
594
- },
595
- ajaxPost: function(url, data, callback, sync) {
596
- var query = [];
597
- for (var key in data) {
598
- query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
599
- }
600
- this.ajaxSendData(url, callback, 'POST', query.join('&'), sync)
601
- },
602
- /**
603
- * @param {string} event
604
- * @param {(Object|null)} properties
605
- * @param {(Function|null)} callback
606
- */
607
- sendEvent: function(event, properties, callback) {
608
- properties = properties || {};
609
- async = true;
610
- var cookieData = getCookie(); /* get cookie data */
611
- if (cookieData) {
612
- var key;
613
- for (key in cookieData) {
614
- properties[key] = cookieData[key];
615
- }
616
- }
617
- if (!properties.id) {
618
- properties.id = getId();
619
- }
620
- var props = {
621
- e: event,
622
- t: (new Date()).toISOString(),
623
- kv: properties
624
- };
625
- var path = settings.api_host + '/track?data=' + encodeURIComponent(JSON.stringify(props));
626
- if (corsEnabled) {
627
- /* CORS */
628
- var xhr = new XMLHttpRequest();
629
- xhr.open('GET', path, async);
630
- xhr.withCredentials = async;
631
- xhr.send(null);
632
- } else {
633
- /* jsonP */
634
- var el = document.createElement('script');
635
- el.type = 'text/javascript';
636
- el.async = async;
637
- el.defer = async;
638
- el.src = path;
639
- var insertAt = document.getElementsByTagName('script')[0];
640
- insertAt.parentNode.insertBefore(el, insertAt);
641
- }
642
- return action(callback), self;
643
- },
644
- domReady: function(win, fn) {
645
-
646
- var done = false,
647
- top = true,
648
-
649
- doc = win.document,
650
- root = doc.documentElement,
651
-
652
- add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
653
- rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
654
- pre = doc.addEventListener ? '' : 'on',
655
-
656
- init = function(e) {
657
- if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
658
- (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
659
- if (!done && (done = true)) fn.call(win, e.type || e);
660
- },
661
-
662
- poll = function() {
663
- try {
664
- root.doScroll('left');
665
- } catch (e) {
666
- setTimeout(poll, 50);
667
- return;
668
- }
669
- init('poll');
670
- };
671
-
672
- if (doc.readyState == 'complete') {
673
-
674
- fn.call(win, 'lazy');
675
-
676
- } else {
677
- if (doc.createEventObject && root.doScroll) {
678
- try {
679
- top = !win.frameElement;
680
- } catch (e) {}
681
- if (top) poll();
682
- }
683
- doc[add](pre + 'DOMContentLoaded', init, false);
684
- doc[add](pre + 'readystatechange', init, false);
685
- win[add](pre + 'load', init, false);
686
- }
687
-
688
- },
689
- /* Cross-browser event listening */
690
- addListener: function(element, eventName, listener) {
691
- if (!element) {
692
- return;
693
- }
694
- //console.log(eventName);
695
- //console.log(listener);
696
- if (element.addEventListener) {
697
- element.addEventListener(eventName, listener, false);
698
- } else if (element.attachEvent) {
699
- element.attachEvent("on" + eventName, listener);
700
- } else {
701
- element['on' + eventName] = listener;
702
- }
703
- },
704
- removeListener: function(element, eventName, listener) {
705
-
706
- if (element.removeEventListener) {
707
- element.removeEventListener(eventName, listener, false);
708
- } else if (element.detachEvent) {
709
- element.detachEvent("on" + eventName, listener);
710
- } else {
711
- element["on" + eventName] = null;
712
- }
713
- },
714
- /*
715
- * Throttle function borrowed from:
716
- * Underscore.js 1.5.2
717
- * http://underscorejs.org
718
- * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
719
- * Underscore may be freely distributed under the MIT license.
720
- */
721
- throttle: function(func, wait) {
722
- var context, args, result;
723
- var timeout = null;
724
- var previous = 0;
725
- var later = function() {
726
- previous = new Date;
727
- timeout = null;
728
- result = func.apply(context, args);
729
- };
730
- return function() {
731
- var now = new Date;
732
- if (!previous) previous = now;
733
- var remaining = wait - (now - previous);
734
- context = this;
735
- args = arguments;
736
- if (remaining <= 0) {
737
- clearTimeout(timeout);
738
- timeout = null;
739
- previous = now;
740
- result = func.apply(context, args);
741
- } else if (!timeout) {
742
- timeout = setTimeout(later, remaining);
743
- }
744
- return result;
745
- };
746
- },
747
- /*
748
- * Determine which version of GA is being used
749
- * "ga", "_gaq", and "dataLayer" are the possible globals
750
- */
751
- checkTypeofGA: function() {
752
- if (typeof ga === "function") {
753
- universalGA = true;
754
- }
755
-
756
- if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
757
- classicGA = true;
758
- }
759
-
760
- if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
761
- googleTagManager = true;
762
- }
763
-
764
- },
765
- /**
766
- * Caches user's search data in the browser until they can be saved to the database
767
- */
768
- cacheSearchData: function(searchData, form) {
769
-
770
- if(storageSupported){
771
- //store the searches in the local storage
772
- var stored = _inbound.totalStorage.getItem('inbound_search_storage');
773
- if(stored){
774
- //if there are stored searches, put the new one in the first index
775
- stored.unshift(searchData);
776
- _inbound.totalStorage.setItem('inbound_search_storage', stored);
777
- }else{
778
- //if there aren't any searches stored, save the current search
779
- var store = [searchData];
780
- _inbound.totalStorage.setItem('inbound_search_storage', store);
781
- }
782
- }else{
783
- //if local storage is not possible, store the data in a cookie
784
- var new_search = JSON.stringify(searchData),
785
- stored_searches = this.readCookie('inbound_search_storage');
786
-
787
- if(stored_searches){
788
- //add the old searches to the new one
789
- new_search += ('SPLIT-TOKEN' + stored_searches);
790
- }
791
- this.createCookie('inbound_search_storage', new_search, '180');
792
- }
793
-
794
- _inbound.Forms.releaseFormSubmit(form);
795
- },
796
- /**
797
- * Stores search data to the database on page load.
798
- * If successful, it erases the cached searches from the user's browser
799
- */
800
- storeSearchData: function(){
801
-
802
- /*if there isn't a lead id or the nonce isn't set, don't try to store the data*/
803
- if(!inbound_settings.wp_lead_data.lead_id || !inbound_settings.wp_lead_data.lead_nonce){
804
- return;
805
- }
806
-
807
- var dataToSend = [],
808
- localStorageData = _inbound.totalStorage.getItem('inbound_search_storage'),
809
- cookieStorageData = this.readCookie('inbound_search_storage');
810
-
811
- /*if nothing is stored, exit*/
812
- if(!localStorageData && !cookieStorageData){
813
- return;
814
- }
815
-
816
- /*if set, add the cookie search data to the data to send*/
817
- if(cookieStorageData){
818
- cookieStorageData = cookieStorageData.split('SPLIT-TOKEN');
819
-
820
- for(var i in cookieStorageData){
821
- //console.log(cookieStorageData[i]);
822
- dataToSend.push(JSON.parse(cookieStorageData[i]));
823
- }
824
- }
825
-
826
- /*if set, add the locally stored data to the data to send*/
827
- if(localStorageData){
828
- dataToSend = dataToSend.concat(localStorageData);
829
- }
830
-
831
- dataToSend.sort(function(a, b){ return a.timestamp - b.timestamp; });
832
-
833
- dataToSend = encodeURIComponent(JSON.stringify(dataToSend));
834
-
835
- var package = {'action' : 'inbound_search_store', 'data' : dataToSend, 'nonce' : inbound_settings.wp_lead_data.lead_nonce, 'lead_id' : inbound_settings.wp_lead_data.lead_id };
836
-
837
- callback = function(status){
838
- if(status){ status = JSON.parse(status); }
839
-
840
- if(status.success){
841
- //log the success!
842
- console.log(status.success);
843
- //erase the stored data
844
- _inbound.Utils.eraseCookie('inbound_search_storage');
845
- _inbound.totalStorage.deleteItem('inbound_search_storage');
846
- }
847
-
848
- if(status.error){
849
- console.log(status.error);
850
- }
851
- };
852
- this.ajaxPost(inbound_settings.admin_url, package, callback);
853
- }
854
- };
855
-
856
- return _inbound;
857
-
858
- })(_inbound || {});
1
+ /**
2
+ * # _inbound UTILS
3
+ *
4
+ * This file contains all of the utility functions used by analytics
5
+ *
6
+ * @contributor David Wells <david@inboundnow.com>
7
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
8
+ * @version 0.0.2
9
+ */
10
+
11
+ var _inboundUtils = (function(_inbound) {
12
+
13
+ var storageSupported,
14
+ corsEnabled = window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest(),
15
+ toString = Object.prototype.toString,
16
+ currentPage = ('https:' == location.protocol ? 'https://' : 'http://') + location.hostname + location.pathname.replace(/\/$/, "");
17
+
18
+ var settings = {
19
+ api_host: currentPage,
20
+ track_pageview: true,
21
+ track_links_timeout: 300,
22
+ cookie_name: '_sp',
23
+ cookie_expiration: 365,
24
+ cookie_domain: (host = location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i)) ? host[0] : ''
25
+ };
26
+
27
+ _inbound.Utils = {
28
+ init: function() {
29
+
30
+ this.polyFills();
31
+ this.checkLocalStorage();
32
+ this.SetUID();
33
+ this.storeReferralData();
34
+
35
+ },
36
+ /*! http://stackoverflow.com/questions/951791/javascript-global-error-handling */
37
+ /* Polyfills for missing browser functionality */
38
+ polyFills: function() {
39
+ /* Console.log fix for old browsers */
40
+ if (!window.console) {
41
+ window.console = {};
42
+ }
43
+ var m = [
44
+ "log", "info", "warn", "error", "debug", "trace", "dir", "group",
45
+ "groupCollapsed", "groupEnd", "time", "timeEnd", "profile", "profileEnd",
46
+ "dirxml", "assert", "count", "markTimeline", "timeStamp", "clear"
47
+ ];
48
+ // define undefined methods as noops to prevent errors
49
+ for (var i = 0; i < m.length; i++) {
50
+ if (!window.console[m[i]]) {
51
+ window.console[m[i]] = function() {};
52
+ }
53
+ }
54
+ /* Event trigger polyfill for IE9 and 10
55
+ (function() {
56
+ function CustomEvent(event, params) {
57
+ params = params || {
58
+ bubbles: false,
59
+ cancelable: false,
60
+ detail: undefined
61
+ };
62
+ var evt = document.createEvent('CustomEvent');
63
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
64
+ return evt;
65
+ }
66
+
67
+ CustomEvent.prototype = window.Event.prototype;
68
+
69
+ window.CustomEvent = CustomEvent;
70
+ })();*/
71
+
72
+ /*\
73
+ |*| Polyfill Date.toISOString
74
+ \*/
75
+ if (!Date.prototype.toISOString) {
76
+ (function() {
77
+ /**
78
+ * @param {number} text
79
+ * @returns {?}
80
+ */
81
+ function pad(text) {
82
+ /** @type {string} */
83
+ var code = String(text);
84
+ return 1 === code.length && (code = '0' + code), code;
85
+ }
86
+ /**
87
+ * @returns {string}
88
+ */
89
+ Date.prototype.toISOString = function() {
90
+ return this.getUTCFullYear() + '-' + pad(this.getUTCMonth() + 1) + '-' + pad(this.getUTCDate()) + 'T' + pad(this.getUTCHours()) + ':' + pad(this.getUTCMinutes()) + ':' + pad(this.getUTCSeconds()) + '.' + String((this.getUTCMilliseconds() / 1E3).toFixed(3)).slice(2, 5) + 'Z';
91
+ };
92
+ })();
93
+ }
94
+
95
+ /* custom event for ie8+ https://gist.github.com/WebReflection/6693661 */
96
+ try {
97
+ new CustomEvent('?');
98
+ } catch (o_O) {
99
+ /*!(C) Andrea Giammarchi -- WTFPL License*/
100
+ this.CustomEvent = function(
101
+ eventName,
102
+ defaultInitDict
103
+ ) {
104
+
105
+ // the infamous substitute
106
+ function CustomEvent(type, eventInitDict) {
107
+ var event = document.createEvent(eventName);
108
+ if (type !== null) {
109
+ initCustomEvent.call(
110
+ event,
111
+ type, (eventInitDict || (
112
+ // if falsy we can just use defaults
113
+ eventInitDict = defaultInitDict
114
+ )).bubbles,
115
+ eventInitDict.cancelable,
116
+ eventInitDict.detail
117
+ );
118
+ } else {
119
+ // no need to put the expando property otherwise
120
+ // since an event cannot be initialized twice
121
+ // previous case is the most common one anyway
122
+ // but if we end up here ... there it goes
123
+ event.initCustomEvent = initCustomEvent;
124
+ }
125
+ return event;
126
+ }
127
+
128
+ // borrowed or attached at runtime
129
+ function initCustomEvent(
130
+ type, bubbles, cancelable, detail
131
+ ) {
132
+ this['init' + eventName](type, bubbles, cancelable, detail);
133
+ 'detail' in this || (this.detail = detail);
134
+ }
135
+
136
+ // that's it
137
+ return CustomEvent;
138
+ }(
139
+ // is this IE9 or IE10 ?
140
+ // where CustomEvent is there
141
+ // but not usable as construtor ?
142
+ this.CustomEvent ?
143
+ // use the CustomEvent interface in such case
144
+ 'CustomEvent' : 'Event',
145
+ // otherwise the common compatible one
146
+ {
147
+ bubbles: false,
148
+ cancelable: false,
149
+ detail: null
150
+ }
151
+ );
152
+ }
153
+ /* querySelectorAll polyfill for ie7+ */
154
+ if (!document.querySelectorAll) {
155
+ document.querySelectorAll = function(selectors) {
156
+ var style = document.createElement('style'),
157
+ elements = [],
158
+ element;
159
+ document.documentElement.firstChild.appendChild(style);
160
+ document._qsa = [];
161
+
162
+ style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
163
+ window.scrollBy(0, 0);
164
+ style.parentNode.removeChild(style);
165
+
166
+ while (document._qsa.length) {
167
+ element = document._qsa.shift();
168
+ element.style.removeAttribute('x-qsa');
169
+ elements.push(element);
170
+ }
171
+ document._qsa = null;
172
+ return elements;
173
+ };
174
+ }
175
+
176
+ if (!document.querySelector) {
177
+ document.querySelector = function(selectors) {
178
+ var elements = document.querySelectorAll(selectors);
179
+ return (elements.length) ? elements[0] : null;
180
+ };
181
+ }
182
+ /* Innertext shim for firefox https://github.com/duckinator/innerText-polyfill/blob/master/innertext.js */
183
+ if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
184
+ HTMLElement.prototype.__defineGetter__("innerText", function() {
185
+ var selection = window.getSelection(),
186
+ ranges = [],
187
+ str;
188
+
189
+ // Save existing selections.
190
+ for (var i = 0; i < selection.rangeCount; i++) {
191
+ ranges[i] = selection.getRangeAt(i);
192
+ }
193
+
194
+ // Deselect everything.
195
+ selection.removeAllRanges();
196
+
197
+ // Select `el` and all child nodes.
198
+ // 'this' is the element .innerText got called on
199
+ selection.selectAllChildren(this);
200
+
201
+ // Get the string representation of the selected nodes.
202
+ str = selection.toString();
203
+
204
+ // Deselect everything. Again.
205
+ selection.removeAllRanges();
206
+
207
+ // Restore all formerly existing selections.
208
+ for (var i = 0; i < ranges.length; i++) {
209
+ selection.addRange(ranges[i]);
210
+ }
211
+
212
+ // Oh look, this is what we wanted.
213
+ // String representation of the element, close to as rendered.
214
+ return str;
215
+ })
216
+ }
217
+ },
218
+ /**
219
+ * Create cookie
220
+ *
221
+ * ```js
222
+ * // Creates cookie for 10 days
223
+ * _inbound.Utils.createCookie( 'cookie_name', 'value', 10 );
224
+ * ```
225
+ *
226
+ * @param {string} name Name of cookie
227
+ * @param {string} value Value of cookie
228
+ * @param {string} days Length of storage
229
+ */
230
+ createCookie: function(name, value, days) {
231
+ var expires = "";
232
+ if (days) {
233
+ var date = new Date();
234
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
235
+ expires = "; expires=" + date.toGMTString();
236
+ }
237
+ document.cookie = name + "=" + value + expires + "; path=/";
238
+ },
239
+ /**
240
+ * Read cookie value
241
+ *
242
+ * ```js
243
+ * var cookie = _inbound.Utils.readCookie( 'cookie_name' );
244
+ * console.log(cookie); // cookie value
245
+ * ```
246
+ * @param {string} name name of cookie
247
+ * @return {string} value of cookie
248
+ */
249
+ readCookie: function(name) {
250
+ var nameEQ = name + "=";
251
+ var ca = document.cookie.split(';');
252
+ for (var i = 0; i < ca.length; i++) {
253
+ var c = ca[i];
254
+ while (c.charAt(0) === ' ') {
255
+ c = c.substring(1, c.length);
256
+ }
257
+ if (c.indexOf(nameEQ) === 0) {
258
+ return c.substring(nameEQ.length, c.length);
259
+ }
260
+ }
261
+ return null;
262
+ },
263
+ /**
264
+ * Erase cookie
265
+ *
266
+ * ```js
267
+ * // usage:
268
+ * _inbound.Utils.eraseCookie( 'cookie_name' );
269
+ * // deletes 'cookie_name' value
270
+ * ```
271
+ * @param {string} name name of cookie
272
+ * @return {string} value of cookie
273
+ */
274
+ eraseCookie: function(name) {
275
+ this.createCookie(name, "", -1);
276
+ },
277
+ /* Get All Cookies */
278
+ getAllCookies: function() {
279
+ var cookies = {};
280
+ if (document.cookie && document.cookie !== '') {
281
+ var split = document.cookie.split(';');
282
+ for (var i = 0; i < split.length; i++) {
283
+ var name_value = split[i].split("=");
284
+ name_value[0] = name_value[0].replace(/^ /, '');
285
+ cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
286
+ }
287
+ }
288
+ _inbound.totalStorage('inbound_cookies', cookies); // store cookie data
289
+ return cookies;
290
+ },
291
+ /* Grab URL params and save */
292
+ setUrlParams: function() {
293
+ var urlParams = {};
294
+
295
+ (function() {
296
+ var e,
297
+ d = function(s) {
298
+ return decodeURIComponent(s).replace(/\+/g, " ");
299
+ },
300
+ q = window.location.search.substring(1),
301
+ r = /([^&=]+)=?([^&]*)/g;
302
+
303
+ while (e = r.exec(q)) {
304
+ if (e[1].indexOf("[") == "-1")
305
+ urlParams[d(e[1])] = d(e[2]);
306
+ else {
307
+ var b1 = e[1].indexOf("["),
308
+ aN = e[1].slice(b1 + 1, e[1].indexOf("]", b1)),
309
+ pN = d(e[1].slice(0, b1));
310
+
311
+ if (typeof urlParams[pN] != "object")
312
+ urlParams[d(pN)] = {},
313
+ urlParams[d(pN)].length = 0;
314
+
315
+ if (aN)
316
+ urlParams[d(pN)][d(aN)] = d(e[2]);
317
+ else
318
+ Array.prototype.push.call(urlParams[d(pN)], d(e[2]));
319
+
320
+ }
321
+ }
322
+ })();
323
+
324
+ /* Set Param Cookies */
325
+ for (var k in urlParams) {
326
+ /* account for wordpress media uploader bug */
327
+ if (k == 'action') {
328
+ continue;
329
+ }
330
+
331
+ if (typeof urlParams[k] == "object") {
332
+ for (var k2 in urlParams[k])
333
+ this.createCookie(k2, urlParams[k][k2], 30);
334
+ } else {
335
+ this.createCookie(k, urlParams[k], 30);
336
+ }
337
+ }
338
+ /* Set Param LocalStorage */
339
+ if (storageSupported) {
340
+ var pastParams = _inbound.totalStorage('inbound_url_params') || {};
341
+ var params = this.mergeObjs(pastParams, urlParams);
342
+ _inbound.totalStorage('inbound_url_params', params); // store cookie data
343
+ }
344
+
345
+ var options = {
346
+ 'option1': 'yo',
347
+ 'option2': 'woooo'
348
+ };
349
+
350
+ _inbound.trigger('url_parameters', urlParams, options);
351
+
352
+ },
353
+ getAllUrlParams: function() {
354
+ var get_params = {};
355
+ if (storageSupported) {
356
+ var get_params = _inbound.totalStorage('inbound_url_params');
357
+ }
358
+ return get_params;
359
+ },
360
+ /* Get url param */
361
+ getParameterVal: function(name, string) {
362
+ return (RegExp(name + '=' + '(.+?)(&|$)').exec(string) || [, false])[1];
363
+ },
364
+ // Check local storage
365
+ // provate browsing safari fix https://github.com/marcuswestin/store.js/issues/42#issuecomment-25274685
366
+ checkLocalStorage: function() {
367
+ if ('localStorage' in window) {
368
+ try {
369
+ ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
370
+ if (typeof ls == 'undefined' || typeof window.JSON == 'undefined') {
371
+ storageSupported = false;
372
+ } else {
373
+ storageSupported = true;
374
+ }
375
+
376
+ } catch (err) {
377
+ storageSupported = false;
378
+ }
379
+ }
380
+ return storageSupported;
381
+ /* http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
382
+ var hasStorage;
383
+ hasStorage = function() {
384
+ var mod, result;
385
+ try {
386
+ mod = new Date;
387
+ localStorage.setItem(mod, mod.toString());
388
+ result = localStorage.getItem(mod) === mod.toString();
389
+ localStorage.removeItem(mod);
390
+ return result;
391
+ } catch (_error) {}
392
+ };
393
+ */
394
+ },
395
+ // http://stackoverflow.com/questions/4391575/how-to-find-the-size-of-localstorage
396
+ showLocalStorageSize: function() {
397
+ function stringSizeBytes(str) {
398
+ return str.length * 2;
399
+ }
400
+
401
+ function toMB(bytes) {
402
+ return bytes / 1024 / 1024;
403
+ }
404
+
405
+ function toSize(key) {
406
+ return {
407
+ name: key,
408
+ size: stringSizeBytes(localStorage[key])
409
+ };
410
+ }
411
+
412
+ function toSizeMB(info) {
413
+ info.size = toMB(info.size).toFixed(2) + ' MB';
414
+ return info;
415
+ }
416
+
417
+ var sizes = Object.keys(localStorage).map(toSize).map(toSizeMB);
418
+
419
+ console.table(sizes);
420
+ },
421
+ /* Add days to datetime */
422
+ addDays: function(myDate, days) {
423
+ return new Date(myDate.getTime() + days * 24 * 60 * 60 * 1000);
424
+ },
425
+ GetDate: function() {
426
+ var timeNow = new Date(),
427
+ d = timeNow.getDate(),
428
+ dPre = (d < 10) ? "0" : "",
429
+ y = timeNow.getFullYear(),
430
+ h = timeNow.getHours(),
431
+ hPre = (h < 10) ? "0" : "",
432
+ min = timeNow.getMinutes(),
433
+ minPre = (min < 10) ? "0" : "",
434
+ sec = timeNow.getSeconds(),
435
+ secPre = (sec < 10) ? "0" : "",
436
+ m = timeNow.getMonth() + 1,
437
+ mPre = (m < 10) ? "0" : "";
438
+
439
+ var datetime = y + '/' + mPre + m + "/" + dPre + d + " " + hPre + h + ":" + minPre + min + ":" + secPre + sec;
440
+ /* format 2014/11/13 18:22:02 */
441
+ return datetime;
442
+ },
443
+ /* Set Expiration Date of Session Logging. LEGACY Not in Use */
444
+ SetSessionTimeout: function() {
445
+ var session = this.readCookie("lead_session_expire");
446
+ //console.log(session_check);
447
+ if (!session) {
448
+ //_inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
449
+ } else {
450
+ //_inbound.trigger('session_resume'); // trigger 'inbound_analytics_session_active'
451
+ }
452
+ var d = new Date();
453
+ d.setTime(d.getTime() + 30 * 60 * 1000);
454
+
455
+ this.createCookie("lead_session_expire", true, d); // Set cookie on page load
456
+
457
+ },
458
+ storeReferralData: function() {
459
+ //console.log(expire_time);
460
+ var d = new Date(),
461
+ referrer = document.referrer || "Direct Traffic",
462
+ referrer_cookie = _inbound.Utils.readCookie("inbound_referral_site"),
463
+ original_src = _inbound.totalStorage('inbound_original_referral');
464
+
465
+ d.setTime(d.getTime() + 30 * 60 * 1000);
466
+
467
+ if (!referrer_cookie) {
468
+ this.createCookie("inbound_referral_site", referrer, d);
469
+ }
470
+ if (!original_src) {
471
+ _inbound.totalStorage('inbound_original_referral', original_src);
472
+ }
473
+ },
474
+ CreateUID: function(length) {
475
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split(''),
476
+ str = '';
477
+ if (!length) {
478
+ length = Math.floor(Math.random() * chars.length);
479
+ }
480
+ for (var i = 0; i < length; i++) {
481
+ str += chars[Math.floor(Math.random() * chars.length)];
482
+ }
483
+ return str;
484
+ },
485
+ generateGUID: function(a) {
486
+ return a ? (a ^ 16 * Math.random() >> a / 4).toString(16) : ([1E7] + -1E3 + -4E3 + -8E3 + -1E11).replace(/[018]/g, guid);
487
+ },
488
+ SetUID: function(leadUID) {
489
+ /* Set Lead UID */
490
+ if (!this.readCookie("wp_lead_uid")) {
491
+ var wp_lead_uid = leadUID || this.CreateUID(35);
492
+ this.createCookie("wp_lead_uid", wp_lead_uid);
493
+ }
494
+ },
495
+ /* Count number of session visits */
496
+ countProperties: function(obj) {
497
+ var count = 0;
498
+ for (var prop in obj) {
499
+ if (obj.hasOwnProperty(prop)) {
500
+ ++count;
501
+ }
502
+ }
503
+ return count;
504
+ },
505
+ mergeObjs: function(obj1, obj2) {
506
+ var obj3 = {};
507
+ for (var attrname in obj1) {
508
+ obj3[attrname] = obj1[attrname];
509
+ }
510
+ for (var attrname in obj2) {
511
+ obj3[attrname] = obj2[attrname];
512
+ }
513
+ return obj3;
514
+ },
515
+ hasClass: function(className, el) {
516
+ var hasClass;
517
+ if ('classList' in document.documentElement) {
518
+ var hasClass = el.classList.contains(className);
519
+ } else {
520
+ var hasClass = new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); /* IE Polyfill */
521
+ }
522
+ return hasClass;
523
+ },
524
+ addClass: function(className, el) {
525
+ if ('classList' in document.documentElement) {
526
+ el.classList.add(className);
527
+ } else {
528
+ if (!this.hasClass(el, className)) {
529
+ el.className += (el.className ? ' ' : '') + className;
530
+ }
531
+ }
532
+ },
533
+ removeClass: function(className, el) {
534
+ if ('classList' in document.documentElement) {
535
+ el.classList.remove(className);
536
+ } else {
537
+ if (this.hasClass(el, className)) {
538
+ el.className = el.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), '');
539
+ }
540
+ }
541
+ },
542
+ removeElement: function(el) {
543
+ el.parentNode.removeChild(el);
544
+ },
545
+ trim: function(s) {
546
+ s = s.replace(/(^\s*)|(\s*$)/gi, "");
547
+ s = s.replace(/[ ]{2,}/gi, " ");
548
+ s = s.replace(/\n /, "\n");
549
+ return s;
550
+ },
551
+ ajaxPolyFill: function() {
552
+ if (typeof XMLHttpRequest !== 'undefined') {
553
+ return new XMLHttpRequest();
554
+ }
555
+ var versions = [
556
+ "MSXML2.XmlHttp.5.0",
557
+ "MSXML2.XmlHttp.4.0",
558
+ "MSXML2.XmlHttp.3.0",
559
+ "MSXML2.XmlHttp.2.0",
560
+ "Microsoft.XmlHttp"
561
+ ];
562
+
563
+ var xhr;
564
+ for (var i = 0; i < versions.length; i++) {
565
+ try {
566
+ xhr = new ActiveXObject(versions[i]);
567
+ break;
568
+ } catch (e) {}
569
+ }
570
+ return xhr;
571
+ },
572
+ ajaxSendData: function(url, callback, method, data, sync) {
573
+ var x = this.ajaxPolyFill();
574
+ /* timeout for safari idiocy */
575
+ setTimeout(function() {
576
+ x.open(method, url, true);
577
+ x.onreadystatechange = function() {
578
+ if (x.readyState == 4) {
579
+ callback(x.responseText)
580
+ }
581
+ };
582
+ if (method == 'POST') {
583
+ x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
584
+ }
585
+ x.send(data);
586
+ }, 100);
587
+ },
588
+ ajaxGet: function(url, data, callback, sync) {
589
+ var query = [];
590
+ for (var key in data) {
591
+ query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
592
+ }
593
+ this.ajaxSendData(url + '?' + query.join('&'), callback, 'GET', null, sync)
594
+ },
595
+ ajaxPost: function(url, data, callback, sync) {
596
+ var query = [];
597
+ for (var key in data) {
598
+ query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
599
+ }
600
+ this.ajaxSendData(url, callback, 'POST', query.join('&'), sync)
601
+ },
602
+ /**
603
+ * @param {string} event
604
+ * @param {(Object|null)} properties
605
+ * @param {(Function|null)} callback
606
+ */
607
+ sendEvent: function(event, properties, callback) {
608
+ properties = properties || {};
609
+ async = true;
610
+ var cookieData = getCookie(); /* get cookie data */
611
+ if (cookieData) {
612
+ var key;
613
+ for (key in cookieData) {
614
+ properties[key] = cookieData[key];
615
+ }
616
+ }
617
+ if (!properties.id) {
618
+ properties.id = getId();
619
+ }
620
+ var props = {
621
+ e: event,
622
+ t: (new Date()).toISOString(),
623
+ kv: properties
624
+ };
625
+ var path = settings.api_host + '/track?data=' + encodeURIComponent(JSON.stringify(props));
626
+ if (corsEnabled) {
627
+ /* CORS */
628
+ var xhr = new XMLHttpRequest();
629
+ xhr.open('GET', path, async);
630
+ xhr.withCredentials = async;
631
+ xhr.send(null);
632
+ } else {
633
+ /* jsonP */
634
+ var el = document.createElement('script');
635
+ el.type = 'text/javascript';
636
+ el.async = async;
637
+ el.defer = async;
638
+ el.src = path;
639
+ var insertAt = document.getElementsByTagName('script')[0];
640
+ insertAt.parentNode.insertBefore(el, insertAt);
641
+ }
642
+ return action(callback), self;
643
+ },
644
+ domReady: function(win, fn) {
645
+
646
+ var done = false,
647
+ top = true,
648
+
649
+ doc = win.document,
650
+ root = doc.documentElement,
651
+
652
+ add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
653
+ rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
654
+ pre = doc.addEventListener ? '' : 'on',
655
+
656
+ init = function(e) {
657
+ if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
658
+ (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
659
+ if (!done && (done = true)) fn.call(win, e.type || e);
660
+ },
661
+
662
+ poll = function() {
663
+ try {
664
+ root.doScroll('left');
665
+ } catch (e) {
666
+ setTimeout(poll, 50);
667
+ return;
668
+ }
669
+ init('poll');
670
+ };
671
+
672
+ if (doc.readyState == 'complete') {
673
+
674
+ fn.call(win, 'lazy');
675
+
676
+ } else {
677
+ if (doc.createEventObject && root.doScroll) {
678
+ try {
679
+ top = !win.frameElement;
680
+ } catch (e) {}
681
+ if (top) poll();
682
+ }
683
+ doc[add](pre + 'DOMContentLoaded', init, false);
684
+ doc[add](pre + 'readystatechange', init, false);
685
+ win[add](pre + 'load', init, false);
686
+ }
687
+
688
+ },
689
+ /* Cross-browser event listening */
690
+ addListener: function(element, eventName, listener) {
691
+ if (!element) {
692
+ return;
693
+ }
694
+ //console.log(eventName);
695
+ //console.log(listener);
696
+ if (element.addEventListener) {
697
+ element.addEventListener(eventName, listener, false);
698
+ } else if (element.attachEvent) {
699
+ element.attachEvent("on" + eventName, listener);
700
+ } else {
701
+ element['on' + eventName] = listener;
702
+ }
703
+ },
704
+ removeListener: function(element, eventName, listener) {
705
+
706
+ if (element.removeEventListener) {
707
+ element.removeEventListener(eventName, listener, false);
708
+ } else if (element.detachEvent) {
709
+ element.detachEvent("on" + eventName, listener);
710
+ } else {
711
+ element["on" + eventName] = null;
712
+ }
713
+ },
714
+ /*
715
+ * Throttle function borrowed from:
716
+ * Underscore.js 1.5.2
717
+ * http://underscorejs.org
718
+ * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
719
+ * Underscore may be freely distributed under the MIT license.
720
+ */
721
+ throttle: function(func, wait) {
722
+ var context, args, result;
723
+ var timeout = null;
724
+ var previous = 0;
725
+ var later = function() {
726
+ previous = new Date;
727
+ timeout = null;
728
+ result = func.apply(context, args);
729
+ };
730
+ return function() {
731
+ var now = new Date;
732
+ if (!previous) previous = now;
733
+ var remaining = wait - (now - previous);
734
+ context = this;
735
+ args = arguments;
736
+ if (remaining <= 0) {
737
+ clearTimeout(timeout);
738
+ timeout = null;
739
+ previous = now;
740
+ result = func.apply(context, args);
741
+ } else if (!timeout) {
742
+ timeout = setTimeout(later, remaining);
743
+ }
744
+ return result;
745
+ };
746
+ },
747
+ /*
748
+ * Determine which version of GA is being used
749
+ * "ga", "_gaq", and "dataLayer" are the possible globals
750
+ */
751
+ checkTypeofGA: function() {
752
+ if (typeof ga === "function") {
753
+ universalGA = true;
754
+ }
755
+
756
+ if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
757
+ classicGA = true;
758
+ }
759
+
760
+ if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
761
+ googleTagManager = true;
762
+ }
763
+
764
+ },
765
+ /**
766
+ * Caches user's search data in the browser until they can be saved to the database
767
+ */
768
+ cacheSearchData: function(searchData, form) {
769
+
770
+ if(storageSupported){
771
+ //store the searches in the local storage
772
+ var stored = _inbound.totalStorage.getItem('inbound_search_storage');
773
+ if(stored){
774
+ //if there are stored searches, put the new one in the first index
775
+ stored.unshift(searchData);
776
+ _inbound.totalStorage.setItem('inbound_search_storage', stored);
777
+ }else{
778
+ //if there aren't any searches stored, save the current search
779
+ var store = [searchData];
780
+ _inbound.totalStorage.setItem('inbound_search_storage', store);
781
+ }
782
+ }else{
783
+ //if local storage is not possible, store the data in a cookie
784
+ var new_search = JSON.stringify(searchData),
785
+ stored_searches = this.readCookie('inbound_search_storage');
786
+
787
+ if(stored_searches){
788
+ //add the old searches to the new one
789
+ new_search += ('SPLIT-TOKEN' + stored_searches);
790
+ }
791
+ this.createCookie('inbound_search_storage', new_search, '180');
792
+ }
793
+
794
+ _inbound.Forms.releaseFormSubmit(form);
795
+ },
796
+ /**
797
+ * Stores search data to the database on page load.
798
+ * If successful, it erases the cached searches from the user's browser
799
+ */
800
+ storeSearchData: function(){
801
+
802
+ /*if there isn't a lead id or the nonce isn't set, don't try to store the data*/
803
+ if(!inbound_settings.wp_lead_data.lead_id || !inbound_settings.wp_lead_data.lead_nonce){
804
+ return;
805
+ }
806
+
807
+ var dataToSend = [],
808
+ localStorageData = _inbound.totalStorage.getItem('inbound_search_storage'),
809
+ cookieStorageData = this.readCookie('inbound_search_storage');
810
+
811
+ /*if nothing is stored, exit*/
812
+ if(!localStorageData && !cookieStorageData){
813
+ return;
814
+ }
815
+
816
+ /*if set, add the cookie search data to the data to send*/
817
+ if(cookieStorageData){
818
+ cookieStorageData = cookieStorageData.split('SPLIT-TOKEN');
819
+
820
+ for(var i in cookieStorageData){
821
+ //console.log(cookieStorageData[i]);
822
+ dataToSend.push(JSON.parse(cookieStorageData[i]));
823
+ }
824
+ }
825
+
826
+ /*if set, add the locally stored data to the data to send*/
827
+ if(localStorageData){
828
+ dataToSend = dataToSend.concat(localStorageData);
829
+ }
830
+
831
+ dataToSend.sort(function(a, b){ return a.timestamp - b.timestamp; });
832
+
833
+ dataToSend = encodeURIComponent(JSON.stringify(dataToSend));
834
+
835
+ var package = {'action' : 'inbound_search_store', 'data' : dataToSend, 'nonce' : inbound_settings.wp_lead_data.lead_nonce, 'lead_id' : inbound_settings.wp_lead_data.lead_id };
836
+
837
+ callback = function(status){
838
+ if(status){ status = JSON.parse(status); }
839
+
840
+ if(status.success){
841
+ //log the success!
842
+ console.log(status.success);
843
+ //erase the stored data
844
+ _inbound.Utils.eraseCookie('inbound_search_storage');
845
+ _inbound.totalStorage.deleteItem('inbound_search_storage');
846
+ }
847
+
848
+ if(status.error){
849
+ console.log(status.error);
850
+ }
851
+ };
852
+ this.ajaxPost(inbound_settings.admin_url, package, callback);
853
+ }
854
+ };
855
+
856
+ return _inbound;
857
+
858
+ })(_inbound || {});
shared/assets/js/frontend/analytics/inboundAnalytics.js CHANGED
@@ -1,3846 +1,3847 @@
1
- /*! Inbound Analyticsv1.0.0 | (c) 2017 Inbound Now | https://github.com/inboundnow/cta */
2
- /**
3
- * # _inbound
4
- *
5
- * This main the _inbound class
6
- *
7
- * @author David Wells <david@inboundnow.com>
8
- * @author Hudson Atwell <hudson@inboundnow.com>
9
- * @version 0.0.2
10
- */
11
-
12
- var inbound_data = inbound_data || {};
13
- var _inboundOptions = _inboundOptions || {};
14
- /* Ensure global _gaq Google Analytics queue has been initialized. */
15
- var _gaq = _gaq || [];
16
-
17
- var _inbound = (function(options) {
18
-
19
- /* Constants */
20
- var defaults = {
21
- timeout: ( inbound_settings.is_admin ? 500 : 10000 ),
22
- formAutoTracking: true,
23
- formAutoPopulation: true
24
- };
25
-
26
- var Analytics = {
27
- /* Initialize individual modules */
28
- init: function() {
29
- _inbound.Utils.init();
30
-
31
- _inbound.Utils.domReady(window, function() {
32
- /* On Load Analytics Events */
33
- _inbound.DomLoaded();
34
-
35
- });
36
- },
37
- DomLoaded: function() {
38
- _inbound.PageTracking.init();
39
- /* run form mapping */
40
- _inbound.Forms.init();
41
- /* set URL params */
42
- _inbound.Utils.setUrlParams();
43
- _inbound.LeadsAPI.init();
44
- /* run form mapping for dynamically generated forms */
45
- setTimeout(function() {
46
- _inbound.Forms.init();
47
- }, 2000);
48
-
49
- _inbound.trigger('analytics_ready');
50
-
51
- },
52
- /**
53
- * Merge script defaults with user options
54
- * @private
55
- * @param {Object} defaults Default settings
56
- * @param {Object} options User options
57
- * @returns {Object} Merged values of defaults and options
58
- */
59
- extend: function(defaults, options) {
60
- var extended = {};
61
- var prop;
62
- for (prop in defaults) {
63
- if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
64
- extended[prop] = defaults[prop];
65
- }
66
- }
67
- for (prop in options) {
68
- if (Object.prototype.hasOwnProperty.call(options, prop)) {
69
- extended[prop] = options[prop];
70
- }
71
- }
72
- return extended;
73
- },
74
- /* Debugger Function toggled by var debugMode */
75
- debug: function(msg, callback) {
76
- /* legacy */
77
- },
78
- deBugger: function(context, msg, callback) {
79
-
80
- if (!console) {
81
- return;
82
- }
83
- //if app not in debug mode, exit immediately
84
- // check for hash
85
- var hash = (document.location.hash) ? document.location.hash : '',
86
- debugHash = hash.indexOf("#debug") > -1,
87
- msg = msg || false,
88
- logCookie,
89
- logAllMessages,
90
- hashcontext;
91
-
92
- if (hash && hash.match(/debug/)) {
93
- hash = hash.split('-');
94
- hashcontext = hash[1];
95
- }
96
-
97
-
98
- logAllMessages = (_inbound.Utils.readCookie("inbound_debug") === "true") ? true : false;
99
- logCookie = (_inbound.Utils.readCookie("inbound_debug_" + context) === "true") ? true : false;
100
-
101
- if (!logCookie && !debugHash && !logAllMessages) {
102
- // no logger set. exit.
103
- return;
104
- };
105
-
106
- //console.log the message
107
- if (msg && (typeof msg === 'string')) {
108
-
109
- if (logAllMessages || hashcontext === 'all') {
110
- console.log('logAll "' + context + '" =>', msg)
111
- } else if (logCookie) {
112
- console.log('log "' + context + '" =>', msg)
113
- } else if (context === hashcontext) {
114
- console.log('#log "' + context + '" =>', msg)
115
- }
116
-
117
- };
118
-
119
- //execute the callback if one was passed-in
120
- if (callback && (callback instanceof Function)) {
121
- callback();
122
- };
123
- }
124
- };
125
-
126
- var settings = Analytics.extend(defaults, options);
127
- /* Set globals */
128
- Analytics.Settings = settings || {};
129
-
130
- return Analytics;
131
-
132
- })(_inboundOptions);
133
- /**
134
- * # Hooks & Filters
135
- *
136
- * This file contains all of the form functions of the main _inbound object.
137
- * Filters and actions are described below
138
- *
139
- * Forked from https://github.com/carldanley/WP-JS-Hooks/blob/master/src/event-manager.js
140
- *
141
- * @author David Wells <david@inboundnow.com>
142
- * @contributors Hudson Atwell <hudson@inboundnow.com>
143
- * @version 0.0.2
144
- */
145
-
146
- var _inboundHooks = (function (_inbound) {
147
-
148
- /**
149
- * # EventManager
150
- *
151
- * Actions and filters List
152
- * addAction( 'namespace.identifier', callback, priority )
153
- * addFilter( 'namespace.identifier', callback, priority )
154
- * removeAction( 'namespace.identifier' )
155
- * removeFilter( 'namespace.identifier' )
156
- * doAction( 'namespace.identifier', arg1, arg2, moreArgs, finalArg )
157
- * applyFilters( 'namespace.identifier', content )
158
- * @return {[type]} [description]
159
- */
160
-
161
- /**
162
- * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
163
- * that, lowest priority hooks are fired first.
164
- */
165
- var EventManager = function() {
166
- /**
167
- * Maintain a reference to the object scope so our public methods never get confusing.
168
- */
169
- var MethodsAvailable = {
170
- removeFilter : removeFilter,
171
- applyFilters : applyFilters,
172
- addFilter : addFilter,
173
- removeAction : removeAction,
174
- doAction : doAction,
175
- addAction : addAction
176
- };
177
-
178
- /**
179
- * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
180
- * object literal such that looking up the hook utilizes the native object literal hash.
181
- */
182
- var STORAGE = {
183
- actions : {},
184
- filters : {}
185
- };
186
-
187
- /**
188
- * Adds an action to the event manager.
189
- *
190
- * @param action Must contain namespace.identifier
191
- * @param callback Must be a valid callback function before this action is added
192
- * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
193
- * @param [context] Supply a value to be used for this
194
- */
195
- function addAction( action, callback, priority, context ) {
196
- if( typeof action === 'string' && typeof callback === 'function' ) {
197
- priority = parseInt( ( priority || 10 ), 10 );
198
- _addHook( 'actions', action, callback, priority, context );
199
- }
200
-
201
- return MethodsAvailable;
202
- }
203
-
204
- /**
205
- * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
206
- * that the first argument must always be the action.
207
- */
208
- function doAction( /* action, arg1, arg2, ... */ ) {
209
- var args = Array.prototype.slice.call( arguments );
210
- var action = args.shift();
211
-
212
- if( typeof action === 'string' ) {
213
- _runHook( 'actions', action, args );
214
- }
215
-
216
- return MethodsAvailable;
217
- }
218
-
219
- /**
220
- * Removes the specified action if it contains a namespace.identifier & exists.
221
- *
222
- * @param action The action to remove
223
- * @param [callback] Callback function to remove
224
- */
225
- function removeAction( action, callback ) {
226
- if( typeof action === 'string' ) {
227
- _removeHook( 'actions', action, callback );
228
- }
229
-
230
- return MethodsAvailable;
231
- }
232
-
233
- /**
234
- * Adds a filter to the event manager.
235
- *
236
- * @param filter Must contain namespace.identifier
237
- * @param callback Must be a valid callback function before this action is added
238
- * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
239
- * @param [context] Supply a value to be used for this
240
- */
241
- function addFilter( filter, callback, priority, context ) {
242
- if( typeof filter === 'string' && typeof callback === 'function' ) {
243
- //console.log('add filter', filter);
244
- priority = parseInt( ( priority || 10 ), 10 );
245
- _addHook( 'filters', filter, callback, priority );
246
- }
247
-
248
- return MethodsAvailable;
249
- }
250
-
251
- /**
252
- * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
253
- * the first argument must always be the filter.
254
- */
255
- function applyFilters( /* filter, filtered arg, arg2, ... */ ) {
256
- var args = Array.prototype.slice.call( arguments );
257
- var filter = args.shift();
258
-
259
- if( typeof filter === 'string' ) {
260
- return _runHook( 'filters', filter, args );
261
- }
262
-
263
- return MethodsAvailable;
264
- }
265
-
266
- /**
267
- * Removes the specified filter if it contains a namespace.identifier & exists.
268
- *
269
- * @param filter The action to remove
270
- * @param [callback] Callback function to remove
271
- */
272
- function removeFilter( filter, callback ) {
273
- if( typeof filter === 'string') {
274
- _removeHook( 'filters', filter, callback );
275
- }
276
-
277
- return MethodsAvailable;
278
- }
279
-
280
- /**
281
- * Removes the specified hook by resetting the value of it.
282
- *
283
- * @param type Type of hook, either 'actions' or 'filters'
284
- * @param hook The hook (namespace.identifier) to remove
285
- * @private
286
- */
287
- function _removeHook( type, hook, callback, context ) {
288
- if ( !STORAGE[ type ][ hook ] ) {
289
- return;
290
- }
291
- if ( !callback ) {
292
- STORAGE[ type ][ hook ] = [];
293
- } else {
294
- var handlers = STORAGE[ type ][ hook ];
295
- var i;
296
- if ( !context ) {
297
- for ( i = handlers.length; i--; ) {
298
- if ( handlers[i].callback === callback ) {
299
- handlers.splice( i, 1 );
300
- }
301
- }
302
- }
303
- else {
304
- for ( i = handlers.length; i--; ) {
305
- var handler = handlers[i];
306
- if ( handler.callback === callback && handler.context === context) {
307
- handlers.splice( i, 1 );
308
- }
309
- }
310
- }
311
- }
312
- }
313
-
314
- /**
315
- * Adds the hook to the appropriate storage container
316
- *
317
- * @param type 'actions' or 'filters'
318
- * @param hook The hook (namespace.identifier) to add to our event manager
319
- * @param callback The function that will be called when the hook is executed.
320
- * @param priority The priority of this hook. Must be an integer.
321
- * @param [context] A value to be used for this
322
- * @private
323
- */
324
- function _addHook( type, hook, callback, priority, context ) {
325
- var hookObject = {
326
- callback : callback,
327
- priority : priority,
328
- context : context
329
- };
330
-
331
- // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
332
- var hooks = STORAGE[ type ][ hook ];
333
- if( hooks ) {
334
- hooks.push( hookObject );
335
- hooks = _hookInsertSort( hooks );
336
- }
337
- else {
338
- hooks = [ hookObject ];
339
- }
340
-
341
- STORAGE[ type ][ hook ] = hooks;
342
- }
343
-
344
- /**
345
- * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
346
- * than bubble sort, etc: http://jsperf.com/javascript-sort
347
- *
348
- * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
349
- * @private
350
- */
351
- function _hookInsertSort( hooks ) {
352
- var tmpHook, j, prevHook;
353
- for( var i = 1, len = hooks.length; i < len; i++ ) {
354
- tmpHook = hooks[ i ];
355
- j = i;
356
- while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
357
- hooks[ j ] = hooks[ j - 1 ];
358
- --j;
359
- }
360
- hooks[ j ] = tmpHook;
361
- }
362
-
363
- return hooks;
364
- }
365
-
366
- /**
367
- * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
368
- *
369
- * @param type 'actions' or 'filters'
370
- * @param hook The hook ( namespace.identifier ) to be ran.
371
- * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
372
- * @private
373
- */
374
- function _runHook( type, hook, args ) {
375
- var handlers = STORAGE[ type ][ hook ];
376
-
377
- if ( !handlers ) {
378
- return (type === 'filters') ? args[0] : false;
379
- }
380
-
381
- var i = 0, len = handlers.length;
382
- if ( type === 'filters' ) {
383
- for ( ; i < len; i++ ) {
384
- args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
385
- }
386
- } else {
387
- for ( ; i < len; i++ ) {
388
- handlers[ i ].callback.apply( handlers[ i ].context, args );
389
- }
390
- }
391
-
392
- return ( type === 'filters' ) ? args[ 0 ] : true;
393
- }
394
-
395
- // return all of the publicly available methods
396
- return MethodsAvailable;
397
-
398
- };
399
-
400
- _inbound.hooks = new EventManager();
401
-
402
-
403
- /**
404
- * Event Hooks and Filters public methods
405
- */
406
- /*
407
- * add_action
408
- *
409
- * This function uses _inbound.hooks to mimics WP add_action
410
- *
411
- * ```js
412
- * function Inbound_Add_Action_Example(data) {
413
- * // Do stuff here.
414
- * };
415
- * // Add action to the hook
416
- * _inbound.add_action( 'name_of_action', Inbound_Add_Action_Example, 10 );
417
- * ```
418
- */
419
- _inbound.add_action = function() {
420
- // allow multiple action parameters such as 'ready append'
421
- var actions = arguments[0].split(' ');
422
-
423
- for( k in actions ) {
424
-
425
- // prefix action
426
- arguments[0] = 'inbound.' + actions[ k ];
427
-
428
- _inbound.hooks.addAction.apply(this, arguments);
429
- }
430
-
431
- return this;
432
-
433
- };
434
- /*
435
- * remove_action
436
- *
437
- * This function uses _inbound.hooks to mimics WP remove_action
438
- *
439
- * ```js
440
- * // Add remove action 'name_of_action'
441
- * _inbound.remove_action( 'name_of_action');
442
- * ```
443
- *
444
- */
445
- _inbound.remove_action = function() {
446
- // prefix action
447
- arguments[0] = 'inbound.' + arguments[0];
448
- _inbound.hooks.removeAction.apply(this, arguments);
449
-
450
- return this;
451
-
452
- };
453
- /*
454
- * do_action
455
- *
456
- * This function uses _inbound.hooks to mimics WP do_action
457
- * This is used if you want to allow for third party JS plugins to act on your functions
458
- *
459
- */
460
- _inbound.do_action = function() {
461
- // prefix action
462
- arguments[0] = 'inbound.' + arguments[0];
463
- _inbound.hooks.doAction.apply(this, arguments);
464
-
465
- return this;
466
-
467
- };
468
- /*
469
- * add_filter
470
- *
471
- * This function uses _inbound.hooks to mimics WP add_filter
472
- *
473
- * ```js
474
- * _inbound.add_filter( 'urlParamFilter', URL_Param_Filter, 10 );
475
- * function URL_Param_Filter(urlParams) {
476
- *
477
- * var params = urlParams || {};
478
- * // check for item in object
479
- * if(params.utm_source !== "undefined"){
480
- * //alert('url param "utm_source" is here');
481
- * }
482
- *
483
- * // delete item from object
484
- * delete params.utm_source;
485
- *
486
- * return params;
487
- *
488
- * }
489
- * ```
490
- */
491
- _inbound.add_filter = function() {
492
- // prefix action
493
- arguments[0] = 'inbound.' + arguments[0];
494
- _inbound.hooks.addFilter.apply(this, arguments);
495
-
496
- return this;
497
-
498
- };
499
- /*
500
- * remove_filter
501
- *
502
- * This function uses _inbound.hooks to mimics WP remove_filter
503
- *
504
- * ```js
505
- * // Add remove filter 'urlParamFilter'
506
- * _inbound.remove_action( 'urlParamFilter');
507
- * ```
508
- *
509
- */
510
- _inbound.remove_filter = function() {
511
- // prefix action
512
- arguments[0] = 'inbound.' + arguments[0];
513
-
514
- _inbound.hooks.removeFilter.apply(this, arguments);
515
-
516
- return this;
517
-
518
- };
519
- /*
520
- * apply_filters
521
- *
522
- * This function uses _inbound.hooks to mimics WP apply_filters
523
- *
524
- */
525
- _inbound.apply_filters = function() {
526
- //console.log('Filter:' + arguments[0] + " ran on ->", arguments[1]);
527
- // prefix action
528
- arguments[0] = 'inbound.' + arguments[0];
529
-
530
- return _inbound.hooks.applyFilters.apply(this, arguments);
531
-
532
- };
533
-
534
-
535
- return _inbound;
536
-
537
- })(_inbound || {});
538
- /**
539
- * # _inbound UTILS
540
- *
541
- * This file contains all of the utility functions used by analytics
542
- *
543
- * @author David Wells <david@inboundnow.com>
544
- * @contributors Hudson Atwell <hudson@inboundnow.com>
545
- * @version 0.0.2
546
- */
547
-
548
- var _inboundUtils = (function(_inbound) {
549
-
550
- var storageSupported,
551
- corsEnabled = window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest(),
552
- toString = Object.prototype.toString,
553
- currentPage = ('https:' == location.protocol ? 'https://' : 'http://') + location.hostname + location.pathname.replace(/\/$/, "");
554
-
555
- var settings = {
556
- api_host: currentPage,
557
- track_pageview: true,
558
- track_links_timeout: 300,
559
- cookie_name: '_sp',
560
- cookie_expiration: 365,
561
- cookie_domain: (host = location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i)) ? host[0] : ''
562
- };
563
-
564
- _inbound.Utils = {
565
- init: function() {
566
-
567
- this.polyFills();
568
- this.checkLocalStorage();
569
- this.SetUID();
570
- this.storeReferralData();
571
-
572
- },
573
- /*! http://stackoverflow.com/questions/951791/javascript-global-error-handling */
574
- /* Polyfills for missing browser functionality */
575
- polyFills: function() {
576
- /* Console.log fix for old browsers */
577
- if (!window.console) {
578
- window.console = {};
579
- }
580
- var m = [
581
- "log", "info", "warn", "error", "debug", "trace", "dir", "group",
582
- "groupCollapsed", "groupEnd", "time", "timeEnd", "profile", "profileEnd",
583
- "dirxml", "assert", "count", "markTimeline", "timeStamp", "clear"
584
- ];
585
- // define undefined methods as noops to prevent errors
586
- for (var i = 0; i < m.length; i++) {
587
- if (!window.console[m[i]]) {
588
- window.console[m[i]] = function() {};
589
- }
590
- }
591
- /* Event trigger polyfill for IE9 and 10
592
- (function() {
593
- function CustomEvent(event, params) {
594
- params = params || {
595
- bubbles: false,
596
- cancelable: false,
597
- detail: undefined
598
- };
599
- var evt = document.createEvent('CustomEvent');
600
- evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
601
- return evt;
602
- }
603
-
604
- CustomEvent.prototype = window.Event.prototype;
605
-
606
- window.CustomEvent = CustomEvent;
607
- })();*/
608
-
609
- /*\
610
- |*| Polyfill Date.toISOString
611
- \*/
612
- if (!Date.prototype.toISOString) {
613
- (function() {
614
- /**
615
- * @param {number} text
616
- * @returns {?}
617
- */
618
- function pad(text) {
619
- /** @type {string} */
620
- var code = String(text);
621
- return 1 === code.length && (code = '0' + code), code;
622
- }
623
- /**
624
- * @returns {string}
625
- */
626
- Date.prototype.toISOString = function() {
627
- return this.getUTCFullYear() + '-' + pad(this.getUTCMonth() + 1) + '-' + pad(this.getUTCDate()) + 'T' + pad(this.getUTCHours()) + ':' + pad(this.getUTCMinutes()) + ':' + pad(this.getUTCSeconds()) + '.' + String((this.getUTCMilliseconds() / 1E3).toFixed(3)).slice(2, 5) + 'Z';
628
- };
629
- })();
630
- }
631
-
632
- /* custom event for ie8+ https://gist.github.com/WebReflection/6693661 */
633
- try {
634
- new CustomEvent('?');
635
- } catch (o_O) {
636
- /*!(C) Andrea Giammarchi -- WTFPL License*/
637
- this.CustomEvent = function(
638
- eventName,
639
- defaultInitDict
640
- ) {
641
-
642
- // the infamous substitute
643
- function CustomEvent(type, eventInitDict) {
644
- var event = document.createEvent(eventName);
645
- if (type !== null) {
646
- initCustomEvent.call(
647
- event,
648
- type, (eventInitDict || (
649
- // if falsy we can just use defaults
650
- eventInitDict = defaultInitDict
651
- )).bubbles,
652
- eventInitDict.cancelable,
653
- eventInitDict.detail
654
- );
655
- } else {
656
- // no need to put the expando property otherwise
657
- // since an event cannot be initialized twice
658
- // previous case is the most common one anyway
659
- // but if we end up here ... there it goes
660
- event.initCustomEvent = initCustomEvent;
661
- }
662
- return event;
663
- }
664
-
665
- // borrowed or attached at runtime
666
- function initCustomEvent(
667
- type, bubbles, cancelable, detail
668
- ) {
669
- this['init' + eventName](type, bubbles, cancelable, detail);
670
- 'detail' in this || (this.detail = detail);
671
- }
672
-
673
- // that's it
674
- return CustomEvent;
675
- }(
676
- // is this IE9 or IE10 ?
677
- // where CustomEvent is there
678
- // but not usable as construtor ?
679
- this.CustomEvent ?
680
- // use the CustomEvent interface in such case
681
- 'CustomEvent' : 'Event',
682
- // otherwise the common compatible one
683
- {
684
- bubbles: false,
685
- cancelable: false,
686
- detail: null
687
- }
688
- );
689
- }
690
- /* querySelectorAll polyfill for ie7+ */
691
- if (!document.querySelectorAll) {
692
- document.querySelectorAll = function(selectors) {
693
- var style = document.createElement('style'),
694
- elements = [],
695
- element;
696
- document.documentElement.firstChild.appendChild(style);
697
- document._qsa = [];
698
-
699
- style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
700
- window.scrollBy(0, 0);
701
- style.parentNode.removeChild(style);
702
-
703
- while (document._qsa.length) {
704
- element = document._qsa.shift();
705
- element.style.removeAttribute('x-qsa');
706
- elements.push(element);
707
- }
708
- document._qsa = null;
709
- return elements;
710
- };
711
- }
712
-
713
- if (!document.querySelector) {
714
- document.querySelector = function(selectors) {
715
- var elements = document.querySelectorAll(selectors);
716
- return (elements.length) ? elements[0] : null;
717
- };
718
- }
719
- /* Innertext shim for firefox https://github.com/duckinator/innerText-polyfill/blob/master/innertext.js */
720
- if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
721
- HTMLElement.prototype.__defineGetter__("innerText", function() {
722
- var selection = window.getSelection(),
723
- ranges = [],
724
- str;
725
-
726
- // Save existing selections.
727
- for (var i = 0; i < selection.rangeCount; i++) {
728
- ranges[i] = selection.getRangeAt(i);
729
- }
730
-
731
- // Deselect everything.
732
- selection.removeAllRanges();
733
-
734
- // Select `el` and all child nodes.
735
- // 'this' is the element .innerText got called on
736
- selection.selectAllChildren(this);
737
-
738
- // Get the string representation of the selected nodes.
739
- str = selection.toString();
740
-
741
- // Deselect everything. Again.
742
- selection.removeAllRanges();
743
-
744
- // Restore all formerly existing selections.
745
- for (var i = 0; i < ranges.length; i++) {
746
- selection.addRange(ranges[i]);
747
- }
748
-
749
- // Oh look, this is what we wanted.
750
- // String representation of the element, close to as rendered.
751
- return str;
752
- })
753
- }
754
- },
755
- /**
756
- * Create cookie
757
- *
758
- * ```js
759
- * // Creates cookie for 10 days
760
- * _inbound.Utils.createCookie( 'cookie_name', 'value', 10 );
761
- * ```
762
- *
763
- * @param {string} name Name of cookie
764
- * @param {string} value Value of cookie
765
- * @param {string} days Length of storage
766
- */
767
- createCookie: function(name, value, days) {
768
- var expires = "";
769
- if (days) {
770
- var date = new Date();
771
- date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
772
- expires = "; expires=" + date.toGMTString();
773
- }
774
- document.cookie = name + "=" + value + expires + "; path=/";
775
- },
776
- /**
777
- * Read cookie value
778
- *
779
- * ```js
780
- * var cookie = _inbound.Utils.readCookie( 'cookie_name' );
781
- * console.log(cookie); // cookie value
782
- * ```
783
- * @param {string} name name of cookie
784
- * @return {string} value of cookie
785
- */
786
- readCookie: function(name) {
787
- var nameEQ = name + "=";
788
- var ca = document.cookie.split(';');
789
- for (var i = 0; i < ca.length; i++) {
790
- var c = ca[i];
791
- while (c.charAt(0) === ' ') {
792
- c = c.substring(1, c.length);
793
- }
794
- if (c.indexOf(nameEQ) === 0) {
795
- return c.substring(nameEQ.length, c.length);
796
- }
797
- }
798
- return null;
799
- },
800
- /**
801
- * Erase cookie
802
- *
803
- * ```js
804
- * // usage:
805
- * _inbound.Utils.eraseCookie( 'cookie_name' );
806
- * // deletes 'cookie_name' value
807
- * ```
808
- * @param {string} name name of cookie
809
- * @return {string} value of cookie
810
- */
811
- eraseCookie: function(name) {
812
- this.createCookie(name, "", -1);
813
- },
814
- /* Get All Cookies */
815
- getAllCookies: function() {
816
- var cookies = {};
817
- if (document.cookie && document.cookie !== '') {
818
- var split = document.cookie.split(';');
819
- for (var i = 0; i < split.length; i++) {
820
- var name_value = split[i].split("=");
821
- name_value[0] = name_value[0].replace(/^ /, '');
822
- cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
823
- }
824
- }
825
- _inbound.totalStorage('inbound_cookies', cookies); // store cookie data
826
- return cookies;
827
- },
828
- /* Grab URL params and save */
829
- setUrlParams: function() {
830
- var urlParams = {};
831
-
832
- (function() {
833
- var e,
834
- d = function(s) {
835
- return decodeURIComponent(s).replace(/\+/g, " ");
836
- },
837
- q = window.location.search.substring(1),
838
- r = /([^&=]+)=?([^&]*)/g;
839
-
840
- while (e = r.exec(q)) {
841
- if (e[1].indexOf("[") == "-1")
842
- urlParams[d(e[1])] = d(e[2]);
843
- else {
844
- var b1 = e[1].indexOf("["),
845
- aN = e[1].slice(b1 + 1, e[1].indexOf("]", b1)),
846
- pN = d(e[1].slice(0, b1));
847
-
848
- if (typeof urlParams[pN] != "object")
849
- urlParams[d(pN)] = {},
850
- urlParams[d(pN)].length = 0;
851
-
852
- if (aN)
853
- urlParams[d(pN)][d(aN)] = d(e[2]);
854
- else
855
- Array.prototype.push.call(urlParams[d(pN)], d(e[2]));
856
-
857
- }
858
- }
859
- })();
860
-
861
- /* Set Param Cookies */
862
- for (var k in urlParams) {
863
- /* account for wordpress media uploader bug */
864
- if (k == 'action') {
865
- continue;
866
- }
867
-
868
- if (typeof urlParams[k] == "object") {
869
- for (var k2 in urlParams[k])
870
- this.createCookie(k2, urlParams[k][k2], 30);
871
- } else {
872
- this.createCookie(k, urlParams[k], 30);
873
- }
874
- }
875
- /* Set Param LocalStorage */
876
- if (storageSupported) {
877
- var pastParams = _inbound.totalStorage('inbound_url_params') || {};
878
- var params = this.mergeObjs(pastParams, urlParams);
879
- _inbound.totalStorage('inbound_url_params', params); // store cookie data
880
- }
881
-
882
- var options = {
883
- 'option1': 'yo',
884
- 'option2': 'woooo'
885
- };
886
-
887
- _inbound.trigger('url_parameters', urlParams, options);
888
-
889
- },
890
- getAllUrlParams: function() {
891
- var get_params = {};
892
- if (storageSupported) {
893
- var get_params = _inbound.totalStorage('inbound_url_params');
894
- }
895
- return get_params;
896
- },
897
- /* Get url param */
898
- getParameterVal: function(name, string) {
899
- return (RegExp(name + '=' + '(.+?)(&|$)').exec(string) || [, false])[1];
900
- },
901
- // Check local storage
902
- // provate browsing safari fix https://github.com/marcuswestin/store.js/issues/42#issuecomment-25274685
903
- checkLocalStorage: function() {
904
- if ('localStorage' in window) {
905
- try {
906
- ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
907
- if (typeof ls == 'undefined' || typeof window.JSON == 'undefined') {
908
- storageSupported = false;
909
- } else {
910
- storageSupported = true;
911
- }
912
-
913
- } catch (err) {
914
- storageSupported = false;
915
- }
916
- }
917
- return storageSupported;
918
- /* http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
919
- var hasStorage;
920
- hasStorage = function() {
921
- var mod, result;
922
- try {
923
- mod = new Date;
924
- localStorage.setItem(mod, mod.toString());
925
- result = localStorage.getItem(mod) === mod.toString();
926
- localStorage.removeItem(mod);
927
- return result;
928
- } catch (_error) {}
929
- };
930
- */
931
- },
932
- // http://stackoverflow.com/questions/4391575/how-to-find-the-size-of-localstorage
933
- showLocalStorageSize: function() {
934
- function stringSizeBytes(str) {
935
- return str.length * 2;
936
- }
937
-
938
- function toMB(bytes) {
939
- return bytes / 1024 / 1024;
940
- }
941
-
942
- function toSize(key) {
943
- return {
944
- name: key,
945
- size: stringSizeBytes(localStorage[key])
946
- };
947
- }
948
-
949
- function toSizeMB(info) {
950
- info.size = toMB(info.size).toFixed(2) + ' MB';
951
- return info;
952
- }
953
-
954
- var sizes = Object.keys(localStorage).map(toSize).map(toSizeMB);
955
-
956
- console.table(sizes);
957
- },
958
- /* Add days to datetime */
959
- addDays: function(myDate, days) {
960
- return new Date(myDate.getTime() + days * 24 * 60 * 60 * 1000);
961
- },
962
- GetDate: function() {
963
- var timeNow = new Date(),
964
- d = timeNow.getDate(),
965
- dPre = (d < 10) ? "0" : "",
966
- y = timeNow.getFullYear(),
967
- h = timeNow.getHours(),
968
- hPre = (h < 10) ? "0" : "",
969
- min = timeNow.getMinutes(),
970
- minPre = (min < 10) ? "0" : "",
971
- sec = timeNow.getSeconds(),
972
- secPre = (sec < 10) ? "0" : "",
973
- m = timeNow.getMonth() + 1,
974
- mPre = (m < 10) ? "0" : "";
975
-
976
- var datetime = y + '/' + mPre + m + "/" + dPre + d + " " + hPre + h + ":" + minPre + min + ":" + secPre + sec;
977
- /* format 2014/11/13 18:22:02 */
978
- return datetime;
979
- },
980
- /* Set Expiration Date of Session Logging. LEGACY Not in Use */
981
- SetSessionTimeout: function() {
982
- var session = this.readCookie("lead_session_expire");
983
- //console.log(session_check);
984
- if (!session) {
985
- //_inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
986
- } else {
987
- //_inbound.trigger('session_resume'); // trigger 'inbound_analytics_session_active'
988
- }
989
- var d = new Date();
990
- d.setTime(d.getTime() + 30 * 60 * 1000);
991
-
992
- this.createCookie("lead_session_expire", true, d); // Set cookie on page load
993
-
994
- },
995
- storeReferralData: function() {
996
- //console.log(expire_time);
997
- var d = new Date(),
998
- referrer = document.referrer || "Direct Traffic",
999
- referrer_cookie = _inbound.Utils.readCookie("inbound_referral_site"),
1000
- original_src = _inbound.totalStorage('inbound_original_referral');
1001
-
1002
- d.setTime(d.getTime() + 30 * 60 * 1000);
1003
-
1004
- if (!referrer_cookie) {
1005
- this.createCookie("inbound_referral_site", referrer, d);
1006
- }
1007
- if (!original_src) {
1008
- _inbound.totalStorage('inbound_original_referral', original_src);
1009
- }
1010
- },
1011
- CreateUID: function(length) {
1012
- var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split(''),
1013
- str = '';
1014
- if (!length) {
1015
- length = Math.floor(Math.random() * chars.length);
1016
- }
1017
- for (var i = 0; i < length; i++) {
1018
- str += chars[Math.floor(Math.random() * chars.length)];
1019
- }
1020
- return str;
1021
- },
1022
- generateGUID: function(a) {
1023
- return a ? (a ^ 16 * Math.random() >> a / 4).toString(16) : ([1E7] + -1E3 + -4E3 + -8E3 + -1E11).replace(/[018]/g, guid);
1024
- },
1025
- SetUID: function(leadUID) {
1026
- /* Set Lead UID */
1027
- if (!this.readCookie("wp_lead_uid")) {
1028
- var wp_lead_uid = leadUID || this.CreateUID(35);
1029
- this.createCookie("wp_lead_uid", wp_lead_uid);
1030
- }
1031
- },
1032
- /* Count number of session visits */
1033
- countProperties: function(obj) {
1034
- var count = 0;
1035
- for (var prop in obj) {
1036
- if (obj.hasOwnProperty(prop)) {
1037
- ++count;
1038
- }
1039
- }
1040
- return count;
1041
- },
1042
- mergeObjs: function(obj1, obj2) {
1043
- var obj3 = {};
1044
- for (var attrname in obj1) {
1045
- obj3[attrname] = obj1[attrname];
1046
- }
1047
- for (var attrname in obj2) {
1048
- obj3[attrname] = obj2[attrname];
1049
- }
1050
- return obj3;
1051
- },
1052
- hasClass: function(className, el) {
1053
- var hasClass;
1054
- if ('classList' in document.documentElement) {
1055
- var hasClass = el.classList.contains(className);
1056
- } else {
1057
- var hasClass = new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); /* IE Polyfill */
1058
- }
1059
- return hasClass;
1060
- },
1061
- addClass: function(className, el) {
1062
- if ('classList' in document.documentElement) {
1063
- el.classList.add(className);
1064
- } else {
1065
- if (!this.hasClass(el, className)) {
1066
- el.className += (el.className ? ' ' : '') + className;
1067
- }
1068
- }
1069
- },
1070
- removeClass: function(className, el) {
1071
- if ('classList' in document.documentElement) {
1072
- el.classList.remove(className);
1073
- } else {
1074
- if (this.hasClass(el, className)) {
1075
- el.className = el.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), '');
1076
- }
1077
- }
1078
- },
1079
- removeElement: function(el) {
1080
- el.parentNode.removeChild(el);
1081
- },
1082
- trim: function(s) {
1083
- s = s.replace(/(^\s*)|(\s*$)/gi, "");
1084
- s = s.replace(/[ ]{2,}/gi, " ");
1085
- s = s.replace(/\n /, "\n");
1086
- return s;
1087
- },
1088
- ajaxPolyFill: function() {
1089
- if (typeof XMLHttpRequest !== 'undefined') {
1090
- return new XMLHttpRequest();
1091
- }
1092
- var versions = [
1093
- "MSXML2.XmlHttp.5.0",
1094
- "MSXML2.XmlHttp.4.0",
1095
- "MSXML2.XmlHttp.3.0",
1096
- "MSXML2.XmlHttp.2.0",
1097
- "Microsoft.XmlHttp"
1098
- ];
1099
-
1100
- var xhr;
1101
- for (var i = 0; i < versions.length; i++) {
1102
- try {
1103
- xhr = new ActiveXObject(versions[i]);
1104
- break;
1105
- } catch (e) {}
1106
- }
1107
- return xhr;
1108
- },
1109
- ajaxSendData: function(url, callback, method, data, sync) {
1110
- var x = this.ajaxPolyFill();
1111
- /* timeout for safari idiocy */
1112
- setTimeout(function() {
1113
- x.open(method, url, true);
1114
- x.onreadystatechange = function() {
1115
- if (x.readyState == 4) {
1116
- callback(x.responseText)
1117
- }
1118
- };
1119
- if (method == 'POST') {
1120
- x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
1121
- }
1122
- x.send(data);
1123
- }, 100);
1124
- },
1125
- ajaxGet: function(url, data, callback, sync) {
1126
- var query = [];
1127
- for (var key in data) {
1128
- query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
1129
- }
1130
- this.ajaxSendData(url + '?' + query.join('&'), callback, 'GET', null, sync)
1131
- },
1132
- ajaxPost: function(url, data, callback, sync) {
1133
- var query = [];
1134
- for (var key in data) {
1135
- query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
1136
- }
1137
- this.ajaxSendData(url, callback, 'POST', query.join('&'), sync)
1138
- },
1139
- /**
1140
- * @param {string} event
1141
- * @param {(Object|null)} properties
1142
- * @param {(Function|null)} callback
1143
- */
1144
- sendEvent: function(event, properties, callback) {
1145
- properties = properties || {};
1146
- async = true;
1147
- var cookieData = getCookie(); /* get cookie data */
1148
- if (cookieData) {
1149
- var key;
1150
- for (key in cookieData) {
1151
- properties[key] = cookieData[key];
1152
- }
1153
- }
1154
- if (!properties.id) {
1155
- properties.id = getId();
1156
- }
1157
- var props = {
1158
- e: event,
1159
- t: (new Date()).toISOString(),
1160
- kv: properties
1161
- };
1162
- var path = settings.api_host + '/track?data=' + encodeURIComponent(JSON.stringify(props));
1163
- if (corsEnabled) {
1164
- /* CORS */
1165
- var xhr = new XMLHttpRequest();
1166
- xhr.open('GET', path, async);
1167
- xhr.withCredentials = async;
1168
- xhr.send(null);
1169
- } else {
1170
- /* jsonP */
1171
- var el = document.createElement('script');
1172
- el.type = 'text/javascript';
1173
- el.async = async;
1174
- el.defer = async;
1175
- el.src = path;
1176
- var insertAt = document.getElementsByTagName('script')[0];
1177
- insertAt.parentNode.insertBefore(el, insertAt);
1178
- }
1179
- return action(callback), self;
1180
- },
1181
- domReady: function(win, fn) {
1182
-
1183
- var done = false,
1184
- top = true,
1185
-
1186
- doc = win.document,
1187
- root = doc.documentElement,
1188
-
1189
- add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
1190
- rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
1191
- pre = doc.addEventListener ? '' : 'on',
1192
-
1193
- init = function(e) {
1194
- if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
1195
- (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
1196
- if (!done && (done = true)) fn.call(win, e.type || e);
1197
- },
1198
-
1199
- poll = function() {
1200
- try {
1201
- root.doScroll('left');
1202
- } catch (e) {
1203
- setTimeout(poll, 50);
1204
- return;
1205
- }
1206
- init('poll');
1207
- };
1208
-
1209
- if (doc.readyState == 'complete') {
1210
-
1211
- fn.call(win, 'lazy');
1212
-
1213
- } else {
1214
- if (doc.createEventObject && root.doScroll) {
1215
- try {
1216
- top = !win.frameElement;
1217
- } catch (e) {}
1218
- if (top) poll();
1219
- }
1220
- doc[add](pre + 'DOMContentLoaded', init, false);
1221
- doc[add](pre + 'readystatechange', init, false);
1222
- win[add](pre + 'load', init, false);
1223
- }
1224
-
1225
- },
1226
- /* Cross-browser event listening */
1227
- addListener: function(element, eventName, listener) {
1228
- if (!element) {
1229
- return;
1230
- }
1231
- //console.log(eventName);
1232
- //console.log(listener);
1233
- if (element.addEventListener) {
1234
- element.addEventListener(eventName, listener, false);
1235
- } else if (element.attachEvent) {
1236
- element.attachEvent("on" + eventName, listener);
1237
- } else {
1238
- element['on' + eventName] = listener;
1239
- }
1240
- },
1241
- removeListener: function(element, eventName, listener) {
1242
-
1243
- if (element.removeEventListener) {
1244
- element.removeEventListener(eventName, listener, false);
1245
- } else if (element.detachEvent) {
1246
- element.detachEvent("on" + eventName, listener);
1247
- } else {
1248
- element["on" + eventName] = null;
1249
- }
1250
- },
1251
- /*
1252
- * Throttle function borrowed from:
1253
- * Underscore.js 1.5.2
1254
- * http://underscorejs.org
1255
- * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
1256
- * Underscore may be freely distributed under the MIT license.
1257
- */
1258
- throttle: function(func, wait) {
1259
- var context, args, result;
1260
- var timeout = null;
1261
- var previous = 0;
1262
- var later = function() {
1263
- previous = new Date;
1264
- timeout = null;
1265
- result = func.apply(context, args);
1266
- };
1267
- return function() {
1268
- var now = new Date;
1269
- if (!previous) previous = now;
1270
- var remaining = wait - (now - previous);
1271
- context = this;
1272
- args = arguments;
1273
- if (remaining <= 0) {
1274
- clearTimeout(timeout);
1275
- timeout = null;
1276
- previous = now;
1277
- result = func.apply(context, args);
1278
- } else if (!timeout) {
1279
- timeout = setTimeout(later, remaining);
1280
- }
1281
- return result;
1282
- };
1283
- },
1284
- /*
1285
- * Determine which version of GA is being used
1286
- * "ga", "_gaq", and "dataLayer" are the possible globals
1287
- */
1288
- checkTypeofGA: function() {
1289
- if (typeof ga === "function") {
1290
- universalGA = true;
1291
- }
1292
-
1293
- if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
1294
- classicGA = true;
1295
- }
1296
-
1297
- if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
1298
- googleTagManager = true;
1299
- }
1300
-
1301
- },
1302
- /**
1303
- * Caches user's search data in the browser until they can be saved to the database
1304
- */
1305
- cacheSearchData: function(searchData, form) {
1306
-
1307
- if(storageSupported){
1308
- //store the searches in the local storage
1309
- var stored = _inbound.totalStorage.getItem('inbound_search_storage');
1310
- if(stored){
1311
- //if there are stored searches, put the new one in the first index
1312
- stored.unshift(searchData);
1313
- _inbound.totalStorage.setItem('inbound_search_storage', stored);
1314
- }else{
1315
- //if there aren't any searches stored, save the current search
1316
- var store = [searchData];
1317
- _inbound.totalStorage.setItem('inbound_search_storage', store);
1318
- }
1319
- }else{
1320
- //if local storage is not possible, store the data in a cookie
1321
- var new_search = JSON.stringify(searchData),
1322
- stored_searches = this.readCookie('inbound_search_storage');
1323
-
1324
- if(stored_searches){
1325
- //add the old searches to the new one
1326
- new_search += ('SPLIT-TOKEN' + stored_searches);
1327
- }
1328
- this.createCookie('inbound_search_storage', new_search, '180');
1329
- }
1330
-
1331
- _inbound.Forms.releaseFormSubmit(form);
1332
- },
1333
- /**
1334
- * Stores search data to the database on page load.
1335
- * If successful, it erases the cached searches from the user's browser
1336
- */
1337
- storeSearchData: function(){
1338
-
1339
- /*if there isn't a lead id or the nonce isn't set, don't try to store the data*/
1340
- if(!inbound_settings.wp_lead_data.lead_id || !inbound_settings.wp_lead_data.lead_nonce){
1341
- return;
1342
- }
1343
-
1344
- var dataToSend = [],
1345
- localStorageData = _inbound.totalStorage.getItem('inbound_search_storage'),
1346
- cookieStorageData = this.readCookie('inbound_search_storage');
1347
-
1348
- /*if nothing is stored, exit*/
1349
- if(!localStorageData && !cookieStorageData){
1350
- return;
1351
- }
1352
-
1353
- /*if set, add the cookie search data to the data to send*/
1354
- if(cookieStorageData){
1355
- cookieStorageData = cookieStorageData.split('SPLIT-TOKEN');
1356
-
1357
- for(var i in cookieStorageData){
1358
- //console.log(cookieStorageData[i]);
1359
- dataToSend.push(JSON.parse(cookieStorageData[i]));
1360
- }
1361
- }
1362
-
1363
- /*if set, add the locally stored data to the data to send*/
1364
- if(localStorageData){
1365
- dataToSend = dataToSend.concat(localStorageData);
1366
- }
1367
-
1368
- dataToSend.sort(function(a, b){ return a.timestamp - b.timestamp; });
1369
-
1370
- dataToSend = encodeURIComponent(JSON.stringify(dataToSend));
1371
-
1372
- var package = {'action' : 'inbound_search_store', 'data' : dataToSend, 'nonce' : inbound_settings.wp_lead_data.lead_nonce, 'lead_id' : inbound_settings.wp_lead_data.lead_id };
1373
-
1374
- callback = function(status){
1375
- if(status){ status = JSON.parse(status); }
1376
-
1377
- if(status.success){
1378
- //log the success!
1379
- console.log(status.success);
1380
- //erase the stored data
1381
- _inbound.Utils.eraseCookie('inbound_search_storage');
1382
- _inbound.totalStorage.deleteItem('inbound_search_storage');
1383
- }
1384
-
1385
- if(status.error){
1386
- console.log(status.error);
1387
- }
1388
- };
1389
- this.ajaxPost(inbound_settings.admin_url, package, callback);
1390
- }
1391
- };
1392
-
1393
- return _inbound;
1394
-
1395
- })(_inbound || {});
1396
-
1397
- /**
1398
- * # Inbound Forms
1399
- *
1400
- * This file contains all of the form functions of the main _inbound object.
1401
- * Filters and actions are described below
1402
- *
1403
- * @author David Wells <david@inboundnow.com>
1404
- * @contributors Hudson Atwell <hudson@inboundnow.com>
1405
- * @version 0.0.2
1406
- */
1407
- /* Finish Exclusions for CC */
1408
-
1409
- /* Launches form class */
1410
- var InboundForms = (function(_inbound) {
1411
-
1412
- var debugMode = false,
1413
- utils = _inbound.Utils,
1414
- no_match = [],
1415
- rawParams = [],
1416
- mappedParams = [],
1417
- callTracker = {},
1418
- settings = _inbound.Settings;
1419
-
1420
- var FieldMapArray = [
1421
- "first name",
1422
- "last name",
1423
- "name",
1424
- "email",
1425
- "e-mail",
1426
- "phone",
1427
- "website",
1428
- "job title",
1429
- "your favorite food",
1430
- "company",
1431
- "tele",
1432
- "address",
1433
- "comment"
1434
- /* Adding values here maps them */
1435
- ];
1436
-
1437
- _inbound.Forms = {
1438
-
1439
- // Init Form functions
1440
- init: function() {
1441
- _inbound.Forms.runFieldMappingFilters();
1442
- _inbound.Forms.formTrackInit();
1443
- _inbound.Forms.searchTrackInit();
1444
- },
1445
- /**
1446
- * This triggers the forms.field_map filter on the mapping array.
1447
- * This will allow you to add or remore Items from the mapping lookup
1448
- *
1449
- * ### Example inbound.form_map_before filter
1450
- *
1451
- * This is an example of how form mapping can be filtered and
1452
- * additional fields can be mapped via javascript
1453
- *
1454
- * ```js
1455
- * // Adding the filter function
1456
- * function Inbound_Add_Filter_Example( FieldMapArray ) {
1457
- * var map = FieldMapArray || [];
1458
- * map.push('new lookup value');
1459
- *
1460
- * return map;
1461
- * };
1462
- *
1463
- * // Adding the filter on dom ready
1464
- * _inbound.hooks.addFilter( 'inbound.form_map_before', Inbound_Add_Filter_Example, 10 );
1465
- * ```
1466
- *
1467
- * @return {[type]} [description]
1468
- */
1469
- runFieldMappingFilters: function() {
1470
- FieldMapArray = _inbound.hooks.applyFilters('forms.field_map', FieldMapArray);
1471
- //alert(FieldMapArray);
1472
- },
1473
- debug: function(msg, callback) {
1474
- //if app not in debug mode, exit immediately
1475
- if (!debugMode || !console) {
1476
- return;
1477
- }
1478
-
1479
- var msg = msg || false;
1480
- //console.log the message
1481
- if (msg && (typeof msg === 'string')) {
1482
- console.log(msg);
1483
- }
1484
-
1485
- //execute the callback if one was passed-in
1486
- if (callback && (callback instanceof Function)) {
1487
- callback();
1488
- }
1489
- },
1490
- formTrackInit: function() {
1491
-
1492
- for (var i = 0; i < window.document.forms.length; i++) {
1493
- var trackForm = false;
1494
- var form = window.document.forms[i];
1495
- /* process forms only once */
1496
- if (!form.dataset.formProcessed) {
1497
- form.dataset.formProcessed = true;
1498
- trackForm = this.checkTrackStatus(form);
1499
- if (trackForm) {
1500
- this.attachFormSubmitEvent(form); /* attach form listener */
1501
- this.initFormMapping(form);
1502
- }
1503
- }
1504
- }
1505
- },
1506
- searchTrackInit: function(){
1507
-
1508
- /* exit if searches aren't supposed to be tracked, or this function has already been called */
1509
- if(inbound_settings.search_tracking == 'off' || callTracker['searchTrackInit']){
1510
- return;
1511
- }
1512
-
1513
- for (var i = 0; i < window.document.forms.length; i++) {
1514
- var trackForm = false;
1515
- var form = window.document.forms[i];
1516
- /* process forms only once */
1517
- if (!form.dataset.searchChecked) {
1518
- form.dataset.searchChecked = true;
1519
- trackForm = this.checkSearchTrackStatus(form);
1520
- if (trackForm) {
1521
- this.attachSearchFormSubmitEvent(form); /* attach form listener */
1522
- }
1523
- }
1524
- }
1525
-
1526
- /* store the search data on init */
1527
- utils.storeSearchData();
1528
-
1529
- /* log that this function has been called */
1530
- callTracker['searchTrackInit'] = true;
1531
- },
1532
- checkTrackStatus: function(form) {
1533
- var ClassIs = form.getAttribute('class');
1534
- if (ClassIs !== "" && ClassIs !== null) {
1535
- if (ClassIs.toLowerCase().indexOf("wpl-track-me") > -1) {
1536
- return true;
1537
- } else if (ClassIs.toLowerCase().indexOf("inbound-track") > -1) {
1538
- return true;
1539
- } else {
1540
- cb = function() { console.log(form); };
1541
- _inbound.deBugger('forms', "This form not tracked. Please assign on in settings...", cb);
1542
- return false;
1543
- }
1544
- }
1545
- },
1546
- checkSearchTrackStatus: function(form) {
1547
- var ClassIs = form.getAttribute('class'),
1548
- IdIs = form.getAttribute('id');
1549
- if (ClassIs !== "" && ClassIs !== null) {
1550
- if (ClassIs.toLowerCase().indexOf("search") > -1) {
1551
- return true;
1552
- }
1553
- }
1554
- if (IdIs !== "" && IdIs !== null) {
1555
- if (IdIs.toLowerCase().indexOf("search") > -1) {
1556
- return true;
1557
- }
1558
- }else{
1559
- cb = function() { console.log(form); };
1560
- _inbound.deBugger('searches', "This search form is not tracked. Please assign on in settings...", cb);
1561
- return false;
1562
- }
1563
- },
1564
- /* Loop through include/exclude items for tracking */
1565
- loopClassSelectors: function(selectors, action) {
1566
- for (var i = selectors.length - 1; i >= 0; i--) {
1567
-
1568
- var selector = utils.trim(selectors[i])
1569
- if (selector.indexOf("#") === -1 && selector.indexOf(".") === -1) {
1570
- // assign ID as default
1571
- selector = "#" + selector;
1572
- }
1573
- //if(selectors[i] match . or # )
1574
- selector = document.querySelector(selector);
1575
- //console.log("SELECTOR", selector);
1576
- if (selector) {
1577
- if (action === 'add') {
1578
- _inbound.Utils.addClass('wpl-track-me', selector);
1579
- _inbound.Utils.addClass('inbound-track', selector);
1580
- } else {
1581
- _inbound.Utils.removeClass('wpl-track-me', selector);
1582
- _inbound.Utils.removeClass('inbound-track', selector);
1583
- }
1584
- }
1585
- }
1586
- },
1587
- /* Map field fields on load */
1588
- initFormMapping: function(form) {
1589
- var hiddenInputs = [];
1590
-
1591
- for (var i = 0; i < form.elements.length; i++) {
1592
- formInput = form.elements[i];
1593
-
1594
- if (formInput.type === 'hidden') {
1595
- hiddenInputs.push(formInput);
1596
- continue;
1597
- }
1598
-
1599
- //this.ignoreFields(formInput);
1600
- /* Map form fields */
1601
- this.mapField(formInput);
1602
- /* Remember visible inputs */
1603
- this.rememberInputValues(formInput);
1604
- /* Fill visible inputs */
1605
- if (settings.formAutoPopulation && !_inbound.Utils.hasClass( "nopopulate", form ) ) {
1606
- this.fillInputValues(formInput);
1607
- }
1608
-
1609
- }
1610
-
1611
- /* loop hidden inputs */
1612
- for (var n = hiddenInputs.length - 1; n >= 0; n--) {
1613
- formInput = hiddenInputs[n];
1614
- this.mapField(formInput);
1615
- }
1616
-
1617
- //console.log('mapping on load completed');
1618
- },
1619
- /* Maps data attributes to fields on page load */
1620
- mapField: function(input) {
1621
-
1622
- var input_id = input.id || false;
1623
- var input_name = input.name || false;
1624
- var label = this.getInputLabel(input);
1625
-
1626
- if(label){
1627
- //console.log(label[0].innerText);
1628
- var ignoreField = this.ignoreFieldByLabel(label[0].innerText);
1629
- if(ignoreField){
1630
- input.dataset.ignoreFormField = true;
1631
- return false;
1632
- }
1633
- }
1634
-
1635
- /* Loop through all match possiblities */
1636
- for (i = 0; i < FieldMapArray.length; i++) {
1637
- //for (var i = FieldMapArray.length - 1; i >= 0; i--) {
1638
- var found = false;
1639
- var match = FieldMapArray[i];
1640
- var lookingFor = utils.trim(match);
1641
- var nice_name = lookingFor.replace(/ /g, '_');
1642
-
1643
-
1644
- //console.log("NICE NAME", nice_name);
1645
- //console.log('looking for match on ' + lookingFor);
1646
- //_inbound.deBugger('forms', 'looking for match on ' + lookingFor + " nice_name= " + nice_name);
1647
-
1648
- // Check if input has an attached lable using for= tag
1649
- //var $laxbel = $("label[for='" + $element.attr('id') + "']").text();
1650
- //var labxel = 'label[for="' + input_id + '"]';
1651
-
1652
- /* look for name attribute match */
1653
- if (input_name && input_name.toLowerCase().indexOf(lookingFor) > -1) {
1654
- found = true;
1655
- _inbound.deBugger('forms', 'Found matching name attribute for -> ' + lookingFor);
1656
-
1657
- /* look for id match */
1658
- } else if (input_id && input_id.toLowerCase().indexOf(lookingFor) > -1) {
1659
-
1660
- found = true;
1661
- _inbound.deBugger('forms', 'Found matching ID attribute for ->' + lookingFor);
1662
-
1663
- /* Check siblings for label */
1664
- } else if (label) {
1665
- //var label = (label.length > 1 ? label[0] : label);
1666
- //console.log('label', label);
1667
- if (label[0].innerText.toLowerCase().indexOf(lookingFor) > -1) {
1668
-
1669
- found = true;
1670
- _inbound.deBugger('forms', 'Found matching sibling label for -> ' + lookingFor);
1671
-
1672
- }
1673
-
1674
- } else {
1675
- /* no match found */
1676
- //_inbound.deBugger('forms', 'NO Match on ' + lookingFor + " in " + input_name);
1677
- no_match.push(lookingFor);
1678
-
1679
- }
1680
-
1681
- /* Map the field */
1682
- if (found) {
1683
- this.addDataAttr(input, nice_name);
1684
- this.removeArrayItem(FieldMapArray, lookingFor);
1685
- i--; //decrement count
1686
- }
1687
-
1688
- }
1689
-
1690
- return inbound_data;
1691
-
1692
- },
1693
- /* prevent default submission temporarily */
1694
- formListener: function(event) {
1695
- //console.log(event);
1696
- event.preventDefault();
1697
- _inbound.Forms.saveFormData(event.target);
1698
- document.body.style.cursor = "wait";
1699
- },
1700
- /* prevent default submission temporarily */
1701
- searchFormListener: function(event) {
1702
- //console.log(event);
1703
- event.preventDefault();
1704
- _inbound.Forms.saveSearchData(event.target);
1705
- //document.body.style.cursor = "wait";
1706
- },
1707
- /* attach form listeners */
1708
- attachFormSubmitEvent: function(form) {
1709
- utils.addListener(form, 'submit', this.formListener);
1710
- var email_input = document.querySelector('.inbound-email');
1711
- /* utils.addListener(email_input, 'blur', this.mailCheck); */
1712
- },
1713
- /* attach search form listener */
1714
- attachSearchFormSubmitEvent: function(form) {
1715
- utils.addListener(form, 'submit', this.searchFormListener);
1716
- },
1717
- /* Ignore CC data */
1718
- ignoreFieldByLabel: function(label) {
1719
- var ignore_field = false;
1720
-
1721
- if(!label){ return false; }
1722
-
1723
- // Ignore any fields with labels that indicate a credit card field
1724
- if (label.toLowerCase().indexOf('credit card') != -1 || label.toLowerCase().indexOf('card number') != -1) {
1725
- ignore_field = true;
1726
- }
1727
-
1728
- if (label.toLowerCase().indexOf('expiration') != -1 || label.toLowerCase().indexOf('expiry') != -1) {
1729
- ignore_field = true;
1730
- }
1731
-
1732
- if (label.toLowerCase() == 'month' || label.toLowerCase() == 'mm' || label.toLowerCase() == 'yy' || label.toLowerCase() == 'yyyy' || label.toLowerCase() == 'year') {
1733
- ignore_field = true;
1734
- }
1735
-
1736
- if (label.toLowerCase().indexOf('cvv') != -1 || label.toLowerCase().indexOf('cvc') != -1 || label.toLowerCase().indexOf('secure code') != -1 || label.toLowerCase().indexOf('security code') != -1) {
1737
- ignore_field = true;
1738
- }
1739
-
1740
- if(ignore_field){
1741
- _inbound.deBugger('forms', 'ignore ' + label);
1742
- }
1743
-
1744
- return ignore_field;
1745
-
1746
- },
1747
- /* not implemented yet */
1748
- ignoreFieldByValue: function(value){
1749
- var ignore_field = false;
1750
-
1751
- if(!value){ return false; }
1752
-
1753
- if (value.toLowerCase() == 'visa' || value.toLowerCase() == 'mastercard' || value.toLowerCase() == 'american express' || value.toLowerCase() == 'amex' || value.toLowerCase() == 'discover') {
1754
- ignore_field = true;
1755
- }
1756
-
1757
- // Check if value has integers, strip out spaces, then ignore anything with a credit card length (>16) or an expiration/cvv length (<5)
1758
- var int_regex = new RegExp("/^[0-9]+$/");
1759
- if (int_regex.test(value)) {
1760
- var value_no_spaces = value.replace(' ', '');
1761
-
1762
- if (this.isInt(value_no_spaces) && value_no_spaces.length >= 16) {
1763
- ignore_field = true;
1764
- }
1765
-
1766
- }
1767
-
1768
- return ignore_field;
1769
-
1770
- },
1771
- isInt: function(n) {
1772
- return typeof n == "number" && isFinite(n) && n % 1 === 0;
1773
- },
1774
- releaseFormSubmit: function(form) {
1775
- //console.log('remove form listener event');
1776
- document.body.style.cursor = "default";
1777
- utils.removeClass('wpl-track-me', form);
1778
- utils.removeListener(form, 'submit', this.formListener);
1779
- var formClass = form.getAttribute('class');
1780
- if (formClass !== "" && formClass !== null) {
1781
- /* If contact form 7 do this */
1782
- if (formClass.toLowerCase().indexOf("wpcf7-form") != -1) {
1783
- //alert('release')
1784
- setTimeout(function() {
1785
- document.body.style.cursor = "default";
1786
- }, 300);
1787
- return true;
1788
- }
1789
- }
1790
-
1791
- form.submit();
1792
- /* fallback if submit name="submit" */
1793
- setTimeout(function() {
1794
- for (var i = 0; i < form.elements.length; i++) {
1795
- formInput = form.elements[i];
1796
- type = formInput.type || false;
1797
- if (type === "submit" && formInput.name === "submit") {
1798
- form.elements[i].click();
1799
- }
1800
- }
1801
- }, 2000);
1802
-
1803
- },
1804
- saveFormData: function(form) {
1805
- var inputsObject = inputsObject || {};
1806
- for (var i = 0; i < form.elements.length; i++) {
1807
-
1808
- // console.log(inputsObject);
1809
-
1810
- formInput = form.elements[i];
1811
- multiple = false;
1812
-
1813
- if (formInput.name) {
1814
-
1815
- if (formInput.dataset.ignoreFormField) {
1816
- _inbound.deBugger('forms', 'ignore ' + formInput.name);
1817
- continue;
1818
- }
1819
-
1820
- inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
1821
- //inputName = inputName.replace(/-/g, "_");
1822
- if (!inputsObject[inputName]) {
1823
- inputsObject[inputName] = {};
1824
- }
1825
- if (formInput.type) {
1826
- inputsObject[inputName]['type'] = formInput.type;
1827
- }
1828
- if (!inputsObject[inputName]['name']) {
1829
- inputsObject[inputName]['name'] = formInput.name;
1830
- }
1831
- if (formInput.dataset.mapFormField) {
1832
- inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
1833
- }
1834
-
1835
-
1836
- switch (formInput.nodeName) {
1837
-
1838
- case 'INPUT':
1839
- value = this.getInputValue(formInput);
1840
-
1841
-
1842
- if (value === false) {
1843
- continue;
1844
- }
1845
- break;
1846
-
1847
- case 'TEXTAREA':
1848
- value = formInput.value;
1849
- break;
1850
-
1851
- case 'SELECT':
1852
- if (formInput.multiple) {
1853
- values = [];
1854
- multiple = true;
1855
-
1856
- for (var j = 0; j < formInput.length; j++) {
1857
- if (formInput[j].selected) {
1858
- values.push(encodeURIComponent(formInput[j].value));
1859
- }
1860
- }
1861
-
1862
- } else {
1863
- value = (formInput.value);
1864
- }
1865
-
1866
- break;
1867
- }
1868
-
1869
- _inbound.deBugger('forms', 'Input Value = ' + value);
1870
-
1871
-
1872
- if (value) {
1873
- /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
1874
- if (!inputsObject[inputName]['value']) {
1875
- inputsObject[inputName]['value'] = [];
1876
- }
1877
- inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
1878
- var value = multiple ? values.join(',') : encodeURIComponent(value);
1879
-
1880
- }
1881
-
1882
- }
1883
- }
1884
- _inbound.deBugger('forms', inputsObject);
1885
-
1886
- //console.log('These are the raw values', inputsObject);
1887
- //_inbound.totalStorage('the_key', inputsObject);
1888
- //var inputsObject = sortInputs(inputsObject);
1889
-
1890
- var matchCommon = /name|first name|last name|email|e-mail|phone|website|job title|company|tele|address|comment/;
1891
-
1892
- for (var input in inputsObject) {
1893
- //console.log(input);
1894
-
1895
- var inputValue = inputsObject[input]['value'];
1896
- var inputMappedField = inputsObject[input]['map'];
1897
- //if (matchCommon.test(input) !== false) {
1898
- //console.log(input + " Matches Regex run mapping test");
1899
- //var map = inputsObject[input];
1900
- //console.log("MAPP", map);
1901
- //mappedParams.push( input + '=' + inputsObject[input]['value'].join(',') );
1902
- //}
1903
-
1904
- /* Add custom hook here to look for additional values */
1905
- if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
1906
- rawParams.push(input + '=' + inputsObject[input]['value'].join(','));
1907
- }
1908
-
1909
- if (typeof(inputMappedField) != "undefined" && inputMappedField != null && inputsObject[input]['value']) {
1910
- //console.log('Data ATTR', formInput.dataset.mapFormField);
1911
- mappedParams.push(inputMappedField + "=" + inputsObject[input]['value'].join(','));
1912
- if (input === 'email') {
1913
- var email = inputsObject[input]['value'].join(',');
1914
- //alert(email);
1915
-
1916
- }
1917
- }
1918
- }
1919
-
1920
- var raw_params = rawParams.join('&');
1921
- _inbound.deBugger('forms', "Stringified Raw Form PARAMS: " + raw_params);
1922
-
1923
- var mapped_params = mappedParams.join('&');
1924
- _inbound.deBugger('forms', "Stringified Mapped PARAMS" + mapped_params);
1925
-
1926
- /* Check Use form Email or Cookie */
1927
- var email = utils.getParameterVal('email', mapped_params) || utils.readCookie('wp_lead_email');
1928
-
1929
- /* Legacy Email map */
1930
- if (!email) {
1931
- email = utils.getParameterVal('wpleads_email_address', mapped_params);
1932
- }
1933
-
1934
- var fullName = utils.getParameterVal('name', mapped_params);
1935
- var fName = utils.getParameterVal('first_name', mapped_params);
1936
- var lName = utils.getParameterVal('last_name', mapped_params);
1937
-
1938
- // Fallbacks for empty values
1939
- if (!lName && fName) {
1940
- var parts = decodeURI(fName).split(" ");
1941
- if (parts.length > 0) {
1942
- fName = parts[0];
1943
- lName = parts[1];
1944
- }
1945
- }
1946
-
1947
- if (fullName && !lName && !fName) {
1948
- var parts = decodeURI(fullName).split(" ");
1949
- if (parts.length > 0) {
1950
- fName = parts[0];
1951
- lName = parts[1];
1952
- }
1953
- }
1954
-
1955
- fullName = (fName && lName) ? fName + " " + lName : fullName;
1956
-
1957
- if(!fName) { fName = ""; }
1958
- if(!lName) { lName = ""; }
1959
-
1960
- _inbound.deBugger('forms', "fName = " + fName);
1961
- _inbound.deBugger('forms', "lName = " + lName);
1962
- _inbound.deBugger('forms', "fullName = " + fullName);
1963
-
1964
- //return false;
1965
- var page_views = _inbound.totalStorage('page_views') || {};
1966
- var urlParams = _inbound.totalStorage('inbound_url_params') || {};
1967
-
1968
- /* check if redirect url is empty */
1969
- var formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
1970
- var inbound_form_is_ajax = false;
1971
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
1972
- var inbound_form_is_ajax = true;
1973
- }
1974
-
1975
- /* get form id */
1976
- var inbound_form_id = form.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');
1977
- if(inbound_form_id.length > 0 ){
1978
- inbound_form_id = inbound_form_id[0]['value'];
1979
- } else {
1980
- inbound_form_id = 0;
1981
- }
1982
-
1983
- var inboundDATA = {
1984
- 'email': email
1985
- };
1986
-
1987
- /* Get Variation ID */
1988
- if (typeof(landing_path_info) != "undefined") {
1989
- var variation = landing_path_info.variation;
1990
- } else if (typeof(cta_path_info) != "undefined") {
1991
- var variation = cta_path_info.variation;
1992
- } else {
1993
- var variation = inbound_settings.variation_id;
1994
- }
1995
- var post_type = inbound_settings.post_type || 'page';
1996
- var page_id = inbound_settings.post_id || 0;
1997
- // data['wp_lead_uid'] = jQuery.cookie("wp_lead_uid") || null;
1998
- // data['search_data'] = JSON.stringify(jQuery.totalStorage('inbound_search')) || {};
1999
- search_data = {};
2000
- /* Filter here for raw */
2001
- formData = {
2002
- 'action': 'inbound_lead_store',
2003
- 'email': email,
2004
- "full_name": fullName,
2005
- "first_name": fName,
2006
- "last_name": lName,
2007
- 'raw_params': raw_params,
2008
- 'mapped_params': mapped_params,
2009
- 'url_params': JSON.stringify(urlParams),
2010
- 'search_data': 'test',
2011
- 'page_views': JSON.stringify(page_views),
2012
- 'post_type': post_type,
2013
- 'page_id': page_id,
2014
- 'variation': variation,
2015
- 'source': utils.readCookie("inbound_referral_site"),
2016
- 'inbound_submitted': inbound_form_is_ajax,
2017
- 'inbound_form_id': inbound_form_id,
2018
- 'inbound_nonce': inbound_settings.ajax_nonce,
2019
- 'event': form
2020
- };
2021
-
2022
- callback = function(leadID) {
2023
- /* Action Example */
2024
-
2025
- _inbound.deBugger('forms', 'Lead Created with ID: ' + leadID);
2026
- leadID = parseInt(leadID, 10);
2027
- formData.leadID = leadID;
2028
- /* Set Lead cookie ID */
2029
- if (leadID) {
2030
- utils.createCookie("wp_lead_id", leadID);
2031
- _inbound.totalStorage.deleteItem('page_views'); // remove pageviews
2032
- _inbound.totalStorage.deleteItem('tracking_events'); // remove events
2033
- }
2034
-
2035
- _inbound.trigger('form_after_submission', formData);
2036
-
2037
- /* Resume normal form functionality */
2038
- _inbound.Forms.releaseFormSubmit(form);
2039
-
2040
- }
2041
-
2042
- _inbound.trigger('form_before_submission', formData);
2043
-
2044
- utils.ajaxPost(inbound_settings.admin_url, formData, callback);
2045
- },
2046
- saveSearchData: function(form) {
2047
- var inputsObject = inputsObject || {};
2048
- for (var i = 0; i < form.elements.length; i++) {
2049
-
2050
- //console.log(inputsObject);
2051
-
2052
- formInput = form.elements[i];
2053
- multiple = false;
2054
-
2055
- if (formInput.name) {
2056
-
2057
- if (formInput.dataset.ignoreFormField) {
2058
- _inbound.deBugger('searches', 'ignore ' + formInput.name);
2059
- continue;
2060
- }
2061
-
2062
- inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
2063
- //inputName = inputName.replace(/-/g, "_");
2064
- if (!inputsObject[inputName]) {
2065
- inputsObject[inputName] = {};
2066
- }
2067
- if (formInput.type) {
2068
- inputsObject[inputName]['type'] = formInput.type;
2069
-
2070
- }
2071
- if (!inputsObject[inputName]['name']) {
2072
- inputsObject[inputName]['name'] = formInput.name;
2073
- }
2074
- if (formInput.dataset.mapFormField) {
2075
- inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
2076
- }
2077
-
2078
-
2079
- switch (formInput.nodeName) {
2080
-
2081
- case 'INPUT':
2082
- value = this.getInputValue(formInput);
2083
-
2084
-
2085
- if (value === false) {
2086
- continue;
2087
- }
2088
- break;
2089
-
2090
- case 'TEXTAREA':
2091
- value = formInput.value;
2092
- break;
2093
-
2094
- case 'SELECT':
2095
- if (formInput.multiple) {
2096
- values = [];
2097
- multiple = true;
2098
-
2099
- for (var j = 0; j < formInput.length; j++) {
2100
- if (formInput[j].selected) {
2101
- values.push(encodeURIComponent(formInput[j].value));
2102
- }
2103
- }
2104
-
2105
- } else {
2106
- value = (formInput.value);
2107
- }
2108
-
2109
- break;
2110
- }
2111
-
2112
- _inbound.deBugger('searches', 'Input Value = ' + value);
2113
-
2114
-
2115
- if (value) {
2116
- /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
2117
- if (!inputsObject[inputName]['value']) {
2118
- inputsObject[inputName]['value'] = [];
2119
- }
2120
- inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
2121
- var value = multiple ? values.join(',') : encodeURIComponent(value);
2122
-
2123
- }
2124
-
2125
- }
2126
- }
2127
-
2128
- _inbound.deBugger('searches', inputsObject);
2129
-
2130
- /* create an array of search fields //(not fully implemented) at the moment, it only maps the text in the "search" input types*/
2131
- var searchQuery = [];
2132
- for (var input in inputsObject) {
2133
- var inputValue = inputsObject[input]['value'];
2134
- var inputType = inputsObject[input]['type'];
2135
-
2136
- /* Add custom hook here to look for additional values */
2137
- if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
2138
- // This is for mapping all fields of a search form. The resulting string is processed
2139
- // in inbound-pro\classes\admin\report-templates\report.lead-searches-and-comments.php
2140
- // In the function print_action_popup()
2141
- // searchQuery.push(input + '|value|' + inputsObject[input]['value'].join(','));
2142
-
2143
- // get the search input value
2144
- if(inputType == 'search'){
2145
- searchQuery.push('search_text' + '|value|' + inputsObject[input]['value']);
2146
- }
2147
- }
2148
- }
2149
- /* exit if there isn't a search query */
2150
- if(!searchQuery[0]){
2151
- return;
2152
- }
2153
-
2154
- var searchString = searchQuery.join('|field|');
2155
- _inbound.deBugger('searches', "Stringified Search Form PARAMS: " + searchString);
2156
-
2157
- /* Get Variation ID */
2158
- if (typeof(landing_path_info) != "undefined") {
2159
- var variation = landing_path_info.variation;
2160
- } else if (typeof(cta_path_info) != "undefined") {
2161
- var variation = cta_path_info.variation;
2162
- } else {
2163
- var variation = inbound_settings.variation_id;
2164
- }
2165
- var post_type = inbound_settings.post_type || 'page';
2166
- var page_id = inbound_settings.post_id || 0;
2167
-
2168
- var user_UID = utils.readCookie("wp_lead_uid");
2169
-
2170
- /* get the user's email address if possible */
2171
- if(inbound_settings.wp_lead_data.lead_email){
2172
- email = inbound_settings.wp_lead_data.lead_email;
2173
- }else if(utils.readCookie('inbound_wpleads_email_address')){
2174
- email = utils.readCookie('inbound_wpleads_email_address');
2175
- }else{
2176
- email = '';
2177
- }
2178
-
2179
- /* Filter here for raw */
2180
- searchData = {
2181
- 'email': email,
2182
- 'search_data': searchString,
2183
- 'user_UID': user_UID,
2184
- 'post_type': post_type,
2185
- 'page_id': page_id,
2186
- 'variation': variation,
2187
- 'source': utils.readCookie("inbound_referral_site"),
2188
- 'ip_address': inbound_settings.ip_address,
2189
- 'timestamp': Math.floor((new Date).getTime()/1000),
2190
- };
2191
-
2192
- /* filter data before caching it in the user's browser */
2193
- _inbound.trigger('search_before_caching', searchData);
2194
-
2195
- /* cache search data */
2196
- if(inbound_settings.wp_lead_data.lead_id){
2197
- searchData['lead_id'] = inbound_settings.wp_lead_data.lead_id;
2198
- utils.cacheSearchData(searchData, form)
2199
- }else{
2200
- utils.cacheSearchData(searchData, form);
2201
- }
2202
-
2203
-
2204
- },
2205
- rememberInputValues: function(input) {
2206
- var name = (input.name) ? "inbound_" + input.name : '';
2207
- var type = (input.type) ? input.type : 'text';
2208
- if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password" || input.dataset.ignoreFormField) {
2209
- return false;
2210
- }
2211
-
2212
- utils.addListener(input, 'change', function(e) {
2213
- if (e.target.name) {
2214
- /* Check for input type */
2215
- if (type !== "checkbox") {
2216
- var value = e.target.value;
2217
- } else {
2218
- var values = [];
2219
- var checkboxes = document.querySelectorAll('input[name="' + e.target.name + '"]');
2220
- for (var i = 0; i < checkboxes.length; i++) {
2221
- var checked = checkboxes[i].checked;
2222
- if (checked) {
2223
- values.push(checkboxes[i].value);
2224
- }
2225
- value = values.join(',');
2226
- };
2227
- }
2228
- //console.log(e.target.nodeName);
2229
- //console.log('change ' + e.target.name + " " + encodeURIComponent(value));
2230
-
2231
- inputData = {
2232
- name: e.target.name,
2233
- node: e.target.nodeName.toLowerCase(),
2234
- type: type,
2235
- value: value,
2236
- mapping: e.target.dataset.mapFormField
2237
- };
2238
-
2239
- _inbound.trigger('form_input_change', inputData);
2240
- /* Set Field Input Cookies */
2241
- utils.createCookie("inbound_" + e.target.name, encodeURIComponent(value));
2242
- // _inbound.totalStorage('the_key', FormStore);
2243
- /* Push to 'unsubmitted form object' */
2244
- }
2245
-
2246
- });
2247
- },
2248
- fillInputValues: function(input) {
2249
- var name = (input.name) ? "inbound_" + input.name : '';
2250
- var type = (input.type) ? input.type : 'text';
2251
- if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password") {
2252
- return false;
2253
- }
2254
- if (utils.readCookie(name) && name != 'comment') {
2255
-
2256
- value = decodeURIComponent(utils.readCookie(name));
2257
- if (type === 'checkbox' || type === 'radio') {
2258
- var checkbox_vals = value.split(',');
2259
- for (var i = 0; i < checkbox_vals.length; i++) {
2260
- if (input.value.indexOf(checkbox_vals[i]) > -1) {
2261
- input.checked = true;
2262
- }
2263
- }
2264
- } else {
2265
- if (value !== "undefined") {
2266
- input.value = value;
2267
- }
2268
- }
2269
- }
2270
- },
2271
- getInputLabel: function(input){
2272
- var label;
2273
- if(label = this.siblingsIsLabel(input)){
2274
- return label;
2275
- } else if (label = this.CheckParentForLabel(input)) {
2276
- return label;
2277
- } else {
2278
- //console.log("no label nf", input);
2279
- return false;
2280
- }
2281
- },
2282
- /* Get correct input values */
2283
- getInputValue: function(input) {
2284
- var value = false;
2285
-
2286
- switch (input.type) {
2287
- case 'radio':
2288
- case 'checkbox':
2289
- if (input.checked) {
2290
- value = input.value;
2291
- //console.log("CHECKBOX VAL", value)
2292
- }
2293
- break;
2294
-
2295
- case 'text':
2296
- case 'hidden':
2297
- default:
2298
- value = input.value;
2299
- break;
2300
-
2301
- }
2302
-
2303
- return value;
2304
- },
2305
- /* Add data-map-form-field attr to input */
2306
- addDataAttr: function(formInput, match) {
2307
-
2308
- var getAllInputs = document.getElementsByName(formInput.name);
2309
- for (var i = getAllInputs.length - 1; i >= 0; i--) {
2310
- if (!formInput.dataset.mapFormField) {
2311
- getAllInputs[i].dataset.mapFormField = match;
2312
- }
2313
- };
2314
- },
2315
- /* Optimize FieldMapArray array for fewer lookups */
2316
- removeArrayItem: function(array, item) {
2317
- if (array.indexOf) {
2318
- index = array.indexOf(item);
2319
- } else {
2320
- for (index = array.length - 1; index >= 0; --index) {
2321
- if (array[index] === item) {
2322
- break;
2323
- }
2324
- }
2325
- }
2326
- if (index >= 0) {
2327
- array.splice(index, 1);
2328
- }
2329
- //_inbound.deBugger('forms', 'removed ' + item + " from array");
2330
- //console.log('removed ' + item + " from array");
2331
- return;
2332
- },
2333
- /* Look for siblings that are form labels */
2334
- siblingsIsLabel: function(input) {
2335
- var siblings = this.getSiblings(input);
2336
- var labels = [];
2337
- for (var i = siblings.length - 1; i >= 0; i--) {
2338
- if (siblings[i].nodeName.toLowerCase() === 'label') {
2339
- labels.push(siblings[i]);
2340
- }
2341
- };
2342
- /* if only 1 label */
2343
- if (labels.length > 0 && labels.length < 2) {
2344
- return labels;
2345
- }
2346
-
2347
- return false;
2348
- },
2349
- getChildren: function(n, skipMe) {
2350
- var r = [];
2351
- var elem = null;
2352
- for (; n; n = n.nextSibling)
2353
- if (n.nodeType == 1 && n != skipMe)
2354
- r.push(n);
2355
- return r;
2356
- },
2357
- getSiblings: function(n) {
2358
- return this.getChildren(n.parentNode.firstChild, n);
2359
- },
2360
- /* Check parent elements inside form for labels */
2361
- CheckParentForLabel: function(element) {
2362
- if (element.nodeName === 'FORM') {
2363
- return null;
2364
- }
2365
- do {
2366
- var labels = element.getElementsByTagName("label");
2367
- if (labels.length > 0 && labels.length < 2) {
2368
- return element.getElementsByTagName("label");
2369
- }
2370
-
2371
- } while (element = element.parentNode);
2372
-
2373
- return null;
2374
- },
2375
- /* Validate Common Email addresses */
2376
- mailCheck: function() {
2377
- var email_input = document.querySelector('.inbound-email');
2378
- if (email_input) {
2379
- //
2380
- utils.addListener(email_input, 'blur', this.mailCheck);
2381
-
2382
- Mailcheck.run({
2383
- email: document.querySelector('.inbound-email').value,
2384
- suggested: function(suggestion) {
2385
- // callback code
2386
-
2387
- var suggest = document.querySelector('.email_suggestion');
2388
- if (suggest) {
2389
- utils.removeElement(suggest);
2390
- }
2391
- var el = document.createElement("span");
2392
- el.innerHTML = "<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">" + suggestion.full + "</b></i>?</span>";
2393
- email_input.parentNode.insertBefore(el, email_input.nextSibling);
2394
- var update = document.getElementById('email_correction');
2395
- utils.addListener(update, 'click', function() {
2396
- email_input.value = update.innerHTML;
2397
- update.parentNode.parentNode.innerHTML = "Fixed!";
2398
- });
2399
- },
2400
- empty: function() {
2401
- //$(".email_suggestion").html("No Suggestions :(");
2402
- }
2403
- });
2404
- }
2405
- }
2406
-
2407
- };
2408
- /* Mailcheck */
2409
- if (typeof Mailcheck === "undefined") {
2410
- var Mailcheck = {
2411
- domainThreshold: 1,
2412
- topLevelThreshold: 3,
2413
-
2414
- defaultDomains: ["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
2415
- "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
2416
- "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "outlook.com", "icloud.com"
2417
- ],
2418
-
2419
- defaultTopLevelDomains: ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca", "de"],
2420
-
2421
- run: function(opts) {
2422
- opts.domains = opts.domains || Mailcheck.defaultDomains;
2423
- opts.topLevelDomains = opts.topLevelDomains || Mailcheck.defaultTopLevelDomains;
2424
- opts.distanceFunction = opts.distanceFunction || Mailcheck.sift3Distance;
2425
-
2426
- var defaultCallback = function(result) {
2427
- return result;
2428
- };
2429
- var suggestedCallback = opts.suggested || defaultCallback;
2430
- var emptyCallback = opts.empty || defaultCallback;
2431
-
2432
- var result = Mailcheck.suggest(Mailcheck.encodeEmail(opts.email), opts.domains, opts.topLevelDomains, opts.distanceFunction);
2433
-
2434
- return result ? suggestedCallback(result) : emptyCallback();
2435
- },
2436
-
2437
- suggest: function(email, domains, topLevelDomains, distanceFunction) {
2438
- email = email.toLowerCase();
2439
-
2440
- var emailParts = this.splitEmail(email);
2441
-
2442
- var closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction, this.domainThreshold);
2443
-
2444
- if (closestDomain) {
2445
- if (closestDomain != emailParts.domain) {
2446
- // The email address closely matches one of the supplied domains; return a suggestion
2447
- return {
2448
- address: emailParts.address,
2449
- domain: closestDomain,
2450
- full: emailParts.address + "@" + closestDomain
2451
- };
2452
- }
2453
- } else {
2454
- // The email address does not closely match one of the supplied domains
2455
- var closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains, distanceFunction, this.topLevelThreshold);
2456
- if (emailParts.domain && closestTopLevelDomain && closestTopLevelDomain != emailParts.topLevelDomain) {
2457
- // The email address may have a mispelled top-level domain; return a suggestion
2458
- var domain = emailParts.domain;
2459
- closestDomain = domain.substring(0, domain.lastIndexOf(emailParts.topLevelDomain)) + closestTopLevelDomain;
2460
- return {
2461
- address: emailParts.address,
2462
- domain: closestDomain,
2463
- full: emailParts.address + "@" + closestDomain
2464
- };
2465
- }
2466
- }
2467
- /* The email address exactly matches one of the supplied domains, does not closely
2468
- * match any domain and does not appear to simply have a mispelled top-level domain,
2469
- * or is an invalid email address; do not return a suggestion.
2470
- */
2471
- return false;
2472
- },
2473
-
2474
- findClosestDomain: function(domain, domains, distanceFunction, threshold) {
2475
- threshold = threshold || this.topLevelThreshold;
2476
- var dist;
2477
- var minDist = 99;
2478
- var closestDomain = null;
2479
-
2480
- if (!domain || !domains) {
2481
- return false;
2482
- }
2483
- if (!distanceFunction) {
2484
- distanceFunction = this.sift3Distance;
2485
- }
2486
-
2487
- for (var i = 0; i < domains.length; i++) {
2488
- if (domain === domains[i]) {
2489
- return domain;
2490
- }
2491
- dist = distanceFunction(domain, domains[i]);
2492
- if (dist < minDist) {
2493
- minDist = dist;
2494
- closestDomain = domains[i];
2495
- }
2496
- }
2497
-
2498
- if (minDist <= threshold && closestDomain !== null) {
2499
- return closestDomain;
2500
- } else {
2501
- return false;
2502
- }
2503
- },
2504
-
2505
- sift3Distance: function(s1, s2) {
2506
- // sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
2507
- if (s1 === null || s1.length === 0) {
2508
- if (s2 === null || s2.length === 0) {
2509
- return 0;
2510
- } else {
2511
- return s2.length;
2512
- }
2513
- }
2514
-
2515
- if (s2 === null || s2.length === 0) {
2516
- return s1.length;
2517
- }
2518
-
2519
- var c = 0;
2520
- var offset1 = 0;
2521
- var offset2 = 0;
2522
- var lcs = 0;
2523
- var maxOffset = 5;
2524
-
2525
- while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
2526
- if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
2527
- lcs++;
2528
- } else {
2529
- offset1 = 0;
2530
- offset2 = 0;
2531
- for (var i = 0; i < maxOffset; i++) {
2532
- if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
2533
- offset1 = i;
2534
- break;
2535
- }
2536
- if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
2537
- offset2 = i;
2538
- break;
2539
- }
2540
- }
2541
- }
2542
- c++;
2543
- }
2544
- return (s1.length + s2.length) / 2 - lcs;
2545
- },
2546
-
2547
- splitEmail: function(email) {
2548
- var parts = email.trim().split("@");
2549
-
2550
- if (parts.length < 2) {
2551
- return false;
2552
- }
2553
-
2554
- for (var i = 0; i < parts.length; i++) {
2555
- if (parts[i] === "") {
2556
- return false;
2557
- }
2558
- }
2559
-
2560
- var domain = parts.pop();
2561
- var domainParts = domain.split(".");
2562
- var tld = "";
2563
-
2564
- if (domainParts.length === 0) {
2565
- // The address does not have a top-level domain
2566
- return false;
2567
- } else if (domainParts.length == 1) {
2568
- // The address has only a top-level domain (valid under RFC)
2569
- tld = domainParts[0];
2570
- } else {
2571
- // The address has a domain and a top-level domain
2572
- for (var i = 1; i < domainParts.length; i++) {
2573
- tld += domainParts[i] + ".";
2574
- }
2575
- if (domainParts.length >= 2) {
2576
- tld = tld.substring(0, tld.length - 1);
2577
- }
2578
- }
2579
-
2580
- return {
2581
- topLevelDomain: tld,
2582
- domain: domain,
2583
- address: parts.join("@")
2584
- };
2585
- },
2586
-
2587
- // Encode the email address to prevent XSS but leave in valid
2588
- // characters, following this official spec:
2589
- // http://en.wikipedia.org/wiki/Email_address#Syntax
2590
- encodeEmail: function(email) {
2591
- var result = encodeURI(email);
2592
- result = result.replace("%20", " ").replace("%25", "%").replace("%5E", "^")
2593
- .replace("%60", "`").replace("%7B", "{").replace("%7C", "|")
2594
- .replace("%7D", "}");
2595
- return result;
2596
- }
2597
- };
2598
- } // End Mailcheck
2599
-
2600
-
2601
- return _inbound;
2602
-
2603
- })(_inbound || {});
2604
-
2605
- /**
2606
- * # Analytics Events
2607
- *
2608
- * Events are triggered throughout the visitors journey through the site. See more on [Inbound Now][in]
2609
- *
2610
- * @author David Wells <david@inboundnow.com>
2611
- * @contributors Hudson Atwell <hudson@inboundnow.com>
2612
- * @version 0.0.2
2613
- *
2614
- * [in]: http://www.inboundnow.com/
2615
- */
2616
-
2617
- // Add object to _inbound
2618
- var _inboundEvents = (function(_inbound) {
2619
-
2620
-
2621
- _inbound.trigger = function(trigger, data) {
2622
- _inbound.Events[trigger](data);
2623
-
2624
- };
2625
-
2626
- /*!
2627
- *
2628
- * Private Function that Fires & Emits Events
2629
- *
2630
- * There are three options for firing events and they trigger in this order:
2631
- *
2632
- * 1. Vanilla JS dispatch event
2633
- * 2. `_inbound.add_action('namespace', callback, priority)`
2634
- * 3. jQuery Trigger `jQuery.trigger('namespace', callback);`
2635
- *
2636
- * The Event `data` can be filtered before events are triggered
2637
- * with filters. Example: filter_ + "namespace"
2638
- *
2639
- * ```js
2640
- * // Filter Form Data before submissionsz
2641
- * _inbound.add_filter( 'filter_form_before_submission', event_filter_data_example, 10);
2642
- *
2643
- * function event_filter_data_example(data) {
2644
- * var data = data || {};
2645
- * // Do something with data
2646
- * return data;
2647
- * }
2648
- * ```
2649
- *
2650
- * @param {string} eventName Name of the event
2651
- * @param {object} data Data passed to external functions/triggers
2652
- * @param {object} options Options for configuring events
2653
- * @return {null} Nothing returned
2654
- */
2655
- function fireEvent(eventName, data, options) {
2656
- var data = data || {};
2657
- options = options || {};
2658
-
2659
- /*! defaults for JS dispatch event */
2660
- options.bubbles = options.bubbles || true,
2661
- options.cancelable = options.cancelable || true;
2662
-
2663
- /*! Customize Data via filter_ + "namespace" */
2664
- data = _inbound.apply_filters('filter_' + eventName, data);
2665
-
2666
- var is_IE_11 = !(window.ActiveXObject) && "ActiveXObject" in window;
2667
-
2668
- if( typeof CustomEvent === 'function') {
2669
-
2670
- var TriggerEvent = new CustomEvent(eventName, {
2671
- detail: data,
2672
- bubbles: options.bubbles,
2673
- cancelable: options.cancelable
2674
- });
2675
-
2676
- } else {
2677
- var TriggerEvent = document.createEvent("Event");
2678
- TriggerEvent.initEvent(eventName, true, true);
2679
- }
2680
-
2681
- /*! 1. Trigger Pure Javascript Event See: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events for example on creating events */
2682
- window.dispatchEvent(TriggerEvent);
2683
- /*! 2. Trigger _inbound action */
2684
- _inbound.do_action(eventName, data);
2685
- /*! 3. jQuery trigger */
2686
- triggerJQueryEvent(eventName, data);
2687
-
2688
- // console.log('Action:' + eventName + " ran on ->", data);
2689
-
2690
- }
2691
-
2692
- function triggerJQueryEvent(eventName, data) {
2693
- if (window.jQuery) {
2694
- var data = data || {};
2695
- /*! try catch here */
2696
- jQuery(document).trigger(eventName, data);
2697
- }
2698
- };
2699
-
2700
- var universalGA,
2701
- classicGA,
2702
- googleTagManager;
2703
-
2704
- _inbound.Events = {
2705
-
2706
- /**
2707
- * # Event Usage
2708
- *
2709
- * Events are triggered throughout the visitors path through the site.
2710
- * You can hook into these custom actions and filters much like WordPress Core
2711
- *
2712
- * See below for examples
2713
- */
2714
-
2715
- /**
2716
- * Adding Custom Actions
2717
- * ------------------
2718
- * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
2719
- *
2720
- * `
2721
- * _inbound.add_action( 'action_name', callback, priority );
2722
- * `
2723
- *
2724
- * ```js
2725
- * // example:
2726
- *
2727
- * // Add custom function to `page_visit` event
2728
- * _inbound.add_action( 'page_visit', callback, 10 );
2729
- *
2730
- * // add custom callback to trigger when `page_visit` fires
2731
- * function callback(pageData){
2732
- * var pageData = pageData || {};
2733
- * // run callback on 'page_visit' trigger
2734
- * alert(pageData.title);
2735
- * }
2736
- * ```
2737
- *
2738
- * @param {string} action_name Name of the event trigger
2739
- * @param {function} callback function to trigger when event happens
2740
- * @param {int} priority Order to trigger the event in
2741
- *
2742
- */
2743
-
2744
- /**
2745
- * Removing Custom Actions
2746
- * ------------------
2747
- * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
2748
- *
2749
- * `
2750
- * _inbound.remove_action( 'action_name');
2751
- * `
2752
- *
2753
- * ```js
2754
- * // example:
2755
- *
2756
- * _inbound.remove_action( 'page_visit');
2757
- * // all 'page_visit' actions have been deregistered
2758
- * ```
2759
- *
2760
- * @param {string} action_name Name of the event trigger
2761
- *
2762
- */
2763
-
2764
- /**
2765
- * # Event List
2766
- *
2767
- * Events are triggered throughout the visitors journey through the site
2768
- */
2769
-
2770
- /**
2771
- * Triggers when analyics has finished loading
2772
- */
2773
- analytics_ready: function() {
2774
- var ops = {
2775
- 'opt1': true
2776
- };
2777
- var data = {
2778
- 'data': 'xyxy'
2779
- };
2780
- fireEvent('analytics_ready', data, ops);
2781
- },
2782
- /**
2783
- * Triggers when the browser url params are parsed. You can perform custom actions
2784
- * if specific url params exist.
2785
- *
2786
- * ```js
2787
- * // Usage:
2788
- *
2789
- * // Add function to 'url_parameters' event
2790
- * _inbound.add_action( 'url_parameters', url_parameters_func_example, 10);
2791
- *
2792
- * function url_parameters_func_example(urlParams) {
2793
- * var urlParams = urlParams || {};
2794
- * for( var param in urlParams ) {
2795
- * var key = param;
2796
- * var value = urlParams[param];
2797
- * }
2798
- * // All URL Params
2799
- * alert(JSON.stringify(urlParams));
2800
- *
2801
- * // Check if URL parameter `utm_source` exists and matches value
2802
- * if(urlParams.utm_source === "twitter") {
2803
- * alert('This person is from twitter!');
2804
- * }
2805
- * }
2806
- * ```
2807
- */
2808
- url_parameters: function(data) {
2809
- fireEvent('url_parameters', data);
2810
- },
2811
- /**
2812
- * Triggers when session starts
2813
- *
2814
- * ```js
2815
- * // Usage:
2816
- *
2817
- * // Add function to 'session_start' event
2818
- * _inbound.add_action( 'session_start', session_start_func_example, 10);
2819
- *
2820
- * function session_start_func_example(data) {
2821
- * var data = data || {};
2822
- * // session start. Do something for new visitor
2823
- * }
2824
- * ```
2825
- */
2826
- session_start: function() {
2827
- console.log('');
2828
- fireEvent('session_start');
2829
- },
2830
- /**
2831
- * Triggers when visitor session goes idle for more than 30 minutes.
2832
- *
2833
- * ```js
2834
- * // Usage:
2835
- *
2836
- * // Add function to 'session_end' event
2837
- * _inbound.add_action( 'session_end', session_end_func_example, 10);
2838
- *
2839
- * function session_end_func_example(data) {
2840
- * var data = data || {};
2841
- * // Do something when session ends
2842
- * alert("Hey! It's been 30 minutes... where did you go?");
2843
- * }
2844
- * ```
2845
- */
2846
- session_end: function(clockTime) {
2847
- fireEvent('session_end', clockTime);
2848
- console.log('Session End');
2849
- },
2850
- /**
2851
- * Triggers if active session is detected
2852
- *
2853
- * ```js
2854
- * // Usage:
2855
- *
2856
- * // Add function to 'session_active' event
2857
- * _inbound.add_action( 'session_active', session_active_func_example, 10);
2858
- *
2859
- * function session_active_func_example(data) {
2860
- * var data = data || {};
2861
- * // session active
2862
- * }
2863
- * ```
2864
- */
2865
- session_active: function() {
2866
- fireEvent('session_active');
2867
- },
2868
- /**
2869
- * Triggers when visitor session goes idle. Idling occurs after 60 seconds of
2870
- * inactivity or when the visitor switches browser tabs
2871
- *
2872
- * ```js
2873
- * // Usage:
2874
- *
2875
- * // Add function to 'session_idle' event
2876
- * _inbound.add_action( 'session_idle', session_idle_func_example, 10);
2877
- *
2878
- * function session_idle_func_example(data) {
2879
- * var data = data || {};
2880
- * // Do something when session idles
2881
- * alert('Here is a special offer for you!');
2882
- * }
2883
- * ```
2884
- */
2885
- session_idle: function(clockTime) {
2886
- fireEvent('session_idle', clockTime);
2887
- },
2888
- /**
2889
- * Triggers when session is already active and gets resumed
2890
- *
2891
- * ```js
2892
- * // Usage:
2893
- *
2894
- * // Add function to 'session_resume' event
2895
- * _inbound.add_action( 'session_resume', session_resume_func_example, 10);
2896
- *
2897
- * function session_resume_func_example(data) {
2898
- * var data = data || {};
2899
- * // Session exists and is being resumed
2900
- * }
2901
- * ```
2902
- */
2903
- session_resume: function() {
2904
- fireEvent('session_resume');
2905
- },
2906
- /**
2907
- * Session emitter. Runs every 10 seconds. This is a useful function for
2908
- * pinging third party services
2909
- *
2910
- * ```js
2911
- * // Usage:
2912
- *
2913
- * // Add session_heartbeat_func_example function to 'session_heartbeat' event
2914
- * _inbound.add_action( 'session_heartbeat', session_heartbeat_func_example, 10);
2915
- *
2916
- * function session_heartbeat_func_example(data) {
2917
- * var data = data || {};
2918
- * // Do something with every 10 seconds
2919
- * }
2920
- * ```
2921
- */
2922
- session_heartbeat: function(clockTime) {
2923
- var data = {
2924
- 'clock': clockTime,
2925
- 'leadData': InboundLeadData
2926
- };
2927
- fireEvent('session_heartbeat', data);
2928
- },
2929
- /**
2930
- * Triggers Every Page View
2931
- *
2932
- * ```js
2933
- * // Usage:
2934
- *
2935
- * // Add function to 'page_visit' event
2936
- * _inbound.add_action( 'page_visit', page_visit_func_example, 10);
2937
- *
2938
- * function session_idle_func_example(pageData) {
2939
- * var pageData = pageData || {};
2940
- * if( pageData.view_count > 8 ){
2941
- * alert('Wow you have been to this page more than 8 times.');
2942
- * }
2943
- * }
2944
- * ```
2945
- */
2946
- page_visit: function(pageData) {
2947
- fireEvent('page_view', pageData);
2948
- },
2949
- /**
2950
- * Triggers If the visitor has never seen the page before
2951
- *
2952
- * ```js
2953
- * // Usage:
2954
- *
2955
- * // Add function to 'page_first_visit' event
2956
- * _inbound.add_action( 'page_first_visit', page_first_visit_func_example, 10);
2957
- *
2958
- * function page_first_visit_func_example(pageData) {
2959
- * var pageData = pageData || {};
2960
- * alert('Welcome to this page! Its the first time you have seen it')
2961
- * }
2962
- * ```
2963
- */
2964
- page_first_visit: function(pageData) {
2965
- fireEvent('page_first_visit');
2966
- _inbound.deBugger('pages', 'First Ever Page View of this Page');
2967
- },
2968
- /**
2969
- * Triggers If the visitor has seen the page before
2970
- *
2971
- * ```js
2972
- * // Usage:
2973
- *
2974
- * // Add function to 'page_revisit' event
2975
- * _inbound.add_action( 'page_revisit', page_revisit_func_example, 10);
2976
- *
2977
- * function page_revisit_func_example(pageData) {
2978
- * var pageData = pageData || {};
2979
- * alert('Welcome back to this page!');
2980
- * // Show visitor special content/offer
2981
- * }
2982
- * ```
2983
- */
2984
- page_revisit: function(pageData) {
2985
-
2986
- fireEvent('page_revisit', pageData);
2987
-
2988
- var logger = function() {
2989
- console.log('pageData', pageData);
2990
- console.log('Page Revisit viewed ' + pageData + " times");
2991
- }
2992
- _inbound.deBugger('pages', status, logger);
2993
- },
2994
-
2995
- /**
2996
- * `tab_hidden` is triggered when the visitor switches browser tabs
2997
- *
2998
- * ```js
2999
- * // Usage:
3000
- *
3001
- * // Adding the callback
3002
- * function tab_hidden_function( data ) {
3003
- * alert('The Tab is Hidden');
3004
- * };
3005
- *
3006
- * // Hook the function up the the `tab_hidden` event
3007
- * _inbound.add_action( 'tab_hidden', tab_hidden_function, 10 );
3008
- * ```
3009
- */
3010
- tab_hidden: function(data) {
3011
- _inbound.deBugger('pages', 'Tab Hidden');
3012
- fireEvent('tab_hidden');
3013
- },
3014
- /**
3015
- * `tab_visible` is triggered when the visitor switches back to the sites tab
3016
- *
3017
- * ```js
3018
- * // Usage:
3019
- *
3020
- * // Adding the callback
3021
- * function tab_visible_function( data ) {
3022
- * alert('Welcome back to this tab!');
3023
- * // trigger popup or offer special discount etc.
3024
- * };
3025
- *
3026
- * // Hook the function up the the `tab_visible` event
3027
- * _inbound.add_action( 'tab_visible', tab_visible_function, 10 );
3028
- * ```
3029
- */
3030
- tab_visible: function(data) {
3031
- _inbound.deBugger('pages', 'Tab Visible');
3032
- fireEvent('tab_visible');
3033
- },
3034
- /**
3035
- * `tab_mouseout` is triggered when the visitor mouses out of the browser window.
3036
- * This is especially useful for exit popups
3037
- *
3038
- * ```js
3039
- * // Usage:
3040
- *
3041
- * // Adding the callback
3042
- * function tab_mouseout_function( data ) {
3043
- * alert("Wait don't Go");
3044
- * // trigger popup or offer special discount etc.
3045
- * };
3046
- *
3047
- * // Hook the function up the the `tab_mouseout` event
3048
- * _inbound.add_action( 'tab_mouseout', tab_mouseout_function, 10 );
3049
- * ```
3050
- */
3051
- tab_mouseout: function(data) {
3052
- _inbound.deBugger('pages', 'Tab Mouseout');
3053
- fireEvent('tab_mouseout');
3054
- },
3055
- /**
3056
- * `form_input_change` is triggered when tracked form inputs change
3057
- * You can use this to add additional validation or set conditional triggers
3058
- *
3059
- * ```js
3060
- * // Usage:
3061
- *
3062
- * ```
3063
- */
3064
- form_input_change: function(inputData) {
3065
- var logger = function() {
3066
- console.log(inputData);
3067
- //console.log('Page Revisit viewed ' + pageData + " times");
3068
- }
3069
- _inbound.deBugger('forms', 'inputData change. Data=', logger);
3070
- fireEvent('form_input_change', inputData);
3071
- },
3072
- /**
3073
- * `form_before_submission` is triggered before the form is submitted to the server.
3074
- * You can filter the data here or send it to third party services
3075
- *
3076
- * ```js
3077
- * // Usage:
3078
- *
3079
- * // Adding the callback
3080
- * function form_before_submission_function( data ) {
3081
- * var data = data || {};
3082
- * // filter form data
3083
- * };
3084
- *
3085
- * // Hook the function up the the `form_before_submission` event
3086
- * _inbound.add_action( 'form_before_submission', form_before_submission_function, 10 );
3087
- * ```
3088
- */
3089
- form_before_submission: function(formData) {
3090
- fireEvent('form_before_submission', formData);
3091
- },
3092
- /**
3093
- * `form_after_submission` is triggered after the form is submitted to the server.
3094
- * You can filter the data here or send it to third party services
3095
- *
3096
- * ```js
3097
- * // Usage:
3098
- *
3099
- * // Adding the callback
3100
- * function form_after_submission_function( data ) {
3101
- * var data = data || {};
3102
- * // filter form data
3103
- * };
3104
- *
3105
- * // Hook the function up the the `form_after_submission` event
3106
- * _inbound.add_action( 'form_after_submission', form_after_submission_function, 10 );
3107
- * ```
3108
- */
3109
- form_after_submission: function(formData) {
3110
-
3111
- fireEvent('form_after_submission', formData);
3112
-
3113
- },
3114
- /**
3115
- * `search_before_caching` is triggered before the search is stored in the user's browser.
3116
- * If a lead ID is set, the search data will be saved to the server when the next page loads.
3117
- * You can filter the data here or send it to third party services
3118
- *
3119
- * ```js
3120
- * // Usage:
3121
- *
3122
- * // Adding the callback
3123
- * function search_before_caching_function( data ) {
3124
- * var data = data || {};
3125
- * // filter search data
3126
- * };
3127
- *
3128
- * // Hook the function up the the `search_before_caching` event
3129
- * _inbound.add_action( 'search_before_caching', search_before_caching_function, 10 );
3130
- * ```
3131
- */
3132
- search_before_caching: function(searchData) {
3133
- fireEvent('search_before_caching', searchData);
3134
- },
3135
- /*! Scrol depth https://github.com/robflaherty/jquery-scrolldepth/blob/master/jquery.scrolldepth.js */
3136
-
3137
- analyticsError: function(MLHttpRequest, textStatus, errorThrown) {
3138
- var error = new CustomEvent("inbound_analytics_error", {
3139
- detail: {
3140
- MLHttpRequest: MLHttpRequest,
3141
- textStatus: textStatus,
3142
- errorThrown: errorThrown
3143
- }
3144
- });
3145
- window.dispatchEvent(error);
3146
- console.log('Page Save Error');
3147
- }
3148
-
3149
- };
3150
-
3151
- return _inbound;
3152
-
3153
- })(_inbound || {});
3154
-
3155
-
3156
- function inboundFormNoRedirect(){
3157
- /*button == the button that was clicked, form == the form that button belongs to, formRedirectUrl == the link that the form redirects to, if set*/
3158
-
3159
- /*Get the button...*/
3160
- /*If not an iframe*/
3161
- if(window.frames.frameElement == null){
3162
- var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
3163
- }
3164
- /*If it is an iframe*/
3165
- else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
3166
- var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
3167
- }
3168
-
3169
- if ( typeof button == 'undefined' ) {
3170
- return;
3171
- }
3172
-
3173
- var form = button.form,
3174
- formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
3175
-
3176
- /*If the redirect link is not set, or there is a single space in it, the form isn't supposed to redirect. So set the action for void*/
3177
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
3178
- form.action = 'javascript:void(0)';
3179
- }
3180
- }
3181
-
3182
- _inbound.add_action( 'form_before_submission', inboundFormNoRedirect, 10 );
3183
-
3184
- function inboundFormNoRedirectContent(){
3185
-
3186
- /*If not an iframe*/
3187
- if(window.frames.frameElement == null){
3188
- var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
3189
- }
3190
- /*If it is an iframe*/
3191
- else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
3192
- var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
3193
- }
3194
-
3195
-
3196
- if ( typeof button == 'undefined' ) {
3197
- return;
3198
- }
3199
-
3200
- var form = button.form,
3201
- formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),
3202
- btnBackground = jQuery(button).css('background'),
3203
- btnFontColor = jQuery(button).css('color'),
3204
- btnHeight = jQuery(button).css('height'),
3205
- spinner = button.getElementsByClassName('inbound-form-spinner');
3206
-
3207
- if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
3208
- jQuery(spinner).remove();
3209
- jQuery(button).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+ btnBackground +'; color='+ btnFontColor +'; font-size:calc('+ btnHeight +' * .42);"></i></div>');
3210
- }
3211
- }
3212
-
3213
- _inbound.add_action( 'form_after_submission', inboundFormNoRedirectContent, 10 );
3214
-
3215
- /* LocalStorage Component */
3216
- var InboundTotalStorage = (function (_inbound){
3217
-
3218
- var supported, ls, mod = '_inbound';
3219
- if ('localStorage' in window){
3220
- try {
3221
- ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
3222
- if (typeof ls == 'undefined' || typeof window.JSON == 'undefined'){
3223
- supported = false;
3224
- } else {
3225
- supported = true;
3226
- }
3227
- window.localStorage.setItem(mod, '1');
3228
- window.localStorage.removeItem(mod);
3229
- }
3230
- catch (err){
3231
- supported = false;
3232
- }
3233
- }
3234
-
3235
- /* Make the methods public */
3236
- _inbound.totalStorage = function(key, value, options){
3237
- return _inbound.totalStorage.impl.init(key, value);
3238
- };
3239
-
3240
- _inbound.totalStorage.setItem = function(key, value){
3241
- return _inbound.totalStorage.impl.setItem(key, value);
3242
- };
3243
-
3244
- _inbound.totalStorage.getItem = function(key){
3245
- return _inbound.totalStorage.impl.getItem(key);
3246
- };
3247
-
3248
- _inbound.totalStorage.getAll = function(){
3249
- return _inbound.totalStorage.impl.getAll();
3250
- };
3251
-
3252
- _inbound.totalStorage.deleteItem = function(key){
3253
- return _inbound.totalStorage.impl.deleteItem(key);
3254
- };
3255
-
3256
-
3257
- _inbound.totalStorage.impl = {
3258
-
3259
- init: function(key, value){
3260
- if (typeof value != 'undefined') {
3261
- return this.setItem(key, value);
3262
- } else {
3263
- return this.getItem(key);
3264
- }
3265
- },
3266
-
3267
- setItem: function(key, value){
3268
- if (!supported){
3269
- try {
3270
- _inbound.Utils.createCookie(key, value);
3271
- return value;
3272
- } catch(e){
3273
- console.log('Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie');
3274
- }
3275
- }
3276
- var saver = JSON.stringify(value);
3277
- ls.setItem(key, saver);
3278
- return this.parseResult(saver);
3279
- },
3280
- getItem: function(key){
3281
- if (!supported){
3282
- try {
3283
- return this.parseResult(_inbound.Utils.readCookie(key));
3284
- } catch(e){
3285
- return null;
3286
- }
3287
- }
3288
- var item = ls.getItem(key);
3289
- return this.parseResult(item);
3290
- },
3291
- deleteItem: function(key){
3292
- if (!supported){
3293
- try {
3294
- _inbound.Utils.eraseCookie(key, null);
3295
- return true;
3296
- } catch(e){
3297
- return false;
3298
- }
3299
- }
3300
- ls.removeItem(key);
3301
- return true;
3302
- },
3303
- getAll: function(){
3304
- var items = [];
3305
- if (!supported){
3306
- try {
3307
- var pairs = document.cookie.split(";");
3308
- for (var i = 0; i<pairs.length; i++){
3309
- var pair = pairs[i].split('=');
3310
- var key = pair[0];
3311
- items.push({key:key, value:this.parseResult(_inbound.Utils.readCookie(key))});
3312
- }
3313
- } catch(e){
3314
- return null;
3315
- }
3316
- } else {
3317
- for (var j in ls){
3318
- if (j.length){
3319
- items.push({key:j, value:this.parseResult(ls.getItem(j))});
3320
- }
3321
- }
3322
- }
3323
- return items;
3324
- },
3325
- parseResult: function(res){
3326
- var ret;
3327
- try {
3328
- ret = JSON.parse(res);
3329
- if (typeof ret == 'undefined'){
3330
- ret = res;
3331
- }
3332
- if (ret == 'true'){
3333
- ret = true;
3334
- }
3335
- if (ret == 'false'){
3336
- ret = false;
3337
- }
3338
- if (parseFloat(ret) == ret && typeof ret != "object"){
3339
- ret = parseFloat(ret);
3340
- }
3341
- } catch(e){
3342
- ret = res;
3343
- }
3344
- return ret;
3345
- }
3346
- };
3347
- })(_inbound || {});
3348
- /**
3349
- * Leads API functions
3350
- * @param Object _inbound - Main JS object
3351
- * @return Object - include event triggers
3352
- */
3353
- var _inboundLeadsAPI = (function(_inbound) {
3354
- var httpRequest;
3355
- _inbound.LeadsAPI = {
3356
- init: function() {
3357
-
3358
- var utils = _inbound.Utils,
3359
- wp_lead_uid = utils.readCookie("wp_lead_uid"),
3360
- wp_lead_id = utils.readCookie("wp_lead_id"),
3361
- expire_check = utils.readCookie("lead_session_expire"); // check for session
3362
-
3363
- if (!expire_check) {
3364
- _inbound.deBugger('leads', 'expired vistor. Run Processes');
3365
- //var data_to_lookup = global-localized-vars;
3366
- if (wp_lead_id) {
3367
- /* Get InboundLeadData */
3368
- _inbound.LeadsAPI.getAllLeadData();
3369
- }
3370
- }
3371
- },
3372
- setGlobalLeadData: function(data) {
3373
- InboundLeadData = data;
3374
- },
3375
- getAllLeadData: function(expire_check) {
3376
- var wp_lead_id = _inbound.Utils.readCookie("wp_lead_id"),
3377
- leadData = _inbound.totalStorage('inbound_lead_data'),
3378
- leadDataExpire = _inbound.Utils.readCookie("lead_data_expire");
3379
- data = {
3380
- action: 'inbound_get_all_lead_data',
3381
- wp_lead_id: wp_lead_id
3382
- },
3383
- success = function(returnData) {
3384
- var leadData = JSON.parse(returnData);
3385
- _inbound.LeadsAPI.setGlobalLeadData(leadData);
3386
- _inbound.totalStorage('inbound_lead_data', leadData); // store lead data
3387
-
3388
- /* Set 3 day timeout for checking DB for new lead data for Lead_Global var */
3389
- var d = new Date();
3390
- d.setTime(d.getTime() + 30 * 60 * 1000);
3391
- var expire = _inbound.Utils.addDays(d, 3);
3392
- _inbound.Utils.createCookie("lead_data_expire", true, expire);
3393
-
3394
- };
3395
-
3396
- if (!leadData) {
3397
- // Get New Lead Data from DB
3398
- _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3399
-
3400
- } else {
3401
- // set global lead var with localstorage data
3402
- _inbound.LeadsAPI.setGlobalLeadData(leadData);
3403
- _inbound.deBugger('lead', 'Set Global Lead Data from Localstorage');
3404
-
3405
- if (!leadDataExpire) {
3406
- _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3407
- //console.log('Set Global Lead Data from Localstorage');
3408
- _inbound.deBugger('lead', 'localized data old. Pull new from DB');
3409
- //console.log('localized data old. Pull new from DB');
3410
- }
3411
- }
3412
-
3413
- },
3414
- getLeadLists: function() {
3415
- var wp_lead_id = _inbound.Utils.readCookie("wp_lead_id");
3416
- var data = {
3417
- action: 'wpl_check_lists',
3418
- wp_lead_id: wp_lead_id
3419
- };
3420
- var success = function(user_id) {
3421
- _inbound.Utils.createCookie("lead_session_list_check", true, {
3422
- path: '/',
3423
- expires: 1
3424
- });
3425
- _inbound.deBugger('lead', "Lists checked");
3426
- //console.log("Lists checked");
3427
- };
3428
- //_inbound.Utils.doAjax(data, success);
3429
- _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3430
- }
3431
- };
3432
-
3433
- return _inbound;
3434
-
3435
- })(_inbound || {});
3436
- /**
3437
- * # Page View Tracking
3438
- *
3439
- * Page view tracking
3440
- *
3441
- * @author David Wells <david@inboundnow.com>
3442
- * @contributors Hudson Atwell <hudson@inboundnow.com>
3443
- * @version 0.0.2
3444
- */
3445
- /* Launches view tracking */
3446
- var _inboundPageTracking = (function(_inbound) {
3447
-
3448
- var started = false,
3449
- stopped = false,
3450
- turnedOff = false,
3451
- clockTime = parseInt(_inbound.Utils.readCookie("lead_session"), 10) || 0,
3452
- inactiveClockTime = 0,
3453
- startTime = new Date(),
3454
- clockTimer = null,
3455
- inactiveClockTimer = null,
3456
- idleTimer = null,
3457
- reportInterval,
3458
- idleTimeout,
3459
- utils = _inbound.Utils,
3460
- timeNow = _inbound.Utils.GetDate(),
3461
- lsType = 'page_views',
3462
- Pages = _inbound.totalStorage(lsType) || {},
3463
- /*!
3464
- Todo: Use UTC offset
3465
- var x = new Date();
3466
- var currentTime = x.getTimezoneOffset() / 60;
3467
- console.log(currentTime) // gets UTC offset
3468
- */
3469
- id = inbound_settings.post_id || window.location.pathname,
3470
- analyticsTimeout = _inbound.Settings.timeout || 10000;
3471
-
3472
- _inbound.PageTracking = {
3473
-
3474
- init: function(options) {
3475
-
3476
- if(lsType !== 'page_views') {
3477
- return false; // in admin
3478
- }
3479
-
3480
- this.CheckTimeOut();
3481
- // Set up options and defaults
3482
- options = options || {};
3483
- reportInterval = parseInt(options.reportInterval, 10) || 10;
3484
- idleTimeout = parseInt(options.idleTimeout, 10) || 3;
3485
-
3486
- // Basic activity event listeners
3487
- utils.addListener(document, 'keydown', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3488
- utils.addListener(document, 'click', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3489
- utils.addListener(window, 'mousemove', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3490
- //utils.addListener(window, 'scroll', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3491
-
3492
- // Page visibility listeners
3493
- _inbound.PageTracking.checkVisibility();
3494
-
3495
- /* Start Session on page load */
3496
- this.startSession();
3497
-
3498
- },
3499
-
3500
- setIdle: function(reason) {
3501
- var reason = reason || "No Movement",
3502
- msg = 'Session IDLE. Activity Timeout due to ' + reason;
3503
-
3504
- _inbound.deBugger('pages', msg);
3505
-
3506
- clearTimeout(_inbound.PageTracking.idleTimer);
3507
- _inbound.PageTracking.stopClock();
3508
- _inbound.trigger('session_idle');
3509
-
3510
- },
3511
-
3512
- checkVisibility: function() {
3513
- var hidden, visibilityState, visibilityChange;
3514
-
3515
- if (typeof document.hidden !== "undefined") {
3516
- hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
3517
- } else if (typeof document.mozHidden !== "undefined") {
3518
- hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
3519
- } else if (typeof document.msHidden !== "undefined") {
3520
- hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
3521
- } else if (typeof document.webkitHidden !== "undefined") {
3522
- hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
3523
- }
3524
-
3525
- var document_hidden = document[hidden];
3526
-
3527
- _inbound.Utils.addListener(document, visibilityChange, function(e) {
3528
- /*! Listen for visibility changes */
3529
- if (document_hidden != document[hidden]) {
3530
- if (document[hidden]) {
3531
- // Document hidden
3532
- _inbound.trigger('tab_hidden');
3533
- _inbound.PageTracking.setIdle('browser tab switch');
3534
- } else {
3535
- // Document shown
3536
- _inbound.trigger('tab_visible');
3537
- _inbound.PageTracking.pingSession();
3538
- }
3539
-
3540
- document_hidden = document[hidden];
3541
- }
3542
- });
3543
- },
3544
- clock: function() {
3545
- clockTime += 1;
3546
- var niceTime = clockTime / 60;
3547
- var msg = 'Total time spent on Page in this Session: ' + niceTime.toFixed(2) + " min";
3548
- _inbound.deBugger('pages', msg);
3549
- if (clockTime > 0 && (clockTime % reportInterval === 0)) {
3550
-
3551
- var d = new Date();
3552
- d.setTime(d.getTime() + 30 * 60 * 1000);
3553
- utils.createCookie("lead_session", clockTime, d); // Set cookie on page load
3554
-
3555
- /*! every 10 seconds run this */
3556
- //console.log('Session Heartbeat every ' + reportInterval + ' secs');
3557
- _inbound.trigger('session_heartbeat', clockTime);
3558
-
3559
- }
3560
-
3561
- },
3562
- inactiveClock: function() {
3563
- inactiveClockTime += 1;
3564
- var TimeUntilTimeOut = (1800 - inactiveClockTime) / 60;
3565
- var msg = 'Time until Session Timeout: ' + TimeUntilTimeOut.toFixed(2) + " min";
3566
- _inbound.deBugger('pages', msg);
3567
- //console.log('Time until Session Timeout: ', TimeUntilTimeOut.toFixed(2) + " min");
3568
- /* Session timeout after 30min */
3569
- if (inactiveClockTime > 1800) {
3570
-
3571
- // sendEvent(clockTime);
3572
- /*! End session after 30min timeout */
3573
- _inbound.trigger('session_end', InboundLeadData);
3574
- _inbound.Utils.eraseCookie("lead_session");
3575
- /* todo maybe? remove session Cookie */
3576
- inactiveClockTime = 0;
3577
- clearTimeout(inactiveClockTimer);
3578
- }
3579
-
3580
-
3581
- },
3582
- stopClock: function() {
3583
- stopped = true;
3584
- clearTimeout(clockTimer);
3585
- clearTimeout(inactiveClockTimer);
3586
- inactiveClockTimer = setInterval(_inbound.PageTracking.inactiveClock, 1000);
3587
- },
3588
-
3589
- restartClock: function() {
3590
- stopped = false;
3591
-
3592
-
3593
- _inbound.trigger('session_resume');
3594
- _inbound.deBugger('pages', 'Activity resumed. Session Active');
3595
- /* todo add session_resume */
3596
- clearTimeout(clockTimer);
3597
- inactiveClockTime = 0;
3598
- clearTimeout(inactiveClockTimer);
3599
- clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
3600
- },
3601
-
3602
- turnOff: function() {
3603
- _inbound.PageTracking.setIdle();
3604
- turnedOff = true;
3605
- },
3606
-
3607
- turnOn: function() {
3608
- turnedOff = false;
3609
- },
3610
- /* This start only runs once */
3611
- startSession: function() {
3612
- /* todo add session Cookie */
3613
- // Calculate seconds from start to first interaction
3614
- var currentTime = new Date();
3615
- var diff = currentTime - startTime;
3616
-
3617
-
3618
- started = true; // Set global
3619
-
3620
- // Send User Timing Event
3621
- /* Todo session start here */
3622
-
3623
- // Start clock
3624
- clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
3625
- //utils.eraseCookie("lead_session");
3626
- var session = utils.readCookie("lead_session");
3627
-
3628
- if (!session) {
3629
- _inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
3630
- var d = new Date();
3631
- d.setTime(d.getTime() + 30 * 60 * 1000);
3632
- _inbound.Utils.createCookie("lead_session", 1, d); // Set cookie on page load
3633
- } else {
3634
- _inbound.trigger('session_active');
3635
- //console.log("count of secs " + session);
3636
- //_inbound.trigger('session_active'); // trigger 'inbound_analytics_session_active'
3637
- }
3638
-
3639
- this.pingSession();
3640
-
3641
-
3642
- },
3643
- resetInactiveFunc: function() {
3644
- inactiveClockTime = 0;
3645
- clearTimeout(inactiveClockTimer);
3646
- },
3647
- /* Ping Session to keep active */
3648
- pingSession: function(e) {
3649
-
3650
-
3651
- if (turnedOff) {
3652
- return;
3653
- }
3654
-
3655
- if (!started) {
3656
- _inbound.PageTracking.startSession();
3657
- }
3658
-
3659
- if (stopped) {
3660
- _inbound.PageTracking.restartClock();
3661
- }
3662
-
3663
- clearTimeout(idleTimer);
3664
-
3665
- idleTimer = setTimeout(_inbound.PageTracking.setIdle, idleTimeout * 1000 + 100);
3666
-
3667
- if (typeof(e) != "undefined") {
3668
- if (e.type === "mousemove") {
3669
- _inbound.PageTracking.mouseEvents(e);
3670
- }
3671
- }
3672
-
3673
- },
3674
- mouseEvents: function(e) {
3675
-
3676
- if (e.pageY <= 5) {
3677
- _inbound.trigger('tab_mouseout');
3678
- }
3679
-
3680
- },
3681
- /**
3682
- * Returns the pages viewed by the site visitor
3683
- *
3684
- * ```js
3685
- * var pageViews = _inbound.PageTracking.getPageViews();
3686
- * // returns page view object
3687
- * ```
3688
- *
3689
- * @return {object} page view object with page ID as key and timestamp
3690
- */
3691
- getPageViews: function() {
3692
- var local_store = _inbound.Utils.checkLocalStorage();
3693
- if (local_store) {
3694
- var page_views = localStorage.getItem(lsType),
3695
- local_object = JSON.parse(page_views);
3696
- if (typeof local_object == 'object' && local_object) {
3697
- //this.triggerPageView();
3698
- }
3699
- return local_object;
3700
- }
3701
- },
3702
- isRevisit: function(Pages) {
3703
- var revisitCheck = false;
3704
- var Pages = Pages || {};
3705
- var pageSeen = Pages[id];
3706
- if (typeof(pageSeen) != "undefined" && pageSeen !== null) {
3707
- revisitCheck = true;
3708
- }
3709
- return revisitCheck;
3710
- },
3711
- triggerPageView: function(pageRevisit) {
3712
-
3713
- var pageData = {
3714
- title: document.title,
3715
- url: document.location.href,
3716
- path: document.location.pathname,
3717
- count: 1 // default
3718
- };
3719
-
3720
- if (pageRevisit) {
3721
- /* Page Revisit Trigger */
3722
- Pages[id].push(timeNow);
3723
- pageData.count = Pages[id].length;
3724
- _inbound.trigger('page_revisit', pageData);
3725
-
3726
- } else {
3727
- /* Page First Seen Trigger */
3728
- Pages[id] = [];
3729
- Pages[id].push(timeNow);
3730
- _inbound.trigger('page_first_visit', pageData);
3731
- }
3732
-
3733
- _inbound.trigger('page_visit', pageData);
3734
-
3735
- _inbound.totalStorage(lsType, Pages);
3736
-
3737
- this.storePageView();
3738
-
3739
- },
3740
- CheckTimeOut: function() {
3741
-
3742
- var pageRevisit = this.isRevisit(Pages),
3743
- status,
3744
- timeout;
3745
-
3746
- /* Default */
3747
- if (pageRevisit) {
3748
-
3749
- var prev = Pages[id].length - 1,
3750
- lastView = Pages[id][prev],
3751
- timeDiff = Math.abs(new Date(lastView).getTime() - new Date(timeNow).getTime());
3752
-
3753
- timeout = timeDiff > analyticsTimeout;
3754
-
3755
- if (timeout) {
3756
- status = 'Timeout Happened. Page view fired';
3757
- this.triggerPageView(pageRevisit);
3758
- } else {
3759
- time_left = Math.abs((analyticsTimeout - timeDiff)) * 0.001;
3760
- status = analyticsTimeout / 1000 + ' sec timeout not done: ' + time_left + " seconds left";
3761
- }
3762
-
3763
- } else {
3764
- /*! Page never seen before save view */
3765
- this.triggerPageView(pageRevisit);
3766
- }
3767
-
3768
- _inbound.deBugger('pages', status);
3769
- },
3770
- storePageView: function() {
3771
-
3772
- /* ignore if page tracking off and page is not a landing page */
3773
- if ( inbound_settings.page_tracking == 'off' && inbound_settings.post_type != 'landing-page' ) {
3774
- return;
3775
- }
3776
-
3777
- /* Let's try and fire this last - also defines what constitutes a bounce - */
3778
- document.addEventListener("DOMContentLoaded", function() {
3779
- setTimeout(function(){
3780
- var leadID = ( _inbound.Utils.readCookie('wp_lead_id') ) ? _inbound.Utils.readCookie('wp_lead_id') : '';
3781
- var lead_uid = ( _inbound.Utils.readCookie('wp_lead_uid') ) ? _inbound.Utils.readCookie('wp_lead_uid') : '';
3782
- var ctas_loaded = _inbound.totalStorage('wp_cta_loaded');
3783
- var ctas_impressions = _inbound.totalStorage('wp_cta_impressions');
3784
-
3785
- /* now reset impressions */
3786
- _inbound.totalStorage('wp_cta_impressions' , {} );
3787
-
3788
- var data = {
3789
- action: 'inbound_track_lead',
3790
- wp_lead_uid: lead_uid,
3791
- wp_lead_id: leadID,
3792
- page_id: inbound_settings.post_id,
3793
- variation_id: inbound_settings.variation_id,
3794
- post_type: inbound_settings.post_type,
3795
- current_url: window.location.href,
3796
- page_views: JSON.stringify(_inbound.PageTracking.getPageViews()),
3797
- cta_impressions : JSON.stringify(ctas_impressions),
3798
- cta_history : JSON.stringify(ctas_loaded),
3799
- json: '0'
3800
- };
3801
-
3802
- var firePageCallback = function(leadID) {
3803
- //_inbound.Events.page_view_saved(leadID);
3804
- };
3805
- //_inbound.Utils.doAjax(data, firePageCallback);
3806
-
3807
- _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, firePageCallback);
3808
-
3809
- } , 200 );
3810
-
3811
-
3812
- });
3813
-
3814
-
3815
- }
3816
- /*! GA functions
3817
- function log_event(category, action, label) {
3818
- _gaq.push(['_trackEvent', category, action, label]);
3819
- }
3820
-
3821
- function log_click(category, link) {
3822
- log_event(category, 'Click', $(link).text());
3823
- }
3824
- */
3825
- };
3826
-
3827
- return _inbound;
3828
-
3829
- })(_inbound || {});
3830
- /**
3831
- * # Start
3832
- *
3833
- * Runs init functions
3834
- *
3835
- * @author David Wells <david@inboundnow.com>
3836
- * @contributors Hudson Atwell <hudson@inboundnow.com>
3837
- * @version 0.0.2
3838
- */
3839
-
3840
-
3841
- /* Initialize _inbound */
3842
- _inbound.init();
3843
-
3844
- /* Set Global Lead Data */
3845
- InboundLeadData = _inbound.totalStorage('inbound_lead_data') || null;
3846
-
 
1
+ /*! Inbound Analyticsv1.0.0 | (c) 2017 Inbound Now | https://github.com/inboundnow/cta */
2
+ /**
3
+ * # _inbound
4
+ *
5
+ * This main the _inbound class
6
+ *
7
+ * @contributor David Wells <david@inboundnow.com>
8
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
9
+ * @version 0.0.2
10
+ */
11
+
12
+ var inbound_data = inbound_data || {};
13
+ var _inboundOptions = _inboundOptions || {};
14
+ /* Ensure global _gaq Google Analytics queue has been initialized. */
15
+ var _gaq = _gaq || [];
16
+
17
+ var _inbound = (function(options) {
18
+
19
+ /* Constants */
20
+ var defaults = {
21
+ timeout: ( inbound_settings.is_admin ? 500 : 10000 ),
22
+ formAutoTracking: true,
23
+ formAutoPopulation: true
24
+ };
25
+
26
+ var Analytics = {
27
+ /* Initialize individual modules */
28
+ init: function() {
29
+ _inbound.Utils.init();
30
+
31
+ _inbound.Utils.domReady(window, function() {
32
+ /* On Load Analytics Events */
33
+ _inbound.DomLoaded();
34
+
35
+ });
36
+ },
37
+ DomLoaded: function() {
38
+ _inbound.PageTracking.init();
39
+ /* run form mapping */
40
+ _inbound.Forms.init();
41
+ /* set URL params */
42
+ _inbound.Utils.setUrlParams();
43
+ _inbound.LeadsAPI.init();
44
+ /* run form mapping for dynamically generated forms */
45
+ setTimeout(function() {
46
+ _inbound.Forms.init();
47
+ }, 2000);
48
+
49
+ _inbound.trigger('analytics_ready');
50
+
51
+ },
52
+ /**
53
+ * Merge script defaults with user options
54
+ * @private
55
+ * @param {Object} defaults Default settings
56
+ * @param {Object} options User options
57
+ * @returns {Object} Merged values of defaults and options
58
+ */
59
+ extend: function(defaults, options) {
60
+ var extended = {};
61
+ var prop;
62
+ for (prop in defaults) {
63
+ if (Object.prototype.hasOwnProperty.call(defaults, prop)) {
64
+ extended[prop] = defaults[prop];
65
+ }
66
+ }
67
+ for (prop in options) {
68
+ if (Object.prototype.hasOwnProperty.call(options, prop)) {
69
+ extended[prop] = options[prop];
70
+ }
71
+ }
72
+ return extended;
73
+ },
74
+ /* Debugger Function toggled by var debugMode */
75
+ debug: function(msg, callback) {
76
+ /* legacy */
77
+ },
78
+ deBugger: function(context, msg, callback) {
79
+
80
+ if (!console) {
81
+ return;
82
+ }
83
+ //if app not in debug mode, exit immediately
84
+ // check for hash
85
+ var hash = (document.location.hash) ? document.location.hash : '',
86
+ debugHash = hash.indexOf("#debug") > -1,
87
+ msg = msg || false,
88
+ logCookie,
89
+ logAllMessages,
90
+ hashcontext;
91
+
92
+ if (hash && hash.match(/debug/)) {
93
+ hash = hash.split('-');
94
+ hashcontext = hash[1];
95
+ }
96
+
97
+
98
+ logAllMessages = (_inbound.Utils.readCookie("inbound_debug") === "true") ? true : false;
99
+ logCookie = (_inbound.Utils.readCookie("inbound_debug_" + context) === "true") ? true : false;
100
+
101
+ if (!logCookie && !debugHash && !logAllMessages) {
102
+ // no logger set. exit.
103
+ return;
104
+ };
105
+
106
+ //console.log the message
107
+ if (msg && (typeof msg === 'string')) {
108
+
109
+ if (logAllMessages || hashcontext === 'all') {
110
+ console.log('logAll "' + context + '" =>', msg)
111
+ } else if (logCookie) {
112
+ console.log('log "' + context + '" =>', msg)
113
+ } else if (context === hashcontext) {
114
+ console.log('#log "' + context + '" =>', msg)
115
+ }
116
+
117
+ };
118
+
119
+ //execute the callback if one was passed-in
120
+ if (callback && (callback instanceof Function)) {
121
+ callback();
122
+ };
123
+ }
124
+ };
125
+
126
+ var settings = Analytics.extend(defaults, options);
127
+ /* Set globals */
128
+ Analytics.Settings = settings || {};
129
+
130
+ return Analytics;
131
+
132
+ })(_inboundOptions);
133
+ /**
134
+ * # Hooks & Filters
135
+ *
136
+ * This file contains all of the form functions of the main _inbound object.
137
+ * Filters and actions are described below
138
+ *
139
+ * Forked from https://github.com/carldanley/WP-JS-Hooks/blob/master/src/event-manager.js
140
+ *
141
+ * @contributor David Wells <david@inboundnow.com>
142
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
143
+ * @version 0.0.2
144
+ */
145
+
146
+ var _inboundHooks = (function (_inbound) {
147
+
148
+ /**
149
+ * # EventManager
150
+ *
151
+ * Actions and filters List
152
+ * addAction( 'namespace.identifier', callback, priority )
153
+ * addFilter( 'namespace.identifier', callback, priority )
154
+ * removeAction( 'namespace.identifier' )
155
+ * removeFilter( 'namespace.identifier' )
156
+ * doAction( 'namespace.identifier', arg1, arg2, moreArgs, finalArg )
157
+ * applyFilters( 'namespace.identifier', content )
158
+ * @return {[type]} [description]
159
+ */
160
+
161
+ /**
162
+ * Handles managing all events for whatever you plug it into. Priorities for hooks are based on lowest to highest in
163
+ * that, lowest priority hooks are fired first.
164
+ */
165
+ var EventManager = function() {
166
+ /**
167
+ * Maintain a reference to the object scope so our public methods never get confusing.
168
+ */
169
+ var MethodsAvailable = {
170
+ removeFilter : removeFilter,
171
+ applyFilters : applyFilters,
172
+ addFilter : addFilter,
173
+ removeAction : removeAction,
174
+ doAction : doAction,
175
+ addAction : addAction
176
+ };
177
+
178
+ /**
179
+ * Contains the hooks that get registered with this EventManager. The array for storage utilizes a "flat"
180
+ * object literal such that looking up the hook utilizes the native object literal hash.
181
+ */
182
+ var STORAGE = {
183
+ actions : {},
184
+ filters : {}
185
+ };
186
+
187
+ /**
188
+ * Adds an action to the event manager.
189
+ *
190
+ * @param action Must contain namespace.identifier
191
+ * @param callback Must be a valid callback function before this action is added
192
+ * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
193
+ * @param [context] Supply a value to be used for this
194
+ */
195
+ function addAction( action, callback, priority, context ) {
196
+ if( typeof action === 'string' && typeof callback === 'function' ) {
197
+ priority = parseInt( ( priority || 10 ), 10 );
198
+ _addHook( 'actions', action, callback, priority, context );
199
+ }
200
+
201
+ return MethodsAvailable;
202
+ }
203
+
204
+ /**
205
+ * Performs an action if it exists. You can pass as many arguments as you want to this function; the only rule is
206
+ * that the first argument must always be the action.
207
+ */
208
+ function doAction( /* action, arg1, arg2, ... */ ) {
209
+ var args = Array.prototype.slice.call( arguments );
210
+ var action = args.shift();
211
+
212
+ if( typeof action === 'string' ) {
213
+ _runHook( 'actions', action, args );
214
+ }
215
+
216
+ return MethodsAvailable;
217
+ }
218
+
219
+ /**
220
+ * Removes the specified action if it contains a namespace.identifier & exists.
221
+ *
222
+ * @param action The action to remove
223
+ * @param [callback] Callback function to remove
224
+ */
225
+ function removeAction( action, callback ) {
226
+ if( typeof action === 'string' ) {
227
+ _removeHook( 'actions', action, callback );
228
+ }
229
+
230
+ return MethodsAvailable;
231
+ }
232
+
233
+ /**
234
+ * Adds a filter to the event manager.
235
+ *
236
+ * @param filter Must contain namespace.identifier
237
+ * @param callback Must be a valid callback function before this action is added
238
+ * @param [priority=10] Used to control when the function is executed in relation to other callbacks bound to the same hook
239
+ * @param [context] Supply a value to be used for this
240
+ */
241
+ function addFilter( filter, callback, priority, context ) {
242
+ if( typeof filter === 'string' && typeof callback === 'function' ) {
243
+ //console.log('add filter', filter);
244
+ priority = parseInt( ( priority || 10 ), 10 );
245
+ _addHook( 'filters', filter, callback, priority );
246
+ }
247
+
248
+ return MethodsAvailable;
249
+ }
250
+
251
+ /**
252
+ * Performs a filter if it exists. You should only ever pass 1 argument to be filtered. The only rule is that
253
+ * the first argument must always be the filter.
254
+ */
255
+ function applyFilters( /* filter, filtered arg, arg2, ... */ ) {
256
+ var args = Array.prototype.slice.call( arguments );
257
+ var filter = args.shift();
258
+
259
+ if( typeof filter === 'string' ) {
260
+ return _runHook( 'filters', filter, args );
261
+ }
262
+
263
+ return MethodsAvailable;
264
+ }
265
+
266
+ /**
267
+ * Removes the specified filter if it contains a namespace.identifier & exists.
268
+ *
269
+ * @param filter The action to remove
270
+ * @param [callback] Callback function to remove
271
+ */
272
+ function removeFilter( filter, callback ) {
273
+ if( typeof filter === 'string') {
274
+ _removeHook( 'filters', filter, callback );
275
+ }
276
+
277
+ return MethodsAvailable;
278
+ }
279
+
280
+ /**
281
+ * Removes the specified hook by resetting the value of it.
282
+ *
283
+ * @param type Type of hook, either 'actions' or 'filters'
284
+ * @param hook The hook (namespace.identifier) to remove
285
+ * @private
286
+ */
287
+ function _removeHook( type, hook, callback, context ) {
288
+ if ( !STORAGE[ type ][ hook ] ) {
289
+ return;
290
+ }
291
+ if ( !callback ) {
292
+ STORAGE[ type ][ hook ] = [];
293
+ } else {
294
+ var handlers = STORAGE[ type ][ hook ];
295
+ var i;
296
+ if ( !context ) {
297
+ for ( i = handlers.length; i--; ) {
298
+ if ( handlers[i].callback === callback ) {
299
+ handlers.splice( i, 1 );
300
+ }
301
+ }
302
+ }
303
+ else {
304
+ for ( i = handlers.length; i--; ) {
305
+ var handler = handlers[i];
306
+ if ( handler.callback === callback && handler.context === context) {
307
+ handlers.splice( i, 1 );
308
+ }
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ /**
315
+ * Adds the hook to the appropriate storage container
316
+ *
317
+ * @param type 'actions' or 'filters'
318
+ * @param hook The hook (namespace.identifier) to add to our event manager
319
+ * @param callback The function that will be called when the hook is executed.
320
+ * @param priority The priority of this hook. Must be an integer.
321
+ * @param [context] A value to be used for this
322
+ * @private
323
+ */
324
+ function _addHook( type, hook, callback, priority, context ) {
325
+ var hookObject = {
326
+ callback : callback,
327
+ priority : priority,
328
+ context : context
329
+ };
330
+
331
+ // Utilize 'prop itself' : http://jsperf.com/hasownproperty-vs-in-vs-undefined/19
332
+ var hooks = STORAGE[ type ][ hook ];
333
+ if( hooks ) {
334
+ hooks.push( hookObject );
335
+ hooks = _hookInsertSort( hooks );
336
+ }
337
+ else {
338
+ hooks = [ hookObject ];
339
+ }
340
+
341
+ STORAGE[ type ][ hook ] = hooks;
342
+ }
343
+
344
+ /**
345
+ * Use an insert sort for keeping our hooks organized based on priority. This function is ridiculously faster
346
+ * than bubble sort, etc: http://jsperf.com/javascript-sort
347
+ *
348
+ * @param hooks The custom array containing all of the appropriate hooks to perform an insert sort on.
349
+ * @private
350
+ */
351
+ function _hookInsertSort( hooks ) {
352
+ var tmpHook, j, prevHook;
353
+ for( var i = 1, len = hooks.length; i < len; i++ ) {
354
+ tmpHook = hooks[ i ];
355
+ j = i;
356
+ while( ( prevHook = hooks[ j - 1 ] ) && prevHook.priority > tmpHook.priority ) {
357
+ hooks[ j ] = hooks[ j - 1 ];
358
+ --j;
359
+ }
360
+ hooks[ j ] = tmpHook;
361
+ }
362
+
363
+ return hooks;
364
+ }
365
+
366
+ /**
367
+ * Runs the specified hook. If it is an action, the value is not modified but if it is a filter, it is.
368
+ *
369
+ * @param type 'actions' or 'filters'
370
+ * @param hook The hook ( namespace.identifier ) to be ran.
371
+ * @param args Arguments to pass to the action/filter. If it's a filter, args is actually a single parameter.
372
+ * @private
373
+ */
374
+ function _runHook( type, hook, args ) {
375
+ var handlers = STORAGE[ type ][ hook ];
376
+
377
+ if ( !handlers ) {
378
+ return (type === 'filters') ? args[0] : false;
379
+ }
380
+
381
+ var i = 0, len = handlers.length;
382
+ if ( type === 'filters' ) {
383
+ for ( ; i < len; i++ ) {
384
+ args[ 0 ] = handlers[ i ].callback.apply( handlers[ i ].context, args );
385
+ }
386
+ } else {
387
+ for ( ; i < len; i++ ) {
388
+ handlers[ i ].callback.apply( handlers[ i ].context, args );
389
+ }
390
+ }
391
+
392
+ return ( type === 'filters' ) ? args[ 0 ] : true;
393
+ }
394
+
395
+ // return all of the publicly available methods
396
+ return MethodsAvailable;
397
+
398
+ };
399
+
400
+ _inbound.hooks = new EventManager();
401
+
402
+
403
+ /**
404
+ * Event Hooks and Filters public methods
405
+ */
406
+ /*
407
+ * add_action
408
+ *
409
+ * This function uses _inbound.hooks to mimics WP add_action
410
+ *
411
+ * ```js
412
+ * function Inbound_Add_Action_Example(data) {
413
+ * // Do stuff here.
414
+ * };
415
+ * // Add action to the hook
416
+ * _inbound.add_action( 'name_of_action', Inbound_Add_Action_Example, 10 );
417
+ * ```
418
+ */
419
+ _inbound.add_action = function() {
420
+ // allow multiple action parameters such as 'ready append'
421
+ var actions = arguments[0].split(' ');
422
+
423
+ for( k in actions ) {
424
+
425
+ // prefix action
426
+ arguments[0] = 'inbound.' + actions[ k ];
427
+
428
+ _inbound.hooks.addAction.apply(this, arguments);
429
+ }
430
+
431
+ return this;
432
+
433
+ };
434
+ /*
435
+ * remove_action
436
+ *
437
+ * This function uses _inbound.hooks to mimics WP remove_action
438
+ *
439
+ * ```js
440
+ * // Add remove action 'name_of_action'
441
+ * _inbound.remove_action( 'name_of_action');
442
+ * ```
443
+ *
444
+ */
445
+ _inbound.remove_action = function() {
446
+ // prefix action
447
+ arguments[0] = 'inbound.' + arguments[0];
448
+ _inbound.hooks.removeAction.apply(this, arguments);
449
+
450
+ return this;
451
+
452
+ };
453
+ /*
454
+ * do_action
455
+ *
456
+ * This function uses _inbound.hooks to mimics WP do_action
457
+ * This is used if you want to allow for third party JS plugins to act on your functions
458
+ *
459
+ */
460
+ _inbound.do_action = function() {
461
+ // prefix action
462
+ arguments[0] = 'inbound.' + arguments[0];
463
+ _inbound.hooks.doAction.apply(this, arguments);
464
+
465
+ return this;
466
+
467
+ };
468
+ /*
469
+ * add_filter
470
+ *
471
+ * This function uses _inbound.hooks to mimics WP add_filter
472
+ *
473
+ * ```js
474
+ * _inbound.add_filter( 'urlParamFilter', URL_Param_Filter, 10 );
475
+ * function URL_Param_Filter(urlParams) {
476
+ *
477
+ * var params = urlParams || {};
478
+ * // check for item in object
479
+ * if(params.utm_source !== "undefined"){
480
+ * //alert('url param "utm_source" is here');
481
+ * }
482
+ *
483
+ * // delete item from object
484
+ * delete params.utm_source;
485
+ *
486
+ * return params;
487
+ *
488
+ * }
489
+ * ```
490
+ */
491
+ _inbound.add_filter = function() {
492
+ // prefix action
493
+ arguments[0] = 'inbound.' + arguments[0];
494
+ _inbound.hooks.addFilter.apply(this, arguments);
495
+
496
+ return this;
497
+
498
+ };
499
+ /*
500
+ * remove_filter
501
+ *
502
+ * This function uses _inbound.hooks to mimics WP remove_filter
503
+ *
504
+ * ```js
505
+ * // Add remove filter 'urlParamFilter'
506
+ * _inbound.remove_action( 'urlParamFilter');
507
+ * ```
508
+ *
509
+ */
510
+ _inbound.remove_filter = function() {
511
+ // prefix action
512
+ arguments[0] = 'inbound.' + arguments[0];
513
+
514
+ _inbound.hooks.removeFilter.apply(this, arguments);
515
+
516
+ return this;
517
+
518
+ };
519
+ /*
520
+ * apply_filters
521
+ *
522
+ * This function uses _inbound.hooks to mimics WP apply_filters
523
+ *
524
+ */
525
+ _inbound.apply_filters = function() {
526
+ //console.log('Filter:' + arguments[0] + " ran on ->", arguments[1]);
527
+ // prefix action
528
+ arguments[0] = 'inbound.' + arguments[0];
529
+
530
+ return _inbound.hooks.applyFilters.apply(this, arguments);
531
+
532
+ };
533
+
534
+
535
+ return _inbound;
536
+
537
+ })(_inbound || {});
538
+ /**
539
+ * # _inbound UTILS
540
+ *
541
+ * This file contains all of the utility functions used by analytics
542
+ *
543
+ * @contributor David Wells <david@inboundnow.com>
544
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
545
+ * @version 0.0.2
546
+ */
547
+
548
+ var _inboundUtils = (function(_inbound) {
549
+
550
+ var storageSupported,
551
+ corsEnabled = window.XMLHttpRequest && 'withCredentials' in new XMLHttpRequest(),
552
+ toString = Object.prototype.toString,
553
+ currentPage = ('https:' == location.protocol ? 'https://' : 'http://') + location.hostname + location.pathname.replace(/\/$/, "");
554
+
555
+ var settings = {
556
+ api_host: currentPage,
557
+ track_pageview: true,
558
+ track_links_timeout: 300,
559
+ cookie_name: '_sp',
560
+ cookie_expiration: 365,
561
+ cookie_domain: (host = location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i)) ? host[0] : ''
562
+ };
563
+
564
+ _inbound.Utils = {
565
+ init: function() {
566
+
567
+ this.polyFills();
568
+ this.checkLocalStorage();
569
+ this.SetUID();
570
+ this.storeReferralData();
571
+
572
+ },
573
+ /*! http://stackoverflow.com/questions/951791/javascript-global-error-handling */
574
+ /* Polyfills for missing browser functionality */
575
+ polyFills: function() {
576
+ /* Console.log fix for old browsers */
577
+ if (!window.console) {
578
+ window.console = {};
579
+ }
580
+ var m = [
581
+ "log", "info", "warn", "error", "debug", "trace", "dir", "group",
582
+ "groupCollapsed", "groupEnd", "time", "timeEnd", "profile", "profileEnd",
583
+ "dirxml", "assert", "count", "markTimeline", "timeStamp", "clear"
584
+ ];
585
+ // define undefined methods as noops to prevent errors
586
+ for (var i = 0; i < m.length; i++) {
587
+ if (!window.console[m[i]]) {
588
+ window.console[m[i]] = function() {};
589
+ }
590
+ }
591
+ /* Event trigger polyfill for IE9 and 10
592
+ (function() {
593
+ function CustomEvent(event, params) {
594
+ params = params || {
595
+ bubbles: false,
596
+ cancelable: false,
597
+ detail: undefined
598
+ };
599
+ var evt = document.createEvent('CustomEvent');
600
+ evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail);
601
+ return evt;
602
+ }
603
+
604
+ CustomEvent.prototype = window.Event.prototype;
605
+
606
+ window.CustomEvent = CustomEvent;
607
+ })();*/
608
+
609
+ /*\
610
+ |*| Polyfill Date.toISOString
611
+ \*/
612
+ if (!Date.prototype.toISOString) {
613
+ (function() {
614
+ /**
615
+ * @param {number} text
616
+ * @returns {?}
617
+ */
618
+ function pad(text) {
619
+ /** @type {string} */
620
+ var code = String(text);
621
+ return 1 === code.length && (code = '0' + code), code;
622
+ }
623
+ /**
624
+ * @returns {string}
625
+ */
626
+ Date.prototype.toISOString = function() {
627
+ return this.getUTCFullYear() + '-' + pad(this.getUTCMonth() + 1) + '-' + pad(this.getUTCDate()) + 'T' + pad(this.getUTCHours()) + ':' + pad(this.getUTCMinutes()) + ':' + pad(this.getUTCSeconds()) + '.' + String((this.getUTCMilliseconds() / 1E3).toFixed(3)).slice(2, 5) + 'Z';
628
+ };
629
+ })();
630
+ }
631
+
632
+ /* custom event for ie8+ https://gist.github.com/WebReflection/6693661 */
633
+ try {
634
+ new CustomEvent('?');
635
+ } catch (o_O) {
636
+ /*!(C) Andrea Giammarchi -- WTFPL License*/
637
+ this.CustomEvent = function(
638
+ eventName,
639
+ defaultInitDict
640
+ ) {
641
+
642
+ // the infamous substitute
643
+ function CustomEvent(type, eventInitDict) {
644
+ var event = document.createEvent(eventName);
645
+ if (type !== null) {
646
+ initCustomEvent.call(
647
+ event,
648
+ type, (eventInitDict || (
649
+ // if falsy we can just use defaults
650
+ eventInitDict = defaultInitDict
651
+ )).bubbles,
652
+ eventInitDict.cancelable,
653
+ eventInitDict.detail
654
+ );
655
+ } else {
656
+ // no need to put the expando property otherwise
657
+ // since an event cannot be initialized twice
658
+ // previous case is the most common one anyway
659
+ // but if we end up here ... there it goes
660
+ event.initCustomEvent = initCustomEvent;
661
+ }
662
+ return event;
663
+ }
664
+
665
+ // borrowed or attached at runtime
666
+ function initCustomEvent(
667
+ type, bubbles, cancelable, detail
668
+ ) {
669
+ this['init' + eventName](type, bubbles, cancelable, detail);
670
+ 'detail' in this || (this.detail = detail);
671
+ }
672
+
673
+ // that's it
674
+ return CustomEvent;
675
+ }(
676
+ // is this IE9 or IE10 ?
677
+ // where CustomEvent is there
678
+ // but not usable as construtor ?
679
+ this.CustomEvent ?
680
+ // use the CustomEvent interface in such case
681
+ 'CustomEvent' : 'Event',
682
+ // otherwise the common compatible one
683
+ {
684
+ bubbles: false,
685
+ cancelable: false,
686
+ detail: null
687
+ }
688
+ );
689
+ }
690
+ /* querySelectorAll polyfill for ie7+ */
691
+ if (!document.querySelectorAll) {
692
+ document.querySelectorAll = function(selectors) {
693
+ var style = document.createElement('style'),
694
+ elements = [],
695
+ element;
696
+ document.documentElement.firstChild.appendChild(style);
697
+ document._qsa = [];
698
+
699
+ style.styleSheet.cssText = selectors + '{x-qsa:expression(document._qsa && document._qsa.push(this))}';
700
+ window.scrollBy(0, 0);
701
+ style.parentNode.removeChild(style);
702
+
703
+ while (document._qsa.length) {
704
+ element = document._qsa.shift();
705
+ element.style.removeAttribute('x-qsa');
706
+ elements.push(element);
707
+ }
708
+ document._qsa = null;
709
+ return elements;
710
+ };
711
+ }
712
+
713
+ if (!document.querySelector) {
714
+ document.querySelector = function(selectors) {
715
+ var elements = document.querySelectorAll(selectors);
716
+ return (elements.length) ? elements[0] : null;
717
+ };
718
+ }
719
+ /* Innertext shim for firefox https://github.com/duckinator/innerText-polyfill/blob/master/innertext.js */
720
+ if ((!('innerText' in document.createElement('a'))) && ('getSelection' in window)) {
721
+ HTMLElement.prototype.__defineGetter__("innerText", function() {
722
+ var selection = window.getSelection(),
723
+ ranges = [],
724
+ str;
725
+
726
+ // Save existing selections.
727
+ for (var i = 0; i < selection.rangeCount; i++) {
728
+ ranges[i] = selection.getRangeAt(i);
729
+ }
730
+
731
+ // Deselect everything.
732
+ selection.removeAllRanges();
733
+
734
+ // Select `el` and all child nodes.
735
+ // 'this' is the element .innerText got called on
736
+ selection.selectAllChildren(this);
737
+
738
+ // Get the string representation of the selected nodes.
739
+ str = selection.toString();
740
+
741
+ // Deselect everything. Again.
742
+ selection.removeAllRanges();
743
+
744
+ // Restore all formerly existing selections.
745
+ for (var i = 0; i < ranges.length; i++) {
746
+ selection.addRange(ranges[i]);
747
+ }
748
+
749
+ // Oh look, this is what we wanted.
750
+ // String representation of the element, close to as rendered.
751
+ return str;
752
+ })
753
+ }
754
+ },
755
+ /**
756
+ * Create cookie
757
+ *
758
+ * ```js
759
+ * // Creates cookie for 10 days
760
+ * _inbound.Utils.createCookie( 'cookie_name', 'value', 10 );
761
+ * ```
762
+ *
763
+ * @param {string} name Name of cookie
764
+ * @param {string} value Value of cookie
765
+ * @param {string} days Length of storage
766
+ */
767
+ createCookie: function(name, value, days) {
768
+ var expires = "";
769
+ if (days) {
770
+ var date = new Date();
771
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
772
+ expires = "; expires=" + date.toGMTString();
773
+ }
774
+ document.cookie = name + "=" + value + expires + "; path=/";
775
+ },
776
+ /**
777
+ * Read cookie value
778
+ *
779
+ * ```js
780
+ * var cookie = _inbound.Utils.readCookie( 'cookie_name' );
781
+ * console.log(cookie); // cookie value
782
+ * ```
783
+ * @param {string} name name of cookie
784
+ * @return {string} value of cookie
785
+ */
786
+ readCookie: function(name) {
787
+ var nameEQ = name + "=";
788
+ var ca = document.cookie.split(';');
789
+ for (var i = 0; i < ca.length; i++) {
790
+ var c = ca[i];
791
+ while (c.charAt(0) === ' ') {
792
+ c = c.substring(1, c.length);
793
+ }
794
+ if (c.indexOf(nameEQ) === 0) {
795
+ return c.substring(nameEQ.length, c.length);
796
+ }
797
+ }
798
+ return null;
799
+ },
800
+ /**
801
+ * Erase cookie
802
+ *
803
+ * ```js
804
+ * // usage:
805
+ * _inbound.Utils.eraseCookie( 'cookie_name' );
806
+ * // deletes 'cookie_name' value
807
+ * ```
808
+ * @param {string} name name of cookie
809
+ * @return {string} value of cookie
810
+ */
811
+ eraseCookie: function(name) {
812
+ this.createCookie(name, "", -1);
813
+ },
814
+ /* Get All Cookies */
815
+ getAllCookies: function() {
816
+ var cookies = {};
817
+ if (document.cookie && document.cookie !== '') {
818
+ var split = document.cookie.split(';');
819
+ for (var i = 0; i < split.length; i++) {
820
+ var name_value = split[i].split("=");
821
+ name_value[0] = name_value[0].replace(/^ /, '');
822
+ cookies[decodeURIComponent(name_value[0])] = decodeURIComponent(name_value[1]);
823
+ }
824
+ }
825
+ _inbound.totalStorage('inbound_cookies', cookies); // store cookie data
826
+ return cookies;
827
+ },
828
+ /* Grab URL params and save */
829
+ setUrlParams: function() {
830
+ var urlParams = {};
831
+
832
+ (function() {
833
+ var e,
834
+ d = function(s) {
835
+ return decodeURIComponent(s).replace(/\+/g, " ");
836
+ },
837
+ q = window.location.search.substring(1),
838
+ r = /([^&=]+)=?([^&]*)/g;
839
+
840
+ while (e = r.exec(q)) {
841
+ if (e[1].indexOf("[") == "-1")
842
+ urlParams[d(e[1])] = d(e[2]);
843
+ else {
844
+ var b1 = e[1].indexOf("["),
845
+ aN = e[1].slice(b1 + 1, e[1].indexOf("]", b1)),
846
+ pN = d(e[1].slice(0, b1));
847
+
848
+ if (typeof urlParams[pN] != "object")
849
+ urlParams[d(pN)] = {},
850
+ urlParams[d(pN)].length = 0;
851
+
852
+ if (aN)
853
+ urlParams[d(pN)][d(aN)] = d(e[2]);
854
+ else
855
+ Array.prototype.push.call(urlParams[d(pN)], d(e[2]));
856
+
857
+ }
858
+ }
859
+ })();
860
+
861
+ /* Set Param Cookies */
862
+ for (var k in urlParams) {
863
+ /* account for wordpress media uploader bug */
864
+ if (k == 'action') {
865
+ continue;
866
+ }
867
+
868
+ if (typeof urlParams[k] == "object") {
869
+ for (var k2 in urlParams[k])
870
+ this.createCookie(k2, urlParams[k][k2], 30);
871
+ } else {
872
+ this.createCookie(k, urlParams[k], 30);
873
+ }
874
+ }
875
+ /* Set Param LocalStorage */
876
+ if (storageSupported) {
877
+ var pastParams = _inbound.totalStorage('inbound_url_params') || {};
878
+ var params = this.mergeObjs(pastParams, urlParams);
879
+ _inbound.totalStorage('inbound_url_params', params); // store cookie data
880
+ }
881
+
882
+ var options = {
883
+ 'option1': 'yo',
884
+ 'option2': 'woooo'
885
+ };
886
+
887
+ _inbound.trigger('url_parameters', urlParams, options);
888
+
889
+ },
890
+ getAllUrlParams: function() {
891
+ var get_params = {};
892
+ if (storageSupported) {
893
+ var get_params = _inbound.totalStorage('inbound_url_params');
894
+ }
895
+ return get_params;
896
+ },
897
+ /* Get url param */
898
+ getParameterVal: function(name, string) {
899
+ return (RegExp(name + '=' + '(.+?)(&|$)').exec(string) || [, false])[1];
900
+ },
901
+ // Check local storage
902
+ // provate browsing safari fix https://github.com/marcuswestin/store.js/issues/42#issuecomment-25274685
903
+ checkLocalStorage: function() {
904
+ if ('localStorage' in window) {
905
+ try {
906
+ ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
907
+ if (typeof ls == 'undefined' || typeof window.JSON == 'undefined') {
908
+ storageSupported = false;
909
+ } else {
910
+ storageSupported = true;
911
+ }
912
+
913
+ } catch (err) {
914
+ storageSupported = false;
915
+ }
916
+ }
917
+ return storageSupported;
918
+ /* http://spin.atomicobject.com/2013/01/23/ios-private-browsing-localstorage/
919
+ var hasStorage;
920
+ hasStorage = function() {
921
+ var mod, result;
922
+ try {
923
+ mod = new Date;
924
+ localStorage.setItem(mod, mod.toString());
925
+ result = localStorage.getItem(mod) === mod.toString();
926
+ localStorage.removeItem(mod);
927
+ return result;
928
+ } catch (_error) {}
929
+ };
930
+ */
931
+ },
932
+ // http://stackoverflow.com/questions/4391575/how-to-find-the-size-of-localstorage
933
+ showLocalStorageSize: function() {
934
+ function stringSizeBytes(str) {
935
+ return str.length * 2;
936
+ }
937
+
938
+ function toMB(bytes) {
939
+ return bytes / 1024 / 1024;
940
+ }
941
+
942
+ function toSize(key) {
943
+ return {
944
+ name: key,
945
+ size: stringSizeBytes(localStorage[key])
946
+ };
947
+ }
948
+
949
+ function toSizeMB(info) {
950
+ info.size = toMB(info.size).toFixed(2) + ' MB';
951
+ return info;
952
+ }
953
+
954
+ var sizes = Object.keys(localStorage).map(toSize).map(toSizeMB);
955
+
956
+ console.table(sizes);
957
+ },
958
+ /* Add days to datetime */
959
+ addDays: function(myDate, days) {
960
+ return new Date(myDate.getTime() + days * 24 * 60 * 60 * 1000);
961
+ },
962
+ GetDate: function() {
963
+ var timeNow = new Date(),
964
+ d = timeNow.getDate(),
965
+ dPre = (d < 10) ? "0" : "",
966
+ y = timeNow.getFullYear(),
967
+ h = timeNow.getHours(),
968
+ hPre = (h < 10) ? "0" : "",
969
+ min = timeNow.getMinutes(),
970
+ minPre = (min < 10) ? "0" : "",
971
+ sec = timeNow.getSeconds(),
972
+ secPre = (sec < 10) ? "0" : "",
973
+ m = timeNow.getMonth() + 1,
974
+ mPre = (m < 10) ? "0" : "";
975
+
976
+ var datetime = y + '/' + mPre + m + "/" + dPre + d + " " + hPre + h + ":" + minPre + min + ":" + secPre + sec;
977
+ /* format 2014/11/13 18:22:02 */
978
+ return datetime;
979
+ },
980
+ /* Set Expiration Date of Session Logging. LEGACY Not in Use */
981
+ SetSessionTimeout: function() {
982
+ var session = this.readCookie("lead_session_expire");
983
+ //console.log(session_check);
984
+ if (!session) {
985
+ //_inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
986
+ } else {
987
+ //_inbound.trigger('session_resume'); // trigger 'inbound_analytics_session_active'
988
+ }
989
+ var d = new Date();
990
+ d.setTime(d.getTime() + 30 * 60 * 1000);
991
+
992
+ this.createCookie("lead_session_expire", true, d); // Set cookie on page load
993
+
994
+ },
995
+ storeReferralData: function() {
996
+ //console.log(expire_time);
997
+ var d = new Date(),
998
+ referrer = document.referrer || "Direct Traffic",
999
+ referrer_cookie = _inbound.Utils.readCookie("inbound_referral_site"),
1000
+ original_src = _inbound.totalStorage('inbound_original_referral');
1001
+
1002
+ d.setTime(d.getTime() + 30 * 60 * 1000);
1003
+
1004
+ if (!referrer_cookie) {
1005
+ this.createCookie("inbound_referral_site", referrer, d);
1006
+ }
1007
+ if (!original_src) {
1008
+ _inbound.totalStorage('inbound_original_referral', original_src);
1009
+ }
1010
+ },
1011
+ CreateUID: function(length) {
1012
+ var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'.split(''),
1013
+ str = '';
1014
+ if (!length) {
1015
+ length = Math.floor(Math.random() * chars.length);
1016
+ }
1017
+ for (var i = 0; i < length; i++) {
1018
+ str += chars[Math.floor(Math.random() * chars.length)];
1019
+ }
1020
+ return str;
1021
+ },
1022
+ generateGUID: function(a) {
1023
+ return a ? (a ^ 16 * Math.random() >> a / 4).toString(16) : ([1E7] + -1E3 + -4E3 + -8E3 + -1E11).replace(/[018]/g, guid);
1024
+ },
1025
+ SetUID: function(leadUID) {
1026
+ /* Set Lead UID */
1027
+ if (!this.readCookie("wp_lead_uid")) {
1028
+ var wp_lead_uid = leadUID || this.CreateUID(35);
1029
+ this.createCookie("wp_lead_uid", wp_lead_uid);
1030
+ }
1031
+ },
1032
+ /* Count number of session visits */
1033
+ countProperties: function(obj) {
1034
+ var count = 0;
1035
+ for (var prop in obj) {
1036
+ if (obj.hasOwnProperty(prop)) {
1037
+ ++count;
1038
+ }
1039
+ }
1040
+ return count;
1041
+ },
1042
+ mergeObjs: function(obj1, obj2) {
1043
+ var obj3 = {};
1044
+ for (var attrname in obj1) {
1045
+ obj3[attrname] = obj1[attrname];
1046
+ }
1047
+ for (var attrname in obj2) {
1048
+ obj3[attrname] = obj2[attrname];
1049
+ }
1050
+ return obj3;
1051
+ },
1052
+ hasClass: function(className, el) {
1053
+ var hasClass;
1054
+ if ('classList' in document.documentElement) {
1055
+ var hasClass = el.classList.contains(className);
1056
+ } else {
1057
+ var hasClass = new RegExp('(^|\\s)' + className + '(\\s|$)').test(el.className); /* IE Polyfill */
1058
+ }
1059
+ return hasClass;
1060
+ },
1061
+ addClass: function(className, el) {
1062
+ if ('classList' in document.documentElement) {
1063
+ el.classList.add(className);
1064
+ } else {
1065
+ if (!this.hasClass(el, className)) {
1066
+ el.className += (el.className ? ' ' : '') + className;
1067
+ }
1068
+ }
1069
+ },
1070
+ removeClass: function(className, el) {
1071
+ if ('classList' in document.documentElement) {
1072
+ el.classList.remove(className);
1073
+ } else {
1074
+ if (this.hasClass(el, className)) {
1075
+ el.className = el.className.replace(new RegExp('(^|\\s)*' + className + '(\\s|$)*', 'g'), '');
1076
+ }
1077
+ }
1078
+ },
1079
+ removeElement: function(el) {
1080
+ el.parentNode.removeChild(el);
1081
+ },
1082
+ trim: function(s) {
1083
+ s = s.replace(/(^\s*)|(\s*$)/gi, "");
1084
+ s = s.replace(/[ ]{2,}/gi, " ");
1085
+ s = s.replace(/\n /, "\n");
1086
+ return s;
1087
+ },
1088
+ ajaxPolyFill: function() {
1089
+ if (typeof XMLHttpRequest !== 'undefined') {
1090
+ return new XMLHttpRequest();
1091
+ }
1092
+ var versions = [
1093
+ "MSXML2.XmlHttp.5.0",
1094
+ "MSXML2.XmlHttp.4.0",
1095
+ "MSXML2.XmlHttp.3.0",
1096
+ "MSXML2.XmlHttp.2.0",
1097
+ "Microsoft.XmlHttp"
1098
+ ];
1099
+
1100
+ var xhr;
1101
+ for (var i = 0; i < versions.length; i++) {
1102
+ try {
1103
+ xhr = new ActiveXObject(versions[i]);
1104
+ break;
1105
+ } catch (e) {}
1106
+ }
1107
+ return xhr;
1108
+ },
1109
+ ajaxSendData: function(url, callback, method, data, sync) {
1110
+ var x = this.ajaxPolyFill();
1111
+ /* timeout for safari idiocy */
1112
+ setTimeout(function() {
1113
+ x.open(method, url, true);
1114
+ x.onreadystatechange = function() {
1115
+ if (x.readyState == 4) {
1116
+ callback(x.responseText)
1117
+ }
1118
+ };
1119
+ if (method == 'POST') {
1120
+ x.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
1121
+ }
1122
+ x.send(data);
1123
+ }, 100);
1124
+ },
1125
+ ajaxGet: function(url, data, callback, sync) {
1126
+ var query = [];
1127
+ for (var key in data) {
1128
+ query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
1129
+ }
1130
+ this.ajaxSendData(url + '?' + query.join('&'), callback, 'GET', null, sync)
1131
+ },
1132
+ ajaxPost: function(url, data, callback, sync) {
1133
+ var query = [];
1134
+ for (var key in data) {
1135
+ query.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]));
1136
+ }
1137
+ this.ajaxSendData(url, callback, 'POST', query.join('&'), sync)
1138
+ },
1139
+ /**
1140
+ * @param {string} event
1141
+ * @param {(Object|null)} properties
1142
+ * @param {(Function|null)} callback
1143
+ */
1144
+ sendEvent: function(event, properties, callback) {
1145
+ properties = properties || {};
1146
+ async = true;
1147
+ var cookieData = getCookie(); /* get cookie data */
1148
+ if (cookieData) {
1149
+ var key;
1150
+ for (key in cookieData) {
1151
+ properties[key] = cookieData[key];
1152
+ }
1153
+ }
1154
+ if (!properties.id) {
1155
+ properties.id = getId();
1156
+ }
1157
+ var props = {
1158
+ e: event,
1159
+ t: (new Date()).toISOString(),
1160
+ kv: properties
1161
+ };
1162
+ var path = settings.api_host + '/track?data=' + encodeURIComponent(JSON.stringify(props));
1163
+ if (corsEnabled) {
1164
+ /* CORS */
1165
+ var xhr = new XMLHttpRequest();
1166
+ xhr.open('GET', path, async);
1167
+ xhr.withCredentials = async;
1168
+ xhr.send(null);
1169
+ } else {
1170
+ /* jsonP */
1171
+ var el = document.createElement('script');
1172
+ el.type = 'text/javascript';
1173
+ el.async = async;
1174
+ el.defer = async;
1175
+ el.src = path;
1176
+ var insertAt = document.getElementsByTagName('script')[0];
1177
+ insertAt.parentNode.insertBefore(el, insertAt);
1178
+ }
1179
+ return action(callback), self;
1180
+ },
1181
+ domReady: function(win, fn) {
1182
+
1183
+ var done = false,
1184
+ top = true,
1185
+
1186
+ doc = win.document,
1187
+ root = doc.documentElement,
1188
+
1189
+ add = doc.addEventListener ? 'addEventListener' : 'attachEvent',
1190
+ rem = doc.addEventListener ? 'removeEventListener' : 'detachEvent',
1191
+ pre = doc.addEventListener ? '' : 'on',
1192
+
1193
+ init = function(e) {
1194
+ if (e.type == 'readystatechange' && doc.readyState != 'complete') return;
1195
+ (e.type == 'load' ? win : doc)[rem](pre + e.type, init, false);
1196
+ if (!done && (done = true)) fn.call(win, e.type || e);
1197
+ },
1198
+
1199
+ poll = function() {
1200
+ try {
1201
+ root.doScroll('left');
1202
+ } catch (e) {
1203
+ setTimeout(poll, 50);
1204
+ return;
1205
+ }
1206
+ init('poll');
1207
+ };
1208
+
1209
+ if (doc.readyState == 'complete') {
1210
+
1211
+ fn.call(win, 'lazy');
1212
+
1213
+ } else {
1214
+ if (doc.createEventObject && root.doScroll) {
1215
+ try {
1216
+ top = !win.frameElement;
1217
+ } catch (e) {}
1218
+ if (top) poll();
1219
+ }
1220
+ doc[add](pre + 'DOMContentLoaded', init, false);
1221
+ doc[add](pre + 'readystatechange', init, false);
1222
+ win[add](pre + 'load', init, false);
1223
+ }
1224
+
1225
+ },
1226
+ /* Cross-browser event listening */
1227
+ addListener: function(element, eventName, listener) {
1228
+ if (!element) {
1229
+ return;
1230
+ }
1231
+ //console.log(eventName);
1232
+ //console.log(listener);
1233
+ if (element.addEventListener) {
1234
+ element.addEventListener(eventName, listener, false);
1235
+ } else if (element.attachEvent) {
1236
+ element.attachEvent("on" + eventName, listener);
1237
+ } else {
1238
+ element['on' + eventName] = listener;
1239
+ }
1240
+ },
1241
+ removeListener: function(element, eventName, listener) {
1242
+
1243
+ if (element.removeEventListener) {
1244
+ element.removeEventListener(eventName, listener, false);
1245
+ } else if (element.detachEvent) {
1246
+ element.detachEvent("on" + eventName, listener);
1247
+ } else {
1248
+ element["on" + eventName] = null;
1249
+ }
1250
+ },
1251
+ /*
1252
+ * Throttle function borrowed from:
1253
+ * Underscore.js 1.5.2
1254
+ * http://underscorejs.org
1255
+ * (c) 2009-2013 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
1256
+ * Underscore may be freely distributed under the MIT license.
1257
+ */
1258
+ throttle: function(func, wait) {
1259
+ var context, args, result;
1260
+ var timeout = null;
1261
+ var previous = 0;
1262
+ var later = function() {
1263
+ previous = new Date;
1264
+ timeout = null;
1265
+ result = func.apply(context, args);
1266
+ };
1267
+ return function() {
1268
+ var now = new Date;
1269
+ if (!previous) previous = now;
1270
+ var remaining = wait - (now - previous);
1271
+ context = this;
1272
+ args = arguments;
1273
+ if (remaining <= 0) {
1274
+ clearTimeout(timeout);
1275
+ timeout = null;
1276
+ previous = now;
1277
+ result = func.apply(context, args);
1278
+ } else if (!timeout) {
1279
+ timeout = setTimeout(later, remaining);
1280
+ }
1281
+ return result;
1282
+ };
1283
+ },
1284
+ /*
1285
+ * Determine which version of GA is being used
1286
+ * "ga", "_gaq", and "dataLayer" are the possible globals
1287
+ */
1288
+ checkTypeofGA: function() {
1289
+ if (typeof ga === "function") {
1290
+ universalGA = true;
1291
+ }
1292
+
1293
+ if (typeof _gaq !== "undefined" && typeof _gaq.push === "function") {
1294
+ classicGA = true;
1295
+ }
1296
+
1297
+ if (typeof dataLayer !== "undefined" && typeof dataLayer.push === "function") {
1298
+ googleTagManager = true;
1299
+ }
1300
+
1301
+ },
1302
+ /**
1303
+ * Caches user's search data in the browser until they can be saved to the database
1304
+ */
1305
+ cacheSearchData: function(searchData, form) {
1306
+
1307
+ if(storageSupported){
1308
+ //store the searches in the local storage
1309
+ var stored = _inbound.totalStorage.getItem('inbound_search_storage');
1310
+ if(stored){
1311
+ //if there are stored searches, put the new one in the first index
1312
+ stored.unshift(searchData);
1313
+ _inbound.totalStorage.setItem('inbound_search_storage', stored);
1314
+ }else{
1315
+ //if there aren't any searches stored, save the current search
1316
+ var store = [searchData];
1317
+ _inbound.totalStorage.setItem('inbound_search_storage', store);
1318
+ }
1319
+ }else{
1320
+ //if local storage is not possible, store the data in a cookie
1321
+ var new_search = JSON.stringify(searchData),
1322
+ stored_searches = this.readCookie('inbound_search_storage');
1323
+
1324
+ if(stored_searches){
1325
+ //add the old searches to the new one
1326
+ new_search += ('SPLIT-TOKEN' + stored_searches);
1327
+ }
1328
+ this.createCookie('inbound_search_storage', new_search, '180');
1329
+ }
1330
+
1331
+ _inbound.Forms.releaseFormSubmit(form);
1332
+ },
1333
+ /**
1334
+ * Stores search data to the database on page load.
1335
+ * If successful, it erases the cached searches from the user's browser
1336
+ */
1337
+ storeSearchData: function(){
1338
+
1339
+ /*if there isn't a lead id or the nonce isn't set, don't try to store the data*/
1340
+ if(!inbound_settings.wp_lead_data.lead_id || !inbound_settings.wp_lead_data.lead_nonce){
1341
+ return;
1342
+ }
1343
+
1344
+ var dataToSend = [],
1345
+ localStorageData = _inbound.totalStorage.getItem('inbound_search_storage'),
1346
+ cookieStorageData = this.readCookie('inbound_search_storage');
1347
+
1348
+ /*if nothing is stored, exit*/
1349
+ if(!localStorageData && !cookieStorageData){
1350
+ return;
1351
+ }
1352
+
1353
+ /*if set, add the cookie search data to the data to send*/
1354
+ if(cookieStorageData){
1355
+ cookieStorageData = cookieStorageData.split('SPLIT-TOKEN');
1356
+
1357
+ for(var i in cookieStorageData){
1358
+ //console.log(cookieStorageData[i]);
1359
+ dataToSend.push(JSON.parse(cookieStorageData[i]));
1360
+ }
1361
+ }
1362
+
1363
+ /*if set, add the locally stored data to the data to send*/
1364
+ if(localStorageData){
1365
+ dataToSend = dataToSend.concat(localStorageData);
1366
+ }
1367
+
1368
+ dataToSend.sort(function(a, b){ return a.timestamp - b.timestamp; });
1369
+
1370
+ dataToSend = encodeURIComponent(JSON.stringify(dataToSend));
1371
+
1372
+ var package = {'action' : 'inbound_search_store', 'data' : dataToSend, 'nonce' : inbound_settings.wp_lead_data.lead_nonce, 'lead_id' : inbound_settings.wp_lead_data.lead_id };
1373
+
1374
+ callback = function(status){
1375
+ if(status){ status = JSON.parse(status); }
1376
+
1377
+ if(status.success){
1378
+ //log the success!
1379
+ console.log(status.success);
1380
+ //erase the stored data
1381
+ _inbound.Utils.eraseCookie('inbound_search_storage');
1382
+ _inbound.totalStorage.deleteItem('inbound_search_storage');
1383
+ }
1384
+
1385
+ if(status.error){
1386
+ console.log(status.error);
1387
+ }
1388
+ };
1389
+ this.ajaxPost(inbound_settings.admin_url, package, callback);
1390
+ }
1391
+ };
1392
+
1393
+ return _inbound;
1394
+
1395
+ })(_inbound || {});
1396
+
1397
+ /**
1398
+ * # Inbound Forms
1399
+ *
1400
+ * This file contains all of the form functions of the main _inbound object.
1401
+ * Filters and actions are described below
1402
+ *
1403
+ * @contributor David Wells <david@inboundnow.com>
1404
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
1405
+ * @version 0.0.2
1406
+ */
1407
+ /* Finish Exclusions for CC */
1408
+
1409
+ /* Launches form class */
1410
+ var InboundForms = (function(_inbound) {
1411
+
1412
+ var debugMode = false,
1413
+ utils = _inbound.Utils,
1414
+ no_match = [],
1415
+ rawParams = [],
1416
+ mappedParams = [],
1417
+ callTracker = {},
1418
+ settings = _inbound.Settings;
1419
+
1420
+ var FieldMapArray = [
1421
+ "first name",
1422
+ "last name",
1423
+ "name",
1424
+ "email",
1425
+ "e-mail",
1426
+ "phone",
1427
+ "website",
1428
+ "job title",
1429
+ "your favorite food",
1430
+ "company",
1431
+ "tele",
1432
+ "address",
1433
+ "comment"
1434
+ /* Adding values here maps them */
1435
+ ];
1436
+
1437
+ _inbound.Forms = {
1438
+
1439
+ // Init Form functions
1440
+ init: function() {
1441
+ _inbound.Forms.runFieldMappingFilters();
1442
+ _inbound.Forms.formTrackInit();
1443
+ _inbound.Forms.searchTrackInit();
1444
+ },
1445
+ /**
1446
+ * This triggers the forms.field_map filter on the mapping array.
1447
+ * This will allow you to add or remore Items from the mapping lookup
1448
+ *
1449
+ * ### Example inbound.form_map_before filter
1450
+ *
1451
+ * This is an example of how form mapping can be filtered and
1452
+ * additional fields can be mapped via javascript
1453
+ *
1454
+ * ```js
1455
+ * // Adding the filter function
1456
+ * function Inbound_Add_Filter_Example( FieldMapArray ) {
1457
+ * var map = FieldMapArray || [];
1458
+ * map.push('new lookup value');
1459
+ *
1460
+ * return map;
1461
+ * };
1462
+ *
1463
+ * // Adding the filter on dom ready
1464
+ * _inbound.hooks.addFilter( 'inbound.form_map_before', Inbound_Add_Filter_Example, 10 );
1465
+ * ```
1466
+ *
1467
+ * @return {[type]} [description]
1468
+ */
1469
+ runFieldMappingFilters: function() {
1470
+ FieldMapArray = _inbound.hooks.applyFilters('forms.field_map', FieldMapArray);
1471
+ //alert(FieldMapArray);
1472
+ },
1473
+ debug: function(msg, callback) {
1474
+ //if app not in debug mode, exit immediately
1475
+ if (!debugMode || !console) {
1476
+ return;
1477
+ }
1478
+
1479
+ var msg = msg || false;
1480
+ //console.log the message
1481
+ if (msg && (typeof msg === 'string')) {
1482
+ console.log(msg);
1483
+ }
1484
+
1485
+ //execute the callback if one was passed-in
1486
+ if (callback && (callback instanceof Function)) {
1487
+ callback();
1488
+ }
1489
+ },
1490
+ formTrackInit: function() {
1491
+
1492
+ for (var i = 0; i < window.document.forms.length; i++) {
1493
+ var trackForm = false;
1494
+ var form = window.document.forms[i];
1495
+ /* process forms only once */
1496
+ if (!form.dataset.formProcessed) {
1497
+ form.dataset.formProcessed = true;
1498
+ trackForm = this.checkTrackStatus(form);
1499
+ if (trackForm) {
1500
+ this.attachFormSubmitEvent(form); /* attach form listener */
1501
+ this.initFormMapping(form);
1502
+ }
1503
+ }
1504
+ }
1505
+ },
1506
+ searchTrackInit: function(){
1507
+
1508
+ /* exit if searches aren't supposed to be tracked, or this function has already been called */
1509
+ if(inbound_settings.search_tracking == 'off' || callTracker['searchTrackInit']){
1510
+ return;
1511
+ }
1512
+
1513
+ for (var i = 0; i < window.document.forms.length; i++) {
1514
+ var trackForm = false;
1515
+ var form = window.document.forms[i];
1516
+ /* process forms only once */
1517
+ if (!form.dataset.searchChecked) {
1518
+ form.dataset.searchChecked = true;
1519
+ trackForm = this.checkSearchTrackStatus(form);
1520
+ if (trackForm) {
1521
+ this.attachSearchFormSubmitEvent(form); /* attach form listener */
1522
+ }
1523
+ }
1524
+ }
1525
+
1526
+ /* store the search data on init */
1527
+ utils.storeSearchData();
1528
+
1529
+ /* log that this function has been called */
1530
+ callTracker['searchTrackInit'] = true;
1531
+ },
1532
+ checkTrackStatus: function(form) {
1533
+ var ClassIs = form.getAttribute('class');
1534
+ if (ClassIs !== "" && ClassIs !== null) {
1535
+ if (ClassIs.toLowerCase().indexOf("wpl-track-me") > -1) {
1536
+ return true;
1537
+ } else if (ClassIs.toLowerCase().indexOf("inbound-track") > -1) {
1538
+ return true;
1539
+ } else {
1540
+ cb = function() { console.log(form); };
1541
+ _inbound.deBugger('forms', "This form not tracked. Please assign on in settings...", cb);
1542
+ return false;
1543
+ }
1544
+ }
1545
+ },
1546
+ checkSearchTrackStatus: function(form) {
1547
+ var ClassIs = form.getAttribute('class'),
1548
+ IdIs = form.getAttribute('id');
1549
+ if (ClassIs !== "" && ClassIs !== null) {
1550
+ if (ClassIs.toLowerCase().indexOf("search") > -1) {
1551
+ return true;
1552
+ }
1553
+ }
1554
+ if (IdIs !== "" && IdIs !== null) {
1555
+ if (IdIs.toLowerCase().indexOf("search") > -1) {
1556
+ return true;
1557
+ }
1558
+ }else{
1559
+ cb = function() { console.log(form); };
1560
+ _inbound.deBugger('searches', "This search form is not tracked. Please assign on in settings...", cb);
1561
+ return false;
1562
+ }
1563
+ },
1564
+ /* Loop through include/exclude items for tracking */
1565
+ loopClassSelectors: function(selectors, action) {
1566
+ for (var i = selectors.length - 1; i >= 0; i--) {
1567
+
1568
+ var selector = utils.trim(selectors[i])
1569
+ if (selector.indexOf("#") === -1 && selector.indexOf(".") === -1) {
1570
+ // assign ID as default
1571
+ selector = "#" + selector;
1572
+ }
1573
+ //if(selectors[i] match . or # )
1574
+ selector = document.querySelector(selector);
1575
+ //console.log("SELECTOR", selector);
1576
+ if (selector) {
1577
+ if (action === 'add') {
1578
+ _inbound.Utils.addClass('wpl-track-me', selector);
1579
+ _inbound.Utils.addClass('inbound-track', selector);
1580
+ } else {
1581
+ _inbound.Utils.removeClass('wpl-track-me', selector);
1582
+ _inbound.Utils.removeClass('inbound-track', selector);
1583
+ }
1584
+ }
1585
+ }
1586
+ },
1587
+ /* Map field fields on load */
1588
+ initFormMapping: function(form) {
1589
+ var hiddenInputs = [];
1590
+
1591
+ for (var i = 0; i < form.elements.length; i++) {
1592
+ formInput = form.elements[i];
1593
+
1594
+ if (formInput.type === 'hidden') {
1595
+ hiddenInputs.push(formInput);
1596
+ continue;
1597
+ }
1598
+
1599
+ //this.ignoreFields(formInput);
1600
+ /* Map form fields */
1601
+ this.mapField(formInput);
1602
+ /* Remember visible inputs */
1603
+ this.rememberInputValues(formInput);
1604
+ /* Fill visible inputs */
1605
+ if (settings.formAutoPopulation && !_inbound.Utils.hasClass( "nopopulate", form ) ) {
1606
+ this.fillInputValues(formInput);
1607
+ }
1608
+
1609
+ }
1610
+
1611
+ /* loop hidden inputs */
1612
+ for (var n = hiddenInputs.length - 1; n >= 0; n--) {
1613
+ formInput = hiddenInputs[n];
1614
+ this.mapField(formInput);
1615
+ }
1616
+
1617
+ //console.log('mapping on load completed');
1618
+ },
1619
+ /* Maps data attributes to fields on page load */
1620
+ mapField: function(input) {
1621
+
1622
+ var input_id = input.id || false;
1623
+ var input_name = input.name || false;
1624
+ var label = this.getInputLabel(input);
1625
+
1626
+ if(label){
1627
+ //console.log(label[0].innerText);
1628
+ var ignoreField = this.ignoreFieldByLabel(label[0].innerText);
1629
+ if(ignoreField){
1630
+ input.dataset.ignoreFormField = true;
1631
+ return false;
1632
+ }
1633
+ }
1634
+
1635
+ /* Loop through all match possiblities */
1636
+ for (i = 0; i < FieldMapArray.length; i++) {
1637
+ //for (var i = FieldMapArray.length - 1; i >= 0; i--) {
1638
+ var found = false;
1639
+ var match = FieldMapArray[i];
1640
+ var lookingFor = utils.trim(match);
1641
+ var nice_name = lookingFor.replace(/ /g, '_');
1642
+
1643
+
1644
+ //console.log("NICE NAME", nice_name);
1645
+ //console.log('looking for match on ' + lookingFor);
1646
+ //_inbound.deBugger('forms', 'looking for match on ' + lookingFor + " nice_name= " + nice_name);
1647
+
1648
+ // Check if input has an attached lable using for= tag
1649
+ //var $laxbel = $("label[for='" + $element.attr('id') + "']").text();
1650
+ //var labxel = 'label[for="' + input_id + '"]';
1651
+
1652
+ /* look for name attribute match */
1653
+ if (input_name && input_name.toLowerCase().indexOf(lookingFor) > -1) {
1654
+ found = true;
1655
+ _inbound.deBugger('forms', 'Found matching name attribute for -> ' + lookingFor);
1656
+
1657
+ /* look for id match */
1658
+ } else if (input_id && input_id.toLowerCase().indexOf(lookingFor) > -1) {
1659
+
1660
+ found = true;
1661
+ _inbound.deBugger('forms', 'Found matching ID attribute for ->' + lookingFor);
1662
+
1663
+ /* Check siblings for label */
1664
+ } else if (label) {
1665
+ //var label = (label.length > 1 ? label[0] : label);
1666
+ //console.log('label', label);
1667
+ if (label[0].innerText.toLowerCase().indexOf(lookingFor) > -1) {
1668
+
1669
+ found = true;
1670
+ _inbound.deBugger('forms', 'Found matching sibling label for -> ' + lookingFor);
1671
+
1672
+ }
1673
+
1674
+ } else {
1675
+ /* no match found */
1676
+ //_inbound.deBugger('forms', 'NO Match on ' + lookingFor + " in " + input_name);
1677
+ no_match.push(lookingFor);
1678
+
1679
+ }
1680
+
1681
+ /* Map the field */
1682
+ if (found) {
1683
+ this.addDataAttr(input, nice_name);
1684
+ this.removeArrayItem(FieldMapArray, lookingFor);
1685
+ i--; //decrement count
1686
+ }
1687
+
1688
+ }
1689
+
1690
+ return inbound_data;
1691
+
1692
+ },
1693
+ /* prevent default submission temporarily */
1694
+ formListener: function(event) {
1695
+ //console.log(event);
1696
+ event.preventDefault();
1697
+ _inbound.Forms.saveFormData(event.target);
1698
+ document.body.style.cursor = "wait";
1699
+ },
1700
+ /* prevent default submission temporarily */
1701
+ searchFormListener: function(event) {
1702
+ //console.log(event);
1703
+ event.preventDefault();
1704
+ _inbound.Forms.saveSearchData(event.target);
1705
+ //document.body.style.cursor = "wait";
1706
+ },
1707
+ /* attach form listeners */
1708
+ attachFormSubmitEvent: function(form) {
1709
+ utils.addListener(form, 'submit', this.formListener);
1710
+ var email_input = document.querySelector('.inbound-email');
1711
+ /* utils.addListener(email_input, 'blur', this.mailCheck); */
1712
+ },
1713
+ /* attach search form listener */
1714
+ attachSearchFormSubmitEvent: function(form) {
1715
+ utils.addListener(form, 'submit', this.searchFormListener);
1716
+ },
1717
+ /* Ignore CC data */
1718
+ ignoreFieldByLabel: function(label) {
1719
+ var ignore_field = false;
1720
+
1721
+ if(!label){ return false; }
1722
+
1723
+ // Ignore any fields with labels that indicate a credit card field
1724
+ if (label.toLowerCase().indexOf('credit card') != -1 || label.toLowerCase().indexOf('card number') != -1) {
1725
+ ignore_field = true;
1726
+ }
1727
+
1728
+ if (label.toLowerCase().indexOf('expiration') != -1 || label.toLowerCase().indexOf('expiry') != -1) {
1729
+ ignore_field = true;
1730
+ }
1731
+
1732
+ if (label.toLowerCase() == 'month' || label.toLowerCase() == 'mm' || label.toLowerCase() == 'yy' || label.toLowerCase() == 'yyyy' || label.toLowerCase() == 'year') {
1733
+ ignore_field = true;
1734
+ }
1735
+
1736
+ if (label.toLowerCase().indexOf('cvv') != -1 || label.toLowerCase().indexOf('cvc') != -1 || label.toLowerCase().indexOf('secure code') != -1 || label.toLowerCase().indexOf('security code') != -1) {
1737
+ ignore_field = true;
1738
+ }
1739
+
1740
+ if(ignore_field){
1741
+ _inbound.deBugger('forms', 'ignore ' + label);
1742
+ }
1743
+
1744
+ return ignore_field;
1745
+
1746
+ },
1747
+ /* not implemented yet */
1748
+ ignoreFieldByValue: function(value){
1749
+ var ignore_field = false;
1750
+
1751
+ if(!value){ return false; }
1752
+
1753
+ if (value.toLowerCase() == 'visa' || value.toLowerCase() == 'mastercard' || value.toLowerCase() == 'american express' || value.toLowerCase() == 'amex' || value.toLowerCase() == 'discover') {
1754
+ ignore_field = true;
1755
+ }
1756
+
1757
+ // Check if value has integers, strip out spaces, then ignore anything with a credit card length (>16) or an expiration/cvv length (<5)
1758
+ var int_regex = new RegExp("/^[0-9]+$/");
1759
+ if (int_regex.test(value)) {
1760
+ var value_no_spaces = value.replace(' ', '');
1761
+
1762
+ if (this.isInt(value_no_spaces) && value_no_spaces.length >= 16) {
1763
+ ignore_field = true;
1764
+ }
1765
+
1766
+ }
1767
+
1768
+ return ignore_field;
1769
+
1770
+ },
1771
+ isInt: function(n) {
1772
+ return typeof n == "number" && isFinite(n) && n % 1 === 0;
1773
+ },
1774
+ releaseFormSubmit: function(form) {
1775
+ //console.log('remove form listener event');
1776
+ document.body.style.cursor = "default";
1777
+ utils.removeClass('wpl-track-me', form);
1778
+ utils.removeListener(form, 'submit', this.formListener);
1779
+ var formClass = form.getAttribute('class');
1780
+ if (formClass !== "" && formClass !== null) {
1781
+ /* If contact form 7 do this */
1782
+ if (formClass.toLowerCase().indexOf("wpcf7-form") != -1) {
1783
+ //alert('release')
1784
+ setTimeout(function() {
1785
+ document.body.style.cursor = "default";
1786
+ }, 300);
1787
+ return true;
1788
+ }
1789
+ }
1790
+
1791
+ form.submit();
1792
+ /* fallback if submit name="submit" */
1793
+ setTimeout(function() {
1794
+ for (var i = 0; i < form.elements.length; i++) {
1795
+ formInput = form.elements[i];
1796
+ type = formInput.type || false;
1797
+ if (type === "submit" && formInput.name === "submit") {
1798
+ form.elements[i].click();
1799
+ }
1800
+ }
1801
+ }, 2000);
1802
+
1803
+ },
1804
+ saveFormData: function(form) {
1805
+ var inputsObject = inputsObject || {};
1806
+ for (var i = 0; i < form.elements.length; i++) {
1807
+
1808
+ // console.log(inputsObject);
1809
+
1810
+ formInput = form.elements[i];
1811
+ multiple = false;
1812
+
1813
+ if (formInput.name) {
1814
+
1815
+ if (formInput.dataset.ignoreFormField) {
1816
+ _inbound.deBugger('forms', 'ignore ' + formInput.name);
1817
+ continue;
1818
+ }
1819
+
1820
+ inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
1821
+ //inputName = inputName.replace(/-/g, "_");
1822
+ if (!inputsObject[inputName]) {
1823
+ inputsObject[inputName] = {};
1824
+ }
1825
+ if (formInput.type) {
1826
+ inputsObject[inputName]['type'] = formInput.type;
1827
+ }
1828
+ if (!inputsObject[inputName]['name']) {
1829
+ inputsObject[inputName]['name'] = formInput.name;
1830
+ }
1831
+ if (formInput.dataset.mapFormField) {
1832
+ inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
1833
+ }
1834
+
1835
+
1836
+ switch (formInput.nodeName) {
1837
+
1838
+ case 'INPUT':
1839
+ value = this.getInputValue(formInput);
1840
+
1841
+
1842
+ if (value === false) {
1843
+ continue;
1844
+ }
1845
+ break;
1846
+
1847
+ case 'TEXTAREA':
1848
+ value = formInput.value;
1849
+ break;
1850
+
1851
+ case 'SELECT':
1852
+ if (formInput.multiple) {
1853
+ values = [];
1854
+ multiple = true;
1855
+
1856
+ for (var j = 0; j < formInput.length; j++) {
1857
+ if (formInput[j].selected) {
1858
+ values.push(encodeURIComponent(formInput[j].value));
1859
+ }
1860
+ }
1861
+
1862
+ } else {
1863
+ value = (formInput.value);
1864
+ }
1865
+
1866
+ break;
1867
+ }
1868
+
1869
+ _inbound.deBugger('forms', 'Input Value = ' + value);
1870
+
1871
+
1872
+ if (value) {
1873
+ /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
1874
+ if (!inputsObject[inputName]['value']) {
1875
+ inputsObject[inputName]['value'] = [];
1876
+ }
1877
+ inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
1878
+ var value = multiple ? values.join(',') : encodeURIComponent(value);
1879
+
1880
+ }
1881
+
1882
+ }
1883
+ }
1884
+ _inbound.deBugger('forms', inputsObject);
1885
+
1886
+ //console.log('These are the raw values', inputsObject);
1887
+ //_inbound.totalStorage('the_key', inputsObject);
1888
+ //var inputsObject = sortInputs(inputsObject);
1889
+
1890
+ var matchCommon = /name|first name|last name|email|e-mail|phone|website|job title|company|tele|address|comment/;
1891
+
1892
+ for (var input in inputsObject) {
1893
+ //console.log(input);
1894
+
1895
+ var inputValue = inputsObject[input]['value'];
1896
+ var inputMappedField = inputsObject[input]['map'];
1897
+ //if (matchCommon.test(input) !== false) {
1898
+ //console.log(input + " Matches Regex run mapping test");
1899
+ //var map = inputsObject[input];
1900
+ //console.log("MAPP", map);
1901
+ //mappedParams.push( input + '=' + inputsObject[input]['value'].join(',') );
1902
+ //}
1903
+
1904
+ /* Add custom hook here to look for additional values */
1905
+ if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
1906
+ rawParams.push(input + '=' + inputsObject[input]['value'].join(','));
1907
+ }
1908
+
1909
+ if (typeof(inputMappedField) != "undefined" && inputMappedField != null && inputsObject[input]['value']) {
1910
+ //console.log('Data ATTR', formInput.dataset.mapFormField);
1911
+ mappedParams.push(inputMappedField + "=" + inputsObject[input]['value'].join(','));
1912
+ if (input === 'email') {
1913
+ var email = inputsObject[input]['value'].join(',');
1914
+ //alert(email);
1915
+
1916
+ }
1917
+ }
1918
+ }
1919
+
1920
+ var raw_params = rawParams.join('&');
1921
+ _inbound.deBugger('forms', "Stringified Raw Form PARAMS: " + raw_params);
1922
+
1923
+ var mapped_params = mappedParams.join('&');
1924
+ _inbound.deBugger('forms', "Stringified Mapped PARAMS" + mapped_params);
1925
+
1926
+ /* Check Use form Email or Cookie */
1927
+ var email = utils.getParameterVal('email', mapped_params) || utils.readCookie('wp_lead_email');
1928
+
1929
+ /* Legacy Email map */
1930
+ if (!email) {
1931
+ email = utils.getParameterVal('wpleads_email_address', mapped_params);
1932
+ }
1933
+
1934
+ var fullName = utils.getParameterVal('name', mapped_params);
1935
+ var fName = utils.getParameterVal('first_name', mapped_params);
1936
+ var lName = utils.getParameterVal('last_name', mapped_params);
1937
+
1938
+ // Fallbacks for empty values
1939
+ if (!lName && fName) {
1940
+ var parts = decodeURI(fName).split(" ");
1941
+ if (parts.length > 0) {
1942
+ fName = parts[0];
1943
+ lName = parts[1];
1944
+ }
1945
+ }
1946
+
1947
+ if (fullName && !lName && !fName) {
1948
+ var parts = decodeURI(fullName).split(" ");
1949
+ if (parts.length > 0) {
1950
+ fName = parts[0];
1951
+ lName = parts[1];
1952
+ }
1953
+ }
1954
+
1955
+ fullName = (fName && lName) ? fName + " " + lName : fullName;
1956
+
1957
+ if(!fName) { fName = ""; }
1958
+ if(!lName) { lName = ""; }
1959
+
1960
+ _inbound.deBugger('forms', "fName = " + fName);
1961
+ _inbound.deBugger('forms', "lName = " + lName);
1962
+ _inbound.deBugger('forms', "fullName = " + fullName);
1963
+
1964
+ //return false;
1965
+ var page_views = _inbound.totalStorage('page_views') || {};
1966
+ var urlParams = _inbound.totalStorage('inbound_url_params') || {};
1967
+
1968
+ /* check if redirect url is empty */
1969
+ var formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
1970
+ var inbound_form_is_ajax = false;
1971
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
1972
+ var inbound_form_is_ajax = true;
1973
+ }
1974
+
1975
+ /* get form id */
1976
+ var inbound_form_id = form.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');
1977
+ if(inbound_form_id.length > 0 ){
1978
+ inbound_form_id = inbound_form_id[0]['value'];
1979
+ } else {
1980
+ inbound_form_id = 0;
1981
+ }
1982
+
1983
+ var inboundDATA = {
1984
+ 'email': email
1985
+ };
1986
+
1987
+ /* Get Variation ID */
1988
+ if (typeof(landing_path_info) != "undefined") {
1989
+ var variation = landing_path_info.variation;
1990
+ } else if (typeof(cta_path_info) != "undefined") {
1991
+ var variation = cta_path_info.variation;
1992
+ } else {
1993
+ var variation = inbound_settings.variation_id;
1994
+ }
1995
+ var post_type = inbound_settings.post_type || 'page';
1996
+ var page_id = inbound_settings.post_id || 0;
1997
+ // data['wp_lead_uid'] = jQuery.cookie("wp_lead_uid") || null;
1998
+ // data['search_data'] = JSON.stringify(jQuery.totalStorage('inbound_search')) || {};
1999
+ search_data = {};
2000
+ /* Filter here for raw */
2001
+ formData = {
2002
+ 'action': 'inbound_lead_store',
2003
+ 'email': email,
2004
+ "full_name": fullName,
2005
+ "first_name": fName,
2006
+ "last_name": lName,
2007
+ 'raw_params': raw_params,
2008
+ 'mapped_params': mapped_params,
2009
+ 'url_params': JSON.stringify(urlParams),
2010
+ 'search_data': 'test',
2011
+ 'page_views': JSON.stringify(page_views),
2012
+ 'post_type': post_type,
2013
+ 'page_id': page_id,
2014
+ 'variation': variation,
2015
+ 'source': utils.readCookie("inbound_referral_site"),
2016
+ 'inbound_submitted': inbound_form_is_ajax,
2017
+ 'inbound_form_id': inbound_form_id,
2018
+ 'inbound_nonce': inbound_settings.ajax_nonce,
2019
+ 'event': form
2020
+ };
2021
+
2022
+ callback = function(leadID) {
2023
+ /* Action Example */
2024
+
2025
+ _inbound.deBugger('forms', 'Lead Created with ID: ' + leadID);
2026
+ leadID = parseInt(leadID, 10);
2027
+ formData.leadID = leadID;
2028
+ /* Set Lead cookie ID */
2029
+ if (leadID) {
2030
+ utils.createCookie("wp_lead_id", leadID);
2031
+ _inbound.totalStorage.deleteItem('page_views'); // remove pageviews
2032
+ _inbound.totalStorage.deleteItem('tracking_events'); // remove events
2033
+ }
2034
+
2035
+ _inbound.trigger('form_after_submission', formData);
2036
+
2037
+ /* Resume normal form functionality */
2038
+ _inbound.Forms.releaseFormSubmit(form);
2039
+
2040
+ }
2041
+
2042
+ _inbound.trigger('form_before_submission', formData);
2043
+
2044
+ utils.ajaxPost(inbound_settings.admin_url, formData, callback);
2045
+ },
2046
+ saveSearchData: function(form) {
2047
+ var inputsObject = inputsObject || {};
2048
+ for (var i = 0; i < form.elements.length; i++) {
2049
+
2050
+ //console.log(inputsObject);
2051
+
2052
+ formInput = form.elements[i];
2053
+ multiple = false;
2054
+
2055
+ if (formInput.name) {
2056
+
2057
+ if (formInput.dataset.ignoreFormField) {
2058
+ _inbound.deBugger('searches', 'ignore ' + formInput.name);
2059
+ continue;
2060
+ }
2061
+
2062
+ inputName = formInput.name.replace(/\[([^\[]*)\]/g, "%5B%5D$1");
2063
+ //inputName = inputName.replace(/-/g, "_");
2064
+ if (!inputsObject[inputName]) {
2065
+ inputsObject[inputName] = {};
2066
+ }
2067
+ if (formInput.type) {
2068
+ inputsObject[inputName]['type'] = formInput.type;
2069
+
2070
+ }
2071
+ if (!inputsObject[inputName]['name']) {
2072
+ inputsObject[inputName]['name'] = formInput.name;
2073
+ }
2074
+ if (formInput.dataset.mapFormField) {
2075
+ inputsObject[inputName]['map'] = formInput.dataset.mapFormField;
2076
+ }
2077
+
2078
+
2079
+ switch (formInput.nodeName) {
2080
+
2081
+ case 'INPUT':
2082
+ value = this.getInputValue(formInput);
2083
+
2084
+
2085
+ if (value === false) {
2086
+ continue;
2087
+ }
2088
+ break;
2089
+
2090
+ case 'TEXTAREA':
2091
+ value = formInput.value;
2092
+ break;
2093
+
2094
+ case 'SELECT':
2095
+ if (formInput.multiple) {
2096
+ values = [];
2097
+ multiple = true;
2098
+
2099
+ for (var j = 0; j < formInput.length; j++) {
2100
+ if (formInput[j].selected) {
2101
+ values.push(encodeURIComponent(formInput[j].value));
2102
+ }
2103
+ }
2104
+
2105
+ } else {
2106
+ value = (formInput.value);
2107
+ }
2108
+
2109
+ break;
2110
+ }
2111
+
2112
+ _inbound.deBugger('searches', 'Input Value = ' + value);
2113
+
2114
+
2115
+ if (value) {
2116
+ /* inputsObject[inputName].push(multiple ? values.join(',') : encodeURIComponent(value)); */
2117
+ if (!inputsObject[inputName]['value']) {
2118
+ inputsObject[inputName]['value'] = [];
2119
+ }
2120
+ inputsObject[inputName]['value'].push(multiple ? values.join(',') : encodeURIComponent(value));
2121
+ var value = multiple ? values.join(',') : encodeURIComponent(value);
2122
+
2123
+ }
2124
+
2125
+ }
2126
+ }
2127
+
2128
+ _inbound.deBugger('searches', inputsObject);
2129
+
2130
+ /* create an array of search fields //(not fully implemented) at the moment, it only maps the text in the "search" input types*/
2131
+ var searchQuery = [];
2132
+ for (var input in inputsObject) {
2133
+ var inputValue = inputsObject[input]['value'];
2134
+ var inputType = inputsObject[input]['type'];
2135
+
2136
+ /* Add custom hook here to look for additional values */
2137
+ if (typeof(inputValue) != "undefined" && inputValue != null && inputValue != "") {
2138
+ // This is for mapping all fields of a search form. The resulting string is processed
2139
+ // in inbound-pro\classes\admin\report-templates\report.lead-searches-and-comments.php
2140
+ // In the function print_action_popup()
2141
+ // searchQuery.push(input + '|value|' + inputsObject[input]['value'].join(','));
2142
+
2143
+ // get the search input value
2144
+ if(inputType == 'search'){
2145
+ searchQuery.push('search_text' + '|value|' + inputsObject[input]['value']);
2146
+ }
2147
+ }
2148
+ }
2149
+ /* exit if there isn't a search query */
2150
+ if(!searchQuery[0]){
2151
+ return;
2152
+ }
2153
+
2154
+ var searchString = searchQuery.join('|field|');
2155
+ _inbound.deBugger('searches', "Stringified Search Form PARAMS: " + searchString);
2156
+
2157
+ /* Get Variation ID */
2158
+ if (typeof(landing_path_info) != "undefined") {
2159
+ var variation = landing_path_info.variation;
2160
+ } else if (typeof(cta_path_info) != "undefined") {
2161
+ var variation = cta_path_info.variation;
2162
+ } else {
2163
+ var variation = inbound_settings.variation_id;
2164
+ }
2165
+ var post_type = inbound_settings.post_type || 'page';
2166
+ var page_id = inbound_settings.post_id || 0;
2167
+
2168
+ var user_UID = utils.readCookie("wp_lead_uid");
2169
+
2170
+ /* get the user's email address if possible */
2171
+ if(inbound_settings.wp_lead_data.lead_email){
2172
+ email = inbound_settings.wp_lead_data.lead_email;
2173
+ }else if(utils.readCookie('inbound_wpleads_email_address')){
2174
+ email = utils.readCookie('inbound_wpleads_email_address');
2175
+ }else{
2176
+ email = '';
2177
+ }
2178
+
2179
+ /* Filter here for raw */
2180
+ searchData = {
2181
+ 'email': email,
2182
+ 'search_data': searchString,
2183
+ 'user_UID': user_UID,
2184
+ 'post_type': post_type,
2185
+ 'page_id': page_id,
2186
+ 'variation': variation,
2187
+ 'source': utils.readCookie("inbound_referral_site"),
2188
+ 'ip_address': inbound_settings.ip_address,
2189
+ 'timestamp': Math.floor((new Date).getTime()/1000),
2190
+ };
2191
+
2192
+ /* filter data before caching it in the user's browser */
2193
+ _inbound.trigger('search_before_caching', searchData);
2194
+
2195
+ /* cache search data */
2196
+ if(inbound_settings.wp_lead_data.lead_id){
2197
+ searchData['lead_id'] = inbound_settings.wp_lead_data.lead_id;
2198
+ utils.cacheSearchData(searchData, form)
2199
+ }else{
2200
+ utils.cacheSearchData(searchData, form);
2201
+ }
2202
+
2203
+
2204
+ },
2205
+ rememberInputValues: function(input) {
2206
+ var name = (input.name) ? "inbound_" + input.name : '';
2207
+ var type = (input.type) ? input.type : 'text';
2208
+ if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password" || input.dataset.ignoreFormField) {
2209
+ return false;
2210
+ }
2211
+
2212
+ utils.addListener(input, 'change', function(e) {
2213
+ if (e.target.name) {
2214
+ /* Check for input type */
2215
+ if (type !== "checkbox") {
2216
+ var value = e.target.value;
2217
+ } else {
2218
+ var values = [];
2219
+ var checkboxes = document.querySelectorAll('input[name="' + e.target.name + '"]');
2220
+ for (var i = 0; i < checkboxes.length; i++) {
2221
+ var checked = checkboxes[i].checked;
2222
+ if (checked) {
2223
+ values.push(checkboxes[i].value);
2224
+ }
2225
+ value = values.join(',');
2226
+ };
2227
+ }
2228
+ //console.log(e.target.nodeName);
2229
+ //console.log('change ' + e.target.name + " " + encodeURIComponent(value));
2230
+
2231
+ inputData = {
2232
+ name: e.target.name,
2233
+ node: e.target.nodeName.toLowerCase(),
2234
+ type: type,
2235
+ value: value,
2236
+ mapping: e.target.dataset.mapFormField
2237
+ };
2238
+
2239
+ _inbound.trigger('form_input_change', inputData);
2240
+ /* Set Field Input Cookies */
2241
+ utils.createCookie("inbound_" + e.target.name, encodeURIComponent(value));
2242
+ // _inbound.totalStorage('the_key', FormStore);
2243
+ /* Push to 'unsubmitted form object' */
2244
+ }
2245
+
2246
+ });
2247
+ },
2248
+ fillInputValues: function(input) {
2249
+ var name = (input.name) ? "inbound_" + input.name : '';
2250
+ var type = (input.type) ? input.type : 'text';
2251
+ if (type === 'submit' || type === 'hidden' || type === 'file' || type === "password") {
2252
+ return false;
2253
+ }
2254
+ if (utils.readCookie(name) && name != 'comment') {
2255
+
2256
+ value = decodeURIComponent(utils.readCookie(name));
2257
+ if (type === 'checkbox' || type === 'radio') {
2258
+ var checkbox_vals = value.split(',');
2259
+ for (var i = 0; i < checkbox_vals.length; i++) {
2260
+ if (input.value.indexOf(checkbox_vals[i]) > -1) {
2261
+ input.checked = true;
2262
+ }
2263
+ }
2264
+ } else {
2265
+ if (value !== "undefined") {
2266
+ input.value = value;
2267
+ }
2268
+ }
2269
+ }
2270
+ },
2271
+ getInputLabel: function(input){
2272
+ var label;
2273
+ if(label = this.siblingsIsLabel(input)){
2274
+ return label;
2275
+ } else if (label = this.CheckParentForLabel(input)) {
2276
+ return label;
2277
+ } else {
2278
+ //console.log("no label nf", input);
2279
+ return false;
2280
+ }
2281
+ },
2282
+ /* Get correct input values */
2283
+ getInputValue: function(input) {
2284
+ var value = false;
2285
+
2286
+ switch (input.type) {
2287
+ case 'radio':
2288
+ case 'checkbox':
2289
+ if (input.checked) {
2290
+ value = input.value;
2291
+ //console.log("CHECKBOX VAL", value)
2292
+ }
2293
+ break;
2294
+
2295
+ case 'text':
2296
+ case 'hidden':
2297
+ default:
2298
+ value = input.value;
2299
+ break;
2300
+
2301
+ }
2302
+
2303
+ return value;
2304
+ },
2305
+ /* Add data-map-form-field attr to input */
2306
+ addDataAttr: function(formInput, match) {
2307
+
2308
+ var getAllInputs = document.getElementsByName(formInput.name);
2309
+ for (var i = getAllInputs.length - 1; i >= 0; i--) {
2310
+ if (!formInput.dataset.mapFormField) {
2311
+ getAllInputs[i].dataset.mapFormField = match;
2312
+ }
2313
+ };
2314
+ },
2315
+ /* Optimize FieldMapArray array for fewer lookups */
2316
+ removeArrayItem: function(array, item) {
2317
+ if (array.indexOf) {
2318
+ index = array.indexOf(item);
2319
+ } else {
2320
+ for (index = array.length - 1; index >= 0; --index) {
2321
+ if (array[index] === item) {
2322
+ break;
2323
+ }
2324
+ }
2325
+ }
2326
+ if (index >= 0) {
2327
+ array.splice(index, 1);
2328
+ }
2329
+ //_inbound.deBugger('forms', 'removed ' + item + " from array");
2330
+ //console.log('removed ' + item + " from array");
2331
+ return;
2332
+ },
2333
+ /* Look for siblings that are form labels */
2334
+ siblingsIsLabel: function(input) {
2335
+ var siblings = this.getSiblings(input);
2336
+ var labels = [];
2337
+ for (var i = siblings.length - 1; i >= 0; i--) {
2338
+ if (siblings[i].nodeName.toLowerCase() === 'label') {
2339
+ labels.push(siblings[i]);
2340
+ }
2341
+ };
2342
+ /* if only 1 label */
2343
+ if (labels.length > 0 && labels.length < 2) {
2344
+ return labels;
2345
+ }
2346
+
2347
+ return false;
2348
+ },
2349
+ getChildren: function(n, skipMe) {
2350
+ var r = [];
2351
+ var elem = null;
2352
+ for (; n; n = n.nextSibling)
2353
+ if (n.nodeType == 1 && n != skipMe)
2354
+ r.push(n);
2355
+ return r;
2356
+ },
2357
+ getSiblings: function(n) {
2358
+ return this.getChildren(n.parentNode.firstChild, n);
2359
+ },
2360
+ /* Check parent elements inside form for labels */
2361
+ CheckParentForLabel: function(element) {
2362
+ if (element.nodeName === 'FORM') {
2363
+ return null;
2364
+ }
2365
+ do {
2366
+ var labels = element.getElementsByTagName("label");
2367
+ if (labels.length > 0 && labels.length < 2) {
2368
+ return element.getElementsByTagName("label");
2369
+ }
2370
+
2371
+ } while (element = element.parentNode);
2372
+
2373
+ return null;
2374
+ },
2375
+ /* Validate Common Email addresses */
2376
+ mailCheck: function() {
2377
+ var email_input = document.querySelector('.inbound-email');
2378
+ if (email_input) {
2379
+ //
2380
+ utils.addListener(email_input, 'blur', this.mailCheck);
2381
+
2382
+ Mailcheck.run({
2383
+ email: document.querySelector('.inbound-email').value,
2384
+ suggested: function(suggestion) {
2385
+ // callback code
2386
+
2387
+ var suggest = document.querySelector('.email_suggestion');
2388
+ if (suggest) {
2389
+ utils.removeElement(suggest);
2390
+ }
2391
+ var el = document.createElement("span");
2392
+ el.innerHTML = "<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">" + suggestion.full + "</b></i>?</span>";
2393
+ email_input.parentNode.insertBefore(el, email_input.nextSibling);
2394
+ var update = document.getElementById('email_correction');
2395
+ utils.addListener(update, 'click', function() {
2396
+ email_input.value = update.innerHTML;
2397
+ update.parentNode.parentNode.innerHTML = "Fixed!";
2398
+ });
2399
+ },
2400
+ empty: function() {
2401
+ //$(".email_suggestion").html("No Suggestions :(");
2402
+ }
2403
+ });
2404
+ }
2405
+ }
2406
+
2407
+ };
2408
+ /* Mailcheck */
2409
+ if (typeof Mailcheck === "undefined") {
2410
+ var Mailcheck = {
2411
+ domainThreshold: 1,
2412
+ topLevelThreshold: 3,
2413
+
2414
+ defaultDomains: ["yahoo.com", "google.com", "hotmail.com", "gmail.com", "me.com", "aol.com", "mac.com",
2415
+ "live.com", "comcast.net", "googlemail.com", "msn.com", "hotmail.co.uk", "yahoo.co.uk",
2416
+ "facebook.com", "verizon.net", "sbcglobal.net", "att.net", "gmx.com", "mail.com", "outlook.com", "icloud.com"
2417
+ ],
2418
+
2419
+ defaultTopLevelDomains: ["co.jp", "co.uk", "com", "net", "org", "info", "edu", "gov", "mil", "ca", "de"],
2420
+
2421
+ run: function(opts) {
2422
+ opts.domains = opts.domains || Mailcheck.defaultDomains;
2423
+ opts.topLevelDomains = opts.topLevelDomains || Mailcheck.defaultTopLevelDomains;
2424
+ opts.distanceFunction = opts.distanceFunction || Mailcheck.sift3Distance;
2425
+
2426
+ var defaultCallback = function(result) {
2427
+ return result;
2428
+ };
2429
+ var suggestedCallback = opts.suggested || defaultCallback;
2430
+ var emptyCallback = opts.empty || defaultCallback;
2431
+
2432
+ var result = Mailcheck.suggest(Mailcheck.encodeEmail(opts.email), opts.domains, opts.topLevelDomains, opts.distanceFunction);
2433
+
2434
+ return result ? suggestedCallback(result) : emptyCallback();
2435
+ },
2436
+
2437
+ suggest: function(email, domains, topLevelDomains, distanceFunction) {
2438
+ email = email.toLowerCase();
2439
+
2440
+ var emailParts = this.splitEmail(email);
2441
+
2442
+ var closestDomain = this.findClosestDomain(emailParts.domain, domains, distanceFunction, this.domainThreshold);
2443
+
2444
+ if (closestDomain) {
2445
+ if (closestDomain != emailParts.domain) {
2446
+ // The email address closely matches one of the supplied domains; return a suggestion
2447
+ return {
2448
+ address: emailParts.address,
2449
+ domain: closestDomain,
2450
+ full: emailParts.address + "@" + closestDomain
2451
+ };
2452
+ }
2453
+ } else {
2454
+ // The email address does not closely match one of the supplied domains
2455
+ var closestTopLevelDomain = this.findClosestDomain(emailParts.topLevelDomain, topLevelDomains, distanceFunction, this.topLevelThreshold);
2456
+ if (emailParts.domain && closestTopLevelDomain && closestTopLevelDomain != emailParts.topLevelDomain) {
2457
+ // The email address may have a mispelled top-level domain; return a suggestion
2458
+ var domain = emailParts.domain;
2459
+ closestDomain = domain.substring(0, domain.lastIndexOf(emailParts.topLevelDomain)) + closestTopLevelDomain;
2460
+ return {
2461
+ address: emailParts.address,
2462
+ domain: closestDomain,
2463
+ full: emailParts.address + "@" + closestDomain
2464
+ };
2465
+ }
2466
+ }
2467
+ /* The email address exactly matches one of the supplied domains, does not closely
2468
+ * match any domain and does not appear to simply have a mispelled top-level domain,
2469
+ * or is an invalid email address; do not return a suggestion.
2470
+ */
2471
+ return false;
2472
+ },
2473
+
2474
+ findClosestDomain: function(domain, domains, distanceFunction, threshold) {
2475
+ threshold = threshold || this.topLevelThreshold;
2476
+ var dist;
2477
+ var minDist = 99;
2478
+ var closestDomain = null;
2479
+
2480
+ if (!domain || !domains) {
2481
+ return false;
2482
+ }
2483
+ if (!distanceFunction) {
2484
+ distanceFunction = this.sift3Distance;
2485
+ }
2486
+
2487
+ for (var i = 0; i < domains.length; i++) {
2488
+ if (domain === domains[i]) {
2489
+ return domain;
2490
+ }
2491
+ dist = distanceFunction(domain, domains[i]);
2492
+ if (dist < minDist) {
2493
+ minDist = dist;
2494
+ closestDomain = domains[i];
2495
+ }
2496
+ }
2497
+
2498
+ if (minDist <= threshold && closestDomain !== null) {
2499
+ return closestDomain;
2500
+ } else {
2501
+ return false;
2502
+ }
2503
+ },
2504
+
2505
+ sift3Distance: function(s1, s2) {
2506
+ // sift3: http://siderite.blogspot.com/2007/04/super-fast-and-accurate-string-distance.html
2507
+ if (s1 === null || s1.length === 0) {
2508
+ if (s2 === null || s2.length === 0) {
2509
+ return 0;
2510
+ } else {
2511
+ return s2.length;
2512
+ }
2513
+ }
2514
+
2515
+ if (s2 === null || s2.length === 0) {
2516
+ return s1.length;
2517
+ }
2518
+
2519
+ var c = 0;
2520
+ var offset1 = 0;
2521
+ var offset2 = 0;
2522
+ var lcs = 0;
2523
+ var maxOffset = 5;
2524
+
2525
+ while ((c + offset1 < s1.length) && (c + offset2 < s2.length)) {
2526
+ if (s1.charAt(c + offset1) == s2.charAt(c + offset2)) {
2527
+ lcs++;
2528
+ } else {
2529
+ offset1 = 0;
2530
+ offset2 = 0;
2531
+ for (var i = 0; i < maxOffset; i++) {
2532
+ if ((c + i < s1.length) && (s1.charAt(c + i) == s2.charAt(c))) {
2533
+ offset1 = i;
2534
+ break;
2535
+ }
2536
+ if ((c + i < s2.length) && (s1.charAt(c) == s2.charAt(c + i))) {
2537
+ offset2 = i;
2538
+ break;
2539
+ }
2540
+ }
2541
+ }
2542
+ c++;
2543
+ }
2544
+ return (s1.length + s2.length) / 2 - lcs;
2545
+ },
2546
+
2547
+ splitEmail: function(email) {
2548
+ var parts = email.trim().split("@");
2549
+
2550
+ if (parts.length < 2) {
2551
+ return false;
2552
+ }
2553
+
2554
+ for (var i = 0; i < parts.length; i++) {
2555
+ if (parts[i] === "") {
2556
+ return false;
2557
+ }
2558
+ }
2559
+
2560
+ var domain = parts.pop();
2561
+ var domainParts = domain.split(".");
2562
+ var tld = "";
2563
+
2564
+ if (domainParts.length === 0) {
2565
+ // The address does not have a top-level domain
2566
+ return false;
2567
+ } else if (domainParts.length == 1) {
2568
+ // The address has only a top-level domain (valid under RFC)
2569
+ tld = domainParts[0];
2570
+ } else {
2571
+ // The address has a domain and a top-level domain
2572
+ for (var i = 1; i < domainParts.length; i++) {
2573
+ tld += domainParts[i] + ".";
2574
+ }
2575
+ if (domainParts.length >= 2) {
2576
+ tld = tld.substring(0, tld.length - 1);
2577
+ }
2578
+ }
2579
+
2580
+ return {
2581
+ topLevelDomain: tld,
2582
+ domain: domain,
2583
+ address: parts.join("@")
2584
+ };
2585
+ },
2586
+
2587
+ // Encode the email address to prevent XSS but leave in valid
2588
+ // characters, following this official spec:
2589
+ // http://en.wikipedia.org/wiki/Email_address#Syntax
2590
+ encodeEmail: function(email) {
2591
+ var result = encodeURI(email);
2592
+ result = result.replace("%20", " ").replace("%25", "%").replace("%5E", "^")
2593
+ .replace("%60", "`").replace("%7B", "{").replace("%7C", "|")
2594
+ .replace("%7D", "}");
2595
+ return result;
2596
+ }
2597
+ };
2598
+ } // End Mailcheck
2599
+
2600
+
2601
+ return _inbound;
2602
+
2603
+ })(_inbound || {});
2604
+
2605
+ /**
2606
+ * # Analytics Events
2607
+ *
2608
+ * Events are triggered throughout the visitors journey through the site. See more on [Inbound Now][in]
2609
+ *
2610
+ * @contributor David Wells <david@inboundnow.com>
2611
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
2612
+ * @version 0.0.2
2613
+ *
2614
+ * [in]: http://www.inboundnow.com/
2615
+ */
2616
+
2617
+ // Add object to _inbound
2618
+ var _inboundEvents = (function(_inbound) {
2619
+
2620
+
2621
+ _inbound.trigger = function(trigger, data) {
2622
+ _inbound.Events[trigger](data);
2623
+
2624
+ };
2625
+
2626
+ /*!
2627
+ *
2628
+ * Private Function that Fires & Emits Events
2629
+ *
2630
+ * There are three options for firing events and they trigger in this order:
2631
+ *
2632
+ * 1. Vanilla JS dispatch event
2633
+ * 2. `_inbound.add_action('namespace', callback, priority)`
2634
+ * 3. jQuery Trigger `jQuery.trigger('namespace', callback);`
2635
+ *
2636
+ * The Event `data` can be filtered before events are triggered
2637
+ * with filters. Example: filter_ + "namespace"
2638
+ *
2639
+ * ```js
2640
+ * // Filter Form Data before submissionsz
2641
+ * _inbound.add_filter( 'filter_form_before_submission', event_filter_data_example, 10);
2642
+ *
2643
+ * function event_filter_data_example(data) {
2644
+ * var data = data || {};
2645
+ * // Do something with data
2646
+ * return data;
2647
+ * }
2648
+ * ```
2649
+ *
2650
+ * @param {string} eventName Name of the event
2651
+ * @param {object} data Data passed to external functions/triggers
2652
+ * @param {object} options Options for configuring events
2653
+ * @return {null} Nothing returned
2654
+ */
2655
+ function fireEvent(eventName, data, options) {
2656
+ var data = data || {};
2657
+ options = options || {};
2658
+
2659
+ /*! defaults for JS dispatch event */
2660
+ options.bubbles = options.bubbles || true,
2661
+ options.cancelable = options.cancelable || true;
2662
+
2663
+ /*! Customize Data via filter_ + "namespace" */
2664
+ data = _inbound.apply_filters('filter_' + eventName, data);
2665
+
2666
+ var is_IE_11 = !(window.ActiveXObject) && "ActiveXObject" in window;
2667
+
2668
+ if( typeof CustomEvent === 'function') {
2669
+
2670
+ var TriggerEvent = new CustomEvent(eventName, {
2671
+ detail: data,
2672
+ bubbles: options.bubbles,
2673
+ cancelable: options.cancelable
2674
+ });
2675
+
2676
+ } else {
2677
+ var TriggerEvent = document.createEvent("Event");
2678
+ TriggerEvent.initEvent(eventName, true, true);
2679
+ }
2680
+
2681
+ /*! 1. Trigger Pure Javascript Event See: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events for example on creating events */
2682
+ window.dispatchEvent(TriggerEvent);
2683
+ /*! 2. Trigger _inbound action */
2684
+ _inbound.do_action(eventName, data);
2685
+ /*! 3. jQuery trigger */
2686
+ triggerJQueryEvent(eventName, data);
2687
+
2688
+ // console.log('Action:' + eventName + " ran on ->", data);
2689
+
2690
+ }
2691
+
2692
+ function triggerJQueryEvent(eventName, data) {
2693
+ if (window.jQuery) {
2694
+ var data = data || {};
2695
+ /*! try catch here */
2696
+ jQuery(document).trigger(eventName, data);
2697
+ }
2698
+ };
2699
+
2700
+ var universalGA,
2701
+ classicGA,
2702
+ googleTagManager;
2703
+
2704
+ _inbound.Events = {
2705
+
2706
+ /**
2707
+ * # Event Usage
2708
+ *
2709
+ * Events are triggered throughout the visitors path through the site.
2710
+ * You can hook into these custom actions and filters much like WordPress Core
2711
+ *
2712
+ * See below for examples
2713
+ */
2714
+
2715
+ /**
2716
+ * Adding Custom Actions
2717
+ * ------------------
2718
+ * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
2719
+ *
2720
+ * `
2721
+ * _inbound.add_action( 'action_name', callback, priority );
2722
+ * `
2723
+ *
2724
+ * ```js
2725
+ * // example:
2726
+ *
2727
+ * // Add custom function to `page_visit` event
2728
+ * _inbound.add_action( 'page_visit', callback, 10 );
2729
+ *
2730
+ * // add custom callback to trigger when `page_visit` fires
2731
+ * function callback(pageData){
2732
+ * var pageData = pageData || {};
2733
+ * // run callback on 'page_visit' trigger
2734
+ * alert(pageData.title);
2735
+ * }
2736
+ * ```
2737
+ *
2738
+ * @param {string} action_name Name of the event trigger
2739
+ * @param {function} callback function to trigger when event happens
2740
+ * @param {int} priority Order to trigger the event in
2741
+ *
2742
+ */
2743
+
2744
+ /**
2745
+ * Removing Custom Actions
2746
+ * ------------------
2747
+ * You can hook into custom events throughout analytics. See the full list of available [events below](#all-events)
2748
+ *
2749
+ * `
2750
+ * _inbound.remove_action( 'action_name');
2751
+ * `
2752
+ *
2753
+ * ```js
2754
+ * // example:
2755
+ *
2756
+ * _inbound.remove_action( 'page_visit');
2757
+ * // all 'page_visit' actions have been deregistered
2758
+ * ```
2759
+ *
2760
+ * @param {string} action_name Name of the event trigger
2761
+ *
2762
+ */
2763
+
2764
+ /**
2765
+ * # Event List
2766
+ *
2767
+ * Events are triggered throughout the visitors journey through the site
2768
+ */
2769
+
2770
+ /**
2771
+ * Triggers when analyics has finished loading
2772
+ */
2773
+ analytics_ready: function() {
2774
+ var ops = {
2775
+ 'opt1': true
2776
+ };
2777
+ var data = {
2778
+ 'data': 'xyxy'
2779
+ };
2780
+ fireEvent('analytics_ready', data, ops);
2781
+ },
2782
+ /**
2783
+ * Triggers when the browser url params are parsed. You can perform custom actions
2784
+ * if specific url params exist.
2785
+ *
2786
+ * ```js
2787
+ * // Usage:
2788
+ *
2789
+ * // Add function to 'url_parameters' event
2790
+ * _inbound.add_action( 'url_parameters', url_parameters_func_example, 10);
2791
+ *
2792
+ * function url_parameters_func_example(urlParams) {
2793
+ * var urlParams = urlParams || {};
2794
+ * for( var param in urlParams ) {
2795
+ * var key = param;
2796
+ * var value = urlParams[param];
2797
+ * }
2798
+ * // All URL Params
2799
+ * alert(JSON.stringify(urlParams));
2800
+ *
2801
+ * // Check if URL parameter `utm_source` exists and matches value
2802
+ * if(urlParams.utm_source === "twitter") {
2803
+ * alert('This person is from twitter!');
2804
+ * }
2805
+ * }
2806
+ * ```
2807
+ */
2808
+ url_parameters: function(data) {
2809
+ fireEvent('url_parameters', data);
2810
+ },
2811
+ /**
2812
+ * Triggers when session starts
2813
+ *
2814
+ * ```js
2815
+ * // Usage:
2816
+ *
2817
+ * // Add function to 'session_start' event
2818
+ * _inbound.add_action( 'session_start', session_start_func_example, 10);
2819
+ *
2820
+ * function session_start_func_example(data) {
2821
+ * var data = data || {};
2822
+ * // session start. Do something for new visitor
2823
+ * }
2824
+ * ```
2825
+ */
2826
+ session_start: function() {
2827
+ console.log('');
2828
+ fireEvent('session_start');
2829
+ },
2830
+ /**
2831
+ * Triggers when visitor session goes idle for more than 30 minutes.
2832
+ *
2833
+ * ```js
2834
+ * // Usage:
2835
+ *
2836
+ * // Add function to 'session_end' event
2837
+ * _inbound.add_action( 'session_end', session_end_func_example, 10);
2838
+ *
2839
+ * function session_end_func_example(data) {
2840
+ * var data = data || {};
2841
+ * // Do something when session ends
2842
+ * alert("Hey! It's been 30 minutes... where did you go?");
2843
+ * }
2844
+ * ```
2845
+ */
2846
+ session_end: function(clockTime) {
2847
+ fireEvent('session_end', clockTime);
2848
+ console.log('Session End');
2849
+ },
2850
+ /**
2851
+ * Triggers if active session is detected
2852
+ *
2853
+ * ```js
2854
+ * // Usage:
2855
+ *
2856
+ * // Add function to 'session_active' event
2857
+ * _inbound.add_action( 'session_active', session_active_func_example, 10);
2858
+ *
2859
+ * function session_active_func_example(data) {
2860
+ * var data = data || {};
2861
+ * // session active
2862
+ * }
2863
+ * ```
2864
+ */
2865
+ session_active: function() {
2866
+ fireEvent('session_active');
2867
+ },
2868
+ /**
2869
+ * Triggers when visitor session goes idle. Idling occurs after 60 seconds of
2870
+ * inactivity or when the visitor switches browser tabs
2871
+ *
2872
+ * ```js
2873
+ * // Usage:
2874
+ *
2875
+ * // Add function to 'session_idle' event
2876
+ * _inbound.add_action( 'session_idle', session_idle_func_example, 10);
2877
+ *
2878
+ * function session_idle_func_example(data) {
2879
+ * var data = data || {};
2880
+ * // Do something when session idles
2881
+ * alert('Here is a special offer for you!');
2882
+ * }
2883
+ * ```
2884
+ */
2885
+ session_idle: function(clockTime) {
2886
+ fireEvent('session_idle', clockTime);
2887
+ },
2888
+ /**
2889
+ * Triggers when session is already active and gets resumed
2890
+ *
2891
+ * ```js
2892
+ * // Usage:
2893
+ *
2894
+ * // Add function to 'session_resume' event
2895
+ * _inbound.add_action( 'session_resume', session_resume_func_example, 10);
2896
+ *
2897
+ * function session_resume_func_example(data) {
2898
+ * var data = data || {};
2899
+ * // Session exists and is being resumed
2900
+ * }
2901
+ * ```
2902
+ */
2903
+ session_resume: function() {
2904
+ fireEvent('session_resume');
2905
+ },
2906
+ /**
2907
+ * Session emitter. Runs every 10 seconds. This is a useful function for
2908
+ * pinging third party services
2909
+ *
2910
+ * ```js
2911
+ * // Usage:
2912
+ *
2913
+ * // Add session_heartbeat_func_example function to 'session_heartbeat' event
2914
+ * _inbound.add_action( 'session_heartbeat', session_heartbeat_func_example, 10);
2915
+ *
2916
+ * function session_heartbeat_func_example(data) {
2917
+ * var data = data || {};
2918
+ * // Do something with every 10 seconds
2919
+ * }
2920
+ * ```
2921
+ */
2922
+ session_heartbeat: function(clockTime) {
2923
+ var data = {
2924
+ 'clock': clockTime,
2925
+ 'leadData': InboundLeadData
2926
+ };
2927
+ fireEvent('session_heartbeat', data);
2928
+ },
2929
+ /**
2930
+ * Triggers Every Page View
2931
+ *
2932
+ * ```js
2933
+ * // Usage:
2934
+ *
2935
+ * // Add function to 'page_visit' event
2936
+ * _inbound.add_action( 'page_visit', page_visit_func_example, 10);
2937
+ *
2938
+ * function session_idle_func_example(pageData) {
2939
+ * var pageData = pageData || {};
2940
+ * if( pageData.view_count > 8 ){
2941
+ * alert('Wow you have been to this page more than 8 times.');
2942
+ * }
2943
+ * }
2944
+ * ```
2945
+ */
2946
+ page_visit: function(pageData) {
2947
+ fireEvent('page_view', pageData);
2948
+ },
2949
+ /**
2950
+ * Triggers If the visitor has never seen the page before
2951
+ *
2952
+ * ```js
2953
+ * // Usage:
2954
+ *
2955
+ * // Add function to 'page_first_visit' event
2956
+ * _inbound.add_action( 'page_first_visit', page_first_visit_func_example, 10);
2957
+ *
2958
+ * function page_first_visit_func_example(pageData) {
2959
+ * var pageData = pageData || {};
2960
+ * alert('Welcome to this page! Its the first time you have seen it')
2961
+ * }
2962
+ * ```
2963
+ */
2964
+ page_first_visit: function(pageData) {
2965
+ fireEvent('page_first_visit');
2966
+ _inbound.deBugger('pages', 'First Ever Page View of this Page');
2967
+ },
2968
+ /**
2969
+ * Triggers If the visitor has seen the page before
2970
+ *
2971
+ * ```js
2972
+ * // Usage:
2973
+ *
2974
+ * // Add function to 'page_revisit' event
2975
+ * _inbound.add_action( 'page_revisit', page_revisit_func_example, 10);
2976
+ *
2977
+ * function page_revisit_func_example(pageData) {
2978
+ * var pageData = pageData || {};
2979
+ * alert('Welcome back to this page!');
2980
+ * // Show visitor special content/offer
2981
+ * }
2982
+ * ```
2983
+ */
2984
+ page_revisit: function(pageData) {
2985
+
2986
+ fireEvent('page_revisit', pageData);
2987
+
2988
+ var logger = function() {
2989
+ console.log('pageData', pageData);
2990
+ console.log('Page Revisit viewed ' + pageData + " times");
2991
+ }
2992
+ _inbound.deBugger('pages', status, logger);
2993
+ },
2994
+
2995
+ /**
2996
+ * `tab_hidden` is triggered when the visitor switches browser tabs
2997
+ *
2998
+ * ```js
2999
+ * // Usage:
3000
+ *
3001
+ * // Adding the callback
3002
+ * function tab_hidden_function( data ) {
3003
+ * alert('The Tab is Hidden');
3004
+ * };
3005
+ *
3006
+ * // Hook the function up the the `tab_hidden` event
3007
+ * _inbound.add_action( 'tab_hidden', tab_hidden_function, 10 );
3008
+ * ```
3009
+ */
3010
+ tab_hidden: function(data) {
3011
+ _inbound.deBugger('pages', 'Tab Hidden');
3012
+ fireEvent('tab_hidden');
3013
+ },
3014
+ /**
3015
+ * `tab_visible` is triggered when the visitor switches back to the sites tab
3016
+ *
3017
+ * ```js
3018
+ * // Usage:
3019
+ *
3020
+ * // Adding the callback
3021
+ * function tab_visible_function( data ) {
3022
+ * alert('Welcome back to this tab!');
3023
+ * // trigger popup or offer special discount etc.
3024
+ * };
3025
+ *
3026
+ * // Hook the function up the the `tab_visible` event
3027
+ * _inbound.add_action( 'tab_visible', tab_visible_function, 10 );
3028
+ * ```
3029
+ */
3030
+ tab_visible: function(data) {
3031
+ _inbound.deBugger('pages', 'Tab Visible');
3032
+ fireEvent('tab_visible');
3033
+ },
3034
+ /**
3035
+ * `tab_mouseout` is triggered when the visitor mouses out of the browser window.
3036
+ * This is especially useful for exit popups
3037
+ *
3038
+ * ```js
3039
+ * // Usage:
3040
+ *
3041
+ * // Adding the callback
3042
+ * function tab_mouseout_function( data ) {
3043
+ * alert("Wait don't Go");
3044
+ * // trigger popup or offer special discount etc.
3045
+ * };
3046
+ *
3047
+ * // Hook the function up the the `tab_mouseout` event
3048
+ * _inbound.add_action( 'tab_mouseout', tab_mouseout_function, 10 );
3049
+ * ```
3050
+ */
3051
+ tab_mouseout: function(data) {
3052
+ _inbound.deBugger('pages', 'Tab Mouseout');
3053
+ fireEvent('tab_mouseout');
3054
+ },
3055
+ /**
3056
+ * `form_input_change` is triggered when tracked form inputs change
3057
+ * You can use this to add additional validation or set conditional triggers
3058
+ *
3059
+ * ```js
3060
+ * // Usage:
3061
+ *
3062
+ * ```
3063
+ */
3064
+ form_input_change: function(inputData) {
3065
+ var logger = function() {
3066
+ console.log(inputData);
3067
+ //console.log('Page Revisit viewed ' + pageData + " times");
3068
+ }
3069
+ _inbound.deBugger('forms', 'inputData change. Data=', logger);
3070
+ fireEvent('form_input_change', inputData);
3071
+ },
3072
+ /**
3073
+ * `form_before_submission` is triggered before the form is submitted to the server.
3074
+ * You can filter the data here or send it to third party services
3075
+ *
3076
+ * ```js
3077
+ * // Usage:
3078
+ *
3079
+ * // Adding the callback
3080
+ * function form_before_submission_function( data ) {
3081
+ * var data = data || {};
3082
+ * // filter form data
3083
+ * };
3084
+ *
3085
+ * // Hook the function up the the `form_before_submission` event
3086
+ * _inbound.add_action( 'form_before_submission', form_before_submission_function, 10 );
3087
+ * ```
3088
+ */
3089
+ form_before_submission: function(formData) {
3090
+ fireEvent('form_before_submission', formData);
3091
+ },
3092
+ /**
3093
+ * `form_after_submission` is triggered after the form is submitted to the server.
3094
+ * You can filter the data here or send it to third party services
3095
+ *
3096
+ * ```js
3097
+ * // Usage:
3098
+ *
3099
+ * // Adding the callback
3100
+ * function form_after_submission_function( data ) {
3101
+ * var data = data || {};
3102
+ * // filter form data
3103
+ * };
3104
+ *
3105
+ * // Hook the function up the the `form_after_submission` event
3106
+ * _inbound.add_action( 'form_after_submission', form_after_submission_function, 10 );
3107
+ * ```
3108
+ */
3109
+ form_after_submission: function(formData) {
3110
+
3111
+ fireEvent('form_after_submission', formData);
3112
+
3113
+ },
3114
+ /**
3115
+ * `search_before_caching` is triggered before the search is stored in the user's browser.
3116
+ * If a lead ID is set, the search data will be saved to the server when the next page loads.
3117
+ * You can filter the data here or send it to third party services
3118
+ *
3119
+ * ```js
3120
+ * // Usage:
3121
+ *
3122
+ * // Adding the callback
3123
+ * function search_before_caching_function( data ) {
3124
+ * var data = data || {};
3125
+ * // filter search data
3126
+ * };
3127
+ *
3128
+ * // Hook the function up the the `search_before_caching` event
3129
+ * _inbound.add_action( 'search_before_caching', search_before_caching_function, 10 );
3130
+ * ```
3131
+ */
3132
+ search_before_caching: function(searchData) {
3133
+ fireEvent('search_before_caching', searchData);
3134
+ },
3135
+ /*! Scrol depth https://github.com/robflaherty/jquery-scrolldepth/blob/master/jquery.scrolldepth.js */
3136
+
3137
+ analyticsError: function(MLHttpRequest, textStatus, errorThrown) {
3138
+ var error = new CustomEvent("inbound_analytics_error", {
3139
+ detail: {
3140
+ MLHttpRequest: MLHttpRequest,
3141
+ textStatus: textStatus,
3142
+ errorThrown: errorThrown
3143
+ }
3144
+ });
3145
+ window.dispatchEvent(error);
3146
+ console.log('Page Save Error');
3147
+ }
3148
+
3149
+ };
3150
+
3151
+ return _inbound;
3152
+
3153
+ })(_inbound || {});
3154
+
3155
+
3156
+ function inboundFormNoRedirect(){
3157
+ /*button == the button that was clicked, form == the form that button belongs to, formRedirectUrl == the link that the form redirects to, if set*/
3158
+
3159
+ /*Get the button...*/
3160
+ /*If not an iframe*/
3161
+ if(window.frames.frameElement == null){
3162
+ var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
3163
+ }
3164
+ /*If it is an iframe*/
3165
+ else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
3166
+ var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
3167
+ }
3168
+
3169
+ if ( typeof button == 'undefined' ) {
3170
+ return;
3171
+ }
3172
+
3173
+ var form = button.form,
3174
+ formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');
3175
+
3176
+ /*If the redirect link is not set, or there is a single space in it, the form isn't supposed to redirect. So set the action for void*/
3177
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
3178
+ form.action = 'javascript:void(0)';
3179
+ }
3180
+ }
3181
+
3182
+ _inbound.add_action( 'form_before_submission', inboundFormNoRedirect, 10 );
3183
+
3184
+ function inboundFormNoRedirectContent(){
3185
+
3186
+ /*If not an iframe*/
3187
+ if(window.frames.frameElement == null){
3188
+ var button = document.querySelectorAll('button.inbound-button-submit[disabled]')[0];
3189
+ }
3190
+ /*If it is an iframe*/
3191
+ else if(window.frames.frameElement.tagName.toLowerCase() == "iframe"){
3192
+ var button = window.frames.frameElement.contentWindow.document.querySelectorAll('button.inbound-button-submit')[0];
3193
+ }
3194
+
3195
+
3196
+ if ( typeof button == 'undefined' ) {
3197
+ return;
3198
+ }
3199
+
3200
+ var form = button.form,
3201
+ formRedirectUrl = form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),
3202
+ btnBackground = jQuery(button).css('background'),
3203
+ btnFontColor = jQuery(button).css('color'),
3204
+ btnHeight = jQuery(button).css('height'),
3205
+ spinner = button.getElementsByClassName('inbound-form-spinner');
3206
+
3207
+ if(formRedirectUrl.length == 0 || formRedirectUrl[0]['value'] == 'IA=='){
3208
+ jQuery(spinner).remove();
3209
+ jQuery(button).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+ btnBackground +'; color='+ btnFontColor +'; font-size:calc('+ btnHeight +' * .42);"></i></div>');
3210
+ }
3211
+ }
3212
+
3213
+ _inbound.add_action( 'form_after_submission', inboundFormNoRedirectContent, 10 );
3214
+
3215
+ /* LocalStorage Component */
3216
+ var InboundTotalStorage = (function (_inbound){
3217
+
3218
+ var supported, ls, mod = '_inbound';
3219
+ if ('localStorage' in window){
3220
+ try {
3221
+ ls = (typeof window.localStorage === 'undefined') ? undefined : window.localStorage;
3222
+ if (typeof ls == 'undefined' || typeof window.JSON == 'undefined'){
3223
+ supported = false;
3224
+ } else {
3225
+ supported = true;
3226
+ }
3227
+ window.localStorage.setItem(mod, '1');
3228
+ window.localStorage.removeItem(mod);
3229
+ }
3230
+ catch (err){
3231
+ supported = false;
3232
+ }
3233
+ }
3234
+
3235
+ /* Make the methods public */
3236
+ _inbound.totalStorage = function(key, value, options){
3237
+ return _inbound.totalStorage.impl.init(key, value);
3238
+ };
3239
+
3240
+ _inbound.totalStorage.setItem = function(key, value){
3241
+ return _inbound.totalStorage.impl.setItem(key, value);
3242
+ };
3243
+
3244
+ _inbound.totalStorage.getItem = function(key){
3245
+ return _inbound.totalStorage.impl.getItem(key);
3246
+ };
3247
+
3248
+ _inbound.totalStorage.getAll = function(){
3249
+ return _inbound.totalStorage.impl.getAll();
3250
+ };
3251
+
3252
+ _inbound.totalStorage.deleteItem = function(key){
3253
+ return _inbound.totalStorage.impl.deleteItem(key);
3254
+ };
3255
+
3256
+
3257
+ _inbound.totalStorage.impl = {
3258
+
3259
+ init: function(key, value){
3260
+ if (typeof value != 'undefined') {
3261
+ return this.setItem(key, value);
3262
+ } else {
3263
+ return this.getItem(key);
3264
+ }
3265
+ },
3266
+
3267
+ setItem: function(key, value){
3268
+ if (!supported){
3269
+ try {
3270
+ _inbound.Utils.createCookie(key, value);
3271
+ return value;
3272
+ } catch(e){
3273
+ console.log('Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie');
3274
+ }
3275
+ }
3276
+ var saver = JSON.stringify(value);
3277
+ ls.setItem(key, saver);
3278
+ return this.parseResult(saver);
3279
+ },
3280
+ getItem: function(key){
3281
+ if (!supported){
3282
+ try {
3283
+ return this.parseResult(_inbound.Utils.readCookie(key));
3284
+ } catch(e){
3285
+ return null;
3286
+ }
3287
+ }
3288
+ var item = ls.getItem(key);
3289
+ return this.parseResult(item);
3290
+ },
3291
+ deleteItem: function(key){
3292
+ if (!supported){
3293
+ try {
3294
+ _inbound.Utils.eraseCookie(key, null);
3295
+ return true;
3296
+ } catch(e){
3297
+ return false;
3298
+ }
3299
+ }
3300
+ ls.removeItem(key);
3301
+ return true;
3302
+ },
3303
+ getAll: function(){
3304
+ var items = [];
3305
+ if (!supported){
3306
+ try {
3307
+ var pairs = document.cookie.split(";");
3308
+ for (var i = 0; i<pairs.length; i++){
3309
+ var pair = pairs[i].split('=');
3310
+ var key = pair[0];
3311
+ items.push({key:key, value:this.parseResult(_inbound.Utils.readCookie(key))});
3312
+ }
3313
+ } catch(e){
3314
+ return null;
3315
+ }
3316
+ } else {
3317
+ for (var j in ls){
3318
+ if (j.length){
3319
+ items.push({key:j, value:this.parseResult(ls.getItem(j))});
3320
+ }
3321
+ }
3322
+ }
3323
+ return items;
3324
+ },
3325
+ parseResult: function(res){
3326
+ var ret;
3327
+ try {
3328
+ ret = JSON.parse(res);
3329
+ if (typeof ret == 'undefined'){
3330
+ ret = res;
3331
+ }
3332
+ if (ret == 'true'){
3333
+ ret = true;
3334
+ }
3335
+ if (ret == 'false'){
3336
+ ret = false;
3337
+ }
3338
+ if (parseFloat(ret) == ret && typeof ret != "object"){
3339
+ ret = parseFloat(ret);
3340
+ }
3341
+ } catch(e){
3342
+ ret = res;
3343
+ }
3344
+ return ret;
3345
+ }
3346
+ };
3347
+ })(_inbound || {});
3348
+ /**
3349
+ * Leads API functions
3350
+ * @param Object _inbound - Main JS object
3351
+ * @return Object - include event triggers
3352
+ */
3353
+ var _inboundLeadsAPI = (function(_inbound) {
3354
+ var httpRequest;
3355
+ _inbound.LeadsAPI = {
3356
+ init: function() {
3357
+
3358
+ var utils = _inbound.Utils,
3359
+ wp_lead_uid = utils.readCookie("wp_lead_uid"),
3360
+ wp_lead_id = utils.readCookie("wp_lead_id"),
3361
+ expire_check = utils.readCookie("lead_session_expire"); // check for session
3362
+
3363
+ if (!expire_check) {
3364
+ _inbound.deBugger('leads', 'expired vistor. Run Processes');
3365
+ //var data_to_lookup = global-localized-vars;
3366
+ if (wp_lead_id) {
3367
+ /* Get InboundLeadData */
3368
+ _inbound.LeadsAPI.getAllLeadData();
3369
+ }
3370
+ }
3371
+ },
3372
+ setGlobalLeadData: function(data) {
3373
+ InboundLeadData = data;
3374
+ },
3375
+ getAllLeadData: function(expire_check) {
3376
+ var wp_lead_id = _inbound.Utils.readCookie("wp_lead_id"),
3377
+ leadData = _inbound.totalStorage('inbound_lead_data'),
3378
+ leadDataExpire = _inbound.Utils.readCookie("lead_data_expire");
3379
+ data = {
3380
+ action: 'inbound_get_all_lead_data',
3381
+ wp_lead_id: wp_lead_id
3382
+ },
3383
+ success = function(returnData) {
3384
+ var leadData = JSON.parse(returnData);
3385
+ _inbound.LeadsAPI.setGlobalLeadData(leadData);
3386
+ _inbound.totalStorage('inbound_lead_data', leadData); // store lead data
3387
+
3388
+ /* Set 3 day timeout for checking DB for new lead data for Lead_Global var */
3389
+ var d = new Date();
3390
+ d.setTime(d.getTime() + 30 * 60 * 1000);
3391
+ var expire = _inbound.Utils.addDays(d, 3);
3392
+ _inbound.Utils.createCookie("lead_data_expire", true, expire);
3393
+
3394
+ };
3395
+
3396
+ if (!leadData) {
3397
+ // Get New Lead Data from DB
3398
+ _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3399
+
3400
+ } else {
3401
+ // set global lead var with localstorage data
3402
+ _inbound.LeadsAPI.setGlobalLeadData(leadData);
3403
+ _inbound.deBugger('lead', 'Set Global Lead Data from Localstorage');
3404
+
3405
+ if (!leadDataExpire) {
3406
+ _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3407
+ //console.log('Set Global Lead Data from Localstorage');
3408
+ _inbound.deBugger('lead', 'localized data old. Pull new from DB');
3409
+ //console.log('localized data old. Pull new from DB');
3410
+ }
3411
+ }
3412
+
3413
+ },
3414
+ getLeadLists: function() {
3415
+ var wp_lead_id = _inbound.Utils.readCookie("wp_lead_id");
3416
+ var data = {
3417
+ action: 'wpl_check_lists',
3418
+ wp_lead_id: wp_lead_id
3419
+ };
3420
+ var success = function(user_id) {
3421
+ _inbound.Utils.createCookie("lead_session_list_check", true, {
3422
+ path: '/',
3423
+ expires: 1
3424
+ });
3425
+ _inbound.deBugger('lead', "Lists checked");
3426
+ //console.log("Lists checked");
3427
+ };
3428
+ //_inbound.Utils.doAjax(data, success);
3429
+ _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, success);
3430
+ }
3431
+ };
3432
+
3433
+ return _inbound;
3434
+
3435
+ })(_inbound || {});
3436
+ /**
3437
+ * # Page View Tracking
3438
+ *
3439
+ * Page view tracking
3440
+ *
3441
+ * @contributor David Wells <david@inboundnow.com>
3442
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
3443
+ * @version 0.0.2
3444
+ */
3445
+ /* Launches view tracking */
3446
+ var _inboundPageTracking = (function(_inbound) {
3447
+
3448
+ var started = false,
3449
+ stopped = false,
3450
+ turnedOff = false,
3451
+ clockTime = parseInt(_inbound.Utils.readCookie("lead_session"), 10) || 0,
3452
+ inactiveClockTime = 0,
3453
+ startTime = new Date(),
3454
+ clockTimer = null,
3455
+ inactiveClockTimer = null,
3456
+ idleTimer = null,
3457
+ reportInterval,
3458
+ idleTimeout,
3459
+ utils = _inbound.Utils,
3460
+ timeNow = _inbound.Utils.GetDate(),
3461
+ lsType = 'page_views',
3462
+ Pages = _inbound.totalStorage(lsType) || {},
3463
+ /*!
3464
+ Todo: Use UTC offset
3465
+ var x = new Date();
3466
+ var currentTime = x.getTimezoneOffset() / 60;
3467
+ console.log(currentTime) // gets UTC offset
3468
+ */
3469
+ id = inbound_settings.post_id || window.location.pathname,
3470
+ analyticsTimeout = _inbound.Settings.timeout || 10000;
3471
+
3472
+ _inbound.PageTracking = {
3473
+
3474
+ init: function(options) {
3475
+
3476
+ if(lsType !== 'page_views') {
3477
+ return false; // in admin
3478
+ }
3479
+
3480
+ this.CheckTimeOut();
3481
+ // Set up options and defaults
3482
+ options = options || {};
3483
+ reportInterval = parseInt(options.reportInterval, 10) || 10;
3484
+ idleTimeout = parseInt(options.idleTimeout, 10) || 3;
3485
+
3486
+ // Basic activity event listeners
3487
+ utils.addListener(document, 'keydown', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3488
+ utils.addListener(document, 'click', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3489
+ utils.addListener(window, 'mousemove', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3490
+ //utils.addListener(window, 'scroll', utils.throttle(_inbound.PageTracking.pingSession, 1000));
3491
+
3492
+ // Page visibility listeners
3493
+ _inbound.PageTracking.checkVisibility();
3494
+
3495
+ /* Start Session on page load */
3496
+ this.startSession();
3497
+
3498
+ },
3499
+
3500
+ setIdle: function(reason) {
3501
+ var reason = reason || "No Movement",
3502
+ msg = 'Session IDLE. Activity Timeout due to ' + reason;
3503
+
3504
+ _inbound.deBugger('pages', msg);
3505
+
3506
+ clearTimeout(_inbound.PageTracking.idleTimer);
3507
+ _inbound.PageTracking.stopClock();
3508
+ _inbound.trigger('session_idle');
3509
+
3510
+ },
3511
+
3512
+ checkVisibility: function() {
3513
+ var hidden, visibilityState, visibilityChange;
3514
+
3515
+ if (typeof document.hidden !== "undefined") {
3516
+ hidden = "hidden", visibilityChange = "visibilitychange", visibilityState = "visibilityState";
3517
+ } else if (typeof document.mozHidden !== "undefined") {
3518
+ hidden = "mozHidden", visibilityChange = "mozvisibilitychange", visibilityState = "mozVisibilityState";
3519
+ } else if (typeof document.msHidden !== "undefined") {
3520
+ hidden = "msHidden", visibilityChange = "msvisibilitychange", visibilityState = "msVisibilityState";
3521
+ } else if (typeof document.webkitHidden !== "undefined") {
3522
+ hidden = "webkitHidden", visibilityChange = "webkitvisibilitychange", visibilityState = "webkitVisibilityState";
3523
+ }
3524
+
3525
+ var document_hidden = document[hidden];
3526
+
3527
+ _inbound.Utils.addListener(document, visibilityChange, function(e) {
3528
+ /*! Listen for visibility changes */
3529
+ if (document_hidden != document[hidden]) {
3530
+ if (document[hidden]) {
3531
+ // Document hidden
3532
+ _inbound.trigger('tab_hidden');
3533
+ _inbound.PageTracking.setIdle('browser tab switch');
3534
+ } else {
3535
+ // Document shown
3536
+ _inbound.trigger('tab_visible');
3537
+ _inbound.PageTracking.pingSession();
3538
+ }
3539
+
3540
+ document_hidden = document[hidden];
3541
+ }
3542
+ });
3543
+ },
3544
+ clock: function() {
3545
+ clockTime += 1;
3546
+ var niceTime = clockTime / 60;
3547
+ var msg = 'Total time spent on Page in this Session: ' + niceTime.toFixed(2) + " min";
3548
+ _inbound.deBugger('pages', msg);
3549
+ if (clockTime > 0 && (clockTime % reportInterval === 0)) {
3550
+
3551
+ var d = new Date();
3552
+ d.setTime(d.getTime() + 30 * 60 * 1000);
3553
+ utils.createCookie("lead_session", clockTime, d); // Set cookie on page load
3554
+
3555
+ /*! every 10 seconds run this */
3556
+ //console.log('Session Heartbeat every ' + reportInterval + ' secs');
3557
+ _inbound.trigger('session_heartbeat', clockTime);
3558
+
3559
+ }
3560
+
3561
+ },
3562
+ inactiveClock: function() {
3563
+ inactiveClockTime += 1;
3564
+ var TimeUntilTimeOut = (1800 - inactiveClockTime) / 60;
3565
+ var msg = 'Time until Session Timeout: ' + TimeUntilTimeOut.toFixed(2) + " min";
3566
+ _inbound.deBugger('pages', msg);
3567
+ //console.log('Time until Session Timeout: ', TimeUntilTimeOut.toFixed(2) + " min");
3568
+ /* Session timeout after 30min */
3569
+ if (inactiveClockTime > 1800) {
3570
+
3571
+ // sendEvent(clockTime);
3572
+ /*! End session after 30min timeout */
3573
+ _inbound.trigger('session_end', InboundLeadData);
3574
+ _inbound.Utils.eraseCookie("lead_session");
3575
+ /* todo maybe? remove session Cookie */
3576
+ inactiveClockTime = 0;
3577
+ clearTimeout(inactiveClockTimer);
3578
+ }
3579
+
3580
+
3581
+ },
3582
+ stopClock: function() {
3583
+ stopped = true;
3584
+ clearTimeout(clockTimer);
3585
+ clearTimeout(inactiveClockTimer);
3586
+ inactiveClockTimer = setInterval(_inbound.PageTracking.inactiveClock, 1000);
3587
+ },
3588
+
3589
+ restartClock: function() {
3590
+ stopped = false;
3591
+
3592
+
3593
+ _inbound.trigger('session_resume');
3594
+ _inbound.deBugger('pages', 'Activity resumed. Session Active');
3595
+ /* todo add session_resume */
3596
+ clearTimeout(clockTimer);
3597
+ inactiveClockTime = 0;
3598
+ clearTimeout(inactiveClockTimer);
3599
+ clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
3600
+ },
3601
+
3602
+ turnOff: function() {
3603
+ _inbound.PageTracking.setIdle();
3604
+ turnedOff = true;
3605
+ },
3606
+
3607
+ turnOn: function() {
3608
+ turnedOff = false;
3609
+ },
3610
+ /* This start only runs once */
3611
+ startSession: function() {
3612
+ /* todo add session Cookie */
3613
+ // Calculate seconds from start to first interaction
3614
+ var currentTime = new Date();
3615
+ var diff = currentTime - startTime;
3616
+
3617
+
3618
+ started = true; // Set global
3619
+
3620
+ // Send User Timing Event
3621
+ /* Todo session start here */
3622
+
3623
+ // Start clock
3624
+ clockTimer = setInterval(_inbound.PageTracking.clock, 1000);
3625
+ //utils.eraseCookie("lead_session");
3626
+ var session = utils.readCookie("lead_session");
3627
+
3628
+ if (!session) {
3629
+ _inbound.trigger('session_start'); // trigger 'inbound_analytics_session_start'
3630
+ var d = new Date();
3631
+ d.setTime(d.getTime() + 30 * 60 * 1000);
3632
+ _inbound.Utils.createCookie("lead_session", 1, d); // Set cookie on page load
3633
+ } else {
3634
+ _inbound.trigger('session_active');
3635
+ //console.log("count of secs " + session);
3636
+ //_inbound.trigger('session_active'); // trigger 'inbound_analytics_session_active'
3637
+ }
3638
+
3639
+ this.pingSession();
3640
+
3641
+
3642
+ },
3643
+ resetInactiveFunc: function() {
3644
+ inactiveClockTime = 0;
3645
+ clearTimeout(inactiveClockTimer);
3646
+ },
3647
+ /* Ping Session to keep active */
3648
+ pingSession: function(e) {
3649
+
3650
+
3651
+ if (turnedOff) {
3652
+ return;
3653
+ }
3654
+
3655
+ if (!started) {
3656
+ _inbound.PageTracking.startSession();
3657
+ }
3658
+
3659
+ if (stopped) {
3660
+ _inbound.PageTracking.restartClock();
3661
+ }
3662
+
3663
+ clearTimeout(idleTimer);
3664
+
3665
+ idleTimer = setTimeout(_inbound.PageTracking.setIdle, idleTimeout * 1000 + 100);
3666
+
3667
+ if (typeof(e) != "undefined") {
3668
+ if (e.type === "mousemove") {
3669
+ _inbound.PageTracking.mouseEvents(e);
3670
+ }
3671
+ }
3672
+
3673
+ },
3674
+ mouseEvents: function(e) {
3675
+
3676
+ if (e.pageY <= 5) {
3677
+ _inbound.trigger('tab_mouseout');
3678
+ }
3679
+
3680
+ },
3681
+ /**
3682
+ * Returns the pages viewed by the site visitor
3683
+ *
3684
+ * ```js
3685
+ * var pageViews = _inbound.PageTracking.getPageViews();
3686
+ * // returns page view object
3687
+ * ```
3688
+ *
3689
+ * @return {object} page view object with page ID as key and timestamp
3690
+ */
3691
+ getPageViews: function() {
3692
+ var local_store = _inbound.Utils.checkLocalStorage();
3693
+ if (local_store) {
3694
+ var page_views = localStorage.getItem(lsType),
3695
+ local_object = JSON.parse(page_views);
3696
+ if (typeof local_object == 'object' && local_object) {
3697
+ //this.triggerPageView();
3698
+ }
3699
+ return local_object;
3700
+ }
3701
+ },
3702
+ isRevisit: function(Pages) {
3703
+ var revisitCheck = false;
3704
+ var Pages = Pages || {};
3705
+ var pageSeen = Pages[id];
3706
+ if (typeof(pageSeen) != "undefined" && pageSeen !== null) {
3707
+ revisitCheck = true;
3708
+ }
3709
+ return revisitCheck;
3710
+ },
3711
+ triggerPageView: function(pageRevisit) {
3712
+
3713
+ var pageData = {
3714
+ title: document.title,
3715
+ url: document.location.href,
3716
+ path: document.location.pathname,
3717
+ count: 1 // default
3718
+ };
3719
+
3720
+ if (pageRevisit) {
3721
+ /* Page Revisit Trigger */
3722
+ Pages[id].push(timeNow);
3723
+ pageData.count = Pages[id].length;
3724
+ _inbound.trigger('page_revisit', pageData);
3725
+
3726
+ } else {
3727
+ /* Page First Seen Trigger */
3728
+ Pages[id] = [];
3729
+ Pages[id].push(timeNow);
3730
+ _inbound.trigger('page_first_visit', pageData);
3731
+ }
3732
+
3733
+ _inbound.trigger('page_visit', pageData);
3734
+
3735
+ _inbound.totalStorage(lsType, Pages);
3736
+
3737
+ this.storePageView();
3738
+
3739
+ },
3740
+ CheckTimeOut: function() {
3741
+
3742
+ var pageRevisit = this.isRevisit(Pages),
3743
+ status,
3744
+ timeout;
3745
+
3746
+ /* Default */
3747
+ if (pageRevisit) {
3748
+
3749
+ var prev = Pages[id].length - 1,
3750
+ lastView = Pages[id][prev],
3751
+ timeDiff = Math.abs(new Date(lastView).getTime() - new Date(timeNow).getTime());
3752
+
3753
+ timeout = timeDiff > analyticsTimeout;
3754
+
3755
+ if (timeout) {
3756
+ status = 'Timeout Happened. Page view fired';
3757
+ this.triggerPageView(pageRevisit);
3758
+ } else {
3759
+ time_left = Math.abs((analyticsTimeout - timeDiff)) * 0.001;
3760
+ status = analyticsTimeout / 1000 + ' sec timeout not done: ' + time_left + " seconds left";
3761
+ }
3762
+
3763
+ } else {
3764
+ /*! Page never seen before save view */
3765
+ this.triggerPageView(pageRevisit);
3766
+ }
3767
+
3768
+ _inbound.deBugger('pages', status);
3769
+ },
3770
+ storePageView: function() {
3771
+ var stored = false;
3772
+
3773
+ /* ignore if page tracking off and page is not a landing page */
3774
+ if ( inbound_settings.page_tracking == 'off' && inbound_settings.post_type != 'landing-page' ) {
3775
+ return;
3776
+ }
3777
+
3778
+ /* Let's try and fire this last - also defines what constitutes a bounce - */
3779
+ document.onreadystatechange = function(){
3780
+
3781
+ if(document.readyState !== 'loading' && stored === false){
3782
+ setTimeout(function(){
3783
+ var leadID = ( _inbound.Utils.readCookie('wp_lead_id') ) ? _inbound.Utils.readCookie('wp_lead_id') : '';
3784
+ var lead_uid = ( _inbound.Utils.readCookie('wp_lead_uid') ) ? _inbound.Utils.readCookie('wp_lead_uid') : '';
3785
+ var ctas_loaded = _inbound.totalStorage('wp_cta_loaded');
3786
+ var ctas_impressions = _inbound.totalStorage('wp_cta_impressions');
3787
+ stored = true;
3788
+
3789
+ /* now reset impressions */
3790
+ _inbound.totalStorage('wp_cta_impressions' , {} );
3791
+
3792
+ var data = {
3793
+ action: 'inbound_track_lead',
3794
+ wp_lead_uid: lead_uid,
3795
+ wp_lead_id: leadID,
3796
+ page_id: inbound_settings.post_id,
3797
+ variation_id: inbound_settings.variation_id,
3798
+ post_type: inbound_settings.post_type,
3799
+ current_url: window.location.href,
3800
+ page_views: JSON.stringify(_inbound.PageTracking.getPageViews()),
3801
+ cta_impressions : JSON.stringify(ctas_impressions),
3802
+ cta_history : JSON.stringify(ctas_loaded),
3803
+ json: '0'
3804
+ };
3805
+
3806
+ var firePageCallback = function(leadID) {
3807
+ //_inbound.Events.page_view_saved(leadID);
3808
+ };
3809
+ //_inbound.Utils.doAjax(data, firePageCallback);
3810
+
3811
+ _inbound.Utils.ajaxPost(inbound_settings.admin_url, data, firePageCallback);
3812
+
3813
+ } , 200 );
3814
+ }
3815
+ }
3816
+ }
3817
+ /*! GA functions
3818
+ function log_event(category, action, label) {
3819
+ _gaq.push(['_trackEvent', category, action, label]);
3820
+ }
3821
+
3822
+ function log_click(category, link) {
3823
+ log_event(category, 'Click', $(link).text());
3824
+ }
3825
+ */
3826
+ };
3827
+
3828
+ return _inbound;
3829
+
3830
+ })(_inbound || {});
3831
+ /**
3832
+ * # Start
3833
+ *
3834
+ * Runs init functions
3835
+ *
3836
+ * @contributor David Wells <david@inboundnow.com>
3837
+ * @contributor Hudson Atwell <hudson@inboundnow.com>
3838
+ * @version 0.0.2
3839
+ */
3840
+
3841
+
3842
+ /* Initialize _inbound */
3843
+ _inbound.init();
3844
+
3845
+ /* Set Global Lead Data */
3846
+ InboundLeadData = _inbound.totalStorage('inbound_lead_data') || null;
3847
+
shared/assets/js/frontend/analytics/inboundAnalytics.min.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! Inbound Analyticsv1.0.0 | (c) 2017 Inbound Now | https://github.com/inboundnow/cta */
2
- function inboundFormNoRedirect(){if(null==window.frames.frameElement)e=document.querySelectorAll("button.inbound-button-submit[disabled]")[0];else if("iframe"==window.frames.frameElement.tagName.toLowerCase())var e=window.frames.frameElement.contentWindow.document.querySelectorAll("button.inbound-button-submit")[0];if(void 0!==e){var t=e.form,n=t.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');0!=n.length&&"IA=="!=n[0].value||(t.action="javascript:void(0)")}}function inboundFormNoRedirectContent(){if(null==window.frames.frameElement)e=document.querySelectorAll("button.inbound-button-submit[disabled]")[0];else if("iframe"==window.frames.frameElement.tagName.toLowerCase())var e=window.frames.frameElement.contentWindow.document.querySelectorAll("button.inbound-button-submit")[0];if(void 0!==e){var t=e.form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),n=jQuery(e).css("background"),o=jQuery(e).css("color"),i=jQuery(e).css("height"),a=e.getElementsByClassName("inbound-form-spinner");0!=t.length&&"IA=="!=t[0].value||(jQuery(a).remove(),jQuery(e).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+n+"; color="+o+"; font-size:calc("+i+' * .42);"></i></div>'))}}var inbound_data=inbound_data||{},_inboundOptions=_inboundOptions||{},_gaq=_gaq||[],_inbound=function(e){var t={timeout:inbound_settings.is_admin?500:1e4,formAutoTracking:!0,formAutoPopulation:!0},n={init:function(){_inbound.Utils.init(),_inbound.Utils.domReady(window,function(){_inbound.DomLoaded()})},DomLoaded:function(){_inbound.PageTracking.init(),_inbound.Forms.init(),_inbound.Utils.setUrlParams(),_inbound.LeadsAPI.init(),setTimeout(function(){_inbound.Forms.init()},2e3),_inbound.trigger("analytics_ready")},extend:function(e,t){var n,o={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(o[n]=e[n]);for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(o[n]=t[n]);return o},debug:function(e,t){},deBugger:function(e,t,n){if(console){var o,i,a,r=document.location.hash?document.location.hash:"",s=r.indexOf("#debug")>-1,t=t||!1;r&&r.match(/debug/)&&(a=(r=r.split("-"))[1]),i="true"===_inbound.Utils.readCookie("inbound_debug"),((o="true"===_inbound.Utils.readCookie("inbound_debug_"+e))||s||i)&&(t&&"string"==typeof t&&(i||"all"===a?console.log('logAll "'+e+'" =>',t):o?console.log('log "'+e+'" =>',t):e===a&&console.log('#log "'+e+'" =>',t)),n&&n instanceof Function&&n())}}},o=n.extend(t,e);return n.Settings=o||{},n}(_inboundOptions),_inboundHooks=function(e){return e.hooks=new function(){function e(e,t,n,o){if(a[e][t])if(n){var i,r=a[e][t];if(o)for(i=r.length;i--;){var s=r[i];s.callback===n&&s.context===o&&r.splice(i,1)}else for(i=r.length;i--;)r[i].callback===n&&r.splice(i,1)}else a[e][t]=[]}function t(e,t,o,i,r){var s={callback:o,priority:i,context:r},l=a[e][t];l?(l.push(s),l=n(l)):l=[s],a[e][t]=l}function n(e){for(var t,n,o,i=1,a=e.length;i<a;i++){for(t=e[i],n=i;(o=e[n-1])&&o.priority>t.priority;)e[n]=e[n-1],--n;e[n]=t}return e}function o(e,t,n){var o=a[e][t];if(!o)return"filters"===e&&n[0];var i=0,r=o.length;if("filters"===e)for(;i<r;i++)n[0]=o[i].callback.apply(o[i].context,n);else for(;i<r;i++)o[i].callback.apply(o[i].context,n);return"filters"!==e||n[0]}var i={removeFilter:function(t,n){return"string"==typeof t&&e("filters",t,n),i},applyFilters:function(){var e=Array.prototype.slice.call(arguments),t=e.shift();return"string"==typeof t?o("filters",t,e):i},addFilter:function(e,n,o,a){return"string"==typeof e&&"function"==typeof n&&t("filters",e,n,o=parseInt(o||10,10)),i},removeAction:function(t,n){return"string"==typeof t&&e("actions",t,n),i},doAction:function(){var e=Array.prototype.slice.call(arguments),t=e.shift();return"string"==typeof t&&o("actions",t,e),i},addAction:function(e,n,o,a){return"string"==typeof e&&"function"==typeof n&&t("actions",e,n,o=parseInt(o||10,10),a),i}},a={actions:{},filters:{}};return i},e.add_action=function(){var t=arguments[0].split(" ");for(k in t)arguments[0]="inbound."+t[k],e.hooks.addAction.apply(this,arguments);return this},e.remove_action=function(){return arguments[0]="inbound."+arguments[0],e.hooks.removeAction.apply(this,arguments),this},e.do_action=function(){return arguments[0]="inbound."+arguments[0],e.hooks.doAction.apply(this,arguments),this},e.add_filter=function(){return arguments[0]="inbound."+arguments[0],e.hooks.addFilter.apply(this,arguments),this},e.remove_filter=function(){return arguments[0]="inbound."+arguments[0],e.hooks.removeFilter.apply(this,arguments),this},e.apply_filters=function(){return arguments[0]="inbound."+arguments[0],e.hooks.applyFilters.apply(this,arguments)},e}(_inbound||{}),_inboundUtils=function(e){var t,n=window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest,o=(Object.prototype.toString,{api_host:("https:"==location.protocol?"https://":"http://")+location.hostname+location.pathname.replace(/\/$/,""),track_pageview:!0,track_links_timeout:300,cookie_name:"_sp",cookie_expiration:365,cookie_domain:(host=location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i))?host[0]:""});return e.Utils={init:function(){this.polyFills(),this.checkLocalStorage(),this.SetUID(),this.storeReferralData()},polyFills:function(){window.console||(window.console={});for(var e=["log","info","warn","error","debug","trace","dir","group","groupCollapsed","groupEnd","time","timeEnd","profile","profileEnd","dirxml","assert","count","markTimeline","timeStamp","clear"],t=0;t<e.length;t++)window.console[e[t]]||(window.console[e[t]]=function(){});Date.prototype.toISOString||function(){function e(e){var t=String(e);return 1===t.length&&(t="0"+t),t}Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+e(this.getUTCMonth()+1)+"-"+e(this.getUTCDate())+"T"+e(this.getUTCHours())+":"+e(this.getUTCMinutes())+":"+e(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"}}();try{new CustomEvent("?")}catch(e){this.CustomEvent=function(e,t){function n(t,n,o,i){this["init"+e](t,n,o,i),"detail"in this||(this.detail=i)}return function(o,i){var a=document.createEvent(e);return null!==o?n.call(a,o,(i||(i=t)).bubbles,i.cancelable,i.detail):a.initCustomEvent=n,a}}(this.CustomEvent?"CustomEvent":"Event",{bubbles:!1,cancelable:!1,detail:null})}document.querySelectorAll||(document.querySelectorAll=function(e){var t,n=document.createElement("style"),o=[];for(document.documentElement.firstChild.appendChild(n),document._qsa=[],n.styleSheet.cssText=e+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",window.scrollBy(0,0),n.parentNode.removeChild(n);document._qsa.length;)(t=document._qsa.shift()).style.removeAttribute("x-qsa"),o.push(t);return document._qsa=null,o}),document.querySelector||(document.querySelector=function(e){var t=document.querySelectorAll(e);return t.length?t[0]:null}),!("innerText"in document.createElement("a"))&&"getSelection"in window&&HTMLElement.prototype.__defineGetter__("innerText",function(){for(var e,t=window.getSelection(),n=[],o=0;o<t.rangeCount;o++)n[o]=t.getRangeAt(o);t.removeAllRanges(),t.selectAllChildren(this),e=t.toString(),t.removeAllRanges();for(o=0;o<n.length;o++)t.addRange(n[o]);return e})},createCookie:function(e,t,n){var o="";if(n){var i=new Date;i.setTime(i.getTime()+24*n*60*60*1e3),o="; expires="+i.toGMTString()}document.cookie=e+"="+t+o+"; path=/"},readCookie:function(e){for(var t=e+"=",n=document.cookie.split(";"),o=0;o<n.length;o++){for(var i=n[o];" "===i.charAt(0);)i=i.substring(1,i.length);if(0===i.indexOf(t))return i.substring(t.length,i.length)}return null},eraseCookie:function(e){this.createCookie(e,"",-1)},getAllCookies:function(){var t={};if(document.cookie&&""!==document.cookie)for(var n=document.cookie.split(";"),o=0;o<n.length;o++){var i=n[o].split("=");i[0]=i[0].replace(/^ /,""),t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return e.totalStorage("inbound_cookies",t),t},setUrlParams:function(){var n={};!function(){for(var e,t=function(e){return decodeURIComponent(e).replace(/\+/g," ")},o=window.location.search.substring(1),i=/([^&=]+)=?([^&]*)/g;e=i.exec(o);)if("-1"==e[1].indexOf("["))n[t(e[1])]=t(e[2]);else{var a=e[1].indexOf("["),r=e[1].slice(a+1,e[1].indexOf("]",a)),s=t(e[1].slice(0,a));"object"!=typeof n[s]&&(n[t(s)]={},n[t(s)].length=0),r?n[t(s)][t(r)]=t(e[2]):Array.prototype.push.call(n[t(s)],t(e[2]))}}();for(var o in n)if("action"!=o)if("object"==typeof n[o])for(var i in n[o])this.createCookie(i,n[o][i],30);else this.createCookie(o,n[o],30);if(t){var a=e.totalStorage("inbound_url_params")||{},r=this.mergeObjs(a,n);e.totalStorage("inbound_url_params",r)}var s={option1:"yo",option2:"woooo"};e.trigger("url_parameters",n,s)},getAllUrlParams:function(){n={};if(t)var n=e.totalStorage("inbound_url_params");return n},getParameterVal:function(e,t){return(RegExp(e+"=(.+?)(&|$)").exec(t)||[,!1])[1]},checkLocalStorage:function(){if("localStorage"in window)try{ls=void 0===window.localStorage?void 0:window.localStorage,t="undefined"!=typeof ls&&void 0!==window.JSON}catch(e){t=!1}return t},showLocalStorageSize:function(){function e(e){return 2*e.length}function t(e){return e/1024/1024}var n=Object.keys(localStorage).map(function(t){return{name:t,size:e(localStorage[t])}}).map(function(e){return e.size=t(e.size).toFixed(2)+" MB",e});console.table(n)},addDays:function(e,t){return new Date(e.getTime()+24*t*60*60*1e3)},GetDate:function(){var e=new Date,t=e.getDate(),n=t<10?"0":"",o=e.getFullYear(),i=e.getHours(),a=i<10?"0":"",r=e.getMinutes(),s=r<10?"0":"",l=e.getSeconds(),u=l<10?"0":"",d=e.getMonth()+1;return o+"/"+(d<10?"0":"")+d+"/"+n+t+" "+a+i+":"+s+r+":"+u+l},SetSessionTimeout:function(){this.readCookie("lead_session_expire");var e=new Date;e.setTime(e.getTime()+18e5),this.createCookie("lead_session_expire",!0,e)},storeReferralData:function(){var t=new Date,n=document.referrer||"Direct Traffic",o=e.Utils.readCookie("inbound_referral_site"),i=e.totalStorage("inbound_original_referral");t.setTime(t.getTime()+18e5),o||this.createCookie("inbound_referral_site",n,t),i||e.totalStorage("inbound_original_referral",i)},CreateUID:function(e){var t="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".split(""),n="";e||(e=Math.floor(Math.random()*t.length));for(var o=0;o<e;o++)n+=t[Math.floor(Math.random()*t.length)];return n},generateGUID:function(e){return e?(e^16*Math.random()>>e/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,guid)},SetUID:function(e){if(!this.readCookie("wp_lead_uid")){var t=e||this.CreateUID(35);this.createCookie("wp_lead_uid",t)}},countProperties:function(e){var t=0;for(var n in e)e.hasOwnProperty(n)&&++t;return t},mergeObjs:function(e,t){var n={};for(var o in e)n[o]=e[o];for(var o in t)n[o]=t[o];return n},hasClass:function(e,t){if("classList"in document.documentElement)n=t.classList.contains(e);else var n=new RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className);return n},addClass:function(e,t){"classList"in document.documentElement?t.classList.add(e):this.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(e,t){"classList"in document.documentElement?t.classList.remove(e):this.hasClass(t,e)&&(t.className=t.className.replace(new RegExp("(^|\\s)*"+e+"(\\s|$)*","g"),""))},removeElement:function(e){e.parentNode.removeChild(e)},trim:function(e){return e=e.replace(/(^\s*)|(\s*$)/gi,""),e=e.replace(/[ ]{2,}/gi," "),e=e.replace(/\n /,"\n")},ajaxPolyFill:function(){if("undefined"!=typeof XMLHttpRequest)return new XMLHttpRequest;for(var e,t=["MSXML2.XmlHttp.5.0","MSXML2.XmlHttp.4.0","MSXML2.XmlHttp.3.0","MSXML2.XmlHttp.2.0","Microsoft.XmlHttp"],n=0;n<t.length;n++)try{e=new ActiveXObject(t[n]);break}catch(e){}return e},ajaxSendData:function(e,t,n,o,i){var a=this.ajaxPolyFill();setTimeout(function(){a.open(n,e,!0),a.onreadystatechange=function(){4==a.readyState&&t(a.responseText)},"POST"==n&&a.setRequestHeader("Content-type","application/x-www-form-urlencoded"),a.send(o)},100)},ajaxGet:function(e,t,n,o){var i=[];for(var a in t)i.push(encodeURIComponent(a)+"="+encodeURIComponent(t[a]));this.ajaxSendData(e+"?"+i.join("&"),n,"GET",null,o)},ajaxPost:function(e,t,n,o){var i=[];for(var a in t)i.push(encodeURIComponent(a)+"="+encodeURIComponent(t[a]));this.ajaxSendData(e,n,"POST",i.join("&"),o)},sendEvent:function(e,t,i){t=t||{},async=!0;var a=getCookie();if(a){var r;for(r in a)t[r]=a[r]}t.id||(t.id=getId());var s={e:e,t:(new Date).toISOString(),kv:t},l=o.api_host+"/track?data="+encodeURIComponent(JSON.stringify(s));if(n){var u=new XMLHttpRequest;u.open("GET",l,async),u.withCredentials=async,u.send(null)}else{var d=document.createElement("script");d.type="text/javascript",d.async=async,d.defer=async,d.src=l;var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(d,c)}return action(i),self},domReady:function(e,t){var n=!1,o=!0,i=e.document,a=i.documentElement,r=i.addEventListener?"addEventListener":"attachEvent",s=i.addEventListener?"removeEventListener":"detachEvent",l=i.addEventListener?"":"on",u=function(o){"readystatechange"==o.type&&"complete"!=i.readyState||(("load"==o.type?e:i)[s](l+o.type,u,!1),!n&&(n=!0)&&t.call(e,o.type||o))},d=function(){try{a.doScroll("left")}catch(e){return void setTimeout(d,50)}u("poll")};if("complete"==i.readyState)t.call(e,"lazy");else{if(i.createEventObject&&a.doScroll){try{o=!e.frameElement}catch(e){}o&&d()}i[r](l+"DOMContentLoaded",u,!1),i[r](l+"readystatechange",u,!1),e[r](l+"load",u,!1)}},addListener:function(e,t,n){e&&(e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent?e.attachEvent("on"+t,n):e["on"+t]=n)},removeListener:function(e,t,n){e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent?e.detachEvent("on"+t,n):e["on"+t]=null},throttle:function(e,t){var n,o,i,a=null,r=0,s=function(){r=new Date,a=null,i=e.apply(n,o)};return function(){var l=new Date;r||(r=l);var u=t-(l-r);return n=this,o=arguments,u<=0?(clearTimeout(a),a=null,r=l,i=e.apply(n,o)):a||(a=setTimeout(s,u)),i}},checkTypeofGA:function(){"function"==typeof ga&&(universalGA=!0),void 0!==_gaq&&"function"==typeof _gaq.push&&(classicGA=!0),"undefined"!=typeof dataLayer&&"function"==typeof dataLayer.push&&(googleTagManager=!0)},cacheSearchData:function(n,o){if(t){var i=e.totalStorage.getItem("inbound_search_storage");if(i)i.unshift(n),e.totalStorage.setItem("inbound_search_storage",i);else{var a=[n];e.totalStorage.setItem("inbound_search_storage",a)}}else{var r=JSON.stringify(n),s=this.readCookie("inbound_search_storage");s&&(r+="SPLIT-TOKEN"+s),this.createCookie("inbound_search_storage",r,"180")}e.Forms.releaseFormSubmit(o)},storeSearchData:function(){if(inbound_settings.wp_lead_data.lead_id&&inbound_settings.wp_lead_data.lead_nonce){var t=[],n=e.totalStorage.getItem("inbound_search_storage"),o=this.readCookie("inbound_search_storage");if(n||o){if(o){o=o.split("SPLIT-TOKEN");for(var i in o)t.push(JSON.parse(o[i]))}n&&(t=t.concat(n)),t.sort(function(e,t){return e.timestamp-t.timestamp});var a={action:"inbound_search_store",data:t=encodeURIComponent(JSON.stringify(t)),nonce:inbound_settings.wp_lead_data.lead_nonce,lead_id:inbound_settings.wp_lead_data.lead_id};callback=function(t){t&&(t=JSON.parse(t)),t.success&&(console.log(t.success),e.Utils.eraseCookie("inbound_search_storage"),e.totalStorage.deleteItem("inbound_search_storage")),t.error&&console.log(t.error)},this.ajaxPost(inbound_settings.admin_url,a,callback)}}}},e}(_inbound||{}),InboundForms=function(e){var t=e.Utils,n=[],o=[],a=[],r={},s=e.Settings,l=["first name","last name","name","email","e-mail","phone","website","job title","your favorite food","company","tele","address","comment"];if(e.Forms={init:function(){e.Forms.runFieldMappingFilters(),e.Forms.formTrackInit(),e.Forms.searchTrackInit()},runFieldMappingFilters:function(){l=e.hooks.applyFilters("forms.field_map",l)},debug:function(e,t){return},formTrackInit:function(){for(var e=0;e<window.document.forms.length;e++){var t=window.document.forms[e];t.dataset.formProcessed||(t.dataset.formProcessed=!0,this.checkTrackStatus(t)&&(this.attachFormSubmitEvent(t),this.initFormMapping(t)))}},searchTrackInit:function(){if("off"!=inbound_settings.search_tracking&&!r.searchTrackInit){for(var e=0;e<window.document.forms.length;e++){var n=window.document.forms[e];n.dataset.searchChecked||(n.dataset.searchChecked=!0,this.checkSearchTrackStatus(n)&&this.attachSearchFormSubmitEvent(n))}t.storeSearchData(),r.searchTrackInit=!0}},checkTrackStatus:function(t){var n=t.getAttribute("class");if(""!==n&&null!==n)return n.toLowerCase().indexOf("wpl-track-me")>-1||(n.toLowerCase().indexOf("inbound-track")>-1||(cb=function(){console.log(t)},e.deBugger("forms","This form not tracked. Please assign on in settings...",cb),!1))},checkSearchTrackStatus:function(t){var n=t.getAttribute("class"),o=t.getAttribute("id");return""!==n&&null!==n&&n.toLowerCase().indexOf("search")>-1||(""===o||null===o?(cb=function(){console.log(t)},e.deBugger("searches","This search form is not tracked. Please assign on in settings...",cb),!1):o.toLowerCase().indexOf("search")>-1||void 0)},loopClassSelectors:function(n,o){for(var i=n.length-1;i>=0;i--){var a=t.trim(n[i]);-1===a.indexOf("#")&&-1===a.indexOf(".")&&(a="#"+a),(a=document.querySelector(a))&&("add"===o?(e.Utils.addClass("wpl-track-me",a),e.Utils.addClass("inbound-track",a)):(e.Utils.removeClass("wpl-track-me",a),e.Utils.removeClass("inbound-track",a)))}},initFormMapping:function(t){for(var n=[],o=0;o<t.elements.length;o++)formInput=t.elements[o],"hidden"!==formInput.type?(this.mapField(formInput),this.rememberInputValues(formInput),s.formAutoPopulation&&!e.Utils.hasClass("nopopulate",t)&&this.fillInputValues(formInput)):n.push(formInput);for(var i=n.length-1;i>=0;i--)formInput=n[i],this.mapField(formInput)},mapField:function(o){var a=o.id||!1,r=o.name||!1,s=this.getInputLabel(o);if(s&&this.ignoreFieldByLabel(s[0].innerText))return o.dataset.ignoreFormField=!0,!1;for(i=0;i<l.length;i++){var u=!1,d=l[i],c=t.trim(d),m=c.replace(/ /g,"_");r&&r.toLowerCase().indexOf(c)>-1?(u=!0,e.deBugger("forms","Found matching name attribute for -> "+c)):a&&a.toLowerCase().indexOf(c)>-1?(u=!0,e.deBugger("forms","Found matching ID attribute for ->"+c)):s?s[0].innerText.toLowerCase().indexOf(c)>-1&&(u=!0,e.deBugger("forms","Found matching sibling label for -> "+c)):n.push(c),u&&(this.addDataAttr(o,m),this.removeArrayItem(l,c),i--)}return inbound_data},formListener:function(t){t.preventDefault(),e.Forms.saveFormData(t.target),document.body.style.cursor="wait"},searchFormListener:function(t){t.preventDefault(),e.Forms.saveSearchData(t.target)},attachFormSubmitEvent:function(e){t.addListener(e,"submit",this.formListener);document.querySelector(".inbound-email")},attachSearchFormSubmitEvent:function(e){t.addListener(e,"submit",this.searchFormListener)},ignoreFieldByLabel:function(t){var n=!1;return!!t&&(-1==t.toLowerCase().indexOf("credit card")&&-1==t.toLowerCase().indexOf("card number")||(n=!0),-1==t.toLowerCase().indexOf("expiration")&&-1==t.toLowerCase().indexOf("expiry")||(n=!0),"month"!=t.toLowerCase()&&"mm"!=t.toLowerCase()&&"yy"!=t.toLowerCase()&&"yyyy"!=t.toLowerCase()&&"year"!=t.toLowerCase()||(n=!0),-1==t.toLowerCase().indexOf("cvv")&&-1==t.toLowerCase().indexOf("cvc")&&-1==t.toLowerCase().indexOf("secure code")&&-1==t.toLowerCase().indexOf("security code")||(n=!0),n&&e.deBugger("forms","ignore "+t),n)},ignoreFieldByValue:function(e){var t=!1;if(!e)return!1;if("visa"!=e.toLowerCase()&&"mastercard"!=e.toLowerCase()&&"american express"!=e.toLowerCase()&&"amex"!=e.toLowerCase()&&"discover"!=e.toLowerCase()||(t=!0),new RegExp("/^[0-9]+$/").test(e)){var n=e.replace(" ","");this.isInt(n)&&n.length>=16&&(t=!0)}return t},isInt:function(e){return"number"==typeof e&&isFinite(e)&&e%1==0},releaseFormSubmit:function(e){document.body.style.cursor="default",t.removeClass("wpl-track-me",e),t.removeListener(e,"submit",this.formListener);var n=e.getAttribute("class");if(""!==n&&null!==n&&-1!=n.toLowerCase().indexOf("wpcf7-form"))return setTimeout(function(){document.body.style.cursor="default"},300),!0;e.submit(),setTimeout(function(){for(var t=0;t<e.elements.length;t++)formInput=e.elements[t],type=formInput.type||!1,"submit"===type&&"submit"===formInput.name&&e.elements[t].click()},2e3)},saveFormData:function(n){for(var i=i||{},r=0;r<n.elements.length;r++)if(formInput=n.elements[r],multiple=!1,formInput.name){if(formInput.dataset.ignoreFormField){e.deBugger("forms","ignore "+formInput.name);continue}switch(inputName=formInput.name.replace(/\[([^\[]*)\]/g,"%5B%5D$1"),i[inputName]||(i[inputName]={}),formInput.type&&(i[inputName].type=formInput.type),i[inputName].name||(i[inputName].name=formInput.name),formInput.dataset.mapFormField&&(i[inputName].map=formInput.dataset.mapFormField),formInput.nodeName){case"INPUT":if(!1===(l=this.getInputValue(formInput)))continue;break;case"TEXTAREA":l=formInput.value;break;case"SELECT":if(formInput.multiple){values=[],multiple=!0;for(var s=0;s<formInput.length;s++)formInput[s].selected&&values.push(encodeURIComponent(formInput[s].value))}else l=formInput.value}if(e.deBugger("forms","Input Value = "+l),l){i[inputName].value||(i[inputName].value=[]),i[inputName].value.push(multiple?values.join(","):encodeURIComponent(l));var l=multiple?values.join(","):encodeURIComponent(l)}}e.deBugger("forms",i);for(var u in i){var d=i[u].value,c=i[u].map;if(void 0!==d&&null!=d&&""!=d&&o.push(u+"="+i[u].value.join(",")),void 0!==c&&null!=c&&i[u].value&&(a.push(c+"="+i[u].value.join(",")),"email"===u))var m=i[u].value.join(",")}var f=o.join("&");e.deBugger("forms","Stringified Raw Form PARAMS: "+f);var g=a.join("&");e.deBugger("forms","Stringified Mapped PARAMS"+g),(m=t.getParameterVal("email",g)||t.readCookie("wp_lead_email"))||(m=t.getParameterVal("wpleads_email_address",g));var p=t.getParameterVal("name",g),h=t.getParameterVal("first_name",g),v=t.getParameterVal("last_name",g);if(!v&&h&&(_=decodeURI(h).split(" ")).length>0&&(h=_[0],v=_[1]),p&&!v&&!h){var _=decodeURI(p).split(" ");_.length>0&&(h=_[0],v=_[1])}p=h&&v?h+" "+v:p,h||(h=""),v||(v=""),e.deBugger("forms","fName = "+h),e.deBugger("forms","lName = "+v),e.deBugger("forms","fullName = "+p);var b=e.totalStorage("page_views")||{},y=e.totalStorage("inbound_url_params")||{},w=n.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),k=!1;if(0==w.length||"IA=="==w[0].value)k=!0;var S=n.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');S=S.length>0?S[0].value:0;if("undefined"!=typeof landing_path_info)C=landing_path_info.variation;else if("undefined"!=typeof cta_path_info)C=cta_path_info.variation;else var C=inbound_settings.variation_id;var I=inbound_settings.post_type||"page",L=inbound_settings.post_id||0;search_data={},formData={action:"inbound_lead_store",email:m,full_name:p,first_name:h,last_name:v,raw_params:f,mapped_params:g,url_params:JSON.stringify(y),search_data:"test",page_views:JSON.stringify(b),post_type:I,page_id:L,variation:C,source:t.readCookie("inbound_referral_site"),inbound_submitted:k,inbound_form_id:S,inbound_nonce:inbound_settings.ajax_nonce,event:n},callback=function(o){e.deBugger("forms","Lead Created with ID: "+o),o=parseInt(o,10),formData.leadID=o,o&&(t.createCookie("wp_lead_id",o),e.totalStorage.deleteItem("page_views"),e.totalStorage.deleteItem("tracking_events")),e.trigger("form_after_submission",formData),e.Forms.releaseFormSubmit(n)},e.trigger("form_before_submission",formData),t.ajaxPost(inbound_settings.admin_url,formData,callback)},saveSearchData:function(n){for(var o=o||{},i=0;i<n.elements.length;i++)if(formInput=n.elements[i],multiple=!1,formInput.name){if(formInput.dataset.ignoreFormField){e.deBugger("searches","ignore "+formInput.name);continue}switch(inputName=formInput.name.replace(/\[([^\[]*)\]/g,"%5B%5D$1"),o[inputName]||(o[inputName]={}),formInput.type&&(o[inputName].type=formInput.type),o[inputName].name||(o[inputName].name=formInput.name),formInput.dataset.mapFormField&&(o[inputName].map=formInput.dataset.mapFormField),formInput.nodeName){case"INPUT":if(!1===(r=this.getInputValue(formInput)))continue;break;case"TEXTAREA":r=formInput.value;break;case"SELECT":if(formInput.multiple){values=[],multiple=!0;for(var a=0;a<formInput.length;a++)formInput[a].selected&&values.push(encodeURIComponent(formInput[a].value))}else r=formInput.value}if(e.deBugger("searches","Input Value = "+r),r){o[inputName].value||(o[inputName].value=[]),o[inputName].value.push(multiple?values.join(","):encodeURIComponent(r));var r=multiple?values.join(","):encodeURIComponent(r)}}e.deBugger("searches",o);var s=[];for(var l in o){var u=o[l].value,d=o[l].type;void 0!==u&&null!=u&&""!=u&&"search"==d&&s.push("search_text|value|"+o[l].value)}if(s[0]){var c=s.join("|field|");if(e.deBugger("searches","Stringified Search Form PARAMS: "+c),"undefined"!=typeof landing_path_info)m=landing_path_info.variation;else if("undefined"!=typeof cta_path_info)m=cta_path_info.variation;else var m=inbound_settings.variation_id;var f=inbound_settings.post_type||"page",g=inbound_settings.post_id||0,p=t.readCookie("wp_lead_uid");inbound_settings.wp_lead_data.lead_email?email=inbound_settings.wp_lead_data.lead_email:t.readCookie("inbound_wpleads_email_address")?email=t.readCookie("inbound_wpleads_email_address"):email="",searchData={email:email,search_data:c,user_UID:p,post_type:f,page_id:g,variation:m,source:t.readCookie("inbound_referral_site"),ip_address:inbound_settings.ip_address,timestamp:Math.floor((new Date).getTime()/1e3)},e.trigger("search_before_caching",searchData),inbound_settings.wp_lead_data.lead_id?(searchData.lead_id=inbound_settings.wp_lead_data.lead_id,t.cacheSearchData(searchData,n)):t.cacheSearchData(searchData,n)}},rememberInputValues:function(n){n.name&&n.name;var o=n.type?n.type:"text";if("submit"===o||"hidden"===o||"file"===o||"password"===o||n.dataset.ignoreFormField)return!1;t.addListener(n,"change",function(n){if(n.target.name){if("checkbox"!==o)var i=n.target.value;else for(var a=[],r=document.querySelectorAll('input[name="'+n.target.name+'"]'),s=0;s<r.length;s++)r[s].checked&&a.push(r[s].value),i=a.join(",");inputData={name:n.target.name,node:n.target.nodeName.toLowerCase(),type:o,value:i,mapping:n.target.dataset.mapFormField},e.trigger("form_input_change",inputData),t.createCookie("inbound_"+n.target.name,encodeURIComponent(i))}})},fillInputValues:function(e){var n=e.name?"inbound_"+e.name:"",o=e.type?e.type:"text";if("submit"===o||"hidden"===o||"file"===o||"password"===o)return!1;if(t.readCookie(n)&&"comment"!=n)if(value=decodeURIComponent(t.readCookie(n)),"checkbox"===o||"radio"===o)for(var i=value.split(","),a=0;a<i.length;a++)e.value.indexOf(i[a])>-1&&(e.checked=!0);else"undefined"!==value&&(e.value=value)},getInputLabel:function(e){var t;return(t=this.siblingsIsLabel(e))?t:!!(t=this.CheckParentForLabel(e))&&t},getInputValue:function(e){var t=!1;switch(e.type){case"radio":case"checkbox":e.checked&&(t=e.value);break;case"text":case"hidden":default:t=e.value}return t},addDataAttr:function(e,t){for(var n=document.getElementsByName(e.name),o=n.length-1;o>=0;o--)e.dataset.mapFormField||(n[o].dataset.mapFormField=t)},removeArrayItem:function(e,t){if(e.indexOf)index=e.indexOf(t);else for(index=e.length-1;index>=0&&e[index]!==t;--index);index>=0&&e.splice(index,1)},siblingsIsLabel:function(e){for(var t=this.getSiblings(e),n=[],o=t.length-1;o>=0;o--)"label"===t[o].nodeName.toLowerCase()&&n.push(t[o]);return n.length>0&&n.length<2&&n},getChildren:function(e,t){for(var n=[];e;e=e.nextSibling)1==e.nodeType&&e!=t&&n.push(e);return n},getSiblings:function(e){return this.getChildren(e.parentNode.firstChild,e)},CheckParentForLabel:function(e){if("FORM"===e.nodeName)return null;do{var t=e.getElementsByTagName("label");if(t.length>0&&t.length<2)return e.getElementsByTagName("label")}while(e=e.parentNode);return null},mailCheck:function(){var e=document.querySelector(".inbound-email");e&&(t.addListener(e,"blur",this.mailCheck),u.run({email:document.querySelector(".inbound-email").value,suggested:function(n){var o=document.querySelector(".email_suggestion");o&&t.removeElement(o);var i=document.createElement("span");i.innerHTML="<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">"+n.full+"</b></i>?</span>",e.parentNode.insertBefore(i,e.nextSibling);var a=document.getElementById("email_correction");t.addListener(a,"click",function(){e.value=a.innerHTML,a.parentNode.parentNode.innerHTML="Fixed!"})},empty:function(){}}))}},void 0===u)var u={domainThreshold:1,topLevelThreshold:3,defaultDomains:["yahoo.com","google.com","hotmail.com","gmail.com","me.com","aol.com","mac.com","live.com","comcast.net","googlemail.com","msn.com","hotmail.co.uk","yahoo.co.uk","facebook.com","verizon.net","sbcglobal.net","att.net","gmx.com","mail.com","outlook.com","icloud.com"],defaultTopLevelDomains:["co.jp","co.uk","com","net","org","info","edu","gov","mil","ca","de"],run:function(e){e.domains=e.domains||u.defaultDomains,e.topLevelDomains=e.topLevelDomains||u.defaultTopLevelDomains,e.distanceFunction=e.distanceFunction||u.sift3Distance;var t=function(e){return e},n=e.suggested||t,o=e.empty||t,i=u.suggest(u.encodeEmail(e.email),e.domains,e.topLevelDomains,e.distanceFunction);return i?n(i):o()},suggest:function(e,t,n,o){e=e.toLowerCase();var i=this.splitEmail(e),a=this.findClosestDomain(i.domain,t,o,this.domainThreshold);if(a){if(a!=i.domain)return{address:i.address,domain:a,full:i.address+"@"+a}}else{var r=this.findClosestDomain(i.topLevelDomain,n,o,this.topLevelThreshold);if(i.domain&&r&&r!=i.topLevelDomain){var s=i.domain;return a=s.substring(0,s.lastIndexOf(i.topLevelDomain))+r,{address:i.address,domain:a,full:i.address+"@"+a}}}return!1},findClosestDomain:function(e,t,n,o){o=o||this.topLevelThreshold;var i,a=99,r=null;if(!e||!t)return!1;n||(n=this.sift3Distance);for(var s=0;s<t.length;s++){if(e===t[s])return e;(i=n(e,t[s]))<a&&(a=i,r=t[s])}return a<=o&&null!==r&&r},sift3Distance:function(e,t){if(null===e||0===e.length)return null===t||0===t.length?0:t.length;if(null===t||0===t.length)return e.length;for(var n=0,o=0,i=0,a=0;n+o<e.length&&n+i<t.length;){if(e.charAt(n+o)==t.charAt(n+i))a++;else{o=0,i=0;for(var r=0;r<5;r++){if(n+r<e.length&&e.charAt(n+r)==t.charAt(n)){o=r;break}if(n+r<t.length&&e.charAt(n)==t.charAt(n+r)){i=r;break}}}n++}return(e.length+t.length)/2-a},splitEmail:function(e){var t=e.trim().split("@");if(t.length<2)return!1;for(a=0;a<t.length;a++)if(""===t[a])return!1;var n=t.pop(),o=n.split("."),i="";if(0===o.length)return!1;if(1==o.length)i=o[0];else{for(var a=1;a<o.length;a++)i+=o[a]+".";o.length>=2&&(i=i.substring(0,i.length-1))}return{topLevelDomain:i,domain:n,address:t.join("@")}},encodeEmail:function(e){var t=encodeURI(e);return t=t.replace("%20"," ").replace("%25","%").replace("%5E","^").replace("%60","`").replace("%7B","{").replace("%7C","|").replace("%7D","}")}};return e}(_inbound||{}),_inboundEvents=function(e){function t(t,o,i){var o=o||{};(i=i||{}).bubbles=i.bubbles||!0,i.cancelable=i.cancelable||!0,o=e.apply_filters("filter_"+t,o);!window.ActiveXObject&&window;if("function"==typeof CustomEvent)var a=new CustomEvent(t,{detail:o,bubbles:i.bubbles,cancelable:i.cancelable});else(a=document.createEvent("Event")).initEvent(t,!0,!0);window.dispatchEvent(a),e.do_action(t,o),n(t,o)}function n(e,t){if(window.jQuery){var t=t||{};jQuery(document).trigger(e,t)}}e.trigger=function(t,n){e.Events[t](n)};return e.Events={analytics_ready:function(){t("analytics_ready",{data:"xyxy"},{opt1:!0})},url_parameters:function(e){t("url_parameters",e)},session_start:function(){console.log(""),t("session_start")},session_end:function(e){t("session_end",e),console.log("Session End")},session_active:function(){t("session_active")},session_idle:function(e){t("session_idle",e)},session_resume:function(){t("session_resume")},session_heartbeat:function(e){t("session_heartbeat",{clock:e,leadData:InboundLeadData})},page_visit:function(e){t("page_view",e)},page_first_visit:function(n){t("page_first_visit"),e.deBugger("pages","First Ever Page View of this Page")},page_revisit:function(n){t("page_revisit",n);e.deBugger("pages",status,function(){console.log("pageData",n),console.log("Page Revisit viewed "+n+" times")})},tab_hidden:function(n){e.deBugger("pages","Tab Hidden"),t("tab_hidden")},tab_visible:function(n){e.deBugger("pages","Tab Visible"),t("tab_visible")},tab_mouseout:function(n){e.deBugger("pages","Tab Mouseout"),t("tab_mouseout")},form_input_change:function(n){e.deBugger("forms","inputData change. Data=",function(){console.log(n)}),t("form_input_change",n)},form_before_submission:function(e){t("form_before_submission",e)},form_after_submission:function(e){t("form_after_submission",e)},search_before_caching:function(e){t("search_before_caching",e)},analyticsError:function(e,t,n){var o=new CustomEvent("inbound_analytics_error",{detail:{MLHttpRequest:e,textStatus:t,errorThrown:n}});window.dispatchEvent(o),console.log("Page Save Error")}},e}(_inbound||{});_inbound.add_action("form_before_submission",inboundFormNoRedirect,10),_inbound.add_action("form_after_submission",inboundFormNoRedirectContent,10);var InboundTotalStorage=function(e){var t,n;if("localStorage"in window)try{n=void 0===window.localStorage?void 0:window.localStorage,t=void 0!==n&&void 0!==window.JSON,window.localStorage.setItem("_inbound","1"),window.localStorage.removeItem("_inbound")}catch(e){t=!1}e.totalStorage=function(t,n,o){return e.totalStorage.impl.init(t,n)},e.totalStorage.setItem=function(t,n){return e.totalStorage.impl.setItem(t,n)},e.totalStorage.getItem=function(t){return e.totalStorage.impl.getItem(t)},e.totalStorage.getAll=function(){return e.totalStorage.impl.getAll()},e.totalStorage.deleteItem=function(t){return e.totalStorage.impl.deleteItem(t)},e.totalStorage.impl={init:function(e,t){return void 0!==t?this.setItem(e,t):this.getItem(e)},setItem:function(o,i){if(!t)try{return e.Utils.createCookie(o,i),i}catch(e){console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie")}var a=JSON.stringify(i);return n.setItem(o,a),this.parseResult(a)},getItem:function(o){if(!t)try{return this.parseResult(e.Utils.readCookie(o))}catch(e){return null}var i=n.getItem(o);return this.parseResult(i)},deleteItem:function(o){if(!t)try{return e.Utils.eraseCookie(o,null),!0}catch(e){return!1}return n.removeItem(o),!0},getAll:function(){var o=[];if(t)for(var i in n)i.length&&o.push({key:i,value:this.parseResult(n.getItem(i))});else try{for(var a=document.cookie.split(";"),r=0;r<a.length;r++){var s=a[r].split("=")[0];o.push({key:s,value:this.parseResult(e.Utils.readCookie(s))})}}catch(e){return null}return o},parseResult:function(e){var t;try{void 0===(t=JSON.parse(e))&&(t=e),"true"==t&&(t=!0),"false"==t&&(t=!1),parseFloat(t)==t&&"object"!=typeof t&&(t=parseFloat(t))}catch(n){t=e}return t}}}(_inbound||{}),_inboundLeadsAPI=function(e){return e.LeadsAPI={init:function(){var t=e.Utils,n=(t.readCookie("wp_lead_uid"),t.readCookie("wp_lead_id"));t.readCookie("lead_session_expire")||(e.deBugger("leads","expired vistor. Run Processes"),n&&e.LeadsAPI.getAllLeadData())},setGlobalLeadData:function(e){InboundLeadData=e},getAllLeadData:function(t){var n=e.Utils.readCookie("wp_lead_id"),o=e.totalStorage("inbound_lead_data"),i=e.Utils.readCookie("lead_data_expire");data={action:"inbound_get_all_lead_data",wp_lead_id:n},success=function(t){var n=JSON.parse(t);e.LeadsAPI.setGlobalLeadData(n),e.totalStorage("inbound_lead_data",n);var o=new Date;o.setTime(o.getTime()+18e5);var i=e.Utils.addDays(o,3);e.Utils.createCookie("lead_data_expire",!0,i)},o?(e.LeadsAPI.setGlobalLeadData(o),e.deBugger("lead","Set Global Lead Data from Localstorage"),i||(e.Utils.ajaxPost(inbound_settings.admin_url,data,success),e.deBugger("lead","localized data old. Pull new from DB"))):e.Utils.ajaxPost(inbound_settings.admin_url,data,success)},getLeadLists:function(){var t={action:"wpl_check_lists",wp_lead_id:e.Utils.readCookie("wp_lead_id")};e.Utils.ajaxPost(inbound_settings.admin_url,t,function(t){e.Utils.createCookie("lead_session_list_check",!0,{path:"/",expires:1}),e.deBugger("lead","Lists checked")})}},e}(_inbound||{}),_inboundPageTracking=function(e){var t,n,o=!1,i=!1,a=!1,r=parseInt(e.Utils.readCookie("lead_session"),10)||0,s=0,l=(new Date,null),u=null,d=null,c=e.Utils,m=e.Utils.GetDate(),f="page_views",g=e.totalStorage(f)||{},p=inbound_settings.post_id||window.location.pathname,h=e.Settings.timeout||1e4;return e.PageTracking={init:function(o){this.CheckTimeOut(),o=o||{},t=parseInt(o.reportInterval,10)||10,n=parseInt(o.idleTimeout,10)||3,c.addListener(document,"keydown",c.throttle(e.PageTracking.pingSession,1e3)),c.addListener(document,"click",c.throttle(e.PageTracking.pingSession,1e3)),c.addListener(window,"mousemove",c.throttle(e.PageTracking.pingSession,1e3)),e.PageTracking.checkVisibility(),this.startSession()},setIdle:function(t){var n="Session IDLE. Activity Timeout due to "+(t=t||"No Movement");e.deBugger("pages",n),clearTimeout(e.PageTracking.idleTimer),e.PageTracking.stopClock(),e.trigger("session_idle")},checkVisibility:function(){var t,n;void 0!==document.hidden?(t="hidden",n="visibilitychange"):void 0!==document.mozHidden?(t="mozHidden",n="mozvisibilitychange"):void 0!==document.msHidden?(t="msHidden",n="msvisibilitychange"):void 0!==document.webkitHidden&&(t="webkitHidden",n="webkitvisibilitychange");var o=document[t];e.Utils.addListener(document,n,function(n){o!=document[t]&&(document[t]?(e.trigger("tab_hidden"),e.PageTracking.setIdle("browser tab switch")):(e.trigger("tab_visible"),e.PageTracking.pingSession()),o=document[t])})},clock:function(){var n="Total time spent on Page in this Session: "+((r+=1)/60).toFixed(2)+" min";if(e.deBugger("pages",n),r>0&&r%t==0){var o=new Date;o.setTime(o.getTime()+18e5),c.createCookie("lead_session",r,o),e.trigger("session_heartbeat",r)}},inactiveClock:function(){var t="Time until Session Timeout: "+((1800-(s+=1))/60).toFixed(2)+" min";e.deBugger("pages",t),s>1800&&(e.trigger("session_end",InboundLeadData),e.Utils.eraseCookie("lead_session"),s=0,clearTimeout(u))},stopClock:function(){i=!0,clearTimeout(l),clearTimeout(u),u=setInterval(e.PageTracking.inactiveClock,1e3)},restartClock:function(){i=!1,e.trigger("session_resume"),e.deBugger("pages","Activity resumed. Session Active"),clearTimeout(l),s=0,clearTimeout(u),l=setInterval(e.PageTracking.clock,1e3)},turnOff:function(){e.PageTracking.setIdle(),a=!0},turnOn:function(){a=!1},startSession:function(){new Date;if(o=!0,l=setInterval(e.PageTracking.clock,1e3),c.readCookie("lead_session"))e.trigger("session_active");else{e.trigger("session_start");var t=new Date;t.setTime(t.getTime()+18e5),e.Utils.createCookie("lead_session",1,t)}this.pingSession()},resetInactiveFunc:function(){s=0,clearTimeout(u)},pingSession:function(t){a||(o||e.PageTracking.startSession(),i&&e.PageTracking.restartClock(),clearTimeout(d),d=setTimeout(e.PageTracking.setIdle,1e3*n+100),void 0!==t&&"mousemove"===t.type&&e.PageTracking.mouseEvents(t))},mouseEvents:function(t){t.pageY<=5&&e.trigger("tab_mouseout")},getPageViews:function(){if(e.Utils.checkLocalStorage()){var t=localStorage.getItem(f),n=JSON.parse(t);return n}},isRevisit:function(e){var t=!1,n=(e=e||{})[p];return void 0!==n&&null!==n&&(t=!0),t},triggerPageView:function(t){var n={title:document.title,url:document.location.href,path:document.location.pathname,count:1};t?(g[p].push(m),n.count=g[p].length,e.trigger("page_revisit",n)):(g[p]=[],g[p].push(m),e.trigger("page_first_visit",n)),e.trigger("page_visit",n),e.totalStorage(f,g),this.storePageView()},CheckTimeOut:function(){var t,n=this.isRevisit(g);if(n){var o=g[p].length-1,i=g[p][o],a=Math.abs(new Date(i).getTime()-new Date(m).getTime());a>h?(t="Timeout Happened. Page view fired",this.triggerPageView(n)):(time_left=.001*Math.abs(h-a),t=h/1e3+" sec timeout not done: "+time_left+" seconds left")}else this.triggerPageView(n);e.deBugger("pages",t)},storePageView:function(){"off"==inbound_settings.page_tracking&&"landing-page"!=inbound_settings.post_type||document.addEventListener("DOMContentLoaded",function(){setTimeout(function(){var t=e.Utils.readCookie("wp_lead_id")?e.Utils.readCookie("wp_lead_id"):"",n=e.Utils.readCookie("wp_lead_uid")?e.Utils.readCookie("wp_lead_uid"):"",o=e.totalStorage("wp_cta_loaded"),i=e.totalStorage("wp_cta_impressions");e.totalStorage("wp_cta_impressions",{});var a={action:"inbound_track_lead",wp_lead_uid:n,wp_lead_id:t,page_id:inbound_settings.post_id,variation_id:inbound_settings.variation_id,post_type:inbound_settings.post_type,current_url:window.location.href,page_views:JSON.stringify(e.PageTracking.getPageViews()),cta_impressions:JSON.stringify(i),cta_history:JSON.stringify(o),json:"0"};e.Utils.ajaxPost(inbound_settings.admin_url,a,function(e){})},200)})}},e}(_inbound||{});_inbound.init(),InboundLeadData=_inbound.totalStorage("inbound_lead_data")||null;
1
+ /*! Inbound Analyticsv1.0.0 | (c) 2017 Inbound Now | https://github.com/inboundnow/cta */
2
+ function inboundFormNoRedirect(){if(null==window.frames.frameElement)e=document.querySelectorAll("button.inbound-button-submit[disabled]")[0];else if("iframe"==window.frames.frameElement.tagName.toLowerCase())var e=window.frames.frameElement.contentWindow.document.querySelectorAll("button.inbound-button-submit")[0];if(void 0!==e){var t=e.form,n=t.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])');0!=n.length&&"IA=="!=n[0].value||(t.action="javascript:void(0)")}}function inboundFormNoRedirectContent(){if(null==window.frames.frameElement)e=document.querySelectorAll("button.inbound-button-submit[disabled]")[0];else if("iframe"==window.frames.frameElement.tagName.toLowerCase())var e=window.frames.frameElement.contentWindow.document.querySelectorAll("button.inbound-button-submit")[0];if(void 0!==e){var t=e.form.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),n=jQuery(e).css("background"),o=jQuery(e).css("color"),i=jQuery(e).css("height"),a=e.getElementsByClassName("inbound-form-spinner");0!=t.length&&"IA=="!=t[0].value||(jQuery(a).remove(),jQuery(e).prepend('<div id="redir-check"><i class="fa fa-check-square" aria-hidden="true" style="background='+n+"; color="+o+"; font-size:calc("+i+' * .42);"></i></div>'))}}var inbound_data=inbound_data||{},_inboundOptions=_inboundOptions||{},_gaq=_gaq||[],_inbound=function(e){var t={timeout:inbound_settings.is_admin?500:1e4,formAutoTracking:!0,formAutoPopulation:!0},n={init:function(){_inbound.Utils.init(),_inbound.Utils.domReady(window,function(){_inbound.DomLoaded()})},DomLoaded:function(){_inbound.PageTracking.init(),_inbound.Forms.init(),_inbound.Utils.setUrlParams(),_inbound.LeadsAPI.init(),setTimeout(function(){_inbound.Forms.init()},2e3),_inbound.trigger("analytics_ready")},extend:function(e,t){var n,o={};for(n in e)Object.prototype.hasOwnProperty.call(e,n)&&(o[n]=e[n]);for(n in t)Object.prototype.hasOwnProperty.call(t,n)&&(o[n]=t[n]);return o},debug:function(e,t){},deBugger:function(e,t,n){if(console){var o,i,a,r=document.location.hash?document.location.hash:"",s=r.indexOf("#debug")>-1,t=t||!1;r&&r.match(/debug/)&&(a=(r=r.split("-"))[1]),i="true"===_inbound.Utils.readCookie("inbound_debug"),((o="true"===_inbound.Utils.readCookie("inbound_debug_"+e))||s||i)&&(t&&"string"==typeof t&&(i||"all"===a?console.log('logAll "'+e+'" =>',t):o?console.log('log "'+e+'" =>',t):e===a&&console.log('#log "'+e+'" =>',t)),n&&n instanceof Function&&n())}}},o=n.extend(t,e);return n.Settings=o||{},n}(_inboundOptions),_inboundHooks=function(e){return e.hooks=new function(){function e(e,t,n,o){if(a[e][t])if(n){var i,r=a[e][t];if(o)for(i=r.length;i--;){var s=r[i];s.callback===n&&s.context===o&&r.splice(i,1)}else for(i=r.length;i--;)r[i].callback===n&&r.splice(i,1)}else a[e][t]=[]}function t(e,t,o,i,r){var s={callback:o,priority:i,context:r},l=a[e][t];l?(l.push(s),l=n(l)):l=[s],a[e][t]=l}function n(e){for(var t,n,o,i=1,a=e.length;i<a;i++){for(t=e[i],n=i;(o=e[n-1])&&o.priority>t.priority;)e[n]=e[n-1],--n;e[n]=t}return e}function o(e,t,n){var o=a[e][t];if(!o)return"filters"===e&&n[0];var i=0,r=o.length;if("filters"===e)for(;i<r;i++)n[0]=o[i].callback.apply(o[i].context,n);else for(;i<r;i++)o[i].callback.apply(o[i].context,n);return"filters"!==e||n[0]}var i={removeFilter:function(t,n){return"string"==typeof t&&e("filters",t,n),i},applyFilters:function(){var e=Array.prototype.slice.call(arguments),t=e.shift();return"string"==typeof t?o("filters",t,e):i},addFilter:function(e,n,o,a){return"string"==typeof e&&"function"==typeof n&&t("filters",e,n,o=parseInt(o||10,10)),i},removeAction:function(t,n){return"string"==typeof t&&e("actions",t,n),i},doAction:function(){var e=Array.prototype.slice.call(arguments),t=e.shift();return"string"==typeof t&&o("actions",t,e),i},addAction:function(e,n,o,a){return"string"==typeof e&&"function"==typeof n&&t("actions",e,n,o=parseInt(o||10,10),a),i}},a={actions:{},filters:{}};return i},e.add_action=function(){var t=arguments[0].split(" ");for(k in t)arguments[0]="inbound."+t[k],e.hooks.addAction.apply(this,arguments);return this},e.remove_action=function(){return arguments[0]="inbound."+arguments[0],e.hooks.removeAction.apply(this,arguments),this},e.do_action=function(){return arguments[0]="inbound."+arguments[0],e.hooks.doAction.apply(this,arguments),this},e.add_filter=function(){return arguments[0]="inbound."+arguments[0],e.hooks.addFilter.apply(this,arguments),this},e.remove_filter=function(){return arguments[0]="inbound."+arguments[0],e.hooks.removeFilter.apply(this,arguments),this},e.apply_filters=function(){return arguments[0]="inbound."+arguments[0],e.hooks.applyFilters.apply(this,arguments)},e}(_inbound||{}),_inboundUtils=function(e){var t,n=window.XMLHttpRequest&&"withCredentials"in new XMLHttpRequest,o=(Object.prototype.toString,{api_host:("https:"==location.protocol?"https://":"http://")+location.hostname+location.pathname.replace(/\/$/,""),track_pageview:!0,track_links_timeout:300,cookie_name:"_sp",cookie_expiration:365,cookie_domain:(host=location.hostname.match(/[a-z0-9][a-z0-9\-]+\.[a-z\.]{2,6}$/i))?host[0]:""});return e.Utils={init:function(){this.polyFills(),this.checkLocalStorage(),this.SetUID(),this.storeReferralData()},polyFills:function(){window.console||(window.console={});for(var e=["log","info","warn","error","debug","trace","dir","group","groupCollapsed","groupEnd","time","timeEnd","profile","profileEnd","dirxml","assert","count","markTimeline","timeStamp","clear"],t=0;t<e.length;t++)window.console[e[t]]||(window.console[e[t]]=function(){});Date.prototype.toISOString||function(){function e(e){var t=String(e);return 1===t.length&&(t="0"+t),t}Date.prototype.toISOString=function(){return this.getUTCFullYear()+"-"+e(this.getUTCMonth()+1)+"-"+e(this.getUTCDate())+"T"+e(this.getUTCHours())+":"+e(this.getUTCMinutes())+":"+e(this.getUTCSeconds())+"."+String((this.getUTCMilliseconds()/1e3).toFixed(3)).slice(2,5)+"Z"}}();try{new CustomEvent("?")}catch(e){this.CustomEvent=function(e,t){function n(t,n,o,i){this["init"+e](t,n,o,i),"detail"in this||(this.detail=i)}return function(o,i){var a=document.createEvent(e);return null!==o?n.call(a,o,(i||(i=t)).bubbles,i.cancelable,i.detail):a.initCustomEvent=n,a}}(this.CustomEvent?"CustomEvent":"Event",{bubbles:!1,cancelable:!1,detail:null})}document.querySelectorAll||(document.querySelectorAll=function(e){var t,n=document.createElement("style"),o=[];for(document.documentElement.firstChild.appendChild(n),document._qsa=[],n.styleSheet.cssText=e+"{x-qsa:expression(document._qsa && document._qsa.push(this))}",window.scrollBy(0,0),n.parentNode.removeChild(n);document._qsa.length;)(t=document._qsa.shift()).style.removeAttribute("x-qsa"),o.push(t);return document._qsa=null,o}),document.querySelector||(document.querySelector=function(e){var t=document.querySelectorAll(e);return t.length?t[0]:null}),!("innerText"in document.createElement("a"))&&"getSelection"in window&&HTMLElement.prototype.__defineGetter__("innerText",function(){for(var e,t=window.getSelection(),n=[],o=0;o<t.rangeCount;o++)n[o]=t.getRangeAt(o);t.removeAllRanges(),t.selectAllChildren(this),e=t.toString(),t.removeAllRanges();for(o=0;o<n.length;o++)t.addRange(n[o]);return e})},createCookie:function(e,t,n){var o="";if(n){var i=new Date;i.setTime(i.getTime()+24*n*60*60*1e3),o="; expires="+i.toGMTString()}document.cookie=e+"="+t+o+"; path=/"},readCookie:function(e){for(var t=e+"=",n=document.cookie.split(";"),o=0;o<n.length;o++){for(var i=n[o];" "===i.charAt(0);)i=i.substring(1,i.length);if(0===i.indexOf(t))return i.substring(t.length,i.length)}return null},eraseCookie:function(e){this.createCookie(e,"",-1)},getAllCookies:function(){var t={};if(document.cookie&&""!==document.cookie)for(var n=document.cookie.split(";"),o=0;o<n.length;o++){var i=n[o].split("=");i[0]=i[0].replace(/^ /,""),t[decodeURIComponent(i[0])]=decodeURIComponent(i[1])}return e.totalStorage("inbound_cookies",t),t},setUrlParams:function(){var n={};!function(){for(var e,t=function(e){return decodeURIComponent(e).replace(/\+/g," ")},o=window.location.search.substring(1),i=/([^&=]+)=?([^&]*)/g;e=i.exec(o);)if("-1"==e[1].indexOf("["))n[t(e[1])]=t(e[2]);else{var a=e[1].indexOf("["),r=e[1].slice(a+1,e[1].indexOf("]",a)),s=t(e[1].slice(0,a));"object"!=typeof n[s]&&(n[t(s)]={},n[t(s)].length=0),r?n[t(s)][t(r)]=t(e[2]):Array.prototype.push.call(n[t(s)],t(e[2]))}}();for(var o in n)if("action"!=o)if("object"==typeof n[o])for(var i in n[o])this.createCookie(i,n[o][i],30);else this.createCookie(o,n[o],30);if(t){var a=e.totalStorage("inbound_url_params")||{},r=this.mergeObjs(a,n);e.totalStorage("inbound_url_params",r)}var s={option1:"yo",option2:"woooo"};e.trigger("url_parameters",n,s)},getAllUrlParams:function(){n={};if(t)var n=e.totalStorage("inbound_url_params");return n},getParameterVal:function(e,t){return(RegExp(e+"=(.+?)(&|$)").exec(t)||[,!1])[1]},checkLocalStorage:function(){if("localStorage"in window)try{ls=void 0===window.localStorage?void 0:window.localStorage,t="undefined"!=typeof ls&&void 0!==window.JSON}catch(e){t=!1}return t},showLocalStorageSize:function(){function e(e){return 2*e.length}function t(e){return e/1024/1024}var n=Object.keys(localStorage).map(function(t){return{name:t,size:e(localStorage[t])}}).map(function(e){return e.size=t(e.size).toFixed(2)+" MB",e});console.table(n)},addDays:function(e,t){return new Date(e.getTime()+24*t*60*60*1e3)},GetDate:function(){var e=new Date,t=e.getDate(),n=t<10?"0":"",o=e.getFullYear(),i=e.getHours(),a=i<10?"0":"",r=e.getMinutes(),s=r<10?"0":"",l=e.getSeconds(),u=l<10?"0":"",d=e.getMonth()+1;return o+"/"+(d<10?"0":"")+d+"/"+n+t+" "+a+i+":"+s+r+":"+u+l},SetSessionTimeout:function(){this.readCookie("lead_session_expire");var e=new Date;e.setTime(e.getTime()+18e5),this.createCookie("lead_session_expire",!0,e)},storeReferralData:function(){var t=new Date,n=document.referrer||"Direct Traffic",o=e.Utils.readCookie("inbound_referral_site"),i=e.totalStorage("inbound_original_referral");t.setTime(t.getTime()+18e5),o||this.createCookie("inbound_referral_site",n,t),i||e.totalStorage("inbound_original_referral",i)},CreateUID:function(e){var t="0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz".split(""),n="";e||(e=Math.floor(Math.random()*t.length));for(var o=0;o<e;o++)n+=t[Math.floor(Math.random()*t.length)];return n},generateGUID:function(e){return e?(e^16*Math.random()>>e/4).toString(16):([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g,guid)},SetUID:function(e){if(!this.readCookie("wp_lead_uid")){var t=e||this.CreateUID(35);this.createCookie("wp_lead_uid",t)}},countProperties:function(e){var t=0;for(var n in e)e.hasOwnProperty(n)&&++t;return t},mergeObjs:function(e,t){var n={};for(var o in e)n[o]=e[o];for(var o in t)n[o]=t[o];return n},hasClass:function(e,t){if("classList"in document.documentElement)n=t.classList.contains(e);else var n=new RegExp("(^|\\s)"+e+"(\\s|$)").test(t.className);return n},addClass:function(e,t){"classList"in document.documentElement?t.classList.add(e):this.hasClass(t,e)||(t.className+=(t.className?" ":"")+e)},removeClass:function(e,t){"classList"in document.documentElement?t.classList.remove(e):this.hasClass(t,e)&&(t.className=t.className.replace(new RegExp("(^|\\s)*"+e+"(\\s|$)*","g"),""))},removeElement:function(e){e.parentNode.removeChild(e)},trim:function(e){return e=e.replace(/(^\s*)|(\s*$)/gi,""),e=e.replace(/[ ]{2,}/gi," "),e=e.replace(/\n /,"\n")},ajaxPolyFill:function(){if("undefined"!=typeof XMLHttpRequest)return new XMLHttpRequest;for(var e,t=["MSXML2.XmlHttp.5.0","MSXML2.XmlHttp.4.0","MSXML2.XmlHttp.3.0","MSXML2.XmlHttp.2.0","Microsoft.XmlHttp"],n=0;n<t.length;n++)try{e=new ActiveXObject(t[n]);break}catch(e){}return e},ajaxSendData:function(e,t,n,o,i){var a=this.ajaxPolyFill();setTimeout(function(){a.open(n,e,!0),a.onreadystatechange=function(){4==a.readyState&&t(a.responseText)},"POST"==n&&a.setRequestHeader("Content-type","application/x-www-form-urlencoded"),a.send(o)},100)},ajaxGet:function(e,t,n,o){var i=[];for(var a in t)i.push(encodeURIComponent(a)+"="+encodeURIComponent(t[a]));this.ajaxSendData(e+"?"+i.join("&"),n,"GET",null,o)},ajaxPost:function(e,t,n,o){var i=[];for(var a in t)i.push(encodeURIComponent(a)+"="+encodeURIComponent(t[a]));this.ajaxSendData(e,n,"POST",i.join("&"),o)},sendEvent:function(e,t,i){t=t||{},async=!0;var a=getCookie();if(a){var r;for(r in a)t[r]=a[r]}t.id||(t.id=getId());var s={e:e,t:(new Date).toISOString(),kv:t},l=o.api_host+"/track?data="+encodeURIComponent(JSON.stringify(s));if(n){var u=new XMLHttpRequest;u.open("GET",l,async),u.withCredentials=async,u.send(null)}else{var d=document.createElement("script");d.type="text/javascript",d.async=async,d.defer=async,d.src=l;var c=document.getElementsByTagName("script")[0];c.parentNode.insertBefore(d,c)}return action(i),self},domReady:function(e,t){var n=!1,o=!0,i=e.document,a=i.documentElement,r=i.addEventListener?"addEventListener":"attachEvent",s=i.addEventListener?"removeEventListener":"detachEvent",l=i.addEventListener?"":"on",u=function(o){"readystatechange"==o.type&&"complete"!=i.readyState||(("load"==o.type?e:i)[s](l+o.type,u,!1),!n&&(n=!0)&&t.call(e,o.type||o))},d=function(){try{a.doScroll("left")}catch(e){return void setTimeout(d,50)}u("poll")};if("complete"==i.readyState)t.call(e,"lazy");else{if(i.createEventObject&&a.doScroll){try{o=!e.frameElement}catch(e){}o&&d()}i[r](l+"DOMContentLoaded",u,!1),i[r](l+"readystatechange",u,!1),e[r](l+"load",u,!1)}},addListener:function(e,t,n){e&&(e.addEventListener?e.addEventListener(t,n,!1):e.attachEvent?e.attachEvent("on"+t,n):e["on"+t]=n)},removeListener:function(e,t,n){e.removeEventListener?e.removeEventListener(t,n,!1):e.detachEvent?e.detachEvent("on"+t,n):e["on"+t]=null},throttle:function(e,t){var n,o,i,a=null,r=0,s=function(){r=new Date,a=null,i=e.apply(n,o)};return function(){var l=new Date;r||(r=l);var u=t-(l-r);return n=this,o=arguments,u<=0?(clearTimeout(a),a=null,r=l,i=e.apply(n,o)):a||(a=setTimeout(s,u)),i}},checkTypeofGA:function(){"function"==typeof ga&&(universalGA=!0),void 0!==_gaq&&"function"==typeof _gaq.push&&(classicGA=!0),"undefined"!=typeof dataLayer&&"function"==typeof dataLayer.push&&(googleTagManager=!0)},cacheSearchData:function(n,o){if(t){var i=e.totalStorage.getItem("inbound_search_storage");if(i)i.unshift(n),e.totalStorage.setItem("inbound_search_storage",i);else{var a=[n];e.totalStorage.setItem("inbound_search_storage",a)}}else{var r=JSON.stringify(n),s=this.readCookie("inbound_search_storage");s&&(r+="SPLIT-TOKEN"+s),this.createCookie("inbound_search_storage",r,"180")}e.Forms.releaseFormSubmit(o)},storeSearchData:function(){if(inbound_settings.wp_lead_data.lead_id&&inbound_settings.wp_lead_data.lead_nonce){var t=[],n=e.totalStorage.getItem("inbound_search_storage"),o=this.readCookie("inbound_search_storage");if(n||o){if(o){o=o.split("SPLIT-TOKEN");for(var i in o)t.push(JSON.parse(o[i]))}n&&(t=t.concat(n)),t.sort(function(e,t){return e.timestamp-t.timestamp});var a={action:"inbound_search_store",data:t=encodeURIComponent(JSON.stringify(t)),nonce:inbound_settings.wp_lead_data.lead_nonce,lead_id:inbound_settings.wp_lead_data.lead_id};callback=function(t){t&&(t=JSON.parse(t)),t.success&&(console.log(t.success),e.Utils.eraseCookie("inbound_search_storage"),e.totalStorage.deleteItem("inbound_search_storage")),t.error&&console.log(t.error)},this.ajaxPost(inbound_settings.admin_url,a,callback)}}}},e}(_inbound||{}),InboundForms=function(e){var t=e.Utils,n=[],o=[],a=[],r={},s=e.Settings,l=["first name","last name","name","email","e-mail","phone","website","job title","your favorite food","company","tele","address","comment"];if(e.Forms={init:function(){e.Forms.runFieldMappingFilters(),e.Forms.formTrackInit(),e.Forms.searchTrackInit()},runFieldMappingFilters:function(){l=e.hooks.applyFilters("forms.field_map",l)},debug:function(e,t){return},formTrackInit:function(){for(var e=0;e<window.document.forms.length;e++){var t=window.document.forms[e];t.dataset.formProcessed||(t.dataset.formProcessed=!0,this.checkTrackStatus(t)&&(this.attachFormSubmitEvent(t),this.initFormMapping(t)))}},searchTrackInit:function(){if("off"!=inbound_settings.search_tracking&&!r.searchTrackInit){for(var e=0;e<window.document.forms.length;e++){var n=window.document.forms[e];n.dataset.searchChecked||(n.dataset.searchChecked=!0,this.checkSearchTrackStatus(n)&&this.attachSearchFormSubmitEvent(n))}t.storeSearchData(),r.searchTrackInit=!0}},checkTrackStatus:function(t){var n=t.getAttribute("class");if(""!==n&&null!==n)return n.toLowerCase().indexOf("wpl-track-me")>-1||(n.toLowerCase().indexOf("inbound-track")>-1||(cb=function(){console.log(t)},e.deBugger("forms","This form not tracked. Please assign on in settings...",cb),!1))},checkSearchTrackStatus:function(t){var n=t.getAttribute("class"),o=t.getAttribute("id");return""!==n&&null!==n&&n.toLowerCase().indexOf("search")>-1||(""===o||null===o?(cb=function(){console.log(t)},e.deBugger("searches","This search form is not tracked. Please assign on in settings...",cb),!1):o.toLowerCase().indexOf("search")>-1||void 0)},loopClassSelectors:function(n,o){for(var i=n.length-1;i>=0;i--){var a=t.trim(n[i]);-1===a.indexOf("#")&&-1===a.indexOf(".")&&(a="#"+a),(a=document.querySelector(a))&&("add"===o?(e.Utils.addClass("wpl-track-me",a),e.Utils.addClass("inbound-track",a)):(e.Utils.removeClass("wpl-track-me",a),e.Utils.removeClass("inbound-track",a)))}},initFormMapping:function(t){for(var n=[],o=0;o<t.elements.length;o++)formInput=t.elements[o],"hidden"!==formInput.type?(this.mapField(formInput),this.rememberInputValues(formInput),s.formAutoPopulation&&!e.Utils.hasClass("nopopulate",t)&&this.fillInputValues(formInput)):n.push(formInput);for(var i=n.length-1;i>=0;i--)formInput=n[i],this.mapField(formInput)},mapField:function(o){var a=o.id||!1,r=o.name||!1,s=this.getInputLabel(o);if(s&&this.ignoreFieldByLabel(s[0].innerText))return o.dataset.ignoreFormField=!0,!1;for(i=0;i<l.length;i++){var u=!1,d=l[i],c=t.trim(d),m=c.replace(/ /g,"_");r&&r.toLowerCase().indexOf(c)>-1?(u=!0,e.deBugger("forms","Found matching name attribute for -> "+c)):a&&a.toLowerCase().indexOf(c)>-1?(u=!0,e.deBugger("forms","Found matching ID attribute for ->"+c)):s?s[0].innerText.toLowerCase().indexOf(c)>-1&&(u=!0,e.deBugger("forms","Found matching sibling label for -> "+c)):n.push(c),u&&(this.addDataAttr(o,m),this.removeArrayItem(l,c),i--)}return inbound_data},formListener:function(t){t.preventDefault(),e.Forms.saveFormData(t.target),document.body.style.cursor="wait"},searchFormListener:function(t){t.preventDefault(),e.Forms.saveSearchData(t.target)},attachFormSubmitEvent:function(e){t.addListener(e,"submit",this.formListener);document.querySelector(".inbound-email")},attachSearchFormSubmitEvent:function(e){t.addListener(e,"submit",this.searchFormListener)},ignoreFieldByLabel:function(t){var n=!1;return!!t&&(-1==t.toLowerCase().indexOf("credit card")&&-1==t.toLowerCase().indexOf("card number")||(n=!0),-1==t.toLowerCase().indexOf("expiration")&&-1==t.toLowerCase().indexOf("expiry")||(n=!0),"month"!=t.toLowerCase()&&"mm"!=t.toLowerCase()&&"yy"!=t.toLowerCase()&&"yyyy"!=t.toLowerCase()&&"year"!=t.toLowerCase()||(n=!0),-1==t.toLowerCase().indexOf("cvv")&&-1==t.toLowerCase().indexOf("cvc")&&-1==t.toLowerCase().indexOf("secure code")&&-1==t.toLowerCase().indexOf("security code")||(n=!0),n&&e.deBugger("forms","ignore "+t),n)},ignoreFieldByValue:function(e){var t=!1;if(!e)return!1;if("visa"!=e.toLowerCase()&&"mastercard"!=e.toLowerCase()&&"american express"!=e.toLowerCase()&&"amex"!=e.toLowerCase()&&"discover"!=e.toLowerCase()||(t=!0),new RegExp("/^[0-9]+$/").test(e)){var n=e.replace(" ","");this.isInt(n)&&n.length>=16&&(t=!0)}return t},isInt:function(e){return"number"==typeof e&&isFinite(e)&&e%1==0},releaseFormSubmit:function(e){document.body.style.cursor="default",t.removeClass("wpl-track-me",e),t.removeListener(e,"submit",this.formListener);var n=e.getAttribute("class");if(""!==n&&null!==n&&-1!=n.toLowerCase().indexOf("wpcf7-form"))return setTimeout(function(){document.body.style.cursor="default"},300),!0;e.submit(),setTimeout(function(){for(var t=0;t<e.elements.length;t++)formInput=e.elements[t],type=formInput.type||!1,"submit"===type&&"submit"===formInput.name&&e.elements[t].click()},2e3)},saveFormData:function(n){for(var i=i||{},r=0;r<n.elements.length;r++)if(formInput=n.elements[r],multiple=!1,formInput.name){if(formInput.dataset.ignoreFormField){e.deBugger("forms","ignore "+formInput.name);continue}switch(inputName=formInput.name.replace(/\[([^\[]*)\]/g,"%5B%5D$1"),i[inputName]||(i[inputName]={}),formInput.type&&(i[inputName].type=formInput.type),i[inputName].name||(i[inputName].name=formInput.name),formInput.dataset.mapFormField&&(i[inputName].map=formInput.dataset.mapFormField),formInput.nodeName){case"INPUT":if(!1===(l=this.getInputValue(formInput)))continue;break;case"TEXTAREA":l=formInput.value;break;case"SELECT":if(formInput.multiple){values=[],multiple=!0;for(var s=0;s<formInput.length;s++)formInput[s].selected&&values.push(encodeURIComponent(formInput[s].value))}else l=formInput.value}if(e.deBugger("forms","Input Value = "+l),l){i[inputName].value||(i[inputName].value=[]),i[inputName].value.push(multiple?values.join(","):encodeURIComponent(l));var l=multiple?values.join(","):encodeURIComponent(l)}}e.deBugger("forms",i);for(var u in i){var d=i[u].value,c=i[u].map;if(void 0!==d&&null!=d&&""!=d&&o.push(u+"="+i[u].value.join(",")),void 0!==c&&null!=c&&i[u].value&&(a.push(c+"="+i[u].value.join(",")),"email"===u))var m=i[u].value.join(",")}var f=o.join("&");e.deBugger("forms","Stringified Raw Form PARAMS: "+f);var g=a.join("&");e.deBugger("forms","Stringified Mapped PARAMS"+g),(m=t.getParameterVal("email",g)||t.readCookie("wp_lead_email"))||(m=t.getParameterVal("wpleads_email_address",g));var p=t.getParameterVal("name",g),h=t.getParameterVal("first_name",g),v=t.getParameterVal("last_name",g);if(!v&&h&&(_=decodeURI(h).split(" ")).length>0&&(h=_[0],v=_[1]),p&&!v&&!h){var _=decodeURI(p).split(" ");_.length>0&&(h=_[0],v=_[1])}p=h&&v?h+" "+v:p,h||(h=""),v||(v=""),e.deBugger("forms","fName = "+h),e.deBugger("forms","lName = "+v),e.deBugger("forms","fullName = "+p);var b=e.totalStorage("page_views")||{},y=e.totalStorage("inbound_url_params")||{},w=n.querySelectorAll('input[value][type="hidden"][name="inbound_furl"]:not([value=""])'),k=!1;if(0==w.length||"IA=="==w[0].value)k=!0;var S=n.querySelectorAll('input[value][type="hidden"][name="inbound_form_id"]');S=S.length>0?S[0].value:0;if("undefined"!=typeof landing_path_info)C=landing_path_info.variation;else if("undefined"!=typeof cta_path_info)C=cta_path_info.variation;else var C=inbound_settings.variation_id;var I=inbound_settings.post_type||"page",L=inbound_settings.post_id||0;search_data={},formData={action:"inbound_lead_store",email:m,full_name:p,first_name:h,last_name:v,raw_params:f,mapped_params:g,url_params:JSON.stringify(y),search_data:"test",page_views:JSON.stringify(b),post_type:I,page_id:L,variation:C,source:t.readCookie("inbound_referral_site"),inbound_submitted:k,inbound_form_id:S,inbound_nonce:inbound_settings.ajax_nonce,event:n},callback=function(o){e.deBugger("forms","Lead Created with ID: "+o),o=parseInt(o,10),formData.leadID=o,o&&(t.createCookie("wp_lead_id",o),e.totalStorage.deleteItem("page_views"),e.totalStorage.deleteItem("tracking_events")),e.trigger("form_after_submission",formData),e.Forms.releaseFormSubmit(n)},e.trigger("form_before_submission",formData),t.ajaxPost(inbound_settings.admin_url,formData,callback)},saveSearchData:function(n){for(var o=o||{},i=0;i<n.elements.length;i++)if(formInput=n.elements[i],multiple=!1,formInput.name){if(formInput.dataset.ignoreFormField){e.deBugger("searches","ignore "+formInput.name);continue}switch(inputName=formInput.name.replace(/\[([^\[]*)\]/g,"%5B%5D$1"),o[inputName]||(o[inputName]={}),formInput.type&&(o[inputName].type=formInput.type),o[inputName].name||(o[inputName].name=formInput.name),formInput.dataset.mapFormField&&(o[inputName].map=formInput.dataset.mapFormField),formInput.nodeName){case"INPUT":if(!1===(r=this.getInputValue(formInput)))continue;break;case"TEXTAREA":r=formInput.value;break;case"SELECT":if(formInput.multiple){values=[],multiple=!0;for(var a=0;a<formInput.length;a++)formInput[a].selected&&values.push(encodeURIComponent(formInput[a].value))}else r=formInput.value}if(e.deBugger("searches","Input Value = "+r),r){o[inputName].value||(o[inputName].value=[]),o[inputName].value.push(multiple?values.join(","):encodeURIComponent(r));var r=multiple?values.join(","):encodeURIComponent(r)}}e.deBugger("searches",o);var s=[];for(var l in o){var u=o[l].value,d=o[l].type;void 0!==u&&null!=u&&""!=u&&"search"==d&&s.push("search_text|value|"+o[l].value)}if(s[0]){var c=s.join("|field|");if(e.deBugger("searches","Stringified Search Form PARAMS: "+c),"undefined"!=typeof landing_path_info)m=landing_path_info.variation;else if("undefined"!=typeof cta_path_info)m=cta_path_info.variation;else var m=inbound_settings.variation_id;var f=inbound_settings.post_type||"page",g=inbound_settings.post_id||0,p=t.readCookie("wp_lead_uid");inbound_settings.wp_lead_data.lead_email?email=inbound_settings.wp_lead_data.lead_email:t.readCookie("inbound_wpleads_email_address")?email=t.readCookie("inbound_wpleads_email_address"):email="",searchData={email:email,search_data:c,user_UID:p,post_type:f,page_id:g,variation:m,source:t.readCookie("inbound_referral_site"),ip_address:inbound_settings.ip_address,timestamp:Math.floor((new Date).getTime()/1e3)},e.trigger("search_before_caching",searchData),inbound_settings.wp_lead_data.lead_id?(searchData.lead_id=inbound_settings.wp_lead_data.lead_id,t.cacheSearchData(searchData,n)):t.cacheSearchData(searchData,n)}},rememberInputValues:function(n){n.name&&n.name;var o=n.type?n.type:"text";if("submit"===o||"hidden"===o||"file"===o||"password"===o||n.dataset.ignoreFormField)return!1;t.addListener(n,"change",function(n){if(n.target.name){if("checkbox"!==o)var i=n.target.value;else for(var a=[],r=document.querySelectorAll('input[name="'+n.target.name+'"]'),s=0;s<r.length;s++)r[s].checked&&a.push(r[s].value),i=a.join(",");inputData={name:n.target.name,node:n.target.nodeName.toLowerCase(),type:o,value:i,mapping:n.target.dataset.mapFormField},e.trigger("form_input_change",inputData),t.createCookie("inbound_"+n.target.name,encodeURIComponent(i))}})},fillInputValues:function(e){var n=e.name?"inbound_"+e.name:"",o=e.type?e.type:"text";if("submit"===o||"hidden"===o||"file"===o||"password"===o)return!1;if(t.readCookie(n)&&"comment"!=n)if(value=decodeURIComponent(t.readCookie(n)),"checkbox"===o||"radio"===o)for(var i=value.split(","),a=0;a<i.length;a++)e.value.indexOf(i[a])>-1&&(e.checked=!0);else"undefined"!==value&&(e.value=value)},getInputLabel:function(e){var t;return(t=this.siblingsIsLabel(e))?t:!!(t=this.CheckParentForLabel(e))&&t},getInputValue:function(e){var t=!1;switch(e.type){case"radio":case"checkbox":e.checked&&(t=e.value);break;case"text":case"hidden":default:t=e.value}return t},addDataAttr:function(e,t){for(var n=document.getElementsByName(e.name),o=n.length-1;o>=0;o--)e.dataset.mapFormField||(n[o].dataset.mapFormField=t)},removeArrayItem:function(e,t){if(e.indexOf)index=e.indexOf(t);else for(index=e.length-1;index>=0&&e[index]!==t;--index);index>=0&&e.splice(index,1)},siblingsIsLabel:function(e){for(var t=this.getSiblings(e),n=[],o=t.length-1;o>=0;o--)"label"===t[o].nodeName.toLowerCase()&&n.push(t[o]);return n.length>0&&n.length<2&&n},getChildren:function(e,t){for(var n=[];e;e=e.nextSibling)1==e.nodeType&&e!=t&&n.push(e);return n},getSiblings:function(e){return this.getChildren(e.parentNode.firstChild,e)},CheckParentForLabel:function(e){if("FORM"===e.nodeName)return null;do{var t=e.getElementsByTagName("label");if(t.length>0&&t.length<2)return e.getElementsByTagName("label")}while(e=e.parentNode);return null},mailCheck:function(){var e=document.querySelector(".inbound-email");e&&(t.addListener(e,"blur",this.mailCheck),u.run({email:document.querySelector(".inbound-email").value,suggested:function(n){var o=document.querySelector(".email_suggestion");o&&t.removeElement(o);var i=document.createElement("span");i.innerHTML="<span class=\"email_suggestion\">Did youu mean <b><i id='email_correction' style='cursor: pointer;' title=\"click to update\">"+n.full+"</b></i>?</span>",e.parentNode.insertBefore(i,e.nextSibling);var a=document.getElementById("email_correction");t.addListener(a,"click",function(){e.value=a.innerHTML,a.parentNode.parentNode.innerHTML="Fixed!"})},empty:function(){}}))}},void 0===u)var u={domainThreshold:1,topLevelThreshold:3,defaultDomains:["yahoo.com","google.com","hotmail.com","gmail.com","me.com","aol.com","mac.com","live.com","comcast.net","googlemail.com","msn.com","hotmail.co.uk","yahoo.co.uk","facebook.com","verizon.net","sbcglobal.net","att.net","gmx.com","mail.com","outlook.com","icloud.com"],defaultTopLevelDomains:["co.jp","co.uk","com","net","org","info","edu","gov","mil","ca","de"],run:function(e){e.domains=e.domains||u.defaultDomains,e.topLevelDomains=e.topLevelDomains||u.defaultTopLevelDomains,e.distanceFunction=e.distanceFunction||u.sift3Distance;var t=function(e){return e},n=e.suggested||t,o=e.empty||t,i=u.suggest(u.encodeEmail(e.email),e.domains,e.topLevelDomains,e.distanceFunction);return i?n(i):o()},suggest:function(e,t,n,o){e=e.toLowerCase();var i=this.splitEmail(e),a=this.findClosestDomain(i.domain,t,o,this.domainThreshold);if(a){if(a!=i.domain)return{address:i.address,domain:a,full:i.address+"@"+a}}else{var r=this.findClosestDomain(i.topLevelDomain,n,o,this.topLevelThreshold);if(i.domain&&r&&r!=i.topLevelDomain){var s=i.domain;return a=s.substring(0,s.lastIndexOf(i.topLevelDomain))+r,{address:i.address,domain:a,full:i.address+"@"+a}}}return!1},findClosestDomain:function(e,t,n,o){o=o||this.topLevelThreshold;var i,a=99,r=null;if(!e||!t)return!1;n||(n=this.sift3Distance);for(var s=0;s<t.length;s++){if(e===t[s])return e;(i=n(e,t[s]))<a&&(a=i,r=t[s])}return a<=o&&null!==r&&r},sift3Distance:function(e,t){if(null===e||0===e.length)return null===t||0===t.length?0:t.length;if(null===t||0===t.length)return e.length;for(var n=0,o=0,i=0,a=0;n+o<e.length&&n+i<t.length;){if(e.charAt(n+o)==t.charAt(n+i))a++;else{o=0,i=0;for(var r=0;r<5;r++){if(n+r<e.length&&e.charAt(n+r)==t.charAt(n)){o=r;break}if(n+r<t.length&&e.charAt(n)==t.charAt(n+r)){i=r;break}}}n++}return(e.length+t.length)/2-a},splitEmail:function(e){var t=e.trim().split("@");if(t.length<2)return!1;for(a=0;a<t.length;a++)if(""===t[a])return!1;var n=t.pop(),o=n.split("."),i="";if(0===o.length)return!1;if(1==o.length)i=o[0];else{for(var a=1;a<o.length;a++)i+=o[a]+".";o.length>=2&&(i=i.substring(0,i.length-1))}return{topLevelDomain:i,domain:n,address:t.join("@")}},encodeEmail:function(e){var t=encodeURI(e);return t=t.replace("%20"," ").replace("%25","%").replace("%5E","^").replace("%60","`").replace("%7B","{").replace("%7C","|").replace("%7D","}")}};return e}(_inbound||{}),_inboundEvents=function(e){function t(t,o,i){var o=o||{};(i=i||{}).bubbles=i.bubbles||!0,i.cancelable=i.cancelable||!0,o=e.apply_filters("filter_"+t,o);!window.ActiveXObject&&window;if("function"==typeof CustomEvent)var a=new CustomEvent(t,{detail:o,bubbles:i.bubbles,cancelable:i.cancelable});else(a=document.createEvent("Event")).initEvent(t,!0,!0);window.dispatchEvent(a),e.do_action(t,o),n(t,o)}function n(e,t){if(window.jQuery){var t=t||{};jQuery(document).trigger(e,t)}}e.trigger=function(t,n){e.Events[t](n)};return e.Events={analytics_ready:function(){t("analytics_ready",{data:"xyxy"},{opt1:!0})},url_parameters:function(e){t("url_parameters",e)},session_start:function(){console.log(""),t("session_start")},session_end:function(e){t("session_end",e),console.log("Session End")},session_active:function(){t("session_active")},session_idle:function(e){t("session_idle",e)},session_resume:function(){t("session_resume")},session_heartbeat:function(e){t("session_heartbeat",{clock:e,leadData:InboundLeadData})},page_visit:function(e){t("page_view",e)},page_first_visit:function(n){t("page_first_visit"),e.deBugger("pages","First Ever Page View of this Page")},page_revisit:function(n){t("page_revisit",n);e.deBugger("pages",status,function(){console.log("pageData",n),console.log("Page Revisit viewed "+n+" times")})},tab_hidden:function(n){e.deBugger("pages","Tab Hidden"),t("tab_hidden")},tab_visible:function(n){e.deBugger("pages","Tab Visible"),t("tab_visible")},tab_mouseout:function(n){e.deBugger("pages","Tab Mouseout"),t("tab_mouseout")},form_input_change:function(n){e.deBugger("forms","inputData change. Data=",function(){console.log(n)}),t("form_input_change",n)},form_before_submission:function(e){t("form_before_submission",e)},form_after_submission:function(e){t("form_after_submission",e)},search_before_caching:function(e){t("search_before_caching",e)},analyticsError:function(e,t,n){var o=new CustomEvent("inbound_analytics_error",{detail:{MLHttpRequest:e,textStatus:t,errorThrown:n}});window.dispatchEvent(o),console.log("Page Save Error")}},e}(_inbound||{});_inbound.add_action("form_before_submission",inboundFormNoRedirect,10),_inbound.add_action("form_after_submission",inboundFormNoRedirectContent,10);var InboundTotalStorage=function(e){var t,n;if("localStorage"in window)try{n=void 0===window.localStorage?void 0:window.localStorage,t=void 0!==n&&void 0!==window.JSON,window.localStorage.setItem("_inbound","1"),window.localStorage.removeItem("_inbound")}catch(e){t=!1}e.totalStorage=function(t,n,o){return e.totalStorage.impl.init(t,n)},e.totalStorage.setItem=function(t,n){return e.totalStorage.impl.setItem(t,n)},e.totalStorage.getItem=function(t){return e.totalStorage.impl.getItem(t)},e.totalStorage.getAll=function(){return e.totalStorage.impl.getAll()},e.totalStorage.deleteItem=function(t){return e.totalStorage.impl.deleteItem(t)},e.totalStorage.impl={init:function(e,t){return void 0!==t?this.setItem(e,t):this.getItem(e)},setItem:function(o,i){if(!t)try{return e.Utils.createCookie(o,i),i}catch(e){console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie")}var a=JSON.stringify(i);return n.setItem(o,a),this.parseResult(a)},getItem:function(o){if(!t)try{return this.parseResult(e.Utils.readCookie(o))}catch(e){return null}var i=n.getItem(o);return this.parseResult(i)},deleteItem:function(o){if(!t)try{return e.Utils.eraseCookie(o,null),!0}catch(e){return!1}return n.removeItem(o),!0},getAll:function(){var o=[];if(t)for(var i in n)i.length&&o.push({key:i,value:this.parseResult(n.getItem(i))});else try{for(var a=document.cookie.split(";"),r=0;r<a.length;r++){var s=a[r].split("=")[0];o.push({key:s,value:this.parseResult(e.Utils.readCookie(s))})}}catch(e){return null}return o},parseResult:function(e){var t;try{void 0===(t=JSON.parse(e))&&(t=e),"true"==t&&(t=!0),"false"==t&&(t=!1),parseFloat(t)==t&&"object"!=typeof t&&(t=parseFloat(t))}catch(n){t=e}return t}}}(_inbound||{}),_inboundLeadsAPI=function(e){return e.LeadsAPI={init:function(){var t=e.Utils,n=(t.readCookie("wp_lead_uid"),t.readCookie("wp_lead_id"));t.readCookie("lead_session_expire")||(e.deBugger("leads","expired vistor. Run Processes"),n&&e.LeadsAPI.getAllLeadData())},setGlobalLeadData:function(e){InboundLeadData=e},getAllLeadData:function(t){var n=e.Utils.readCookie("wp_lead_id"),o=e.totalStorage("inbound_lead_data"),i=e.Utils.readCookie("lead_data_expire");data={action:"inbound_get_all_lead_data",wp_lead_id:n},success=function(t){var n=JSON.parse(t);e.LeadsAPI.setGlobalLeadData(n),e.totalStorage("inbound_lead_data",n);var o=new Date;o.setTime(o.getTime()+18e5);var i=e.Utils.addDays(o,3);e.Utils.createCookie("lead_data_expire",!0,i)},o?(e.LeadsAPI.setGlobalLeadData(o),e.deBugger("lead","Set Global Lead Data from Localstorage"),i||(e.Utils.ajaxPost(inbound_settings.admin_url,data,success),e.deBugger("lead","localized data old. Pull new from DB"))):e.Utils.ajaxPost(inbound_settings.admin_url,data,success)},getLeadLists:function(){var t={action:"wpl_check_lists",wp_lead_id:e.Utils.readCookie("wp_lead_id")};e.Utils.ajaxPost(inbound_settings.admin_url,t,function(t){e.Utils.createCookie("lead_session_list_check",!0,{path:"/",expires:1}),e.deBugger("lead","Lists checked")})}},e}(_inbound||{}),_inboundPageTracking=function(e){var t,n,o=!1,i=!1,a=!1,r=parseInt(e.Utils.readCookie("lead_session"),10)||0,s=0,l=(new Date,null),u=null,d=null,c=e.Utils,m=e.Utils.GetDate(),f="page_views",g=e.totalStorage(f)||{},p=inbound_settings.post_id||window.location.pathname,h=e.Settings.timeout||1e4;return e.PageTracking={init:function(o){this.CheckTimeOut(),o=o||{},t=parseInt(o.reportInterval,10)||10,n=parseInt(o.idleTimeout,10)||3,c.addListener(document,"keydown",c.throttle(e.PageTracking.pingSession,1e3)),c.addListener(document,"click",c.throttle(e.PageTracking.pingSession,1e3)),c.addListener(window,"mousemove",c.throttle(e.PageTracking.pingSession,1e3)),e.PageTracking.checkVisibility(),this.startSession()},setIdle:function(t){var n="Session IDLE. Activity Timeout due to "+(t=t||"No Movement");e.deBugger("pages",n),clearTimeout(e.PageTracking.idleTimer),e.PageTracking.stopClock(),e.trigger("session_idle")},checkVisibility:function(){var t,n;void 0!==document.hidden?(t="hidden",n="visibilitychange"):void 0!==document.mozHidden?(t="mozHidden",n="mozvisibilitychange"):void 0!==document.msHidden?(t="msHidden",n="msvisibilitychange"):void 0!==document.webkitHidden&&(t="webkitHidden",n="webkitvisibilitychange");var o=document[t];e.Utils.addListener(document,n,function(n){o!=document[t]&&(document[t]?(e.trigger("tab_hidden"),e.PageTracking.setIdle("browser tab switch")):(e.trigger("tab_visible"),e.PageTracking.pingSession()),o=document[t])})},clock:function(){var n="Total time spent on Page in this Session: "+((r+=1)/60).toFixed(2)+" min";if(e.deBugger("pages",n),r>0&&r%t==0){var o=new Date;o.setTime(o.getTime()+18e5),c.createCookie("lead_session",r,o),e.trigger("session_heartbeat",r)}},inactiveClock:function(){var t="Time until Session Timeout: "+((1800-(s+=1))/60).toFixed(2)+" min";e.deBugger("pages",t),s>1800&&(e.trigger("session_end",InboundLeadData),e.Utils.eraseCookie("lead_session"),s=0,clearTimeout(u))},stopClock:function(){i=!0,clearTimeout(l),clearTimeout(u),u=setInterval(e.PageTracking.inactiveClock,1e3)},restartClock:function(){i=!1,e.trigger("session_resume"),e.deBugger("pages","Activity resumed. Session Active"),clearTimeout(l),s=0,clearTimeout(u),l=setInterval(e.PageTracking.clock,1e3)},turnOff:function(){e.PageTracking.setIdle(),a=!0},turnOn:function(){a=!1},startSession:function(){new Date;if(o=!0,l=setInterval(e.PageTracking.clock,1e3),c.readCookie("lead_session"))e.trigger("session_active");else{e.trigger("session_start");var t=new Date;t.setTime(t.getTime()+18e5),e.Utils.createCookie("lead_session",1,t)}this.pingSession()},resetInactiveFunc:function(){s=0,clearTimeout(u)},pingSession:function(t){a||(o||e.PageTracking.startSession(),i&&e.PageTracking.restartClock(),clearTimeout(d),d=setTimeout(e.PageTracking.setIdle,1e3*n+100),void 0!==t&&"mousemove"===t.type&&e.PageTracking.mouseEvents(t))},mouseEvents:function(t){t.pageY<=5&&e.trigger("tab_mouseout")},getPageViews:function(){if(e.Utils.checkLocalStorage()){var t=localStorage.getItem(f),n=JSON.parse(t);return n}},isRevisit:function(e){var t=!1,n=(e=e||{})[p];return void 0!==n&&null!==n&&(t=!0),t},triggerPageView:function(t){var n={title:document.title,url:document.location.href,path:document.location.pathname,count:1};t?(g[p].push(m),n.count=g[p].length,e.trigger("page_revisit",n)):(g[p]=[],g[p].push(m),e.trigger("page_first_visit",n)),e.trigger("page_visit",n),e.totalStorage(f,g),this.storePageView()},CheckTimeOut:function(){var t,n=this.isRevisit(g);if(n){var o=g[p].length-1,i=g[p][o],a=Math.abs(new Date(i).getTime()-new Date(m).getTime());a>h?(t="Timeout Happened. Page view fired",this.triggerPageView(n)):(time_left=.001*Math.abs(h-a),t=h/1e3+" sec timeout not done: "+time_left+" seconds left")}else this.triggerPageView(n);e.deBugger("pages",t)},storePageView:function(){var t=!1;"off"==inbound_settings.page_tracking&&"landing-page"!=inbound_settings.post_type||(document.onreadystatechange=function(){"loading"!==document.readyState&&!1===t&&setTimeout(function(){var n=e.Utils.readCookie("wp_lead_id")?e.Utils.readCookie("wp_lead_id"):"",o=e.Utils.readCookie("wp_lead_uid")?e.Utils.readCookie("wp_lead_uid"):"",i=e.totalStorage("wp_cta_loaded"),a=e.totalStorage("wp_cta_impressions");t=!0,e.totalStorage("wp_cta_impressions",{});var r={action:"inbound_track_lead",wp_lead_uid:o,wp_lead_id:n,page_id:inbound_settings.post_id,variation_id:inbound_settings.variation_id,post_type:inbound_settings.post_type,current_url:window.location.href,page_views:JSON.stringify(e.PageTracking.getPageViews()),cta_impressions:JSON.stringify(a),cta_history:JSON.stringify(i),json:"0"};e.Utils.ajaxPost(inbound_settings.admin_url,r,function(e){})},200)})}},e}(_inbound||{});_inbound.init(),InboundLeadData=_inbound.totalStorage("inbound_lead_data")||null;
shared/assets/js/global/debug.js DELETED
@@ -1,37 +0,0 @@
1
- var errors = [];
2
- window.onerror = function(msg, url, linenumber) {
3
- //alert('Error message: '+msg+' URL: '+url+' Line Number: '+linenumber);
4
- errors.push(msg + ' from ' + url + ' on line ' +linenumber);
5
- }
6
- jQuery(document).ready(function($) {
7
- var url = window.location.href;
8
- var match = url.match(/\?inbound_js/);
9
- var param = "?";
10
- if(match) {
11
- var param = "?";
12
- }
13
- var url = url.replace('?inbound_js', '');
14
- var url = url.replace('&inbound_js', '') + param + "inbound-dequeue-scripts";
15
- setTimeout(function() {
16
- document.write("<strong>Below are the javascript errors on this page</strong> " + "<br>");
17
- /*if (errors.length === 0 ) {
18
- document.write("<strong style="color:green;">None Detected</strong> " + "<br>");
19
- }*/
20
- document.write("<div id='errors-here'>");
21
- for (var i=0,len=errors.length; i<len; i++){
22
- document.write(i + 1 + ". " + errors[i] + "<br>");
23
- }
24
- document.write("</div>");
25
- document.write("<div style=\'margin-top:20px;\'><strong>You need to fix these errors for things to work. There are 3 options:</strong> " + "<br>");
26
- document.write("<strong>1. <a href=\'" +url+ "\'>Click here and dequeue (turn off) the broken javascript files</a> from this page.</strong> " + "<br>");
27
- document.write("<strong>2. Contact the original developer of the plugin/theme causing the error.</strong> " + "<br>");
28
- document.write("<strong>3. Disable the plugin or theme causing the conflict.</strong> " + "<br></div>");
29
- }, 500);
30
- setTimeout(function() {
31
- var theDiv = document.getElementById("errors-here");
32
- if(theDiv.innerHTML.length == 0){
33
- theDiv.innerHTML = "<strong style='color:green;''>No JS Errors Detected</strong> " + "<br><br>" + "Sometimes errors are not detectatble by this handy tool, to double check please <a href='http://www.youtube.com/watch?v=x19VOytW9DM'>follow these instructions</a>";
34
- theDiv.style.display="inline";
35
- }
36
- }, 1000);
37
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
shared/assets/js/global/inbound-dequeue-scripts.js DELETED
@@ -1,49 +0,0 @@
1
- jQuery(document).ready(function($) {
2
- jQuery("body").on('click', 'label', function () {
3
-
4
- var status = jQuery(this).attr('class');
5
- var status = status.replace('turn-', "");
6
- if (status === 'off') {
7
- jQuery(this).parent().find('.switch-button').addClass('status-off');
8
- } else {
9
- jQuery(this).parent().find('.switch-button').removeClass('status-off');
10
- }
11
- var the_script = jQuery(this).parent().attr('id');
12
- var post_id = $('#inbound-dequeue-id').text();
13
- var admin_screen = $('#inbound-fix-page').attr('data-admin-screen');
14
- if (typeof (admin_screen) != "undefined" && admin_screen != null && admin_screen != "") {
15
- var admin_screen = admin_screen;
16
- var action = 'inbound_dequeue_admin_js'
17
- } else {
18
- var admin_screen = "";
19
- var action = 'inbound_dequeue_js'
20
- }
21
- console.log(the_script);
22
- console.log(status);
23
-
24
- jQuery.ajax({
25
- type: 'POST',
26
- url: inbound_debug.admin_url,
27
- context: this,
28
- data: {
29
- action: action,
30
- post_id: post_id,
31
- status: status,
32
- the_script: the_script,
33
- admin_screen: admin_screen
34
- },
35
-
36
- success: function (data) {
37
- console.log("The script " + the_script + " has been turned " + status);
38
- var self = this;
39
- var str = data;
40
- var obj = JSON.parse(str);
41
- console.log(obj);
42
- },
43
-
44
- error: function (MLHttpRequest, textStatus, errorThrown) {
45
- alert("Ajax not enabled");
46
- }
47
- });
48
- });
49
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
shared/classes/class.acf-bootstrap.php CHANGED
@@ -1,88 +1,94 @@
1
- <?php
2
-
3
- class Inbound_Shared_ACF_BootStrap {
4
-
5
- public function __construct() {
6
- self::load_acf();
7
- self::load_acf_extended_fields();
8
- }
9
-
10
- public static function load_acf() {
11
-
12
- /* load ACF if not already loaded */
13
- if( !class_exists('acf') || defined('ACF_PRELOADED') ) {
14
-
15
- define( 'ACF_LITE', true );
16
- define( 'ACF_FREE', true );
17
-
18
- include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/acf.php');
19
-
20
- /* customize ACF path */
21
- add_filter('acf/settings/path', array( __CLASS__, 'define_acf_settings_path' ) );
22
-
23
- /* customize ACF URL path */
24
- add_filter('acf/settings/dir', array( __CLASS__, 'define_acf_settings_url' ) );
25
-
26
- /* Hide ACF field group menu item */
27
- add_filter('acf/settings/show_admin', '__return_false');
28
-
29
-
30
- } else {
31
- /* find out if ACF free or ACF Pro is installed & activated*/
32
- include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
33
- if ( !function_exists('acf_add_local_field_group') ) {
34
- define( 'ACF_FREE', true );
35
- } else {
36
- define( 'ACF_PRO', true );
37
- add_filter('lp_init' , array(__CLASS__,'acf_register_global') , 20 , 1 ); /* registeres a global of registered field values for support between ACF5 & ACF6 */
38
- }
39
- }
40
- }
41
-
42
- /**
43
- * Load extended/custom field support
44
- */
45
- public static function load_acf_extended_fields() {
46
- if (!function_exists('register_fields_font_awesome')) {
47
- include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields-font-awesome/acf-font-awesome.php');
48
- include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/acf-field-date-time-picker/acf-date-time-picker.php');
49
- }
50
- }
51
-
52
- /**
53
- * If ACF Pro is active then register a global for active fields - this provides legacy support to Landing Pages
54
- */
55
- public static function acf_register_global( $field_group ) {
56
- $GLOBALS['acf_register_field_group'][] = array(
57
- 'fields' => acf_local()->fields
58
- );
59
- }
60
- /**
61
- * define custom ACF path
62
- * @param $path
63
- * @return string
64
- */
65
- public static function define_acf_settings_path( $path ) {
66
-
67
- $path = INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/';
68
-
69
- return $path;
70
-
71
- }
72
-
73
- /**
74
- * define custom settings URL
75
- * @param $url
76
- * @return string
77
- */
78
- public static function define_acf_settings_url( $url ) {
79
-
80
- $url = INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/';
81
-
82
- return $url;
83
- }
84
-
85
- }
86
-
87
-
 
 
 
 
 
 
88
  new Inbound_Shared_ACF_BootStrap;
1
+ <?php
2
+
3
+ /**
4
+ * Class loads ACF4 if no other ACF version has been loaded prior
5
+ * @package ACF
6
+ */
7
+
8
+
9
+ class Inbound_Shared_ACF_BootStrap {
10
+
11
+ public function __construct() {
12
+ self::load_acf();
13
+ self::load_acf_extended_fields();
14
+ }
15
+
16
+ public static function load_acf() {
17
+
18
+ /* load ACF if not already loaded */
19
+ if( !class_exists('acf') || defined('ACF_PRELOADED') ) {
20
+
21
+ define( 'ACF_LITE', true );
22
+ define( 'ACF_FREE', true );
23
+
24
+ include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/acf.php');
25
+
26
+ /* customize ACF path */
27
+ add_filter('acf/settings/path', array( __CLASS__, 'define_acf_settings_path' ) );
28
+
29
+ /* customize ACF URL path */
30
+ add_filter('acf/settings/dir', array( __CLASS__, 'define_acf_settings_url' ) );
31
+
32
+ /* Hide ACF field group menu item */
33
+ add_filter('acf/settings/show_admin', '__return_false');
34
+
35
+
36
+ } else {
37
+ /* find out if ACF free or ACF Pro is installed & activated*/
38
+ include_once( ABSPATH . 'wp-admin/includes/plugin.php' );
39
+ if ( !function_exists('acf_add_local_field_group') ) {
40
+ define( 'ACF_FREE', true );
41
+ } else {
42
+ define( 'ACF_PRO', true );
43
+ add_filter('lp_init' , array(__CLASS__,'acf_register_global') , 20 , 1 ); /* registeres a global of registered field values for support between ACF5 & ACF6 */
44
+ }
45
+ }
46
+ }
47
+
48
+ /**
49
+ * Load extended/custom field support
50
+ */
51
+ public static function load_acf_extended_fields() {
52
+ if (!function_exists('register_fields_font_awesome')) {
53
+ include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields-font-awesome/acf-font-awesome.php');
54
+ include_once( INBOUNDNOW_SHARED_PATH . 'assets/plugins/acf-field-date-time-picker/acf-date-time-picker.php');
55
+ }
56
+ }
57
+
58
+ /**
59
+ * If ACF Pro is active then register a global for active fields - this provides legacy support to Landing Pages
60
+ */
61
+ public static function acf_register_global( $field_group ) {
62
+ $GLOBALS['acf_register_field_group'][] = array(
63
+ 'fields' => acf_local()->fields
64
+ );
65
+ }
66
+ /**
67
+ * define custom ACF path
68
+ * @param $path
69
+ * @return string
70
+ */
71
+ public static function define_acf_settings_path( $path ) {
72
+
73
+ $path = INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/';
74
+
75
+ return $path;
76
+
77
+ }
78
+
79
+ /**
80
+ * define custom settings URL
81
+ * @param $url
82
+ * @return string
83
+ */
84
+ public static function define_acf_settings_url( $url ) {
85
+
86
+ $url = INBOUNDNOW_SHARED_PATH . 'assets/plugins/advanced-custom-fields/';
87
+
88
+ return $url;
89
+ }
90
+
91
+ }
92
+
93
+
94
  new Inbound_Shared_ACF_BootStrap;
shared/classes/class.ajax.php CHANGED
@@ -1,165 +1,154 @@
1
- <?php
2
-
3
- /**
4
- * This class loads miscellaneous WordPress AJAX listeners
5
- */
6
- if (!class_exists('Inbound_Ajax')) {
7
-
8
- class Inbound_Ajax {
9
-
10
- /**
11
- * Initializes classs
12
- */
13
- public function __construct() {
14
- self::load_hooks();
15
- }
16
-
17
- /**
18
- * Loads hooks and filters
19
- */
20
- public static function load_hooks() {
21
-
22
- /* Ajax that runs on pageload */
23
- add_action('wp_ajax_nopriv_inbound_ajax', array(__CLASS__, 'run_ajax_actions'));
24
- add_action('wp_ajax_inbound_ajax', array(__CLASS__, 'run_ajax_actions'));
25
-
26
-
27
- /* Increases the page view statistics of lead on page load */
28
- add_action('wp_ajax_inbound_track_lead', array(__CLASS__, 'track_lead'));
29
- add_action('wp_ajax_nopriv_inbound_track_lead', array(__CLASS__, 'track_lead'));
30
-
31
- }
32
-
33
- /**
34
- * Executes hook that runs all ajax actions
35
- */
36
- public static function run_ajax_actions() {
37
-
38
- }
39
-
40
- /**
41
- * Listen for page view event
42
- */
43
- public static function track_lead() {
44
-
45
- global $wpdb;
46
-
47
- /* check for known bots and ignore */
48
- if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/bot|crawl|slurp|spider/i', $_SERVER['HTTP_USER_AGENT'])) {
49
- return;
50
- }
51
-
52
- $lead_data['lead_id'] = (isset($_POST['wp_lead_id'])) ? $_POST['wp_lead_id'] : '';
53
- $lead_data['nature'] = (isset($_POST['nature'])) ? $_POST['nature'] : 'non-conversion'; /* what is nature? */
54
- $lead_data['json'] = (isset($_POST['json'])) ? addslashes($_POST['json']) : 0;
55
- $lead_data['wp_lead_uid'] = (isset($_POST['wp_lead_uid'])) ? $_POST['wp_lead_uid'] : 0;
56
- $lead_data['page_id'] = (isset($_POST['page_id'])) ? $_POST['page_id'] : 0;
57
- $lead_data['current_url'] = (isset($_POST['current_url'])) ? $_POST['current_url'] : 'notfound';
58
- $lead_data['variation_id'] = (isset($_POST['variation_id'])) ? $_POST['variation_id'] : '0';
59
-
60
- $timezone_format = 'Y-m-d G:i:s T';
61
- $lead_data['datetime'] = date_i18n($timezone_format);
62
-
63
- $page_views = stripslashes($_POST['page_views']);
64
-
65
- $page_views = ($page_views) ? $page_views : '';
66
- $lead_data['event_details']['funnel'] = json_decode($page_views,true);
67
- $lead_data['funnel'] = $page_views;
68
-
69
- /* update funnel cookie */
70
- if (isset($_COOKIE['inbound_page_views']) && !$page_views) {
71
- $_SESSION['inbound_page_views'] = stripslashes($_COOKIE['inbound_page_views']);
72
- } else {
73
- $_SESSION['inbound_page_views'] = $page_views;
74
- }
75
-
76
- /* update lead data and set lead lists into cookies */
77
- if ($lead_data['lead_id']) {
78
- self::update_page_view_obj($lead_data);
79
- self::set_current_lists($lead_data['lead_id']);
80
- }
81
-
82
- /* create page_view event */
83
- if ($lead_data['page_id']) {
84
- Inbound_Events::store_page_view($lead_data);
85
- }
86
-
87
- /* record CTA impressions */
88
- $cta_impressions = ( isset($_POST['cta_impressions']) ) ? json_decode(stripslashes($_POST['cta_impressions']),true) : array();
89
-
90
- foreach ( $cta_impressions as $cta_id => $vid ) {
91
- do_action('wp_cta_record_impression', (int) $cta_id, (int) $vid );
92
- }
93
-
94
- /* update content data */
95
- do_action('lp_record_impression', $lead_data['page_id'], $_POST['post_type'], $_POST['variation_id']);
96
-
97
- die();
98
- }
99
-
100
- public static function update_page_view_obj($lead_data) {
101
-
102
- if (!$lead_data['page_id']) {
103
- return;
104
- }
105
-
106
- $current_page_view_count = get_post_meta($lead_data['lead_id'], 'wpleads_page_view_count', true);
107
- $increment_page_views = $current_page_view_count + 1;
108
-
109
- update_post_meta($lead_data['lead_id'], 'wpleads_page_view_count', $increment_page_views); // update count
110
-
111
- $time = current_time('timestamp', 0); // Current wordpress time from settings
112
- $wordpress_date_time = date("Y-m-d G:i:s T", $time);
113
- $page_view_data = get_post_meta($lead_data['lead_id'], 'page_views', TRUE);
114
-
115
-
116
- // If page_view meta exists do this
117
- if ($page_view_data) {
118
- $current_count = 0; // default
119
- $timeout = 30; // 30 Timeout analytics tracking for same page timestamps
120
- $page_view_data = json_decode($page_view_data, true);
121
- // increment view count on page
122
- if (isset($page_view_data[$lead_data['page_id']])) {
123
- $current_count = count($page_view_data[$lead_data['page_id']]);
124
- $last_view = $page_view_data[$lead_data['page_id']][$current_count];
125
- $timeout = abs(strtotime($last_view) - strtotime($wordpress_date_time));
126
- }
127
- // If page hasn't been viewed in past 30 seconds. Log it
128
- if ($timeout >= 30) {
129
- $page_view_data[$lead_data['page_id']][$current_count + 1] = $wordpress_date_time;
130
- $page_view_data = json_encode($page_view_data);
131
- update_post_meta($lead_data['lead_id'], 'page_views', $page_view_data);
132
- }
133
- } else {
134
- // Create page_view meta if it doesn't exist
135
- $page_view_data = array();
136
- $page_view_data[$lead_data['page_id']][0] = $wordpress_date_time;
137
- $page_view_data = json_encode($page_view_data);
138
- update_post_meta($lead_data['lead_id'], 'page_views', $page_view_data);
139
- }
140
- /* Run hook that tells WordPress lead data has been updated */
141
- do_action('wplead_page_view', $lead_data);
142
- }
143
-
144
- public static function set_current_lists($lead_id) {
145
- $terms = get_the_terms( $lead_id , 'wplead_list_category' );
146
-
147
- if ( $terms && ! is_wp_error( $terms ) ) {
148
- $lead_list = array();
149
- $count = 0;
150
-
151
- foreach ( $terms as $term ) {
152
- $lead_list[] = $term->term_id;
153
- $count++;
154
- }
155
-
156
- $list_array = json_encode(array('ids' => $lead_list));;
157
- setcookie('wp_lead_list', $list_array, time() + (20 * 365 * 24 * 60 * 60), '/');
158
- }
159
- }
160
-
161
- }
162
-
163
- /* Loads Inbound_Ajax pre init */
164
- $Inbound_Ajax = new Inbound_Ajax();
165
  }
1
+ <?php
2
+ /**
3
+ * Class provides ajax that tracks lead frontend movement
4
+ * @package Shared
5
+ * @subpackage Tracking
6
+ */
7
+ if (!class_exists('Inbound_Ajax')) {
8
+
9
+ class Inbound_Ajax {
10
+
11
+ /**
12
+ * Initializes classs
13
+ */
14
+ public function __construct() {
15
+ self::load_hooks();
16
+ }
17
+
18
+ /**
19
+ * Loads hooks and filters
20
+ */
21
+ public static function load_hooks() {
22
+
23
+ /* Increases the page view statistics of lead on page load */
24
+ add_action('wp_ajax_inbound_track_lead', array(__CLASS__, 'track_lead'));
25
+ add_action('wp_ajax_nopriv_inbound_track_lead', array(__CLASS__, 'track_lead'));
26
+
27
+ }
28
+
29
+ /**
30
+ * Listen for page view event
31
+ */
32
+ public static function track_lead() {
33
+
34
+ global $wpdb;
35
+
36
+ /* check for known bots and ignore */
37
+ if (isset($_SERVER['HTTP_USER_AGENT']) && preg_match('/bot|crawl|slurp|spider/i', $_SERVER['HTTP_USER_AGENT'])) {
38
+ return;
39
+ }
40
+
41
+ $lead_data['lead_id'] = (isset($_POST['wp_lead_id'])) ? $_POST['wp_lead_id'] : '';
42
+ $lead_data['nature'] = (isset($_POST['nature'])) ? $_POST['nature'] : 'non-conversion'; /* what is nature? */
43
+ $lead_data['json'] = (isset($_POST['json'])) ? addslashes($_POST['json']) : 0;
44
+ $lead_data['wp_lead_uid'] = (isset($_POST['wp_lead_uid'])) ? $_POST['wp_lead_uid'] : 0;
45
+ $lead_data['page_id'] = (isset($_POST['page_id'])) ? $_POST['page_id'] : 0;
46
+ $lead_data['current_url'] = (isset($_POST['current_url'])) ? $_POST['current_url'] : 'notfound';
47
+ $lead_data['variation_id'] = (isset($_POST['variation_id'])) ? $_POST['variation_id'] : '0';
48
+
49
+ $timezone_format = 'Y-m-d G:i:s T';
50
+ $lead_data['datetime'] = date_i18n($timezone_format);
51
+
52
+ $page_views = stripslashes($_POST['page_views']);
53
+
54
+ $page_views = ($page_views) ? $page_views : '';
55
+ $lead_data['event_details']['funnel'] = json_decode($page_views,true);
56
+ $lead_data['funnel'] = $page_views;
57
+
58
+ /* update funnel cookie */
59
+ if (isset($_COOKIE['inbound_page_views']) && !$page_views) {
60
+ $_SESSION['inbound_page_views'] = stripslashes($_COOKIE['inbound_page_views']);
61
+ } else {
62
+ $_SESSION['inbound_page_views'] = $page_views;
63
+ }
64
+
65
+ /* update lead data and set lead lists into cookies */
66
+ if ($lead_data['lead_id']) {
67
+ self::update_page_view_obj($lead_data);
68
+ self::set_current_lists($lead_data['lead_id']);
69
+ }
70
+
71
+ /* create page_view event */
72
+ if ($lead_data['page_id']) {
73
+ Inbound_Events::store_page_view($lead_data);
74
+ }
75
+
76
+ /* record CTA impressions */
77
+ $cta_impressions = ( isset($_POST['cta_impressions']) ) ? json_decode(stripslashes($_POST['cta_impressions']),true) : array();
78
+
79
+ foreach ( $cta_impressions as $cta_id => $vid ) {
80
+ do_action('wp_cta_record_impression', (int) $cta_id, (int) $vid );
81
+ }
82
+
83
+ /* update content data */
84
+ do_action('lp_record_impression', $lead_data['page_id'], $_POST['post_type'], $_POST['variation_id']);
85
+
86
+ die();
87
+ }
88
+
89
+ public static function update_page_view_obj($lead_data) {
90
+
91
+ if (!$lead_data['page_id']) {
92
+ return;
93
+ }
94
+
95
+ $current_page_view_count = get_post_meta($lead_data['lead_id'], 'wpleads_page_view_count', true);
96
+ $increment_page_views = $current_page_view_count + 1;
97
+
98
+ update_post_meta($lead_data['lead_id'], 'wpleads_page_view_count', $increment_page_views); // update count
99
+
100
+ $time = current_time('timestamp', 0); // Current wordpress time from settings
101
+ $wordpress_date_time = date("Y-m-d G:i:s T", $time);
102
+ $page_view_data = get_post_meta($lead_data['lead_id'], 'page_views', TRUE);
103
+
104
+
105
+ // If page_view meta exists do this
106
+ if ($page_view_data) {
107
+ $current_count = 0; // default
108
+ $timeout = 30; // 30 Timeout analytics tracking for same page timestamps
109
+ $page_view_data = json_decode($page_view_data, true);
110
+ // increment view count on page
111
+ if (isset($page_view_data[$lead_data['page_id']])) {
112
+ $current_count = count($page_view_data[$lead_data['page_id']]);
113
+ $last_view = $page_view_data[$lead_data['page_id']][$current_count];
114
+ $timeout = abs(strtotime($last_view) - strtotime($wordpress_date_time));
115
+ }
116
+ // If page hasn't been viewed in past 30 seconds. Log it
117
+ if ($timeout >= 30) {
118
+ $page_view_data[$lead_data['page_id']][$current_count + 1] = $wordpress_date_time;
119
+ $page_view_data = json_encode($page_view_data);
120
+ update_post_meta($lead_data['lead_id'], 'page_views', $page_view_data);
121
+ }
122
+ } else {
123
+ // Create page_view meta if it doesn't exist
124
+ $page_view_data = array();
125
+ $page_view_data[$lead_data['page_id']][0] = $wordpress_date_time;
126
+ $page_view_data = json_encode($page_view_data);
127
+ update_post_meta($lead_data['lead_id'], 'page_views', $page_view_data);
128
+ }
129
+ /* Run hook that tells WordPress lead data has been updated */
130
+ do_action('wplead_page_view', $lead_data);
131
+ }
132
+
133
+ public static function set_current_lists($lead_id) {
134
+ $terms = get_the_terms( $lead_id , 'wplead_list_category' );
135
+
136
+ if ( $terms && ! is_wp_error( $terms ) ) {
137
+ $lead_list = array();
138
+ $count = 0;
139
+
140
+ foreach ( $terms as $term ) {
141
+ $lead_list[] = $term->term_id;
142
+ $count++;
143
+ }
144
+
145
+ $list_array = json_encode(array('ids' => $lead_list));;
146
+ setcookie('wp_lead_list', $list_array, time() + (20 * 365 * 24 * 60 * 60), '/');
147
+ }
148
+ }
149
+
150
+ }
151
+
152
+ /* Loads Inbound_Ajax pre init */
153
+ $Inbound_Ajax = new Inbound_Ajax();
 
 
 
 
 
 
 
 
 
 
 
154
  }
shared/classes/class.confirm-double-optin.php CHANGED
@@ -1,194 +1,200 @@
1
- <?php
2
- if(!class_exists('Inbound_Confirm_Double_Optin')){
3
-
4
- class Inbound_Confirm_Double_Optin{
5
-
6
- /**
7
- * Initialize class
8
- */
9
- function __construct(){
10
- self::add_hooks();
11
- }
12
-
13
- /**
14
- * Load Hooks and Filters
15
- */
16
- public static function add_hooks(){
17
-
18
- /* Shortcode for displaying list double opt in confirmation form */
19
- add_action( 'init' , array( __CLASS__, 'process_confrimation' ), 20 );
20
-
21
- /* Process shortcode to produce the confirmation link,
22
- * the name is different from the one the user uses to prevent early rendering */
23
- add_shortcode( 'list-double-optin-link', array( __CLASS__, 'render_confirm_link' ) );
24
-
25
- }
26
-
27
-
28
- /**
29
- * @param $atts
30
- * @return string
31
- */
32
- public static function process_confrimation(){
33
- global $inbound_settings;
34
-
35
- if (!isset($_REQUEST['inbound-action']) || $_REQUEST['inbound-action'] != 'confirm' ) {
36
- return;
37
- }
38
-
39
- /* get all lead lists */
40
- $lead_lists = Inbound_Leads::get_lead_lists_as_array();
41
-
42
- /* decode token */
43
- $params = Inbound_API::get_args_from_token( sanitize_text_field($_GET['token'] ));
44
-
45
- if ( !isset( $params['lead_id'] ) ) {
46
- return;
47
- }
48
-
49
- self::confirm_being_added_to_lists($params);
50
- }
51
-
52
-
53
- /**
54
- * Creates the double optin confirmation link
55
- * The shorcode used by the user is: inbound-list-double-optin-link.
56
- * But Inbound_List_Double_Optin::add_confirm_link_shortcode_params trims the name to: list-double-optin-link.
57
- * Then it gets rendered.
58
- * The reason for this is so the shorcode isn't rendered until the atts have been added to it.
59
- */
60
- public static function render_confirm_link( $params ) {
61
-
62
- $params = shortcode_atts( array(
63
- 'lead_id' => '',
64
- 'list_ids' => '-1',
65
- 'email_id' => '-1'
66
- ), $params, 'list-double-optin-link');
67
- /* check to see if lead id is set as a REQUEST */
68
- if ( isset($params['lead_id']) ) {
69
- $params['lead_id'] = intval($params['lead_id']);
70
- } else if ( isset($_REQUEST['lead_id']) ) {
71
- $params['lead_id'] = intval($_REQUEST['lead_id']);
72
- } else if ( isset($_COOKIE['wp_lead_id']) ) {
73
- $params['lead_id'] = intval($_COOKIE['wp_lead_id']);
74
- }
75
- /* Add variation id to confirm link */
76
- $params['variation_id'] = ( isset($_REQUEST['inbvid']) ) ? intval($_REQUEST['inbvid']) : intval(0);
77
-
78
- /* generate confirm link */
79
- $confirm_link = self::generate_confirm_link( $params );
80
- return $confirm_link;
81
- }
82
-
83
-
84
-
85
- /**
86
- * Generates confirm url given lead id and lists
87
- * @param ARRAY $params contains: lead_id (INT ), list_ids (MIXED), email_id (INT)
88
- * @return STRING $confirm_url
89
- */
90
- public static function generate_confirm_link( $params ) {
91
- if (!isset($params['lead_id']) || !$params['lead_id']) {
92
- return __( '#confirm-not-available-in-online-mode' , 'inbound-pro' );
93
- }
94
- if (isset($_GET['lead_lists']) && !is_array($_GET['lead_lists'])){
95
- $params['list_ids'] = explode( ',' , $_GET['lead_lists']);
96
- } else if (isset($params['list_ids']) && !is_array($params['list_ids'])) {
97
- $params['list_ids'] = explode( ',' , $params['list_ids']);
98
- }
99
- $args = array_merge( $params , $_GET );
100
- $token = Inbound_API::analytics_get_tracking_code( $args );
101
-
102
- if(!defined('INBOUND_PRO_CURRENT_VERSION')){
103
- $double_optin_page_id = get_option('list-double-optin-page-id', '');
104
- }else{
105
- $settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
106
- $double_optin_page_id = $settings['leads']['list-double-optin-page-id'];
107
- }
108
-
109
- if ( empty($double_optin_page_id) ) {
110
- $post = get_page_by_title( __( 'Confirm Subscription' , 'inbound-pro' ) );
111
- $double_optin_page_id = $post->ID;
112
- }
113
-
114
- $base_url = get_permalink( $double_optin_page_id );
115
-
116
- return add_query_arg( array( 'token'=>$token , 'inbound-action' => 'confirm' ) , $base_url );
117
- }
118
-
119
-
120
-
121
- /**
122
- * Decodes confirm token into an array of parameters
123
- * @param STRING $reader_id Encoded lead id.
124
- * @return ARRAY $confirm array of confirmation data
125
- */
126
- public static function decode_confirm_token( $token ) {
127
- $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
128
- $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
129
- $decrypted_string =
130
- trim(
131
- mcrypt_decrypt(
132
- MCRYPT_RIJNDAEL_256 , substr( SECURE_AUTH_KEY , 0 , 16 ) , base64_decode( str_replace(array('-', '_', '^'), array('+', '/', '='), $token ) ) , MCRYPT_MODE_ECB, $iv
133
- )
134
- );
135
- return json_decode($decrypted_string , true);
136
- }
137
-
138
- /**
139
- * Adds the lead to lists he selected when filling out the confirmation form.
140
- * If all_lists was selected, all lists currently waiting for confirmation will be selected and the lead will be added to those.
141
- */
142
- public static function confirm_being_added_to_lists($params, $all = false){
143
-
144
- /*get the double optin waiting list id*/
145
- if(!defined('INBOUND_PRO_CURRENT_VERSION')){
146
- $double_optin_list_id = get_option('list-double-optin-list-id', '');
147
- }else{
148
- $settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
149
- $double_optin_list_id = $settings['leads']['list-double-optin-list-id'];
150
- }
151
-
152
-
153
- /*get the lists waiting to be opted into*/
154
- $stored_double_optin_lists = get_post_meta($params['lead_id'], 'double_optin_lists', true);
155
-
156
- /*if there aren't any lists, exit*/
157
- if(empty($stored_double_optin_lists)){
158
- return;
159
- }
160
-
161
- /*if opt into all lists has been selected, set list ids to all stored list ids*/
162
- if($all){
163
- $params['list_ids'] = $stored_double_optin_lists;
164
- }
165
-
166
- /**for each supplied list, add the lead to the list.
167
- * And remove the list id from the array of lists needing to be opted into**/
168
- foreach($params['list_ids'] as $list_id){
169
- Inbound_Leads::add_lead_to_list($params['lead_id'], $list_id);
170
-
171
- if(in_array($list_id, $stored_double_optin_lists)){
172
- $index = array_search($list_id, $stored_double_optin_lists);
173
- unset($stored_double_optin_lists[$index]);
174
- }
175
- }
176
-
177
- /**if there are still lists awaiting double optin confirmation after the "waiting" meta listing has been updated**/
178
- if(!empty($stored_double_optin_lists)){
179
- /*update the "waiting" meta listing with the remaining lists*/
180
- update_post_meta($params['lead_id'], 'double_optin_lists', array_values($stored_double_optin_lists));
181
- }else{
182
- /**if there are no lists awaiting double optin confirmation**/
183
- /*remove the meta listing for double optin*/
184
- delete_post_meta($params['lead_id'], 'double_optin_lists');
185
- /*remove this lead from the double optin list*/
186
- wp_remove_object_terms($params['lead_id'], $double_optin_list_id, 'wplead_list_category');
187
- /*update the lead status*/
188
- update_post_meta( $params['lead_id'], 'wp_lead_status', 'active');
189
- }
190
- }
191
- }
192
- new Inbound_Confirm_Double_Optin;
193
-
194
- }
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class provides shortcodes, listeners, and processing methods for double optin confirmation
5
+ * @package Shared
6
+ * @subpackage DoubleOptin
7
+ */
8
+ if(!class_exists('Inbound_Confirm_Double_Optin')){
9
+
10
+ class Inbound_Confirm_Double_Optin{
11
+
12
+ /**
13
+ * Initialize class
14
+ */
15
+ function __construct(){
16
+ self::add_hooks();
17
+ }
18
+
19
+ /**
20
+ * Load Hooks and Filters
21
+ */
22
+ public static function add_hooks(){
23
+
24
+ /* Shortcode for displaying list double opt in confirmation form */
25
+ add_action( 'init' , array( __CLASS__, 'process_confrimation' ), 20 );
26
+
27
+ /* Process shortcode to produce the confirmation link,
28
+ * the name is different from the one the user uses to prevent early rendering */
29
+ add_shortcode( 'list-double-optin-link', array( __CLASS__, 'render_confirm_link' ) );
30
+
31
+ }
32
+
33
+
34
+ /**
35
+ * @param $atts
36
+ * @return string
37
+ */
38
+ public static function process_confrimation(){
39
+ global $inbound_settings;
40
+
41
+ if (!isset($_REQUEST['inbound-action']) || $_REQUEST['inbound-action'] != 'confirm' ) {
42
+ return;
43
+ }
44
+
45
+ /* get all lead lists */
46
+ $lead_lists = Inbound_Leads::get_lead_lists_as_array();
47
+
48
+ /* decode token */
49
+ $params = Inbound_API::get_args_from_token( sanitize_text_field($_GET['token'] ));
50
+
51
+ if ( !isset( $params['lead_id'] ) ) {
52
+ return;
53
+ }
54
+
55
+ self::confirm_being_added_to_lists($params);
56
+ }
57
+
58
+
59
+ /**
60
+ * Creates the double optin confirmation link
61
+ * The shorcode used by the user is: inbound-list-double-optin-link.
62
+ * But Inbound_List_Double_Optin::add_confirm_link_shortcode_params trims the name to: list-double-optin-link.
63
+ * Then it gets rendered.
64
+ * The reason for this is so the shorcode isn't rendered until the atts have been added to it.
65
+ */
66
+ public static function render_confirm_link( $params ) {
67
+
68
+ $params = shortcode_atts( array(
69
+ 'lead_id' => '',
70
+ 'list_ids' => '-1',
71
+ 'email_id' => '-1'
72
+ ), $params, 'list-double-optin-link');
73
+ /* check to see if lead id is set as a REQUEST */
74
+ if ( isset($params['lead_id']) ) {
75
+ $params['lead_id'] = intval($params['lead_id']);
76
+ } else if ( isset($_REQUEST['lead_id']) ) {
77
+ $params['lead_id'] = intval($_REQUEST['lead_id']);
78
+ } else if ( isset($_COOKIE['wp_lead_id']) ) {
79
+ $params['lead_id'] = intval($_COOKIE['wp_lead_id']);
80
+ }
81
+ /* Add variation id to confirm link */
82
+ $params['variation_id'] = ( isset($_REQUEST['inbvid']) ) ? intval($_REQUEST['inbvid']) : intval(0);
83
+
84
+ /* generate confirm link */
85
+ $confirm_link = self::generate_confirm_link( $params );
86
+ return $confirm_link;
87
+ }
88
+
89
+
90
+
91
+ /**
92
+ * Generates confirm url given lead id and lists
93
+ * @param ARRAY $params contains: lead_id (INT ), list_ids (MIXED), email_id (INT)
94
+ * @return STRING $confirm_url
95
+ */
96
+ public static function generate_confirm_link( $params ) {
97
+ if (!isset($params['lead_id']) || !$params['lead_id']) {
98
+ return __( '#confirm-not-available-in-online-mode' , 'inbound-pro' );
99
+ }
100
+ if (isset($_GET['lead_lists']) && !is_array($_GET['lead_lists'])){
101
+ $params['list_ids'] = explode( ',' , $_GET['lead_lists']);
102
+ } else if (isset($params['list_ids']) && !is_array($params['list_ids'])) {
103
+ $params['list_ids'] = explode( ',' , $params['list_ids']);
104
+ }
105
+ $args = array_merge( $params , $_GET );
106
+ $token = Inbound_API::analytics_get_tracking_code( $args );
107
+
108
+ if(!defined('INBOUND_PRO_CURRENT_VERSION')){
109
+ $double_optin_page_id = get_option('list-double-optin-page-id', '');
110
+ }else{
111
+ $settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
112
+ $double_optin_page_id = $settings['leads']['list-double-optin-page-id'];
113
+ }
114
+
115
+ if ( empty($double_optin_page_id) ) {
116
+ $post = get_page_by_title( __( 'Confirm Subscription' , 'inbound-pro' ) );
117
+ $double_optin_page_id = $post->ID;
118
+ }
119
+
120
+ $base_url = get_permalink( $double_optin_page_id );
121
+
122
+ return add_query_arg( array( 'token'=>$token , 'inbound-action' => 'confirm' ) , $base_url );
123
+ }
124
+
125
+
126
+
127
+ /**
128
+ * Decodes confirm token into an array of parameters
129
+ * @param STRING $reader_id Encoded lead id.
130
+ * @return ARRAY $confirm array of confirmation data
131
+ */
132
+ public static function decode_confirm_token( $token ) {
133
+ $iv_size = mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB);
134
+ $iv = mcrypt_create_iv($iv_size, MCRYPT_RAND);
135
+ $decrypted_string =
136
+ trim(
137
+ mcrypt_decrypt(
138
+ MCRYPT_RIJNDAEL_256 , substr( SECURE_AUTH_KEY , 0 , 16 ) , base64_decode( str_replace(array('-', '_', '^'), array('+', '/', '='), $token ) ) , MCRYPT_MODE_ECB, $iv
139
+ )
140
+ );
141
+ return json_decode($decrypted_string , true);
142
+ }
143
+
144
+ /**
145
+ * Adds the lead to lists he selected when filling out the confirmation form.
146
+ * If all_lists was selected, all lists currently waiting for confirmation will be selected and the lead will be added to those.
147
+ */
148
+ public static function confirm_being_added_to_lists($params, $all = false){
149
+
150
+ /*get the double optin waiting list id*/
151
+ if(!defined('INBOUND_PRO_CURRENT_VERSION')){
152
+ $double_optin_list_id = get_option('list-double-optin-list-id', '');
153
+ }else{
154
+ $settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
155
+ $double_optin_list_id = $settings['leads']['list-double-optin-list-id'];
156
+ }
157
+
158
+
159
+ /*get the lists waiting to be opted into*/
160
+ $stored_double_optin_lists = get_post_meta($params['lead_id'], 'double_optin_lists', true);
161
+
162
+ /*if there aren't any lists, exit*/
163
+ if(empty($stored_double_optin_lists)){
164
+ return;
165
+ }
166
+
167
+ /*if opt into all lists has been selected, set list ids to all stored list ids*/
168
+ if($all){
169
+ $params['list_ids'] = $stored_double_optin_lists;
170
+ }
171
+
172
+ /**for each supplied list, add the lead to the list.
173
+ * And remove the list id from the array of lists needing to be opted into**/
174
+ foreach($params['list_ids'] as $list_id){
175
+ Inbound_Leads::add_lead_to_list($params['lead_id'], $list_id);
176
+
177
+ if(in_array($list_id, $stored_double_optin_lists)){
178
+ $index = array_search($list_id, $stored_double_optin_lists);
179
+ unset($stored_double_optin_lists[$index]);
180
+ }
181
+ }
182
+
183
+ /**if there are still lists awaiting double optin confirmation after the "waiting" meta listing has been updated**/
184
+ if(!empty($stored_double_optin_lists)){
185
+ /*update the "waiting" meta listing with the remaining lists*/
186
+ update_post_meta($params['lead_id'], 'double_optin_lists', array_values($stored_double_optin_lists));
187
+ }else{
188
+ /**if there are no lists awaiting double optin confirmation**/
189
+ /*remove the meta listing for double optin*/
190
+ delete_post_meta($params['lead_id'], 'double_optin_lists');
191
+ /*remove this lead from the double optin list*/
192
+ wp_remove_object_terms($params['lead_id'], $double_optin_list_id, 'wplead_list_category');
193
+ /*update the lead status*/
194
+ update_post_meta( $params['lead_id'], 'wp_lead_status', 'active');
195
+ }
196
+ }
197
+ }
198
+ new Inbound_Confirm_Double_Optin;
199
+
200
+ }
shared/classes/class.database-routines.php CHANGED
@@ -1,312 +1,314 @@
1
- <?php
2
-
3
- /* Public methods in this class will be run at least once during plugin activation script. */
4
- /* Updater methods fired are stored in transient to prevent repeat processing */
5
-
6
- if ( !class_exists('Inbound_Upgrade_Routines') ) {
7
-
8
- class Inbound_Upgrade_Routines {
9
- static $routines;
10
- static $past_version;
11
- static $current_version;
12
-
13
- /**
14
- * Run upgrade routines defined in this class
15
- */
16
- public static function load() {
17
- self::define_routines();
18
- self::load_routines();
19
- }
20
-
21
- /**
22
- * Add fallback listener to make sure database upgrade routines are ran even if the activation protocol does not fire.
23
- * This function compares the shared files version in the database to the INBOUNDNOW_SHARED_DBRV constant. If they aren't the same then it runs the routines.
24
- *
25
- */
26
- public static function add_update_check() {
27
- self::set_versions( array('scope'=>'shared') );
28
- if ( self::$past_version != self::$current_version ) {
29
- self::load();
30
- }
31
- }
32
-
33
- /**
34
- * Defines upgrade routines to run.
35
- */
36
- public static function define_routines() {
37
-
38
- /* alter page view table */
39
- self::$routines['page-views-table-1'] = array(
40
- 'id' => 'page-views-table-1',
41
- 'scope' => 'shared',
42
- 'introduced' => '1.0.2',
43
- 'callback' => array( __CLASS__ , 'alter_page_views_table_1')
44
- );
45
-
46
- /* alter page view table */
47
- self::$routines['events-table-1'] = array(
48
- 'id' => 'events-table-1',
49
- 'scope' => 'shared',
50
- 'introduced' => '1.0.2',
51
- 'callback' => array( __CLASS__ , 'alter_events_table_1')
52
- );
53
-
54
- /* alter events table */
55
- self::$routines['events-table-2'] = array(
56
- 'id' => 'events-table-2',
57
- 'scope' => 'shared',
58
- 'introduced' => '1.0.5',
59
- 'callback' => array( __CLASS__ , 'alter_events_table_1_0_5')
60
- );
61
-
62
- /* alter events table */
63
- self::$routines['events-table-3'] = array(
64
- 'id' => 'events-table-3',
65
- 'scope' => 'shared',
66
- 'introduced' => '1.0.8',
67
- 'callback' => array( __CLASS__ , 'alter_events_table_1_0_8')
68
- );
69
-
70
- /* alter automation queue table */
71
- self::$routines['automation-queue-table-1'] = array(
72
- 'id' => 'automation-queue-table-1',
73
- 'scope' => 'shared',
74
- 'introduced' => '1.0.3',
75
- 'callback' => array( __CLASS__ , 'alter_automation_queue_table_1')
76
- );
77
-
78
-
79
- /* alter events table */
80
- self::$routines['events-pageviews-107'] = array(
81
- 'id' => 'events-pageviews-107',
82
- 'scope' => 'shared',
83
- 'introduced' => '1.0.7',
84
- 'callback' => array( __CLASS__ , 'alter_events_pageviews_107')
85
- );
86
-
87
- /* alter events table */
88
- self::$routines['inbound-settings-109'] = array(
89
- 'id' => 'inbound-settings-109',
90
- 'scope' => 'shared',
91
- 'introduced' => '1.0.9',
92
- 'callback' => array( __CLASS__ , 'alter_inbound_settings_109')
93
- );
94
- }
95
-
96
- /**
97
- *
98
- */
99
- public static function load_routines() {
100
-
101
- self::$routines = apply_filters( 'inbound-pro/upgrade-routines' , self::$routines);
102
-
103
- foreach (self::$routines as $routine) {
104
- /* set versions int static vars */
105
- self::set_versions($routine);
106
-
107
- /* compare versions and see last installed version is beneath the introduced version */
108
- if (
109
- self::$past_version
110
- &&
111
- !version_compare( self::$past_version , $routine['introduced'] , '<')
112
- &&
113
- !isset($_GET['force_upgrade_routines'])
114
- ) {
115
- continue;
116
- }
117
-
118
- /* run the routine */
119
- call_user_func(array($routine['callback'][0] , $routine['callback'][1]) );
120
- }
121
-
122
- /* set shared version transient */
123
- update_option('inbound_shared_version' , INBOUNDNOW_SHARED_DBRV , false);
124
- }
125
-
126
-
127
- /**
128
- * @param $routine
129
- */
130
- public static function set_versions( $routine ) {
131
- switch($routine['scope']) {
132
- case 'shared':
133
- self::$past_version = get_option('inbound_shared_version');
134
- self::$current_version = INBOUNDNOW_SHARED_DBRV;
135
- break;
136
- case 'leads':
137
- self::$past_version = get_transient('leads_shared_version');
138
- self::$current_version = WPL_CURRENT_VERSION;
139
- break;
140
- case 'landing-pages':
141
- self::$past_version = get_transient('lp_current_version');
142
- self::$current_version = LANDINGPAGES_CURRENT_VERSION;
143
- break;
144
- case 'cta':
145
- self::$past_version = get_transient('cta_current_version');
146
- self::$current_version = WP_CTA_CURRENT_VERSION;
147
- break;
148
- }
149
- }
150
-
151
- /**
152
- * Alter pageview table from INT to VARCHARR
153
- * @param $routines
154
- */
155
- public static function alter_page_views_table_1() {
156
- global $wpdb;
157
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
158
-
159
- $table_name = $wpdb->prefix . "inbound_page_views";
160
-
161
- /* add ip field if does not exist */
162
-
163
- $col_check = $wpdb->get_row("SELECT * FROM " . $table_name);
164
-
165
- if(!isset($col_check->ip)) {
166
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `ip` VARCHAR(45) NOT NULL");
167
- } else {
168
- $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `ip` VARCHAR(45)" );
169
- }
170
-
171
- }
172
-
173
- /**
174
- * @migration-type: alter inbound_events table
175
- * @mirgration: adds columns list_id funnel, and source to events table
176
- */
177
- public static function alter_events_table_1() {
178
-
179
- global $wpdb;
180
-
181
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
182
- $table_name = $wpdb->prefix . "inbound_events";
183
-
184
- $col_check = $wpdb->get_row("SELECT * FROM " . $table_name);
185
-
186
- if(!isset($col_check->funnel)) {
187
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `funnel` text NOT NULL");
188
- }
189
-
190
- if(!isset($col_check->source)) {
191
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `source` text NOT NULL");
192
- }
193
-
194
- if(!isset($col_check->list_id)) {
195
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `list_id` mediumint(20) NOT NULL");
196
- }
197
-
198
- }
199
-
200
- /**
201
- * @migration-type: alter inbound_events table
202
- * @mirgration: adds columns list_id funnel, and source to events table
203
- */
204
- public static function alter_events_table_1_0_5() {
205
-
206
- global $wpdb;
207
-
208
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
209
- $table_name = $wpdb->prefix . "inbound_events";
210
-
211
- $col_check = $wpdb->get_row("SELECT * FROM " . $table_name);
212
-
213
- if(!isset($col_check->rule_id)) {
214
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `rule_id` mediumint(20) NOT NULL");
215
- }
216
-
217
- if(!isset($col_check->job_id)) {
218
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `job_id` mediumint(20) NOT NULL");
219
- }
220
- }
221
-
222
-
223
- /**
224
- * @migration-type: alter inbound_events table
225
- * @mirgration: adds columns list_id funnel, and source to events table
226
- */
227
- public static function alter_events_table_1_0_8() {
228
-
229
- global $wpdb;
230
-
231
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
232
- $table_name = $wpdb->prefix . "inbound_events";
233
-
234
- $col_check = $wpdb->get_row("SELECT * FROM " . $table_name);
235
-
236
- if(!isset($col_check->comment_id)) {
237
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `comment_id` mediumint(20) NOT NULL");
238
- }
239
- }
240
-
241
- /**
242
- * @migration-type: alter inbound_events,inbound_pageviews table
243
- * @mirgration: convert page_id to VARCHAR to accept complex ids related to taxonomy archives
244
- */
245
- public static function alter_events_pageviews_107() {
246
-
247
- global $wpdb;
248
-
249
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
250
-
251
- /* events table */
252
- $table_name = $wpdb->prefix . "inbound_events";
253
- $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `page_id` VARCHAR(20)" );
254
-
255
- /* pageviews table */
256
- $table_name = $wpdb->prefix . "inbound_page_views";
257
- $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `page_id` VARCHAR(20)" );
258
- }
259
-
260
-
261
- /**
262
- * @migration-type: alter inbound_automation_queue table
263
- * @mirgration: adds columns lead_id
264
- */
265
- public static function alter_automation_queue_table_1() {
266
-
267
- global $wpdb;
268
-
269
- require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
270
- $table_name = $wpdb->prefix . "inbound_automation_queue";
271
-
272
- $col_check = $wpdb->get_row("SELECT * FROM " . $table_name);
273
-
274
- if(!isset($col_check->lead_id)) {
275
- $wpdb->get_results("ALTER TABLE {$table_name} ADD `lead_id` mediumint(20) NOT NULL");
276
- }
277
- }
278
-
279
-
280
- /**
281
- * @migration-type: inbound pro settings array
282
- * @mirgration: change the 'mailer' key to 'mailer' key in $inbound_settings
283
- */
284
- public static function alter_inbound_settings_109() {
285
- if (class_exists('Inbound_Options_API')) {
286
- $inbound_settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
287
- $inbound_settings['mailer'] = (isset($inbound_settings['mailer'])) ? $inbound_settings['mailer'] : array();
288
- unset($inbound_settings['inbound-mailer']);
289
- Inbound_Options_API::update_option('inbound-pro', 'settings', $inbound_settings);
290
- }
291
- }
292
- }
293
-
294
- /* set fallback action in case routines do not run via activation */
295
- add_action('admin_init' , array( 'Inbound_Upgrade_Routines' , 'add_update_check') );
296
-
297
-
298
- /**
299
- * Listen for Database Repair Call
300
- */
301
- if (isset($_REQUEST['force_upgrade_routines']) && $_REQUEST['force_upgrade_routines'] ) {
302
- Inbound_Events::create_page_views_table();
303
- Inbound_Events::create_events_table();
304
- if (class_exists('Inbound_Automation_Activation')) {
305
- Inbound_Automation_Activation::create_automation_queue_table();
306
- }
307
- if (class_exists('Inbound_Mailer_Activation')) {
308
- Inbound_Mailer_Activation::create_email_queue_table();
309
- }
310
- Inbound_Upgrade_Routines::load();
311
- }
312
- }
1
+ <?php
2
+
3
+ /**
4
+ * Class for defining and loading shared database routines
5
+ * @package Shared
6
+ * @subpackage DatabaseRoutines
7
+ */
8
+ if ( !class_exists('Inbound_Upgrade_Routines') ) {
9
+
10
+ class Inbound_Upgrade_Routines {
11
+ static $routines;
12
+ static $past_version;
13
+ static $current_version;
14
+
15
+ /**
16
+ * Run upgrade routines defined in this class
17
+ */
18
+ public static function load() {
19
+ self::define_routines();
20
+ self::load_routines();
21
+ }
22
+
23
+ /**
24
+ * Add fallback listener to make sure database upgrade routines are ran even if the activation protocol does not fire.
25
+ * This function compares the shared files version in the database to the INBOUNDNOW_SHARED_DBRV constant. If they aren't the same then it runs the routines.
26
+ *
27
+ */
28
+ public static function add_update_check() {
29
+ self::set_versions( array('scope'=>'shared') );
30
+ if ( self::$past_version != self::$current_version ) {
31
+ self::load();
32
+ }
33
+ }
34
+
35
+ /**
36
+ * Defines upgrade routines to run.
37
+ */
38
+ public static function define_routines() {
39
+
40
+ /* alter page view table */
41
+ self::$routines['page-views-table-1'] = array(
42
+ 'id' => 'page-views-table-1',
43
+ 'scope' => 'shared',
44
+ 'introduced' => '1.0.2',
45
+ 'callback' => array( __CLASS__ , 'alter_page_views_table_1')
46
+ );
47
+
48
+ /* alter page view table */
49
+ self::$routines['events-table-1'] = array(
50
+ 'id' => 'events-table-1',
51
+ 'scope' => 'shared',
52
+ 'introduced' => '1.0.2',
53
+ 'callback' => array( __CLASS__ , 'alter_events_table_1')
54
+ );
55
+
56
+ /* alter events table */
57
+ self::$routines['events-table-2'] = array(
58
+ 'id' => 'events-table-2',
59
+ 'scope' => 'shared',
60
+ 'introduced' => '1.0.5',
61
+ 'callback' => array( __CLASS__ , 'alter_events_table_1_0_5')
62
+ );
63
+
64
+ /* alter events table */
65
+ self::$routines['events-table-3'] = array(
66
+ 'id' => 'events-table-3',
67
+ 'scope' => 'shared',
68
+ 'introduced' => '1.0.8',
69
+ 'callback' => array( __CLASS__ , 'alter_events_table_1_0_8')
70
+ );
71
+
72
+ /* alter automation queue table */
73
+ self::$routines['automation-queue-table-1'] = array(
74
+ 'id' => 'automation-queue-table-1',
75
+ 'scope' => 'shared',
76
+ 'introduced' => '1.0.3',
77
+ 'callback' => array( __CLASS__ , 'alter_automation_queue_table_1')
78
+ );
79
+
80
+
81
+ /* alter events table */
82
+ self::$routines['events-pageviews-107'] = array(
83
+ 'id' => 'events-pageviews-107',
84
+ 'scope' => 'shared',
85
+ 'introduced' => '1.0.7',
86
+ 'callback' => array( __CLASS__ , 'alter_events_pageviews_107')
87
+ );
88
+
89
+ /* alter events table */
90
+ self::$routines['inbound-settings-109'] = array(
91
+ 'id' => 'inbound-settings-109',
92
+ 'scope' => 'shared',
93
+ 'introduced' => '1.0.9',
94
+ 'callback' => array( __CLASS__ , 'alter_inbound_settings_109')
95
+ );
96
+ }
97
+
98
+ /**
99
+ *
100
+ */
101
+ public static function load_routines() {
102
+
103
+ self::$routines = apply_filters( 'inbound-pro/upgrade-routines' , self::$routines);
104
+
105
+ foreach (self::$routines as $routine) {
106
+ /* set versions int static vars */
107
+ self::set_versions($routine);
108
+
109
+ /* compare versions and see last installed version is beneath the introduced version */
110
+ if (
111
+ self::$past_version
112
+ &&
113
+ !version_compare( self::$past_version , $routine['introduced'] , '<')
114
+ &&
115
+ !isset($_GET['force_upgrade_routines'])
116
+ ) {
117
+ continue;
118
+ }
119
+
120
+ /* run the routine */
121
+ call_user_func(array($routine['callback'][0] , $routine['callback'][1]) );
122
+ }
123
+
124
+ /* set shared version transient */
125
+ update_option('inbound_shared_version' , INBOUNDNOW_SHARED_DBRV , false);
126
+ }
127
+
128
+
129
+ /**
130
+ * @param $routine
131
+ */
132
+ public static function set_versions( $routine ) {
133
+ switch($routine['scope']) {
134
+ case 'shared':
135
+ self::$past_version = get_option('inbound_shared_version');
136
+ self::$current_version = INBOUNDNOW_SHARED_DBRV;
137
+ break;
138
+ case 'leads':
139
+ self::$past_version = get_transient('leads_shared_version');
140
+ self::$current_version = WPL_CURRENT_VERSION;
141
+ break;
142
+ case 'landing-pages':
143
+ self::$past_version = get_transient('lp_current_version');
144
+ self::$current_version = LANDINGPAGES_CURRENT_VERSION;
145
+ break;
146
+ case 'cta':
147
+ self::$past_version = get_transient('cta_current_version');
148
+ self::$current_version = WP_CTA_CURRENT_VERSION;
149
+ break;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Alter pageview table from INT to VARCHARR
155
+ * @param $routines
156
+ */
157
+ public static function alter_page_views_table_1() {
158
+ global $wpdb;
159
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
160
+
161
+ $table_name = $wpdb->prefix . "inbound_page_views";
162
+
163
+ /* add ip field if does not exist */
164
+
165
+ $col_check = $wpdb->get_row("SELECT * FROM " . $table_name ." limit 1");
166
+
167
+ if(!isset($col_check->ip)) {
168
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `ip` VARCHAR(45) NOT NULL");
169
+ } else {
170
+ $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `ip` VARCHAR(45)" );
171
+ }
172
+
173
+ }
174
+
175
+ /**
176
+ * @migration-type: alter inbound_events table
177
+ * @mirgration: adds columns list_id funnel, and source to events table
178
+ */
179
+ public static function alter_events_table_1() {
180
+
181
+ global $wpdb;
182
+
183
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
184
+ $table_name = $wpdb->prefix . "inbound_events";
185
+
186
+ $col_check = $wpdb->get_row("SELECT * FROM " . $table_name ." limit 1");
187
+
188
+ if(!isset($col_check->funnel)) {
189
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `funnel` text NOT NULL");
190
+ }
191
+
192
+ if(!isset($col_check->source)) {
193
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `source` text NOT NULL");
194
+ }
195
+
196
+ if(!isset($col_check->list_id)) {
197
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `list_id` mediumint(20) NOT NULL");
198
+ }
199
+
200
+ }
201
+
202
+ /**
203
+ * @migration-type: alter inbound_events table
204
+ * @mirgration: adds columns list_id funnel, and source to events table
205
+ */
206
+ public static function alter_events_table_1_0_5() {
207
+
208
+ global $wpdb;
209
+
210
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
211
+ $table_name = $wpdb->prefix . "inbound_events";
212
+
213
+ $col_check = $wpdb->get_row("SELECT * FROM " . $table_name ." limit 1");
214
+
215
+ if(!isset($col_check->rule_id)) {
216
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `rule_id` mediumint(20) NOT NULL");
217
+ }
218
+
219
+ if(!isset($col_check->job_id)) {
220
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `job_id` mediumint(20) NOT NULL");
221
+ }
222
+ }
223
+
224
+
225
+ /**
226
+ * @migration-type: alter inbound_events table
227
+ * @mirgration: adds columns list_id funnel, and source to events table
228
+ */
229
+ public static function alter_events_table_1_0_8() {
230
+
231
+ global $wpdb;
232
+
233
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
234
+ $table_name = $wpdb->prefix . "inbound_events";
235
+
236
+ $col_check = $wpdb->get_row("SELECT * FROM " . $table_name ." limit 1");
237
+
238
+ if(!isset($col_check->comment_id)) {
239
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `comment_id` mediumint(20) NOT NULL");
240
+ }
241
+ }
242
+
243
+ /**
244
+ * @migration-type: alter inbound_events,inbound_pageviews table
245
+ * @mirgration: convert page_id to VARCHAR to accept complex ids related to taxonomy archives
246
+ */
247
+ public static function alter_events_pageviews_107() {
248
+
249
+ global $wpdb;
250
+
251
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
252
+
253
+ /* events table */
254
+ $table_name = $wpdb->prefix . "inbound_events";
255
+ $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `page_id` VARCHAR(20)" );
256
+
257
+ /* pageviews table */
258
+ $table_name = $wpdb->prefix . "inbound_page_views";
259
+ $wpdb->get_results( "ALTER TABLE {$table_name} MODIFY COLUMN `page_id` VARCHAR(20)" );
260
+ }
261
+
262
+
263
+ /**
264
+ * @migration-type: alter inbound_automation_queue table
265
+ * @mirgration: adds columns lead_id
266
+ */
267
+ public static function alter_automation_queue_table_1() {
268
+
269
+ global $wpdb;
270
+
271
+ require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
272
+ $table_name = $wpdb->prefix . "inbound_automation_queue";
273
+
274
+ $col_check = $wpdb->get_row("SELECT * FROM " . $table_name ." limit 1");
275
+
276
+ if(!isset($col_check->lead_id)) {
277
+ $wpdb->get_results("ALTER TABLE {$table_name} ADD `lead_id` mediumint(20) NOT NULL");
278
+ }
279
+ }
280
+
281
+
282
+ /**
283
+ * @migration-type: inbound pro settings array
284
+ * @mirgration: change the 'mailer' key to 'mailer' key in $inbound_settings
285
+ */
286
+ public static function alter_inbound_settings_109() {
287
+ if (class_exists('Inbound_Options_API')) {
288
+ $inbound_settings = Inbound_Options_API::get_option('inbound-pro', 'settings', array());
289
+ $inbound_settings['mailer'] = (isset($inbound_settings['inbound-mailer'])) ? $inbound_settings['inbound-mailer'] : array();
290
+ unset($inbound_settings['inbound-mailer']);
291
+ Inbound_Options_API::update_option('inbound-pro', 'settings', $inbound_settings);
292
+ }
293
+ }
294
+ }
295
+
296
+ /* set fallback action in case routines do not run via activation */
297
+ add_action('admin_init' , array( 'Inbound_Upgrade_Routines' , 'add_update_check') );
298
+
299
+
300
+ /**
301
+ * Listen for Database Repair Call
302
+ */
303
+ if (isset($_REQUEST['force_upgrade_routines']) && $_REQUEST['force_upgrade_routines'] ) {
304
+ Inbound_Events::create_page_views_table();
305
+ Inbound_Events::create_events_table();
306
+ if (class_exists('Inbound_Automation_Activation')) {
307
+ Inbound_Automation_Activation::create_automation_queue_table();
308
+ }
309
+ if (class_exists('Inbound_Mailer_Activation')) {
310
+ Inbound_Mailer_Activation::create_email_queue_table();
311
+ }
312
+ Inbound_Upgrade_Routines: