Custom Twitter Feeds - Version 1.5

Version Description

  • New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a Twitter feed to posts and pages.
  • Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
Download this release

Release Info

Developer smashballoon
Plugin Icon 128x128 Custom Twitter Feeds
Version 1.5
Comparing to
See all releases

Code changes from version 1.4.1 to 1.5

README.txt CHANGED
@@ -2,24 +2,24 @@
2
  Author: Smash Balloon
3
  Contributors: smashballoon, craig-at-smash-balloon
4
  Support Website: http://smashballoon/custom-twitter-feeds/
5
- Tags: Twitter, Twitter feed, Tweets, Twitter widget, Custom Twitter Feed
6
  Requires at least: 3.4
7
  Tested up to: 5.4
8
- Stable tag: 1.4.1
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
12
- Custom Twitter Feeds allows you to display completely customizable Twitter feeds of a user timeline, home timeline, or hashtag on your website.
13
 
14
  == Description ==
15
- Display **completely customizable**, **responsive** and **search engine crawlable** versions of your Twitter feed on your website. Completely match the look and feel of your site with tons of customization options!
16
 
17
  = Features =
18
- * **Completely Customizable** - by default inherits your theme's styles
19
  * Twitter feed content is **crawlable by search engines** adding SEO value to your site
20
- * **Completely responsive and mobile optimized** - works on any screen size
21
  * Display tweets from any user, your own Twitter account and those you follow, or from a specific hashtag
22
- * Display **multiple feeds** from different Twitter users on multiple pages or widgets
23
  * Post caching means that your Twitter feed loads **lightning fast** and minimizes Twitter API requests
24
  * **Infinitely load more** of your Tweets with the 'Load More' button
25
  * Built-in easy to use "Custom Twitter Feeds" Widget
@@ -36,7 +36,7 @@ For simple step-by-step directions on how to set up the Custom Twitter Feeds plu
36
  * **Improve your SEO** as all of that quality keyword-rich Twitter content is directly embedded into your website
37
  * **Keep users on your site** by letting them reply and like your Tweets directly on your site
38
  * Display your Twitter content **your way** to perfectly match your website's style
39
- * The plugin is **updated regularly** with new features, bug-fixes and Twitter API changes
40
  * Support is quick and effective
41
  * We're dedicated to providing the **most customizable**, **robust** and **well supported** Twitter feed plugin in the world!
42
 
@@ -48,17 +48,17 @@ We're dedicated to providing the most robust and well supported Twitter feed plu
48
  If you like the plugin then please consider leaving a review, as it really helps to support the plugin. If you have an issue then please allow us to help you fix it before leaving a review. Just [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support') what the problem is and we'll get back to you right away.
49
 
50
  = Featured Reviews =
51
- "***Great for Customization** - Great plugin! Superb customization options! Am changing all twitter feeds for all my websites to this! I have tried several plugins so far. This is the best I have come across :)*" - [@nehavivekpatil](https://wordpress.org/support/topic/great-for-customization-2/)
52
 
53
  "***Excellent plug-in. Excellent support.** - Excellent instructions and guidance. Patient and prompt support for the newbies. Regular updates. You can't lose. Thank you Smash Balloon.* - [@nuspa](https://wordpress.org/support/topic/excellent-plug-in-excellent-support-2/)
54
 
55
- "***Amazing Support and a Great Twitter Plugin** - I am using the free version of this plugin and identified a real problem in the display on my website. I emailed support and Craig was all over the fix for me. Seven or so emails later (even on a Sunday) the problem was fixed and Craig was still reaching out to make sure all was well.*
56
 
57
  *In a world of crappy support for premium plugins, and even worse support for free plugins, this kind of support (and listening to customers) that Craig and Smash Balloon provided me make their premium products worth every penny. In fact, I am going to buy them on my next project. They deserve it.*
58
 
59
  *Thanks again, Craig!*" - [@kcwebguy](https://wordpress.org/support/topic/amazing-support-and-a-great-twitter-plugin/)
60
 
61
- "***At last a configurable Feed that WORKS!** - I have messed with Twitter feeds on sites so many times only to be frustrated. This Plugin is the best I have tried. Stable, configurable and not too hefty a drag on performance. Well done!* - [@mikemac67](https://wordpress.org/support/topic/at-last-a-configurable-feed-that-works/)
62
 
63
  "***Everything you'll need in a Twitter feed plugin** - We've tried a few Twitter feed plugins across a bunch of our sites, and this one is now our go-to favorite. Most of the others we've tried have either styling or performance issues, but we've been using this plugin for a few months now, and it's been perfect. There are tons of customization options, the feed looks great, and... it works!*
64
 
@@ -67,33 +67,33 @@ If you like the plugin then please consider leaving a review, as it really helps
67
  = Pro Version =
68
  We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features:
69
 
70
- * Display Tweets from **multiple users or hashtags in the same feed**
71
  * Display **photos**, **videos**, and **gifs** and view them in a **popup lightbox** directly on your site
72
  * Multi-column **Masonry layout** [demo](http://smashballoon.com/custom-twitter-feeds/demo/masonry?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Masonry Demo')
73
- * Allow **filtering** of user timelines include/exclude any/all keywords/hashtags
74
- * Customizable **Carousels** and **Slideshows**
75
- * Fully functional **search endpoint**
76
  * Display Tweets you're mentioned in
77
  * Tweet-specific **moderation system**
78
  * Visual **Twitter cards** displayed with links which support them
79
  * Include **Tweet replies** (in reply to tweets)
80
- * **Combine multiple feeds** into one
81
  * **Autoload more Tweets** when scrolling
82
 
83
  Try the Pro version [demo here](http://smashballoon.com/custom-twitter-feeds/demo?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Demo')
84
 
85
  == Installation ==
86
  1. Install the Custom Twitter Feeds plugin either via the WordPress plugin directory, or by uploading the files to your web server (in the /wp-content/plugins/ directory).
87
- 2. Activate the plugin through the 'Plugins' menu in WordPress.
88
  3. Navigate to the 'Twitter Feed' settings page to configure your Twitter feed.
89
- 4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your feed.
90
  5. You can display multiple Twitter feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
91
 
92
  For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
93
 
94
  = Setting up the Free Custom Twitter Feeds WordPress Plugin =
95
 
96
- The Custom Twitter Feeds plugin is brand new and so we're currently working on improving our documentation for it. If you have an issue or question please submit a support ticket and we'll get back to you as soon as we can.
97
 
98
  1) Once you've installed the Custom Twitter Feeds plugin click on the "Twitter Feeds" item in your WordPress menu
99
 
@@ -122,7 +122,7 @@ Note; the plugin does not obtain permission to edit or write to your Twitter acc
122
 
123
  = Can I display multiple Twitter feeds on my site or on the same page? =
124
 
125
- Yep. You can display multiple Twitter feeds by using our built-in shortcode options, for example: `[custom-twitter-feeds screenname="smashballoon" num=3]`.
126
 
127
  = How do I embed the Twitter Feed directly into a WordPress page template? =
128
 
@@ -130,15 +130,15 @@ You can embed your Twitter feed directly into a template file by using the WordP
130
 
131
  = Is the content of my Twitter feed crawlable by search engines and how does it help improve my SEO? =
132
 
133
- Unlike other Twitter plugins which use iframes to embed your Twitter feed into your page once it's loaded, the Custom Twitter Feeds uses server-side code to embed your Twitter feed content directly into your page. This adds dynamic, search engine crawlable content to your site.
134
 
135
  = Will Custom Twitter Feeds work with W3 Total Cache or other caching plugins? =
136
 
137
- The Custom Twitter Feeds plugin should work in compatibility with most, if not all, caching plugins, but you may need to tweak the settings in order to allow the Twitter feed to update successfully and display your latest posts. If you are experiencing problems with your Twitter feed not updating then try disabling either 'Page Caching' or 'Object Caching' in W3 Total Cache (or any other similar caching plugin) to see whether that fixes the problem and the Twitter feed displays and updates successfully.
138
 
139
  = The font in my Twitter feed isn't inherited from my website =
140
 
141
- Be default your Twitter feed should inherit the font from your website. If this isn't the case then it's possible that something in the theme is preventing this from happening. An issue that we've run into before is that <code> tags are added into the Twitter feed HTML by either the theme, another plugin, or perhaps even the shortcode being wrapped in code tags in your page editor. To fix this;
142
 
143
  1) First – go to your page editor, click on the 'Text' tab in the top right of the editor box, and check whether the [custom-twitter-feeds] shortcode is being wrapped in <code> tags, like so: <code>[custom-twitter-feeds]</code>. If it is, then remove them.
144
 
@@ -153,12 +153,12 @@ You can replace the font-family with whatever font your theme is using.
153
  The main differences between the free version and Pro version of the Custom Twitter Feeds plugin are listed below. The extra features available in the Pro version are as follows:
154
 
155
  - Display photos, videos, and gifs in your Tweets, with support for 3rd party services such as YouTube, Vimeo, Vine, and SoundCloud
156
- - View media in a pop-up lightbox directly on your site
157
  - Display Tweets in a multi-column Masonry layout
158
  - Display your Twitter feed in rotating Carousels / Slideshows
159
  - Twitter Cards (rich, standout links) are displayed in Tweets for links that support them
160
  - Create advanced Twitter "Search" feeds or use the "Mentions" timeline
161
- - Combine feeds of multiple types (i.e. User feeds and Hashtag Twitter feeds combined)
162
  - Filter feeds by hashtag, word(s), or remove specific tweets
163
  - Autoload more tweets when scrolling to the bottom of the Twitter feed
164
  - Include Tweet replies ("in reply to")
@@ -167,7 +167,7 @@ You can see the demo of the Custom Twitter Feeds Pro version [here](https://smas
167
 
168
  = I'm receiving an error message when trying to display my Twitter feed =
169
 
170
- If you receive an error message when trying to display your Twitter Feed then you can use the error reference below to diagnose the issue and find the relevant solution.
171
 
172
  **Twitter Error:**
173
  Could not authenticate you
@@ -186,7 +186,7 @@ Invalid or expired token
186
  Unable to load tweets (with no other explanation)
187
 
188
  **Causes:**
189
- - You may need to raise the number of tweets to retrieve and display in your Twitter feed - See #3 below
190
 
191
  **Twitter Error:**
192
  Sorry, that Twitter page does not exist
@@ -214,7 +214,7 @@ The easiest way to verify this is by going back to the Settings page for the Cus
214
 
215
  **#2 - You have checked the box to use your own Twitter app information but one or more of the fields are incorrect**
216
 
217
- All four fields, consumer token, consumer secret, access token, and access token secret, need to come from the Twitter app that you set up on Twitter.com. Try returning to your personal Twitter app management page https://apps.twitter.com/ and confirming that all four fields, consumer token, consumer secret, access token, and access token secret, and entered correctly in the corresponding fields on the plugin's "Configure" tab.
218
 
219
  **#3 - You may need to raise the number of tweets to retrieve**
220
 
@@ -222,21 +222,21 @@ Navigate to the type of Twitter feed you are trying to display on twitter.com. F
222
 
223
  If you're still having trouble displaying your Tweets after trying the common issues above then please [contact support](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf) for assistance.
224
 
225
- = Why do you guys love providing support so much? =
226
-
227
- Not many developers love providing support, but we're a rare breed here at Smash Balloon. We take pride in the fact that you've chosen to use our plugin and want to help you with any issues you might have using it. It brings us joy to see our plugins out there in the wild making a difference on people's websites and we love interacting with any and all of our users! If you have a question or issue just let us know, we'll get back to you in no time to help!
228
-
229
  == Screenshots ==
230
 
231
- 1. By default the plugin inherits styles from your theme
232
  2. Display multiple Twitter feeds and customize each one
233
  3. Custom Twitter Feeds Settings pages
234
- 4. Built in customization settings make customizing your feed easy
235
- 5. Additional styling options are built into the plugin
236
- 6. Use handy shortcode options to customize individual feeds
237
- 7. To display a feed just copy and paste the shortcode into a widget or page
238
 
239
  == Changelog ==
 
 
 
 
240
  = 1.4.1 =
241
  * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
242
  * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
@@ -244,117 +244,22 @@ Not many developers love providing support, but we're a rare breed here at Smash
244
  * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
245
 
246
  = 1.4 =
247
- * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings on the "Customize" tab "Show/Hide" settings.
248
  * New: Minified versions of JavaScript and CSS files now used.
249
- * New: JavaScript file added to the source of the page only when the shortcode is used on the page.
250
- * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file on the "Customize" tab, "Advanced" area.
251
- * New: Tweets that are replies to the same account or mention the same account can be included in the feed by enabling the setting "Always include replies to self in the feed".
252
  * New: Added a setting to completely disable Twitter intents widget.js file.
253
  * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
254
- * Tweak: Changed styling of media placeholder icons.
255
  * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
256
- * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the feed.
257
  * Tweak: Tweet text line height style set to 1.4.
258
  * Fix: Bio text not displaying in header even when enabled.
259
 
260
  = 1.3 =
261
- * New: You can now choose to only display a certain amount of text characters in your Tweets, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
262
  * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
263
  * Fix: Removed target="_blank" from div element
264
 
265
- = 1.2.11 =
266
- * Fix: Quoted tweets would not display if saving settings on the "Customize tab". After updating, Enable "Quoted tweet box" in the "Show/Hide" section to display quoted tweets
267
- * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
268
-
269
- = 1.2.10 =
270
- * Tweak: Updated the Twitter login button URL
271
- * Fix: Avatar would appear to the right of Tweets or not at all in some versions of Firefox
272
- * Fix: Minor bug fixes
273
-
274
- = 1.2.9 =
275
- * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
276
- * Fix: AJAX load more not working when AJAX themes being used
277
-
278
- = 1.2.8 =
279
- * Fix: "Preserve settings on uninstall" setting not working correctly
280
- * Fix: Clear cache button would not clear all caches in some situations
281
-
282
- = 1.2.7 =
283
- * New: Added screen reader labels for improved accessibility
284
- * Fix: Bug with "persistent" cache duplicating tweets
285
-
286
- = 1.2.6 =
287
- * Fix: Twitter hashtag feeds now work for hashtags beginning with a number e.g. #1august
288
- * Fix: Added a workaround for a minor formatting issue caused by some themes
289
-
290
- = 1.2.5 =
291
- * New: Added an option to remove the placeholder icon that's used when a Tweet contains media. This can be found under `Customize > Show/Hide > Media placeholder` or using the `exclude` shortcode option, eg: `exclude="placeholder"`.
292
- * Fix: Individual tweet links in mobile would not properly link to mobile.twitter.com
293
- * Fix: Error message displayed instead of empty tweets for empty persistent caches
294
-
295
- = 1.2.4 =
296
- * Fix: Custom date format not working in settings area
297
- * Fix: Twitter avatars would disappear from the feed in Firefox for certain accounts
298
- * Fix: Changed http: links to https:
299
- * Fix: Fixed retweets always being included in persistent caches during the initial tweet retrieval
300
-
301
- = 1.2.3 =
302
- * Fix: Fix php warning when no media in tweets under certain circumstances
303
- * Fix: Missing "alt" attributes for Twitter avatars
304
-
305
- = 1.2.2 =
306
- * Fix: Fixed an issue with include/exclude string to array conversion warning
307
-
308
- = 1.2.1 =
309
- * Fix: Fixed an issue where line breaks in tweet text were being ignored
310
- * Fix: Fixed an issue where a small line was added to quoted tweets in retweets that have no media
311
-
312
- = 1.2 =
313
- * New: The plugin now uses persistent tweet caching for Twitter hashtag feeds. By default, when displaying hashtag feed Twitter only returns Tweets from the last 7 days, but the persistent cache now allows you to display these Tweets indefinitely.
314
- * New: Tweets with media will have an icon and label that links to the tweet on twitter.com
315
- * Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new Twitter access token
316
-
317
- = 1.1.8 =
318
- * Tweak: Updated Custom Twitter Feed plugin links for new WordPress.org repo
319
- * Fix: Minor bug fixes
320
-
321
- = 1.1.7 =
322
- * Fix: Fixed an issue with the Twitter Access Token and Twitter Secrets not automatically being saved when initially obtaining them
323
- * Fix: Fixed an issue with some customization settings not saving occasionally
324
-
325
- = 1.1.6 =
326
- * Fix: Fixed an issue when creating a Search feed using the built-in Custom Twitter Feeds widget box
327
- * Fix: Fixed an issue with the checkbox that allows you to toggle links on/off in the Tweet text
328
-
329
- = 1.1.5 =
330
- * Fix: Fixed a rare issue when loading more Tweets
331
- * Fix: When there is no bio text in the header then the Twitter screenname text is now automatically centered vertically
332
-
333
- = 1.1.4 =
334
- * Fix: Fixed an issue with some setting checkboxes
335
- * Fix: Fixed a rare encoding issue which occurred on some server configurations
336
- * Tested with the upcoming WordPress 4.6 update
337
-
338
- = 1.1.3 =
339
- * Fix: Prevented any duplicate Twitter Tweets from being shown
340
- * Fix: Fixed a rare issue where a couple of settings weren't being saved successfully
341
-
342
- = 1.1.2 =
343
- * New: Launched a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features!
344
- * Fix: Minor bug fixes
345
-
346
- = 1.1.1 =
347
- * Fix: Added SSL support for Twitter avatar images so https version is used
348
- * Fix: Fixed an issue with the "hours" custom text string displaying the "minutes" text instead
349
-
350
- = 1.1 =
351
- * New: Added a setting to translate the "Retweeted" text
352
- * Tweak: If there aren't enough Tweets to populate the feed them Ajax in more automatically
353
- * Fix: Custom JavaScript is now rerun every time the Load More button is used
354
- * Fix: CSS display tweaks
355
-
356
- = 1.0.1 =
357
- * Bug fixes
358
-
359
- = 1.0 =
360
- * Launched the Custom Twitter Feeds plugin!
2
  Author: Smash Balloon
3
  Contributors: smashballoon, craig-at-smash-balloon
4
  Support Website: http://smashballoon/custom-twitter-feeds/
5
+ Tags: Twitter, Twitter feed, Twitter Tweets, Twitter widget, Custom Twitter Feed
6
  Requires at least: 3.4
7
  Tested up to: 5.4
8
+ Stable tag: 1.5
9
  License: GPLv2 or later
10
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
 
12
+ Custom Twitter Feeds allows you to display completely customizable Twitter feeds of a user timeline, home timeline, or a Twitter hashtag on your website.
13
 
14
  == Description ==
15
+ Display **completely customizable**, **responsive** and **search engine crawlable** Twitter feeds on your website. The Custom Twitter Feeds plugin will completely match the look and feel of your site and includes tons of customization options!
16
 
17
  = Features =
18
+ * **Completely Customizable** - by default the Twitter feeds will inherit your theme's styles
19
  * Twitter feed content is **crawlable by search engines** adding SEO value to your site
20
+ * **Completely responsive and mobile optimized** - your Twitter feeds work on any screen size
21
  * Display tweets from any user, your own Twitter account and those you follow, or from a specific hashtag
22
+ * Display **multiple Twitter feeds** from different Twitter users on multiple pages or widgets
23
  * Post caching means that your Twitter feed loads **lightning fast** and minimizes Twitter API requests
24
  * **Infinitely load more** of your Tweets with the 'Load More' button
25
  * Built-in easy to use "Custom Twitter Feeds" Widget
36
  * **Improve your SEO** as all of that quality keyword-rich Twitter content is directly embedded into your website
37
  * **Keep users on your site** by letting them reply and like your Tweets directly on your site
38
  * Display your Twitter content **your way** to perfectly match your website's style
39
+ * The Custom Twitter Feeds plugin is **updated regularly** with new features, bug-fixes and Twitter API changes
40
  * Support is quick and effective
41
  * We're dedicated to providing the **most customizable**, **robust** and **well supported** Twitter feed plugin in the world!
42
 
48
  If you like the plugin then please consider leaving a review, as it really helps to support the plugin. If you have an issue then please allow us to help you fix it before leaving a review. Just [let us know](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf 'Twitter Feed Support') what the problem is and we'll get back to you right away.
49
 
50
  = Featured Reviews =
51
+ "***Great for Customization** - Great plugin! Superb customization options! Am changing all twitter feeds for all my websites to this! I have tried several Twitter plugins so far. This is the best I have come across :)*" - [@nehavivekpatil](https://wordpress.org/support/topic/great-for-customization-2/)
52
 
53
  "***Excellent plug-in. Excellent support.** - Excellent instructions and guidance. Patient and prompt support for the newbies. Regular updates. You can't lose. Thank you Smash Balloon.* - [@nuspa](https://wordpress.org/support/topic/excellent-plug-in-excellent-support-2/)
54
 
55
+ "***Amazing Support and a Great Twitter Plugin** - I am using the free version of this Twitter plugin and identified a real problem in the display on my website. I emailed support and Craig was all over the fix for me. Seven or so emails later (even on a Sunday) the problem was fixed and Craig was still reaching out to make sure all was well.*
56
 
57
  *In a world of crappy support for premium plugins, and even worse support for free plugins, this kind of support (and listening to customers) that Craig and Smash Balloon provided me make their premium products worth every penny. In fact, I am going to buy them on my next project. They deserve it.*
58
 
59
  *Thanks again, Craig!*" - [@kcwebguy](https://wordpress.org/support/topic/amazing-support-and-a-great-twitter-plugin/)
60
 
61
+ "***At last a configurable Twitter Feed that WORKS!** - I have messed with Twitter feeds on sites so many times only to be frustrated. This Plugin is the best I have tried. Stable, configurable and not too hefty a drag on performance. Well done!* - [@mikemac67](https://wordpress.org/support/topic/at-last-a-configurable-feed-that-works/)
62
 
63
  "***Everything you'll need in a Twitter feed plugin** - We've tried a few Twitter feed plugins across a bunch of our sites, and this one is now our go-to favorite. Most of the others we've tried have either styling or performance issues, but we've been using this plugin for a few months now, and it's been perfect. There are tons of customization options, the feed looks great, and... it works!*
64
 
67
  = Pro Version =
68
  We recently released a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features:
69
 
70
+ * Display Tweets from **multiple users or hashtags in the same Twitter feed**
71
  * Display **photos**, **videos**, and **gifs** and view them in a **popup lightbox** directly on your site
72
  * Multi-column **Masonry layout** [demo](http://smashballoon.com/custom-twitter-feeds/demo/masonry?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Masonry Demo')
73
+ * Allow **filtering** of Twitter timelines include/exclude any/all keywords/hashtags
74
+ * Customizable **Carousels** and **Slideshows** of your Tweets
75
+ * Fully functional Twitter **search endpoint**
76
  * Display Tweets you're mentioned in
77
  * Tweet-specific **moderation system**
78
  * Visual **Twitter cards** displayed with links which support them
79
  * Include **Tweet replies** (in reply to tweets)
80
+ * **Combine multiple Twitter feeds** into one
81
  * **Autoload more Tweets** when scrolling
82
 
83
  Try the Pro version [demo here](http://smashballoon.com/custom-twitter-feeds/demo?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro Demo')
84
 
85
  == Installation ==
86
  1. Install the Custom Twitter Feeds plugin either via the WordPress plugin directory, or by uploading the files to your web server (in the /wp-content/plugins/ directory).
87
+ 2. Activate the Custom Twitter Feeds plugin through the 'Plugins' menu in WordPress.
88
  3. Navigate to the 'Twitter Feed' settings page to configure your Twitter feed.
89
+ 4. Use the shortcode [custom-twitter-feeds] in your page, post or widget to display your Twitter feed.
90
  5. You can display multiple Twitter feeds with different configurations by specifying the necessary parameters directly in the shortcode: [custom-twitter-feeds hashtag=#smashballoon].
91
 
92
  For simple step-by-step directions on how to set up the Custom Twitter Feeds plugin please refer to our [setup guide](http://smashballoon.com/custom-twitter-feeds/free/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds setup guide').
93
 
94
  = Setting up the Free Custom Twitter Feeds WordPress Plugin =
95
 
96
+ If you have an issue or question while setting up or using the plugin then please submit a support ticket and we'll get back to you as soon as we can.
97
 
98
  1) Once you've installed the Custom Twitter Feeds plugin click on the "Twitter Feeds" item in your WordPress menu
99
 
122
 
123
  = Can I display multiple Twitter feeds on my site or on the same page? =
124
 
125
+ Yep. You can display multiple Twitter feeds by using our built-in shortcode options, for example: `[custom-twitter-feeds screenname="smashballoon" num=3]`. You can add it to any post, page, or widget, to easily display your Twitter feed in a beautiful Twitter widget.
126
 
127
  = How do I embed the Twitter Feed directly into a WordPress page template? =
128
 
130
 
131
  = Is the content of my Twitter feed crawlable by search engines and how does it help improve my SEO? =
132
 
133
+ Unlike other Twitter plugins which use iframes to embed your Twitter feed into your page in a widget, the Custom Twitter Feeds uses server-side code to embed your Twitter feed widget content directly into your page. This adds dynamic, search engine crawlable content to your site.
134
 
135
  = Will Custom Twitter Feeds work with W3 Total Cache or other caching plugins? =
136
 
137
+ The Custom Twitter Feeds plugin should work in compatibility with most, if not all, caching plugins, but you may need to tweak the settings in order to allow the Twitter feed to update successfully and display your latest Twitter posts. If you are experiencing problems with your Twitter feed widget not updating then try disabling either 'Page Caching' or 'Object Caching' in W3 Total Cache (or any other similar caching plugin) to see whether that fixes the problem and the Twitter feed displays and updates successfully.
138
 
139
  = The font in my Twitter feed isn't inherited from my website =
140
 
141
+ Be default your Twitter feed should inherit the font from your website. If this isn't the case then it's possible that something in the theme is preventing this from happening. An issue that we've run into before is that <code> tags are added into the Twitter feed widget HTML by either the theme, another plugin, or perhaps even the custom-twitter-feeds shortcode being wrapped in code tags in your page editor. To fix this;
142
 
143
  1) First – go to your page editor, click on the 'Text' tab in the top right of the editor box, and check whether the [custom-twitter-feeds] shortcode is being wrapped in <code> tags, like so: <code>[custom-twitter-feeds]</code>. If it is, then remove them.
144
 
153
  The main differences between the free version and Pro version of the Custom Twitter Feeds plugin are listed below. The extra features available in the Pro version are as follows:
154
 
155
  - Display photos, videos, and gifs in your Tweets, with support for 3rd party services such as YouTube, Vimeo, Vine, and SoundCloud
156
+ - View Twitter media in a pop-up lightbox directly on your site
157
  - Display Tweets in a multi-column Masonry layout
158
  - Display your Twitter feed in rotating Carousels / Slideshows
159
  - Twitter Cards (rich, standout links) are displayed in Tweets for links that support them
160
  - Create advanced Twitter "Search" feeds or use the "Mentions" timeline
161
+ - Combine Twitter feeds of multiple types (i.e. User feeds and Hashtag Twitter feeds combined)
162
  - Filter feeds by hashtag, word(s), or remove specific tweets
163
  - Autoload more tweets when scrolling to the bottom of the Twitter feed
164
  - Include Tweet replies ("in reply to")
167
 
168
  = I'm receiving an error message when trying to display my Twitter feed =
169
 
170
+ If you receive an error message when trying to display your Twitter Feed widget then you can use the error reference below to diagnose the issue and find the relevant solution.
171
 
172
  **Twitter Error:**
173
  Could not authenticate you
186
  Unable to load tweets (with no other explanation)
187
 
188
  **Causes:**
189
+ - You may need to raise the number of tweets to retrieve and display in your Twitter feed widget - See #3 below
190
 
191
  **Twitter Error:**
192
  Sorry, that Twitter page does not exist
214
 
215
  **#2 - You have checked the box to use your own Twitter app information but one or more of the fields are incorrect**
216
 
217
+ All four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret, need to come from the Twitter app that you set up on Twitter.com. Try returning to your personal Twitter app management page https://apps.twitter.com/ and confirming that all four fields, Twitter consumer token, Twitter consumer secret, Twitter access token, and Twitter access token secret are entered correctly in the corresponding fields on the Custom Twitter Feeds plugin's "Configure" tab.
218
 
219
  **#3 - You may need to raise the number of tweets to retrieve**
220
 
222
 
223
  If you're still having trouble displaying your Tweets after trying the common issues above then please [contact support](https://smashballoon.com/custom-twitter-feeds/support/?utm_source=wordpress&utm_campaign=ctf) for assistance.
224
 
 
 
 
 
225
  == Screenshots ==
226
 
227
+ 1. By default the Custom Twitter Feeds plugin inherits styles from your theme
228
  2. Display multiple Twitter feeds and customize each one
229
  3. Custom Twitter Feeds Settings pages
230
+ 4. Built in customization settings make customizing your Twitter feed widget easy
231
+ 5. Additional styling options are built into the Custom Twitter Feeds plugin
232
+ 6. Use handy shortcode options to customize individual Twitter feeds
233
+ 7. To display a Twitter feed just copy and paste the shortcode into a Twitter widget or page
234
 
235
  == Changelog ==
236
+ = 1.5 =
237
+ * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a Twitter feed to posts and pages.
238
+ * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
239
+
240
  = 1.4.1 =
241
  * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
242
  * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
244
  * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
245
 
246
  = 1.4 =
247
+ * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings at Twitter Feeds > Customize > Show/Hide.
248
  * New: Minified versions of JavaScript and CSS files now used.
249
+ * New: JavaScript file added to the source of the page only when the Custom Twitter Feeds shortcode is used on the page.
250
+ * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file using the setting at Twitter Feeds > Customize > Advanced.
251
+ * New: Tweets that are replies to the same Twitter account or mention the same account can be included in the Twitter feed widget by enabling the setting "Always include replies to self in the feed".
252
  * New: Added a setting to completely disable Twitter intents widget.js file.
253
  * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
254
+ * Tweak: Changed styling of Twitter media placeholder icons.
255
  * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
256
+ * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the Twitter feed.
257
  * Tweak: Tweet text line height style set to 1.4.
258
  * Fix: Bio text not displaying in header even when enabled.
259
 
260
  = 1.3 =
261
+ * New: You can now choose to only display a certain amount of text characters in the Tweets in your Twitter widget, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Twitter Feeds > Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
262
  * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
263
  * Fix: Removed target="_blank" from div element
264
 
265
+ [See changelog for all versions](https://plugins.svn.wordpress.org/custom-twitter-feeds/trunk/changelog.txt).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
changelog.txt ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ == Changelog ==
2
+ = 1.5 =
3
+ * New: Added a "Twitter Feeds" Gutenberg block to use in the block editor, allowing you to easily add a feed to posts and pages.
4
+ * Tweak: Added function ctf_init() to easily rerun JavaScript for the plugin.
5
+
6
+ = 1.4.1 =
7
+ * Tweak: Added aria-label attributes to SVG icons for improved accessibility.
8
+ * Fix: Fixed PHP warning that occurred when a URL in the tweet text did not have a full length URL available from the Twitter API.
9
+ * Fix: Using the setting to link the tweet text to Twitter would cause nested links and invalid HTML.
10
+ * Fix: Fixed Twitter logo not displaying if avatar, author, and date were hidden.
11
+
12
+ = 1.4 =
13
+ * New: Twitter logo added to the top right of each tweet to fit with Twitter's display guidelines. Customize the logo on the "Style" tab, "Author" area or hide it using the settings on the "Customize" tab "Show/Hide" settings.
14
+ * New: Minified versions of JavaScript and CSS files now used.
15
+ * New: JavaScript file added to the source of the page only when the shortcode is used on the page.
16
+ * New: SVGs used for icons instead of Font Awesome font file. You can switch back to using the Font Awesome font file on the "Customize" tab, "Advanced" area.
17
+ * New: Tweets that are replies to the same account or mention the same account can be included in the feed by enabling the setting "Always include replies to self in the feed".
18
+ * New: Added a setting to completely disable Twitter intents widget.js file.
19
+ * Tweak: The HTML element used for icons has been switched from "i" to "span" for better accessibility.
20
+ * Tweak: Changed styling of media placeholder icons.
21
+ * Tweak: Combined widgets.js file with native JavaScript file for the plugin.
22
+ * Tweak: Twitter widgets.js will only be enqueued when "actions" are included in the feed.
23
+ * Tweak: Tweet text line height style set to 1.4.
24
+ * Fix: Bio text not displaying in header even when enabled.
25
+
26
+ = 1.3 =
27
+ * New: You can now choose to only display a certain amount of text characters in your Tweets, with a clickable link to display the rest. This is set to be 280 characters by default, but can be changed by using the following setting: Customize > Style > Tweet Text > Text Length, or by using the "textlength" shortcode setting.
28
+ * Tweak: Twitter intents JavaScript not loaded on the page if tweet actions are removed
29
+ * Fix: Removed target="_blank" from div element
30
+
31
+ = 1.2.11 =
32
+ * Fix: Quoted tweets would not display if saving settings on the "Customize tab". After updating, Enable "Quoted tweet box" in the "Show/Hide" section to display quoted tweets
33
+ * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
34
+
35
+ = 1.2.10 =
36
+ * Tweak: Updated the Twitter login button URL
37
+ * Fix: Avatar would appear to the right of Tweets or not at all in some versions of Firefox
38
+ * Fix: Minor bug fixes
39
+
40
+ = 1.2.9 =
41
+ * Fix: Fixed PHP warning caused by trying to count a boolean in certain circumstances
42
+ * Fix: AJAX load more not working when AJAX themes being used
43
+
44
+ = 1.2.8 =
45
+ * Fix: "Preserve settings on uninstall" setting not working correctly
46
+ * Fix: Clear cache button would not clear all caches in some situations
47
+
48
+ = 1.2.7 =
49
+ * New: Added screen reader labels for improved accessibility
50
+ * Fix: Bug with "persistent" cache duplicating tweets
51
+
52
+ = 1.2.6 =
53
+ * Fix: Twitter hashtag feeds now work for hashtags beginning with a number e.g. #1august
54
+ * Fix: Added a workaround for a minor formatting issue caused by some themes
55
+
56
+ = 1.2.5 =
57
+ * New: Added an option to remove the placeholder icon that's used when a Tweet contains media. This can be found under `Customize > Show/Hide > Media placeholder` or using the `exclude` shortcode option, eg: `exclude="placeholder"`.
58
+ * Fix: Individual tweet links in mobile would not properly link to mobile.twitter.com
59
+ * Fix: Error message displayed instead of empty tweets for empty persistent caches
60
+
61
+ = 1.2.4 =
62
+ * Fix: Custom date format not working in settings area
63
+ * Fix: Twitter avatars would disappear from the feed in Firefox for certain accounts
64
+ * Fix: Changed http: links to https:
65
+ * Fix: Fixed retweets always being included in persistent caches during the initial tweet retrieval
66
+
67
+ = 1.2.3 =
68
+ * Fix: Fix php warning when no media in tweets under certain circumstances
69
+ * Fix: Missing "alt" attributes for Twitter avatars
70
+
71
+ = 1.2.2 =
72
+ * Fix: Fixed an issue with include/exclude string to array conversion warning
73
+
74
+ = 1.2.1 =
75
+ * Fix: Fixed an issue where line breaks in tweet text were being ignored
76
+ * Fix: Fixed an issue where a small line was added to quoted tweets in retweets that have no media
77
+
78
+ = 1.2 =
79
+ * New: The plugin now uses persistent tweet caching for Twitter hashtag feeds. By default, when displaying hashtag feed Twitter only returns Tweets from the last 7 days, but the persistent cache now allows you to display these Tweets indefinitely.
80
+ * New: Tweets with media will have an icon and label that links to the tweet on twitter.com
81
+ * Fix: Fixed an issue with checkbox settings being changed unintentionally after obtaining a new Twitter access token
82
+
83
+ = 1.1.8 =
84
+ * Tweak: Updated Custom Twitter Feed plugin links for new WordPress.org repo
85
+ * Fix: Minor bug fixes
86
+
87
+ = 1.1.7 =
88
+ * Fix: Fixed an issue with the Twitter Access Token and Twitter Secrets not automatically being saved when initially obtaining them
89
+ * Fix: Fixed an issue with some customization settings not saving occasionally
90
+
91
+ = 1.1.6 =
92
+ * Fix: Fixed an issue when creating a Search feed using the built-in Custom Twitter Feeds widget box
93
+ * Fix: Fixed an issue with the checkbox that allows you to toggle links on/off in the Tweet text
94
+
95
+ = 1.1.5 =
96
+ * Fix: Fixed a rare issue when loading more Tweets
97
+ * Fix: When there is no bio text in the header then the Twitter screenname text is now automatically centered vertically
98
+
99
+ = 1.1.4 =
100
+ * Fix: Fixed an issue with some setting checkboxes
101
+ * Fix: Fixed a rare encoding issue which occurred on some server configurations
102
+ * Tested with the upcoming WordPress 4.6 update
103
+
104
+ = 1.1.3 =
105
+ * Fix: Prevented any duplicate Twitter Tweets from being shown
106
+ * Fix: Fixed a rare issue where a couple of settings weren't being saved successfully
107
+
108
+ = 1.1.2 =
109
+ * New: Launched a [Pro version](http://smashballoon.com/custom-twitter-feeds/?utm_source=wordpress&utm_campaign=ctf 'Custom Twitter Feeds Pro') which includes some awesome additional features!
110
+ * Fix: Minor bug fixes
111
+
112
+ = 1.1.1 =
113
+ * Fix: Added SSL support for Twitter avatar images so https version is used
114
+ * Fix: Fixed an issue with the "hours" custom text string displaying the "minutes" text instead
115
+
116
+ = 1.1 =
117
+ * New: Added a setting to translate the "Retweeted" text
118
+ * Tweak: If there aren't enough Tweets to populate the feed them Ajax in more automatically
119
+ * Fix: Custom JavaScript is now rerun every time the Load More button is used
120
+ * Fix: CSS display tweaks
121
+
122
+ = 1.0.1 =
123
+ * Bug fixes
124
+
125
+ = 1.0 =
126
+ * Launched the Custom Twitter Feeds plugin!
css/ctf-admin-styles.css CHANGED
@@ -1,660 +1,660 @@
1
- #ctf-admin .ctf-tooltip{
2
- display: none;
3
- padding: 10px 0;
4
- font-weight: normal;
5
- }
6
- #ctf-admin .ctf-tooltip ul{
7
- margin-top: 0;
8
- margin-bottom: 0;
9
- }
10
- #ctf-admin .ctf-tooltip li{
11
- padding: 4px 0;
12
- }
13
- #ctf-admin .ctf-tooltip-link,
14
- #ctf-admin .ctf-external-link{
15
- font-size: 13px;
16
- margin-left: 10px;
17
- }
18
- #ctf-admin .ctf-more-info{
19
- padding: 10px 15px;
20
- margin: 10px 0;
21
- font-size: 13px;
22
- background: #f9f9f9;
23
- background: rgba(255,255,255,0.8);
24
- -moz-border-radius: 8px;
25
- -webkit-border-radius: 8px;
26
- border-radius: 8px;
27
- }
28
- #ctf-admin .postbox .ctf-more-info{
29
- background: #eee;
30
- background: rgba(0,0,0,0.05);
31
- }
32
- #ctf-admin .ctf-more-info p{
33
- font-size: 13px;
34
- }
35
- #ctf-admin .ctf-contents-links{
36
- float: left;
37
- clear: both;
38
- width: 100%;
39
- padding-bottom: 12px;
40
- border-bottom: 1px solid #ccc;
41
- margin-bottom: 15px;
42
- }
43
- #ctf-admin .ctf-contents-links a,
44
- #ctf-admin .ctf-contents-links span{
45
- display: block;
46
- float: left;
47
- padding: 2px 5px;
48
- }
49
- #ctf-admin .ctf-contents-links span{
50
- padding-left: 0;
51
- }
52
- #ctf-admin .ctf-contents-links td,
53
- #ctf-admin .ctf-contents-links th {
54
- padding: 0;
55
- vertical-align: middle;
56
- }
57
- #ctf-admin .ctf_include_replies_toggle {
58
- display: block;
59
- margin: 8px 0 0 142px;
60
- }
61
- #ctf-admin table.ctf_shortcode_table{
62
- border-collapse: collapse;
63
- }
64
- #ctf-admin table.ctf_shortcode_table th,
65
- #ctf-admin table.ctf_shortcode_table td{
66
- border: 1px solid #999;
67
- padding: 0.5rem;
68
- text-align: left;
69
- }
70
- #ctf-admin table.ctf_shortcode_table th{
71
- background: rgba(0,0,0,0.1);
72
- }
73
- #ctf-admin table.ctf_shortcode_table td{
74
- background: rgba(255,255,255,0.5);
75
- }
76
- #ctf-admin .ctf_table_header{
77
- background: #ddd;
78
- font-weight: bold;
79
- }
80
- #ctf-admin .short-text {
81
- width: 50px;
82
- }
83
-
84
- /* Configure tab */
85
-
86
- #ctf-admin .ctf-radio-label {
87
- display: inline-block;
88
- width: 114px;
89
- }
90
- #ctf-admin .ctf-radio th,
91
- #ctf-admin .ctf-radio td {
92
- padding: 10px 10px 5px 0;
93
- }
94
- #ctf-admin #ctf-get-token{
95
- display: block;
96
- float: left;
97
- clear: both;
98
- padding: 0 21px 0 21px;
99
- height: 47px;
100
- line-height: 47px;
101
- background: #1da1f2;
102
- color: #e9eef3;
103
- text-decoration: none;
104
-
105
- -moz-border-radius: 5px;
106
- -webkit-border-radius: 5px;
107
- border-radius: 5px;
108
-
109
- -moz-transition: all 0.5s ease-in-out;
110
- -webkit-transition: all 0.5s ease-in-out;
111
- -o-transition: all 0.5s ease-in-out;
112
- transition: all 0.5s ease-in-out;
113
- }
114
- #ctf-admin #ctf-get-token .fa{
115
- margin-right: 10px;
116
- font-size: 22px;
117
- position: relative;
118
- top: 3px;
119
- }
120
- #ctf-admin #ctf-get-token:hover{
121
- background-color: #0c7abf;
122
- color: #fff;
123
-
124
- -moz-transition: all 0.1s ease-in-out;
125
- -webkit-transition: all 0.1s ease-in-out;
126
- -o-transition: all 0.1s ease-in-out;
127
- transition: all 0.1s ease-in-out;
128
- }
129
- .ctf-note-after-input {
130
- font-style: italic;
131
- font-size: 12px;
132
- }
133
- /* Config info */
134
- #ctf_config{
135
- float: left;
136
- width: 100%;
137
- clear: both;
138
- margin: 10px 0 10px 0;
139
- }
140
- #ctf_config .ctf-tooltip-link{
141
- position: relative;
142
- top: 13px;
143
- left: 6px;
144
- }
145
- #ctf_config_info{
146
- float: left;
147
- clear: both;
148
- padding: 5px 15px;
149
- margin: 10px 0 0 0;
150
-
151
- background: #fff;
152
- border: 1px solid #ddd;
153
-
154
- -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
155
- box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
156
-
157
- -moz-border-radius: 2px;
158
- -webkit-border-radius: 2px;
159
- border-radius: 2px;
160
- }
161
- #ctf-admin .ctf-success{
162
- color: #7ad03a;
163
- font-size: 16px;
164
- padding: 6px;
165
- }
166
-
167
- /* shortcode examples */
168
- #ctf-admin label{
169
- position: relative;
170
- }
171
- #ctf-admin .ctf_shortcode{
172
- display: none;
173
- position: absolute;
174
- z-index: 10;
175
- font-size: 11px;
176
- float: left;
177
- width: auto;
178
- white-space: pre-line;
179
- line-height: 1.4;
180
- max-width: 195px;
181
- }
182
- #ctf-admin .ctf_shortcode_symbol{
183
- position: absolute;
184
- right: -24px;
185
- bottom: 0px;
186
- padding: 2px 4px 3px 3px;
187
- width: auto;
188
- font-size: 11px;
189
- margin-left: 5px;
190
- }
191
-
192
- /* Error notice */
193
- #ctf-admin .ctf_notice, .ctf_notice{
194
- margin-top: 5px;
195
- background: #f9ecda;
196
- padding: 5px 10px;
197
- border: 1px solid #e89a2e;
198
- color: #cf6100;
199
-
200
- -moz-border-radius: 3px;
201
- -webkit-border-radius: 3px;
202
- border-radius: 3px;
203
- }
204
- #ctf-admin .ctf_notice a, .ctf_notice a{
205
- color: #d85600;
206
- }
207
- #ctf-admin .ctf_notice a:hover, .ctf_notice a:hover,
208
- #ctf-admin .ctf_notice a:focus, .ctf_notice a:focus{
209
- color: #a34100;
210
- }
211
- #ctf-admin .ctf_notice p, .ctf_notice p{
212
- margin: 0;
213
- padding: 5px 0;
214
- font-size: 13px;
215
- }
216
- .ctf_user_id_error{
217
- display: none;
218
-
219
- margin: 10px 0;
220
- background: #F7E6E6;
221
- padding: 5px 10px;
222
- border: 1px solid #BA7B7B;
223
- color: #592626;
224
-
225
- -moz-border-radius: 3px;
226
- -webkit-border-radius: 3px;
227
- border-radius: 3px;
228
- }
229
-
230
- /* Review notice */
231
- .ctf_review_notice{
232
- position: relative;
233
- overflow: hidden;
234
- max-width: 870px;
235
- margin-top: 10px;
236
- padding: 10px 10px 7px 10px;
237
-
238
- background: #E6F0E8;
239
- border: 1px solid #6AB074;
240
- color: #214F28;
241
- }
242
- .ctf_bfcm_sale_notice,
243
- .ctf_new_user_sale_notice{
244
- max-width: 672px;
245
- }
246
- .ctf_review_notice img{
247
- width: 74px;
248
- margin: 0 0 0 -100% !important;
249
-
250
- -moz-border-radius: 4px;
251
- -webkit-border-radius: 4px;
252
- border-radius: 4px;
253
- }
254
- .ctf_review_notice .ctf-notice-text{
255
- float: left;
256
- clear: none;
257
- width: 100%;
258
- padding: 0;
259
- }
260
- .ctf_review_notice p{
261
- float: left;
262
- clear: both;
263
- width: auto;
264
- margin: 0 0 0 90px !important;
265
- padding: 2px 40px 2px 0;
266
- line-height: 1.4;
267
- }
268
- .ctf_review_notice .ctf-links{
269
- margin-top: 4px !important;
270
- }
271
- .ctf_review_notice a{
272
- display: inline-block;
273
- padding: 0 8px;
274
- color: #178529;
275
- }
276
- .ctf_review_notice a:hover,
277
- .ctf_review_notice a:focus{
278
- color: #0c7abf;
279
- }
280
- .ctf_review_notice .links{
281
- margin: 0 0 0 82px !important;
282
- padding: 4px 0 0 0;
283
- margin-top: 6px !important;
284
- }
285
- .ctf_review_notice .ctf_notice_close,
286
- .ctf_review_notice .ctf_bfcm_sale_notice_close,
287
- .ctf_review_notice .ctf_new_user_sale_notice_close {
288
- position: absolute;
289
- top: 0;
290
- right: 0;
291
- padding: 10px;
292
- line-height: 1;
293
- }
294
- .ctf_review_notice .ctf_notice_close:hover,
295
- .ctf_review_notice .ctf_notice_close:focus{
296
- color: #a34100;
297
- }
298
- .ctf_review_notice .ctf_offer_btn {
299
- padding: 4px 12px 6px 12px;
300
- background: green;
301
- color: #fff;
302
- border-radius: 4px;
303
- display: inline-block;
304
- text-decoration: none;
305
- margin-left: 0;
306
- }
307
- .ctf_review_notice .ctf_offer_btn:hover, .ctf_review_notice .ctf_offer_btn:focus {
308
- background: #049404;
309
- color: #fff;
310
- }
311
- .ctf_review_notice .ctf_other_notice{
312
- padding-top: 10px;
313
- font-style: italic;
314
- font-size: 12px;
315
- }
316
- .ctf_review_notice .ctf_other_notice a{
317
- padding: 0;
318
- }
319
-
320
- /* Customize tab */
321
- #ctf-admin #ctf_width_options{
322
- margin-top: 10px;
323
- display: none;
324
- }
325
-
326
- /* Pro only notices */
327
- #ctf-admin .ctf_row{
328
- display: block;
329
- width: 100%;
330
- }
331
- #ctf-admin .ctf_pro,
332
- #ctf-admin .ctf_pro label,
333
- #ctf_admin .ctf_pro input{
334
- color: #999 !important;
335
- }
336
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child label,
337
- #ctf-admin .ctf_pro .ctf_layout_type_list {
338
- color: initial !important;
339
- }
340
- #ctf-admin .ctf_shortcode_table tr.ctf_pro{
341
- background: #eacccc;
342
- }
343
- #ctf-admin .ctf_table_key{
344
- display: block;
345
- float: left;
346
- width: 11px;
347
- height: 11px;
348
- border: 1px solid #999;
349
- background: #eacccc;
350
- margin: 3px 6px 0 0;
351
- }
352
- #ctf-admin .ctf_note {
353
- font-size: 12px;
354
- font-style: italic;
355
- margin-left: 5px;
356
- }
357
- #ctf-admin .ctf_checkbox,
358
- #ctf-admin .ctf_checkbox label{
359
- font-size: 13px;
360
- line-height: 1.4em;
361
- padding-top: 10px;
362
- vertical-align: top;
363
- }
364
- #ctf-admin .ctf_pro .ctf_layout_cell img{
365
- opacity: 0.4;
366
- }
367
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child img {
368
- opacity: 1 !important;
369
- }
370
- #ctf-admin .ctf_pro input,
371
- #ctf-admin .ctf_pro select {
372
- color: #999 !important;
373
- }
374
- #ctf-admin .ctf_lock {
375
- display: none;
376
- padding: 8px 10px 8px 13px;
377
- position: absolute;
378
- top: 50%;
379
- left: 50%;
380
- margin-top: -4px;
381
- margin-left: -34px;
382
- background: rgba(255,255,255,0.8);
383
- border-radius: 5px;
384
- box-shadow: 0 0 5px 0 rgba(0,0,0,0.1);
385
- color: #333;
386
- z-index: 99;
387
- font-size: 14px;
388
- color: #666;
389
- font-weight: 700;
390
- text-decoration: none;
391
- border: 1px solid #ccc;
392
- }
393
- #ctf-admin .ctf_lock i {
394
- margin-right: 6px;
395
- }
396
- #ctf-admin .ctf_pro .ctf_layout_cell:hover .ctf_lock{
397
- display: block;
398
- }
399
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child .ctf_lock,
400
- #ctf-admin .ctf_pro .ctf_layout_cell:first-child:hover .ctf_lock {
401
- display: none;
402
- }
403
-
404
- /* Layout */
405
- .form-table .ctf_layout_type {
406
- display: inline-block;
407
- margin-left: 20px;
408
- }
409
- .form-table .ctf_layout_type:first-child {
410
- margin-left: 0;
411
- }
412
- .ctf_layout_setting,
413
- .ctf_box_setting{
414
- margin-bottom: 20px;
415
- }
416
- .ctf_layout_setting:last-child,
417
- .ctf_box_setting:last-child {
418
- margin-bottom: 0;
419
- }
420
- .ctf_layout_setting label,
421
- .ctf_box_setting label {
422
- display: inline-block;
423
- font-weight: bold;
424
- font-size: 14px;
425
- padding-bottom: 5px;
426
- padding-right: 5px;
427
- }
428
- #ctf-admin .ctf_layout_options_wrap .ctf_shortcode_symbol{
429
- bottom: 4px;
430
- right: -20px;
431
- }
432
- #ctf-admin .ctf_layout_options_wrap .ctf_shortcode,
433
- #ctf-admin .ctf_box_setting .ctf_shortcode{
434
- position: relative;
435
- float: none;
436
- top: -4px
437
- }
438
-
439
- .ctf_layout_cell {
440
- position: relative;
441
- padding: 0;
442
- display: inline-block;
443
- float: left;
444
- width: 155px;
445
- padding: 15px 15px 10px 15px;
446
- margin-right: 10px;
447
- background: #f6f6f6;
448
- border: 1px solid #ddd;
449
- -moz-border-radius: 5px;
450
- -webkit-border-radius: 5px;
451
- border-radius: 5px;
452
- vertical-align: top;
453
- }
454
- .ctf_layout_cell:nth-child(4) {
455
- margin-right: 0;
456
- }
457
- @media all and (max-width: 1200px){
458
- .ctf_layout_cell{
459
- width: 21%;
460
- padding: 1%;
461
- margin-right: 1%;
462
- }
463
- }
464
- @media all and (max-width: 480px){
465
- .ctf_layout_cell{
466
- width: 98%;
467
- padding: 1%;
468
- margin: 5px 0;
469
- }
470
- }
471
- .ctf_label {
472
- font-size: 16px;
473
- font-weight: bold;
474
- padding-left: 2px;
475
- }
476
- .ctf_layout_cell:hover,
477
- .ctf_layout_cell:focus{
478
- background: #fcfcfc;
479
- cursor: pointer;
480
- }
481
- .ctf_layout_cell.ctf_layout_selected{
482
- background: #f7faf1;
483
- border: 1px solid #7ad03a;
484
- }
485
- .ctf_layout_cell h3{
486
- font-size: 15px;
487
- margin-top: 0;
488
- }
489
- .ctf_layout_cell img{
490
- width: 100%;
491
- border: 1px solid #ddd;
492
- margin-top: 5px;
493
- }
494
-
495
- #ctf-admin #ctf_width_options{
496
- margin-top: 5px;
497
- display: none;
498
- }
499
- #ctf-admin #ctf_width_options label {
500
- font-size: 13px;
501
- position: relative;
502
- top: -2px;
503
- }
504
- #ctf-admin .ctf_layout_settings{
505
- float: left;
506
- clear: both;
507
- width: 96%;
508
-
509
- padding: 20px 2%;
510
- margin: 10px 0;
511
- font-size: 13px;
512
- background: #f9f9f9;
513
- background: rgba(255,255,255,0.8);
514
-
515
- -moz-border-radius: 8px;
516
- -webkit-border-radius: 8px;
517
- border-radius: 8px;
518
- }
519
- .ctf_layout_setting {
520
- clear:left;
521
- margin-bottom: 10px;
522
- }
523
- #ctf-admin .ctf_layout_options_wrap .ctf-tooltip{
524
- width: 98%;
525
- padding: 10px 1%;
526
- background: #eee;
527
- background: rgba(0,0,0,0.05);
528
- }
529
- #ctf-admin .ctf_layout_mobile_layout_setting {
530
- width: auto;
531
- }
532
-
533
- #ctf-admin .ctf-quick-start{
534
- display: block;
535
- float: left;
536
- clear: both;
537
- min-width: 808px;
538
- margin: 15px 0 0 0;
539
- padding: 15px 20px;
540
-
541
- border: 1px solid #ccc;
542
- background: #eee;
543
- background: rgba(255,255,255,0.5);
544
-
545
- -moz-border-radius: 5px;
546
- -webkit-border-radius: 5px;
547
- border-radius: 5px;
548
- }
549
- #ctf-admin .ctf-quick-start h3,
550
- #ctf-admin .ctf-quick-start p{
551
- margin: 0;
552
- padding: 5px 0;
553
- }
554
- #ctf-admin .ctf-pro-notice,
555
- #ctf-admin .ctf-pro-notice img{
556
- display: block;
557
- float: left;
558
- clear: both;
559
- margin: 20px 0 0 0;
560
- overflow: hidden;
561
-
562
- -moz-border-radius: 4px;
563
- -webkit-border-radius: 4px;
564
- border-radius: 4px;
565
-
566
- -moz-transition: all 0.5s ease-in-out;
567
- -webkit-transition: all 0.5s ease-in-out;
568
- -o-transition: all 0.5s ease-in-out;
569
- transition: all 0.5s ease-in-out;
570
- }
571
- #ctf-admin .ctf-pro-notice img{
572
- margin: 0;
573
- }
574
- #ctf-admin .ctf-pro-notice:hover{
575
- opacity: 0.95;
576
-
577
- -moz-transition: all 0.1s ease-in-out;
578
- -webkit-transition: all 0.1s ease-in-out;
579
- -o-transition: all 0.1s ease-in-out;
580
- transition: all 0.1s ease-in-out;
581
- }
582
- #ctf-admin .ctf-pro-options,
583
- #ctf-admin .ctf-pro-options label{
584
- color: #999 !important;
585
- }
586
- #ctf-admin .ctf-shortcode_table tr.ctf-pro-options{
587
- background: #eacccc;
588
- }
589
- #ctf-admin .ctf-pro-options label,
590
- #ctf-admin .ctf-pro-options .ctf_shortcode {
591
- color: #999 !important;
592
- }
593
- #ctf-admin .ctf_pro .ctf-tooltip-link,
594
- #ctf-admin .ctf-pro-options .ctf-tooltip-link {
595
- color: #aaa !important;
596
- }
597
- #ctf-admin .ctf-success{
598
- font-size: 14px;
599
- }
600
- #toplevel_page_custom-twitter-feeds .dashicons-admin-generic:before {
601
- content: "\f301";
602
- }
603
-
604
- /* Lite Notice */
605
- @media screen and (max-width: 600px) {
606
- #ctf-notice-bar {
607
- display:none !important
608
- }
609
- }
610
-
611
- #ctf-notice-bar {
612
- background-color: #DDDDDD;
613
- color: #777777;
614
- text-align: center;
615
- position: relative;
616
- padding: 7px;
617
- margin-bottom: 0;
618
- opacity: 1;
619
- transition: all .3s ease-in-out;
620
- max-height: 100px;
621
- overflow: hidden
622
- }
623
-
624
- #ctf-notice-bar.out {
625
- opacity: .5;
626
- max-height: 0
627
- }
628
-
629
- #ctf-notice-bar a {
630
- color: #e34f0e;
631
- }
632
-
633
- #ctf-notice-bar a:hover {
634
- color: #b85a1b
635
- }
636
-
637
- #ctf-notice-bar .dismiss {
638
- position: absolute;
639
- top: 0;
640
- right: 0;
641
- border: none;
642
- padding: 5px;
643
- margin-top: 1px;
644
- background: 0 0;
645
- color: #72777c;
646
- cursor: pointer
647
- }
648
-
649
- #ctf-notice-bar .dismiss:before {
650
- background: 0 0;
651
- color: #72777c;
652
- content: "\f335";
653
- display: block;
654
- font: normal 20px/20px dashicons;
655
- speak: none;
656
- height: 20px;
657
- text-align: center;
658
- width: 20px;
659
- -webkit-font-smoothing: antialiased
660
  }
1
+ #ctf-admin .ctf-tooltip{
2
+ display: none;
3
+ padding: 10px 0;
4
+ font-weight: normal;
5
+ }
6
+ #ctf-admin .ctf-tooltip ul{
7
+ margin-top: 0;
8
+ margin-bottom: 0;
9
+ }
10
+ #ctf-admin .ctf-tooltip li{
11
+ padding: 4px 0;
12
+ }
13
+ #ctf-admin .ctf-tooltip-link,
14
+ #ctf-admin .ctf-external-link{
15
+ font-size: 13px;
16
+ margin-left: 10px;
17
+ }
18
+ #ctf-admin .ctf-more-info{
19
+ padding: 10px 15px;
20
+ margin: 10px 0;
21
+ font-size: 13px;
22
+ background: #f9f9f9;
23
+ background: rgba(255,255,255,0.8);
24
+ -moz-border-radius: 8px;
25
+ -webkit-border-radius: 8px;
26
+ border-radius: 8px;
27
+ }
28
+ #ctf-admin .postbox .ctf-more-info{
29
+ background: #eee;
30
+ background: rgba(0,0,0,0.05);
31
+ }
32
+ #ctf-admin .ctf-more-info p{
33
+ font-size: 13px;
34
+ }
35
+ #ctf-admin .ctf-contents-links{
36
+ float: left;
37
+ clear: both;
38
+ width: 100%;
39
+ padding-bottom: 12px;
40
+ border-bottom: 1px solid #ccc;
41
+ margin-bottom: 15px;
42
+ }
43
+ #ctf-admin .ctf-contents-links a,
44
+ #ctf-admin .ctf-contents-links span{
45
+ display: block;
46
+ float: left;
47
+ padding: 2px 5px;
48
+ }
49
+ #ctf-admin .ctf-contents-links span{
50
+ padding-left: 0;
51
+ }
52
+ #ctf-admin .ctf-contents-links td,
53
+ #ctf-admin .ctf-contents-links th {
54
+ padding: 0;
55
+ vertical-align: middle;
56
+ }
57
+ #ctf-admin .ctf_include_replies_toggle {
58
+ display: block;
59
+ margin: 8px 0 0 142px;
60
+ }
61
+ #ctf-admin table.ctf_shortcode_table{
62
+ border-collapse: collapse;
63
+ }
64
+ #ctf-admin table.ctf_shortcode_table th,
65
+ #ctf-admin table.ctf_shortcode_table td{
66
+ border: 1px solid #999;
67
+ padding: 0.5rem;
68
+ text-align: left;
69
+ }
70
+ #ctf-admin table.ctf_shortcode_table th{
71
+ background: rgba(0,0,0,0.1);
72
+ }
73
+ #ctf-admin table.ctf_shortcode_table td{
74
+ background: rgba(255,255,255,0.5);
75
+ }
76
+ #ctf-admin .ctf_table_header{
77
+ background: #ddd;
78
+ font-weight: bold;
79
+ }
80
+ #ctf-admin .short-text {
81
+ width: 50px;
82
+ }
83
+
84
+ /* Configure tab */
85
+
86
+ #ctf-admin .ctf-radio-label {
87
+ display: inline-block;
88
+ width: 114px;
89
+ }
90
+ #ctf-admin .ctf-radio th,
91
+ #ctf-admin .ctf-radio td {
92
+ padding: 10px 10px 5px 0;
93
+ }
94
+ #ctf-admin #ctf-get-token{
95
+ display: block;
96
+ float: left;
97
+ clear: both;
98
+ padding: 0 21px 0 21px;
99
+ height: 47px;
100
+ line-height: 47px;
101
+ background: #1da1f2;
102
+ color: #e9eef3;
103
+ text-decoration: none;
104
+
105
+ -moz-border-radius: 5px;
106
+ -webkit-border-radius: 5px;
107
+ border-radius: 5px;
108
+
109
+ -moz-transition: all 0.5s ease-in-out;
110
+ -webkit-transition: all 0.5s ease-in-out;
111
+ -o-transition: all 0.5s ease-in-out;
112
+ transition: all 0.5s ease-in-out;
113
+ }
114
+ #ctf-admin #ctf-get-token .fa{
115
+ margin-right: 10px;
116
+ font-size: 22px;
117
+ position: relative;
118
+ top: 3px;
119
+ }
120
+ #ctf-admin #ctf-get-token:hover{
121
+ background-color: #0c7abf;
122
+ color: #fff;
123
+
124
+ -moz-transition: all 0.1s ease-in-out;
125
+ -webkit-transition: all 0.1s ease-in-out;
126
+ -o-transition: all 0.1s ease-in-out;
127
+ transition: all 0.1s ease-in-out;
128
+ }
129
+ .ctf-note-after-input {
130
+ font-style: italic;
131
+ font-size: 12px;
132
+ }
133
+ /* Config info */
134
+ #ctf_config{
135
+ float: left;
136
+ width: 100%;
137
+ clear: both;
138
+ margin: 10px 0 10px 0;
139
+ }
140
+ #ctf_config .ctf-tooltip-link{
141
+ position: relative;
142
+ top: 13px;
143
+ left: 6px;
144
+ }
145
+ #ctf_config_info{
146
+ float: left;
147
+ clear: both;
148
+ padding: 5px 15px;
149
+ margin: 10px 0 0 0;
150
+
151
+ background: #fff;
152
+ border: 1px solid #ddd;
153
+
154
+ -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
155
+ box-shadow: inset 0 1px 2px rgba(0,0,0,.07);
156
+
157
+ -moz-border-radius: 2px;
158
+ -webkit-border-radius: 2px;
159
+ border-radius: 2px;
160
+ }
161
+ #ctf-admin .ctf-success{
162
+ color: #7ad03a;
163
+ font-size: 16px;
164
+ padding: 6px;
165
+ }
166
+
167
+ /* shortcode examples */
168
+ #ctf-admin label{
169
+ position: relative;
170
+ }
171
+ #ctf-admin .ctf_shortcode{
172
+ display: none;
173
+ position: absolute;
174
+ z-index: 10;
175
+ font-size: 11px;
176
+ float: left;
177
+ width: auto;
178
+ white-space: pre-line;
179
+ line-height: 1.4;
180
+ max-width: 195px;
181
+ }
182
+ #ctf-admin .ctf_shortcode_symbol{
183
+ position: absolute;
184
+ right: -24px;
185
+ bottom: 0px;
186
+ padding: 2px 4px 3px 3px;
187
+ width: auto;
188
+ font-size: 11px;
189
+ margin-left: 5px;
190
+ }
191
+
192
+ /* Error notice */
193
+ #ctf-admin .ctf_notice, .ctf_notice{
194
+ margin-top: 5px;
195
+ background: #f9ecda;
196
+ padding: 5px 10px;
197
+ border: 1px solid #e89a2e;
198
+ color: #cf6100;
199
+
200
+ -moz-border-radius: 3px;
201
+ -webkit-border-radius: 3px;
202
+ border-radius: 3px;
203
+ }
204
+ #ctf-admin .ctf_notice a, .ctf_notice a{
205
+ color: #d85600;
206
+ }
207
+ #ctf-admin .ctf_notice a:hover, .ctf_notice a:hover,
208
+ #ctf-admin .ctf_notice a:focus, .ctf_notice a:focus{
209
+ color: #a34100;
210
+ }
211
+ #ctf-admin .ctf_notice p, .ctf_notice p{
212
+ margin: 0;
213
+ padding: 5px 0;
214
+ font-size: 13px;
215
+ }
216
+ .ctf_user_id_error{
217
+ display: none;
218
+
219
+ margin: 10px 0;
220
+ background: #F7E6E6;
221
+ padding: 5px 10px;
222
+ border: 1px solid #BA7B7B;
223
+ color: #592626;
224
+
225
+ -moz-border-radius: 3px;
226
+ -webkit-border-radius: 3px;
227
+ border-radius: 3px;
228
+ }
229
+
230
+ /* Review notice */
231
+ .ctf_review_notice{
232
+ position: relative;
233
+ overflow: hidden;
234
+ max-width: 870px;
235
+ margin-top: 10px;
236
+ padding: 10px 10px 7px 10px;
237
+
238
+ background: #E6F0E8;
239
+ border: 1px solid #6AB074;
240
+ color: #214F28;
241
+ }
242
+ .ctf_bfcm_sale_notice,
243
+ .ctf_new_user_sale_notice{
244
+ max-width: 672px;
245
+ }
246
+ .ctf_review_notice img{
247
+ width: 74px;
248
+ margin: 0 0 0 -100% !important;
249
+
250
+ -moz-border-radius: 4px;
251
+ -webkit-border-radius: 4px;
252
+ border-radius: 4px;
253
+ }
254
+ .ctf_review_notice .ctf-notice-text{
255
+ float: left;
256
+ clear: none;
257
+ width: 100%;
258
+ padding: 0;
259
+ }
260
+ .ctf_review_notice p{
261
+ float: left;
262
+ clear: both;
263
+ width: auto;
264
+ margin: 0 0 0 90px !important;
265
+ padding: 2px 40px 2px 0;
266
+ line-height: 1.4;
267
+ }
268
+ .ctf_review_notice .ctf-links{
269
+ margin-top: 4px !important;
270
+ }
271
+ .ctf_review_notice a{
272
+ display: inline-block;
273
+ padding: 0 8px;
274
+ color: #178529;
275
+ }
276
+ .ctf_review_notice a:hover,
277
+ .ctf_review_notice a:focus{
278
+ color: #0c7abf;
279
+ }
280
+ .ctf_review_notice .links{
281
+ margin: 0 0 0 82px !important;
282
+ padding: 4px 0 0 0;
283
+ margin-top: 6px !important;
284
+ }
285
+ .ctf_review_notice .ctf_notice_close,
286
+ .ctf_review_notice .ctf_bfcm_sale_notice_close,
287
+ .ctf_review_notice .ctf_new_user_sale_notice_close {
288
+ position: absolute;
289
+ top: 0;
290
+ right: 0;
291
+ padding: 10px;
292
+ line-height: 1;
293
+ }
294
+ .ctf_review_notice .ctf_notice_close:hover,
295
+ .ctf_review_notice .ctf_notice_close:focus{
296
+ color: #a34100;
297
+ }
298
+ .ctf_review_notice .ctf_offer_btn {
299
+ padding: 4px 12px 6px 12px;
300
+ background: green;
301
+ color: #fff;
302
+ border-radius: 4px;
303
+ display: inline-block;
304
+ text-decoration: none;
305
+ margin-left: 0;
306
+ }
307
+ .ctf_review_notice .ctf_offer_btn:hover, .ctf_review_notice .ctf_offer_btn:focus {
308
+ background: #049404;
309
+ color: #fff;
310
+ }
311
+ .ctf_review_notice .ctf_other_notice{
312
+ padding-top: 10px;
313
+ font-style: italic;
314
+ font-size: 12px;
315
+ }
316
+ .ctf_review_notice .ctf_other_notice a{
317
+ padding: 0;
318
+ }
319
+
320
+ /* Customize tab */
321
+ #ctf-admin #ctf_width_options{
322
+ margin-top: 10px;
323
+ display: none;
324
+ }
325
+
326
+ /* Pro only notices */
327
+ #ctf-admin .ctf_row{
328
+ display: block;
329
+ width: 100%;
330
+ }
331
+ #ctf-admin .ctf_pro,
332
+ #ctf-admin .ctf_pro label,
333
+ #ctf_admin .ctf_pro input{
334
+ color: #999 !important;
335
+ }
336
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child label,
337
+ #ctf-admin .ctf_pro .ctf_layout_type_list {
338
+ color: initial !important;
339
+ }
340
+ #ctf-admin .ctf_shortcode_table tr.ctf_pro{
341
+ background: #eacccc;
342
+ }
343
+ #ctf-admin .ctf_table_key{
344
+ display: block;
345
+ float: left;
346
+ width: 11px;
347
+ height: 11px;
348
+ border: 1px solid #999;
349
+ background: #eacccc;
350
+ margin: 3px 6px 0 0;
351
+ }
352
+ #ctf-admin .ctf_note {
353
+ font-size: 12px;
354
+ font-style: italic;
355
+ margin-left: 5px;
356
+ }
357
+ #ctf-admin .ctf_checkbox,
358
+ #ctf-admin .ctf_checkbox label{
359
+ font-size: 13px;
360
+ line-height: 1.4em;
361
+ padding-top: 10px;
362
+ vertical-align: top;
363
+ }
364
+ #ctf-admin .ctf_pro .ctf_layout_cell img{
365
+ opacity: 0.4;
366
+ }
367
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child img {
368
+ opacity: 1 !important;
369
+ }
370
+ #ctf-admin .ctf_pro input,
371
+ #ctf-admin .ctf_pro select {
372
+ color: #999 !important;
373
+ }
374
+ #ctf-admin .ctf_lock {
375
+ display: none;
376
+ padding: 8px 10px 8px 13px;
377
+ position: absolute;
378
+ top: 50%;
379
+ left: 50%;
380
+ margin-top: -4px;
381
+ margin-left: -34px;
382
+ background: rgba(255,255,255,0.8);
383
+ border-radius: 5px;
384
+ box-shadow: 0 0 5px 0 rgba(0,0,0,0.1);
385
+ color: #333;
386
+ z-index: 99;
387
+ font-size: 14px;
388
+ color: #666;
389
+ font-weight: 700;
390
+ text-decoration: none;
391
+ border: 1px solid #ccc;
392
+ }
393
+ #ctf-admin .ctf_lock i {
394
+ margin-right: 6px;
395
+ }
396
+ #ctf-admin .ctf_pro .ctf_layout_cell:hover .ctf_lock{
397
+ display: block;
398
+ }
399
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child .ctf_lock,
400
+ #ctf-admin .ctf_pro .ctf_layout_cell:first-child:hover .ctf_lock {
401
+ display: none;
402
+ }
403
+
404
+ /* Layout */
405
+ .form-table .ctf_layout_type {
406
+ display: inline-block;
407
+ margin-left: 20px;
408
+ }
409
+ .form-table .ctf_layout_type:first-child {
410
+ margin-left: 0;
411
+ }
412
+ .ctf_layout_setting,
413
+ .ctf_box_setting{
414
+ margin-bottom: 20px;
415
+ }
416
+ .ctf_layout_setting:last-child,
417
+ .ctf_box_setting:last-child {
418
+ margin-bottom: 0;
419
+ }
420
+ .ctf_layout_setting label,
421
+ .ctf_box_setting label {
422
+ display: inline-block;
423
+ font-weight: bold;
424
+ font-size: 14px;
425
+ padding-bottom: 5px;
426
+ padding-right: 5px;
427
+ }
428
+ #ctf-admin .ctf_layout_options_wrap .ctf_shortcode_symbol{
429
+ bottom: 4px;
430
+ right: -20px;
431
+ }
432
+ #ctf-admin .ctf_layout_options_wrap .ctf_shortcode,
433
+ #ctf-admin .ctf_box_setting .ctf_shortcode{
434
+ position: relative;
435
+ float: none;
436
+ top: -4px
437
+ }
438
+
439
+ .ctf_layout_cell {
440
+ position: relative;
441
+ padding: 0;
442
+ display: inline-block;
443
+ float: left;
444
+ width: 155px;
445
+ padding: 15px 15px 10px 15px;
446
+ margin-right: 10px;
447
+ background: #f6f6f6;
448
+ border: 1px solid #ddd;
449
+ -moz-border-radius: 5px;
450
+ -webkit-border-radius: 5px;
451
+ border-radius: 5px;
452
+ vertical-align: top;
453
+ }
454
+ .ctf_layout_cell:nth-child(4) {
455
+ margin-right: 0;
456
+ }
457
+ @media all and (max-width: 1200px){
458
+ .ctf_layout_cell{
459
+ width: 21%;
460
+ padding: 1%;
461
+ margin-right: 1%;
462
+ }
463
+ }
464
+ @media all and (max-width: 480px){
465
+ .ctf_layout_cell{
466
+ width: 98%;
467
+ padding: 1%;
468
+ margin: 5px 0;
469
+ }
470
+ }
471
+ .ctf_label {
472
+ font-size: 16px;
473
+ font-weight: bold;
474
+ padding-left: 2px;
475
+ }
476
+ .ctf_layout_cell:hover,
477
+ .ctf_layout_cell:focus{
478
+ background: #fcfcfc;
479
+ cursor: pointer;
480
+ }
481
+ .ctf_layout_cell.ctf_layout_selected{
482
+ background: #f7faf1;
483
+ border: 1px solid #7ad03a;
484
+ }
485
+ .ctf_layout_cell h3{
486
+ font-size: 15px;
487
+ margin-top: 0;
488
+ }
489
+ .ctf_layout_cell img{
490
+ width: 100%;
491
+ border: 1px solid #ddd;
492
+ margin-top: 5px;
493
+ }
494
+
495
+ #ctf-admin #ctf_width_options{
496
+ margin-top: 5px;
497
+ display: none;
498
+ }
499
+ #ctf-admin #ctf_width_options label {
500
+ font-size: 13px;
501
+ position: relative;
502
+ top: -2px;
503
+ }
504
+ #ctf-admin .ctf_layout_settings{
505
+ float: left;
506
+ clear: both;
507
+ width: 96%;
508
+
509
+ padding: 20px 2%;
510
+ margin: 10px 0;
511
+ font-size: 13px;
512
+ background: #f9f9f9;
513
+ background: rgba(255,255,255,0.8);
514
+
515
+ -moz-border-radius: 8px;
516
+ -webkit-border-radius: 8px;
517
+ border-radius: 8px;
518
+ }
519
+ .ctf_layout_setting {
520
+ clear:left;
521
+ margin-bottom: 10px;
522
+ }
523
+ #ctf-admin .ctf_layout_options_wrap .ctf-tooltip{
524
+ width: 98%;
525
+ padding: 10px 1%;
526
+ background: #eee;
527
+ background: rgba(0,0,0,0.05);
528
+ }
529
+ #ctf-admin .ctf_layout_mobile_layout_setting {
530
+ width: auto;
531
+ }
532
+
533
+ #ctf-admin .ctf-quick-start{
534
+ display: block;
535
+ float: left;
536
+ clear: both;
537
+ min-width: 808px;
538
+ margin: 15px 0 0 0;
539
+ padding: 15px 20px;
540
+
541
+ border: 1px solid #ccc;
542
+ background: #eee;
543
+ background: rgba(255,255,255,0.5);
544
+
545
+ -moz-border-radius: 5px;
546
+ -webkit-border-radius: 5px;
547
+ border-radius: 5px;
548
+ }
549
+ #ctf-admin .ctf-quick-start h3,
550
+ #ctf-admin .ctf-quick-start p{
551
+ margin: 0;
552
+ padding: 5px 0;
553
+ }
554
+ #ctf-admin .ctf-pro-notice,
555
+ #ctf-admin .ctf-pro-notice img{
556
+ display: block;
557
+ float: left;
558
+ clear: both;
559
+ margin: 20px 0 0 0;
560
+ overflow: hidden;
561
+
562
+ -moz-border-radius: 4px;
563
+ -webkit-border-radius: 4px;
564
+ border-radius: 4px;
565
+
566
+ -moz-transition: all 0.5s ease-in-out;
567
+ -webkit-transition: all 0.5s ease-in-out;
568
+ -o-transition: all 0.5s ease-in-out;
569
+ transition: all 0.5s ease-in-out;
570
+ }
571
+ #ctf-admin .ctf-pro-notice img{
572
+ margin: 0;
573
+ }
574
+ #ctf-admin .ctf-pro-notice:hover{
575
+ opacity: 0.95;
576
+
577
+ -moz-transition: all 0.1s ease-in-out;
578
+ -webkit-transition: all 0.1s ease-in-out;
579
+ -o-transition: all 0.1s ease-in-out;
580
+ transition: all 0.1s ease-in-out;
581
+ }
582
+ #ctf-admin .ctf-pro-options,
583
+ #ctf-admin .ctf-pro-options label{
584
+ color: #999 !important;
585
+ }
586
+ #ctf-admin .ctf-shortcode_table tr.ctf-pro-options{
587
+ background: #eacccc;
588
+ }
589
+ #ctf-admin .ctf-pro-options label,
590
+ #ctf-admin .ctf-pro-options .ctf_shortcode {
591
+ color: #999 !important;
592
+ }
593
+ #ctf-admin .ctf_pro .ctf-tooltip-link,
594
+ #ctf-admin .ctf-pro-options .ctf-tooltip-link {
595
+ color: #aaa !important;
596
+ }
597
+ #ctf-admin .ctf-success{
598
+ font-size: 14px;
599
+ }
600
+ #toplevel_page_custom-twitter-feeds .dashicons-admin-generic:before {
601
+ content: "\f301";
602
+ }
603
+
604
+ /* Lite Notice */
605
+ @media screen and (max-width: 600px) {
606
+ #ctf-notice-bar {
607
+ display:none !important
608
+ }
609
+ }
610
+
611
+ #ctf-notice-bar {
612
+ background-color: #DDDDDD;
613
+ color: #777777;
614
+ text-align: center;
615
+ position: relative;
616
+ padding: 7px;
617
+ margin-bottom: 0;
618
+ opacity: 1;
619
+ transition: all .3s ease-in-out;
620
+ max-height: 100px;
621
+ overflow: hidden
622
+ }
623
+
624
+ #ctf-notice-bar.out {
625
+ opacity: .5;
626
+ max-height: 0
627
+ }
628
+
629
+ #ctf-notice-bar a {
630
+ color: #e34f0e;
631
+ }
632
+
633
+ #ctf-notice-bar a:hover {
634
+ color: #b85a1b
635
+ }
636
+
637
+ #ctf-notice-bar .dismiss {
638
+ position: absolute;
639
+ top: 0;
640
+ right: 0;
641
+ border: none;
642
+ padding: 5px;
643
+ margin-top: 1px;
644
+ background: 0 0;
645
+ color: #72777c;
646
+ cursor: pointer
647
+ }
648
+
649
+ #ctf-notice-bar .dismiss:before {
650
+ background: 0 0;
651
+ color: #72777c;
652
+ content: "\f335";
653
+ display: block;
654
+ font: normal 20px/20px dashicons;
655
+ speak: none;
656
+ height: 20px;
657
+ text-align: center;
658
+ width: 20px;
659
+ -webkit-font-smoothing: antialiased
660
  }
css/ctf-blocks.css ADDED
@@ -0,0 +1,3 @@
 
 
 
1
+ .ctf {
2
+ pointer-events: none !important;
3
+ }
css/ctf-styles.css CHANGED
@@ -1,791 +1,791 @@
1
-
2
- /* General */
3
- #ctf{
4
- overflow-y: auto;
5
- -ms-overflow-y: auto;
6
- }
7
- #ctf .ctf-item{
8
- padding: 15px 5px;
9
- border-top: 1px solid #ddd;
10
- border-top: 1px solid rgba(0,0,0,0.1);
11
- overflow: hidden;
12
- }
13
- #ctf .ctf-item:first-child{
14
- border-top: none;
15
- }
16
- #ctf .ctf_remaining {
17
- display: none;
18
- }
19
- #ctf .ctf_more{
20
- padding: 1px;
21
- border: 1px solid transparent;
22
- border-radius: 3px;
23
- display: inline-block;
24
- line-height: 1;
25
- }
26
- #ctf .ctf_more:hover {
27
- cursor: pointer;
28
- background: rgba(0,0,0,0.05);
29
- border: 1px solid rgba(0,0,0,0.1);
30
- text-decoration: none;
31
- }
32
-
33
- /* Header */
34
- #ctf .ctf-header{
35
- float: left;
36
- clear: both;
37
- margin: 0;
38
- padding: 5px 5px 15px 5px;
39
- line-height: 1.2;
40
- width: 100%;
41
- border-bottom: 1px solid #ddd;
42
- border-bottom: 1px solid rgba(0,0,0,0.1);
43
-
44
- -webkit-box-sizing: border-box;
45
- -moz-box-sizing: border-box;
46
- box-sizing: border-box;
47
- }
48
- #ctf .ctf-header a{
49
- float: left;
50
- display: block;
51
- min-width: 100%\9;
52
- }
53
- /* Header avatar */
54
- #ctf .ctf-header-img{
55
- float: left;
56
- position: relative;
57
- width: 48px;
58
- margin: 0 0 0 -100% !important;
59
- overflow: hidden;
60
-
61
- -moz-border-radius: 40px;
62
- -webkit-border-radius: 40px;
63
- border-radius: 40px;
64
- }
65
- #ctf .ctf-header-img img{
66
- display: block;
67
- float: left;
68
- margin: 0 !important;
69
- padding: 0 !important;
70
- border: none !important;
71
-
72
- -moz-border-radius: 40px;
73
- -webkit-border-radius: 40px;
74
- border-radius: 40px;
75
- }
76
- #ctf .ctf-author-avatar {
77
- clear: both;
78
- margin: 3px 6px 0 -53px;
79
- }
80
- #ctf .ctf-author-name {
81
- margin-left: 6px;
82
- }
83
- #ctf .ctf-author-box {
84
- margin-left: 53px;
85
- }
86
- /* Header avatar hover */
87
- #ctf .ctf-header-img-hover,
88
- #ctf .ctf-header-generic-icon{
89
- display: none;
90
- position: absolute;
91
- width: 100%;
92
- top: 0;
93
- bottom: 0;
94
- left: 0;
95
- text-align: center;
96
- color: #fff;
97
- background: #1b95e0;
98
- background: rgba(27,149,224,0.85);
99
-
100
- -moz-border-radius: 40px;
101
- -webkit-border-radius: 40px;
102
- border-radius: 40px;
103
- z-index: 999;
104
- }
105
- #ctf .ctf-header-img-hover .fa,
106
- #ctf .ctf-header-generic-icon .fa,
107
- #ctf .ctf-header-img-hover svg,
108
- #ctf .ctf-header-generic-icon svg{
109
- position: absolute;
110
- top: 50%;
111
- left: 50%;
112
- margin-top: -10px;
113
- margin-left: -8px;
114
- font-size: 20px;
115
- z-index: 1000;
116
- }
117
- #ctf .ctf-header-img-hover svg,
118
- #ctf .ctf-header-generic-icon svg {
119
- margin-top: -10px;
120
- margin-left: -10px;
121
- }
122
- /* Header text */
123
- #ctf .ctf-header-text{
124
- float: left;
125
- width: 100%;
126
- padding-top: 5px;
127
- }
128
- #ctf .ctf-no-bio .ctf-header-text{
129
- padding-top: 13px;
130
- }
131
- #ctf .ctf-header a{
132
- text-decoration: none;
133
- }
134
- #ctf .ctf-header-text .ctf-header-user,
135
- #ctf .ctf-header-text p{
136
- float: left;
137
- clear: both;
138
- width: auto;
139
- margin: 0 0 0 60px !important;
140
- padding: 0 !important;
141
- }
142
- #ctf .ctf-header-name{
143
- font-weight: bold;
144
- margin-right: 6px;
145
- }
146
- #ctf .ctf-header-text .ctf-header-bio{
147
- padding: 0 !important;
148
- word-break: break-word;
149
- }
150
- #ctf .ctf-header-text .ctf-verified{
151
- margin-right: 5px;
152
- }
153
- /* Follow */
154
- #ctf .ctf-header .ctf-header-follow{
155
- display: inline-block;
156
- padding: 3px 6px;
157
- margin: 0 0 0 2px;
158
- position: relative;
159
- top: -2px;
160
- background: #1b95e0;
161
- color: #fff;
162
- font-size: 11px;
163
-
164
- -moz-border-radius: 3px;
165
- -webkit-border-radius: 3px;
166
- border-radius: 3px;
167
-
168
- -moz-transition: background 0.6s ease-in-out;
169
- -webkit-transition: background 0.6s ease-in-out;
170
- -o-transition: background 0.6s ease-in-out;
171
- transition: background 0.6s ease-in-out;
172
- }
173
- #ctf .ctf-header .ctf-header-follow .fa,
174
- #ctf .ctf-header .ctf-header-follow svg{
175
- margin-right: 3px;
176
- font-size: 12px;
177
- }
178
- #ctf .ctf-header a:hover .ctf-header-follow{
179
- background: #0c7abf;
180
-
181
- -moz-transition: background 0.2s ease-in-out;
182
- -webkit-transition: background 0.2s ease-in-out;
183
- -o-transition: background 0.2s ease-in-out;
184
- transition: background 0.2s ease-in-out;
185
- }
186
- /* Generic header version */
187
- #ctf .ctf-header-type-generic .ctf-header-img{
188
- color: #fff;
189
- width: 48px;
190
- height: 48px;
191
- position: relative;
192
- }
193
- #ctf .ctf-header-type-generic .ctf-header-link:hover .ctf-header-generic-icon{
194
- background: #0c7abf;
195
- }
196
- #ctf .ctf-header-type-generic .ctf-header-no-bio{
197
- padding-top: 9px !important;
198
- font-size: 16px;
199
- font-weight: bold;
200
- }
201
- #ctf .ctf-header-type-generic .ctf-header-generic-icon{
202
- display: block;
203
- color: #fff;
204
- background: #1b95e0;
205
-
206
- -moz-transition: background 0.2s ease-in-out;
207
- -webkit-transition: background 0.2s ease-in-out;
208
- -o-transition: background 0.2s ease-in-out;
209
- transition: background 0.2s ease-in-out;
210
- }
211
- #ctf .ctf-header-type-generic:hover .ctf-header-generic-icon{
212
- display: block;
213
- opacity: 1;
214
- }
215
- #ctf .ctf-header .ctf-header-user{
216
- font-size: 16px;
217
- line-height: 1.3;
218
- -ms-word-wrap: break-word;
219
- word-break: break-word;
220
- }
221
- #ctf .ctf-header p,
222
- #ctf .ctf-header .ctf-header-screenname{
223
- font-size: 13px;
224
- line-height: 1.3;
225
- }
226
-
227
- /* screenreader */
228
-
229
- .ctf-screenreader{
230
- text-indent: -9999px !important;
231
- display: inline-block !important;
232
- width: 0 !important;
233
- height: 0 !important;
234
- line-height: 0 !important;
235
- text-align: left !important;
236
- }
237
-
238
- /* No bio */
239
- #ctf .ctf-header-text.sbi-no-info .ctf-header-user{
240
- padding-top: 9px !important;
241
- }
242
- #ctf .ctf-header-text.sbi-no-bio .ctf-header-counts{
243
- clear: both;
244
- }
245
-
246
- /* Context */
247
- #ctf .ctf-context {
248
- float: left;
249
- width: 100%;
250
- margin-left: 57px;
251
- margin-bottom: 2px;
252
- }
253
- #ctf .ctf-retweet-icon{
254
- display: inline-block;
255
- font-size: inherit;
256
- background: #19cf86;
257
- color: #fff !important;
258
- padding: 1px 3px 3px 3px;
259
- line-height: 1;
260
- margin: 0 5px 0 0;
261
-
262
- -moz-border-radius: 3px;
263
- -webkit-border-radius: 3px;
264
- border-radius: 3px;
265
- }
266
-
267
- /* Author */
268
- #ctf .ctf-author-box{
269
- margin-left: 56px;
270
- }
271
- #ctf .ctf-author-avatar{
272
- width: 48px;
273
- height: 48px;
274
- margin: 3px 6px 0 -56px;
275
- float: left;
276
- overflow: hidden;
277
- border: none;
278
-
279
- -moz-border-radius: 5px;
280
- -webkit-border-radius: 5px;
281
- border-radius: 5px;
282
- }
283
- #ctf .ctf-author-avatar img{
284
- margin: 0 !important;
285
- padding: 0;
286
- }
287
- #ctf .ctf-author-name,
288
- #ctf .ctf-author-screenname,
289
- #ctf .ctf-verified,
290
- #ctf .ctf-tweet-meta{
291
- margin-left: 4px;
292
- margin-right: 4px;
293
- display: inline-block;
294
- }
295
- #ctf .ctf-screename-sep{
296
- margin: 0 2px;
297
- display: inline-block;
298
- }
299
- #ctf .ctf-author-name{
300
- font-weight: bold !important;
301
- margin-top: 4px;
302
- }
303
- #ctf .ctf-author-box-link{
304
- color: black;
305
- text-decoration: none;
306
- display: inline;
307
- line-height: 1.5 !important;
308
- }
309
- #ctf .ctf-verified,
310
- #ctf .ctf-quoted-verified {
311
- color: #77c7f7;
312
- margin-left: 0;
313
- }
314
- #ctf .ctf-quoted-verified {
315
- margin-right: 4px;
316
- }
317
- #ctf .ctf-tweet-meta{
318
- display: inline-block;
319
- }
320
- #ctf .ctf-replied-to-text a {
321
- font-size: inherit;
322
- color: inherit;
323
- font-weight: inherit;
324
- }
325
- #ctf p.ctf-tweet-text,
326
- #ctf p.ctf-media-link {
327
- padding: 0 !important;
328
- margin: 0 !important;
329
- line-height: 1.4;
330
- }
331
- #ctf .ctf-tweet-content,
332
- #ctf .ctf-tweet-actions{
333
- margin-left: 58px;
334
- }
335
- #ctf .ctf-corner-logo {
336
- display: inline-block;
337
- float: right;
338
- color: #1b95e0;
339
- font-size: 24px;
340
- }
341
- #ctf.ctf-narrow .ctf-corner-logo {
342
- font-size: 20px;
343
- }
344
- /* No avatar */
345
- #ctf .ctf-hide-avatar .ctf-context,
346
- #ctf .ctf-hide-avatar .ctf-tweet-content,
347
- #ctf .ctf-hide-avatar .ctf-tweet-actions,
348
- #ctf .ctf-hide-avatar .ctf-quoted-tweet,
349
- #ctf .ctf-hide-avatar .ctf-author-box,
350
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-context,
351
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-content,
352
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-actions,
353
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-quoted-tweet,
354
- #ctf.ctf-narrow .ctf-hide-avatar .ctf-author-box,
355
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-context,
356
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-content,
357
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-actions,
358
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-quoted-tweet,
359
- #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-author-box{
360
- margin-left: 0;
361
- }
362
-
363
- /*Quoted tweets*/
364
- #ctf .ctf-quoted-tweet{
365
- display: block;
366
- /*float: left;*/
367
- clear: both;
368
- margin: 7px 0 5px 58px;
369
- padding: 8px 12px;
370
- border: 1px solid #ddd;
371
- border: 1px solid rgba(0,0,0,0.1);
372
- text-decoration: none;
373
-
374
- -moz-border-radius: 5px;
375
- -webkit-border-radius: 5px;
376
- border-radius: 5px;
377
-
378
- -moz-transition: background 0.1s ease-in-out;
379
- -webkit-transition: background 0.1s ease-in-out;
380
- -o-transition: background 0.1s ease-in-out;
381
- transition: background 0.1s ease-in-out;
382
- }
383
- #ctf .ctf-quoted-tweet:hover,
384
- #ctf .ctf-quoted-tweet:focus{
385
- background: #eee;
386
- background: rgba(0,0,0,0.05);
387
- border: 1px solid #ddd;
388
- border: 1px solid rgba(0,0,0,0.05);
389
-
390
- -moz-transition: background 0.1s ease-in-out;
391
- -webkit-transition: background 0.1s ease-in-out;
392
- -o-transition: background 0.1s ease-in-out;
393
- transition: background 0.1s ease-in-out;
394
- }
395
- #ctf .ctf-quoted-author-name{
396
- font-weight: bold;
397
- margin-right: 4px;
398
- }
399
- #ctf .ctf-quoted-tweet p{
400
- margin: 2px 0;
401
- }
402
-
403
- /* Actions */
404
- #ctf .ctf-tweet-actions a{
405
- padding: 2px 10px 2px 5px;
406
- float: left;
407
- }
408
- #ctf .ctf-tweet-actions a.ctf-reply,
409
- #ctf .ctf-tweet-actions a.ctf-retweet,
410
- #ctf .ctf-tweet-actions a.ctf-like{
411
- color: #bbb;
412
- color: rgba(0,0,0,0.30);
413
- text-decoration: none;
414
- border: none;
415
- -moz-transition: color 0.1s ease-in-out;
416
- -webkit-transition: color 0.1s ease-in-out;
417
- -o-transition: color 0.1s ease-in-out;
418
- transition: color 0.1s ease-in-out;
419
- }
420
- #ctf .ctf-tweet-actions .ctf-reply{
421
- margin-left: -5px;
422
- }
423
- #ctf .ctf-tweet-actions a:hover,
424
- #ctf .ctf-tweet-actions a:focus{
425
- -moz-transition: color 0.1s ease-in-out;
426
- -webkit-transition: color 0.1s ease-in-out;
427
- -o-transition: color 0.1s ease-in-out;
428
- transition: color 0.1s ease-in-out;
429
- }
430
- #ctf .ctf-tweet-actions .ctf-reply:hover,
431
- #ctf .ctf-tweet-actions .ctf-reply:focus{
432
- color: #E34F0E !important;
433
- }
434
- #ctf .ctf-tweet-actions .ctf-retweet:hover,
435
- #ctf .ctf-tweet-actions .ctf-retweet:focus{
436
- color: #19cf86 !important;
437
- }
438
- #ctf .ctf-tweet-actions .ctf-like:hover,
439
- #ctf .ctf-tweet-actions .ctf-like:focus{
440
- color: #e81c4f !important;
441
- }
442
- #ctf .ctf-action-count {
443
- display: none;
444
- float: right;
445
- padding-left: 3px;
446
- font-weight: bold;
447
- font-size: 11px;
448
- line-height: 14px;
449
- }
450
- #ctf .ctf-tweet-text-media-wrap,
451
- #ctf .ctf-quoted-tweet-text-media-wrap{
452
- margin-left: 5px;
453
- padding: 4px;
454
- display: inline-block;
455
- border: 1px solid #ddd;
456
- border: 1px solid rgba(0,0,0,0.15);
457
-
458
- color: inherit;
459
- text-decoration: none;
460
- line-height: 1;
461
-
462
- -moz-border-radius: 2px;
463
- -webkit-border-radius: 2px;
464
- border-radius: 2px;
465
-
466
- -moz-transition: background 0.1s ease-in-out;
467
- -webkit-transition: background 0.1s ease-in-out;
468
- -o-transition: background 0.1s ease-in-out;
469
- transition: background 0.1s ease-in-out;
470
- }
471
- #ctf .ctf-tweet-text-media-wrap:hover,
472
- #ctf .ctf-tweet-text-media-wrap:focus{
473
- background: #eee;
474
- background: rgba(0,0,0,0.05);
475
- border: 1px solid #ddd;
476
- border: 1px solid rgba(0,0,0,0.1);
477
-
478
- -moz-transition: background 0.1s ease-in-out;
479
- -webkit-transition: background 0.1s ease-in-out;
480
- -o-transition: background 0.1s ease-in-out;
481
- transition: background 0.1s ease-in-out;
482
- }
483
- .ctf-multi-media-icon .ctf-tweet-text-media,
484
- .ctf-quoted-tweet-text .ctf-tweet-text-media{
485
- margin-left: 3px;
486
- }
487
-
488
- /* Load more */
489
- #ctf #ctf-more{
490
- display: block;
491
- border: none;
492
- background: #eee;
493
- background: rgba(0,0,0,0.05);
494
- width: 100%;
495
- min-height: 30px;
496
- padding: 5px 0;
497
- margin: 0;
498
- position: relative;
499
- text-align: center;
500
- box-sizing: border-box;
501
- outline: none;
502
- text-decoration: none;
503
-
504
- -moz-border-radius: 5px;
505
- -webkit-border-radius: 5px;
506
- border-radius: 5px;
507
-
508
- -moz-transition: background 0.1s ease-in-out;
509
- -webkit-transition: background 0.1s ease-in-out;
510
- -o-transition: background 0.1s ease-in-out;
511
- transition: background 0.1s ease-in-out;
512
- }
513
- #ctf #ctf-more:hover{
514
- background: #ddd;
515
- background: rgba(0,0,0,0.1);
516
- -moz-transition: background 0.1s ease-in-out;
517
- -webkit-transition: background 0.1s ease-in-out;
518
- -o-transition: background 0.1s ease-in-out;
519
- transition: background 0.1s ease-in-out;
520
- }
521
- #ctf #ctf-more.ctf-loading span{
522
- -moz-transition: opacity 0.5s ease-in-out;
523
- -webkit-transition: opacity 0.5s ease-in-out;
524
- -o-transition: opacity 0.5s ease-in-out;
525
- transition: opacity 0.5s ease-in-out;
526
- }
527
- #ctf #ctf-more.ctf-loading span{
528
- filter: alpha(opacity=0);
529
- opacity: 0;
530
- }
531
- #ctf .ctf-loader {
532
- width: 20px;
533
- height: 20px;
534
-
535
- position: absolute;
536
- top: 50%;
537
- left: 50%;
538
- margin: -10px 0 0 -10px;
539
- background-color: unset;
540
- background-color: rgba(0,0,0,0.5);
541
-
542
- border-radius: 100%;
543
- -webkit-animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
544
- animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
545
- }
546
- @-webkit-keyframes ctf-sk-scaleout {
547
- 0% { -webkit-transform: scale(0) }
548
- 100% {
549
- -webkit-transform: scale(1.0);
550
- opacity: 0;
551
- }
552
- }
553
- @keyframes ctf-sk-scaleout {
554
- 0% {
555
- -webkit-transform: scale(0);
556
- transform: scale(0);
557
- } 100% {
558
- -webkit-transform: scale(1.0);
559
- transform: scale(1.0);
560
- opacity: 0;
561
- }
562
- }
563
-
564
- /* Credit link */
565
- .ctf-credit-link{
566
- width: 100%;
567
- clear: both;
568
- padding: 5px 0;
569
- margin-top: 5px;
570
- text-align: center;
571
- font-size: 12px;
572
- }
573
- .ctf-credit-link .fa,
574
- .ctf-credit-link svg {
575
- margin-right: 4px;
576
- }
577
-
578
- /* SVGs */
579
- #ctf svg { box-sizing: unset; }
580
- #ctf svg:not(:root).svg-inline--fa,
581
- #ctf_lightbox svg:not(:root).svg-inline--fa{
582
- box-sizing: unset;
583
- overflow: visible;
584
- width: 1em;
585
- }
586
- #ctf .svg-inline--fa.fa-w-16,
587
- #ctf .svg-inline--fa.fa-w-16{
588
- width: 16px;
589
- }
590
- #ctf .svg-inline--fa.fa-w-20 {
591
- width: 20px;
592
- }
593
- #ctf .ctf-tweet-actions a.ctf-retweet svg,
594
- #ctf .ctf-context .ctf-retweet-icon svg{
595
- width: 1.2em;
596
- }
597
- #ctf .svg-inline--fa,
598
- #ctf_lightbox .svg-inline--fa{
599
- display: inline-block;
600
- font-size: inherit;
601
- height: 1em;
602
- overflow: visible;
603
- vertical-align: -.125em;
604
- }
605
-
606
- #ctf #ctf .ctf-photo-hover span.fa-arrows-alt,
607
- #ctf .ctf-photo-hover svg.fa-arrows-alt {
608
- display: none;
609
- }
610
-
611
- /* Default styles */
612
- #ctf.ctf-styles .ctf-author-screenname,
613
- #ctf.ctf-styles .ctf-context,
614
- #ctf.ctf-styles .ctf-twitterlink,
615
- #ctf.ctf-styles .ctf-tweet-meta{
616
- font-size: 12px;
617
- }
618
-
619
- /* Errors */
620
- #ctf .ctf-error{
621
- width: 100%;
622
- text-align: center;
623
- }
624
- #ctf .ctf-error p,
625
- #ctf .ctf-error iframe,
626
- #ctf .ctf-error code{
627
- padding: 0;
628
- margin: 0 2px;
629
- }
630
- #ctf .ctf-error-user p{
631
- padding-bottom: 5px;
632
- }
633
- #ctf .ctf-error-user{
634
- width: 100%;
635
- }
636
- #ctf .ctf-error-admin{
637
- margin-top: 15px;
638
- display: inline-block;
639
- }
640
- #ctf .ctf-error code{
641
- display: block;
642
- padding: 4px;
643
- }
644
- .ctf_smash_error{
645
- border: 1px solid #ddd;
646
- background: #eee;
647
- color: #333;
648
- margin: 10px 0 0;
649
- padding: 10px 15px;
650
- font-size: 13px;
651
- text-align: center;
652
- clear: both;
653
-
654
- -moz-border-radius: 4px;
655
- -webkit-border-radius: 4px;
656
- border-radius: 4px;
657
- }
658
- .ctf_smash_error p{
659
- padding: 5px 0 !important;
660
- margin: 0 !important;
661
- line-height: 1.3 !important;
662
- }
663
- .ctf_smash_error span{
664
- font-size: 12px;
665
- }
666
-
667
- /* Out of tweets */
668
- #ctf .ctf-out-of-tweets{
669
- width: 100%;
670
- }
671
- #ctf .ctf-out-of-tweets{
672
- display: block;
673
- border: 1px solid rgba(0,0,0,0.1);
674
- background: none;
675
- background: none;
676
- width: 100%;
677
- min-height: 30px;
678
- padding: 5px 0;
679
- margin: 0;
680
- position: relative;
681
- text-align: center;
682
- box-sizing: border-box;
683
-
684
- -moz-border-radius: 5px;
685
- -webkit-border-radius: 5px;
686
- border-radius: 5px;
687
- }
688
- #ctf .ctf-out-of-tweets p,
689
- #ctf .ctf-out-of-tweets iframe{
690
- margin: 0 2px;
691
- padding: 0;
692
- }
693
- #ctf .ctf-out-of-tweets p{
694
- padding: 3px;
695
- display: none;
696
- }
697
-
698
- /* Mobile / Narrow */
699
- #ctf.ctf-narrow .ctf-author-avatar,
700
- #ctf.ctf-narrow .ctf-author-avatar img{
701
- width: 38px;
702
- height: 38px;
703
- }
704
- #ctf.ctf-narrow .ctf-tweet-content,
705
- #ctf.ctf-narrow .ctf-tweet-actions,
706
- #ctf.ctf-narrow .ctf-quoted-tweet{
707
- margin-left: 48px;
708
- }
709
- #ctf.ctf-narrow .ctf-context {
710
- margin-left: 49px;
711
- }
712
- #ctf.ctf-narrow .ctf-author-box{
713
- margin-left: 46px;
714
- }
715
- #ctf.ctf-narrow .ctf-author-avatar {
716
- margin-left: -46px;
717
- }
718
- #ctf.ctf-narrow .ctf-tweet-content{
719
- padding: 3px 0 5px 0;
720
- }
721
- #ctf.ctf-narrow .ctf-tweet-actions {
722
- float: left;
723
- clear: both;
724
- }
725
-
726
- /* Super narrow */
727
- #ctf.ctf-super-narrow .ctf-context,
728
- #ctf.ctf-super-narrow .ctf-tweet-content,
729
- #ctf.ctf-super-narrow .ctf-tweet-actions,
730
- #ctf.ctf-super-narrow .ctf-quoted-tweet {
731
- margin-left: 0;
732
- }
733
- #ctf.ctf-super-narrow .ctf-author-box{
734
- min-height: 32px;
735
- margin-bottom: 2px;
736
- }
737
- #ctf.ctf-super-narrow .ctf-author-avatar,
738
- #ctf.ctf-super-narrow .ctf-author-avatar img{
739
- width: 32px;
740
- height: 32px;
741
- }
742
- #ctf.ctf-super-narrow .ctf-author-box{
743
- margin-left: 38px;
744
- float: left;
745
- clear: both;
746
- }
747
- #ctf.ctf-super-narrow .ctf-author-avatar {
748
- margin-left: -38px;
749
- margin-top: 0;
750
- }
751
- #ctf.ctf-super-narrow .ctf-author-screenname,
752
- #ctf.ctf-super-narrow .ctf-screename-sep {
753
- display: none;
754
- }
755
- #ctf.ctf-super-narrow .ctf-context {
756
- padding-bottom: 5px;
757
- }
758
- #ctf.ctf-super-narrow .ctf-author-name{
759
- display: inline-block;
760
- }
761
- #ctf.ctf-super-narrow .ctf-tweet-content{
762
- float: left;
763
- clear: both;
764
- }
765
- #ctf.ctf-super-narrow .ctf-credit-link{
766
- font-size: 10px;
767
- }
768
- #ctf.ctf-super-narrow .ctf-header-bio{
769
- margin: 0 !important;
770
- padding-top: 5px;
771
- }
772
- #ctf.ctf-super-narrow .ctf-header-user{
773
- min-height: 48px;
774
- }
775
- #ctf.ctf-super-narrow .ctf-header-text{
776
- padding-top: 10px;
777
- }
778
- #ctf.ctf-super-narrow .ctf-header-follow{
779
- margin-top: 5px;
780
- }
781
- #ctf.ctf-super-narrow .ctf-more span{
782
- display: block;
783
- margin-top: 3px;
784
- }
785
-
786
- /* On mobile make the min-width 100% */
787
- @media all and (max-width: 640px){
788
- #ctf.ctf-width-resp{
789
- width: 100% !important;
790
- }
791
  }
1
+
2
+ /* General */
3
+ #ctf{
4
+ overflow-y: auto;
5
+ -ms-overflow-y: auto;
6
+ }
7
+ #ctf .ctf-item{
8
+ padding: 15px 5px;
9
+ border-top: 1px solid #ddd;
10
+ border-top: 1px solid rgba(0,0,0,0.1);
11
+ overflow: hidden;
12
+ }
13
+ #ctf .ctf-item:first-child{
14
+ border-top: none;
15
+ }
16
+ #ctf .ctf_remaining {
17
+ display: none;
18
+ }
19
+ #ctf .ctf_more{
20
+ padding: 1px;
21
+ border: 1px solid transparent;
22
+ border-radius: 3px;
23
+ display: inline-block;
24
+ line-height: 1;
25
+ }
26
+ #ctf .ctf_more:hover {
27
+ cursor: pointer;
28
+ background: rgba(0,0,0,0.05);
29
+ border: 1px solid rgba(0,0,0,0.1);
30
+ text-decoration: none;
31
+ }
32
+
33
+ /* Header */
34
+ #ctf .ctf-header{
35
+ float: left;
36
+ clear: both;
37
+ margin: 0;
38
+ padding: 5px 5px 15px 5px;
39
+ line-height: 1.2;
40
+ width: 100%;
41
+ border-bottom: 1px solid #ddd;
42
+ border-bottom: 1px solid rgba(0,0,0,0.1);
43
+
44
+ -webkit-box-sizing: border-box;
45
+ -moz-box-sizing: border-box;
46
+ box-sizing: border-box;
47
+ }
48
+ #ctf .ctf-header a{
49
+ float: left;
50
+ display: block;
51
+ min-width: 100%\9;
52
+ }
53
+ /* Header avatar */
54
+ #ctf .ctf-header-img{
55
+ float: left;
56
+ position: relative;
57
+ width: 48px;
58
+ margin: 0 0 0 -100% !important;
59
+ overflow: hidden;
60
+
61
+ -moz-border-radius: 40px;
62
+ -webkit-border-radius: 40px;
63
+ border-radius: 40px;
64
+ }
65
+ #ctf .ctf-header-img img{
66
+ display: block;
67
+ float: left;
68
+ margin: 0 !important;
69
+ padding: 0 !important;
70
+ border: none !important;
71
+
72
+ -moz-border-radius: 40px;
73
+ -webkit-border-radius: 40px;
74
+ border-radius: 40px;
75
+ }
76
+ #ctf .ctf-author-avatar {
77
+ clear: both;
78
+ margin: 3px 6px 0 -53px;
79
+ }
80
+ #ctf .ctf-author-name {
81
+ margin-left: 6px;
82
+ }
83
+ #ctf .ctf-author-box {
84
+ margin-left: 53px;
85
+ }
86
+ /* Header avatar hover */
87
+ #ctf .ctf-header-img-hover,
88
+ #ctf .ctf-header-generic-icon{
89
+ display: none;
90
+ position: absolute;
91
+ width: 100%;
92
+ top: 0;
93
+ bottom: 0;
94
+ left: 0;
95
+ text-align: center;
96
+ color: #fff;
97
+ background: #1b95e0;
98
+ background: rgba(27,149,224,0.85);
99
+
100
+ -moz-border-radius: 40px;
101
+ -webkit-border-radius: 40px;
102
+ border-radius: 40px;
103
+ z-index: 999;
104
+ }
105
+ #ctf .ctf-header-img-hover .fa,
106
+ #ctf .ctf-header-generic-icon .fa,
107
+ #ctf .ctf-header-img-hover svg,
108
+ #ctf .ctf-header-generic-icon svg{
109
+ position: absolute;
110
+ top: 50%;
111
+ left: 50%;
112
+ margin-top: -10px;
113
+ margin-left: -8px;
114
+ font-size: 20px;
115
+ z-index: 1000;
116
+ }
117
+ #ctf .ctf-header-img-hover svg,
118
+ #ctf .ctf-header-generic-icon svg {
119
+ margin-top: -10px;
120
+ margin-left: -10px;
121
+ }
122
+ /* Header text */
123
+ #ctf .ctf-header-text{
124
+ float: left;
125
+ width: 100%;
126
+ padding-top: 5px;
127
+ }
128
+ #ctf .ctf-no-bio .ctf-header-text{
129
+ padding-top: 13px;
130
+ }
131
+ #ctf .ctf-header a{
132
+ text-decoration: none;
133
+ }
134
+ #ctf .ctf-header-text .ctf-header-user,
135
+ #ctf .ctf-header-text p{
136
+ float: left;
137
+ clear: both;
138
+ width: auto;
139
+ margin: 0 0 0 60px !important;
140
+ padding: 0 !important;
141
+ }
142
+ #ctf .ctf-header-name{
143
+ font-weight: bold;
144
+ margin-right: 6px;
145
+ }
146
+ #ctf .ctf-header-text .ctf-header-bio{
147
+ padding: 0 !important;
148
+ word-break: break-word;
149
+ }
150
+ #ctf .ctf-header-text .ctf-verified{
151
+ margin-right: 5px;
152
+ }
153
+ /* Follow */
154
+ #ctf .ctf-header .ctf-header-follow{
155
+ display: inline-block;
156
+ padding: 3px 6px;
157
+ margin: 0 0 0 2px;
158
+ position: relative;
159
+ top: -2px;
160
+ background: #1b95e0;
161
+ color: #fff;
162
+ font-size: 11px;
163
+
164
+ -moz-border-radius: 3px;
165
+ -webkit-border-radius: 3px;
166
+ border-radius: 3px;
167
+
168
+ -moz-transition: background 0.6s ease-in-out;
169
+ -webkit-transition: background 0.6s ease-in-out;
170
+ -o-transition: background 0.6s ease-in-out;
171
+ transition: background 0.6s ease-in-out;
172
+ }
173
+ #ctf .ctf-header .ctf-header-follow .fa,
174
+ #ctf .ctf-header .ctf-header-follow svg{
175
+ margin-right: 3px;
176
+ font-size: 12px;
177
+ }
178
+ #ctf .ctf-header a:hover .ctf-header-follow{
179
+ background: #0c7abf;
180
+
181
+ -moz-transition: background 0.2s ease-in-out;
182
+ -webkit-transition: background 0.2s ease-in-out;
183
+ -o-transition: background 0.2s ease-in-out;
184
+ transition: background 0.2s ease-in-out;
185
+ }
186
+ /* Generic header version */
187
+ #ctf .ctf-header-type-generic .ctf-header-img{
188
+ color: #fff;
189
+ width: 48px;
190
+ height: 48px;
191
+ position: relative;
192
+ }
193
+ #ctf .ctf-header-type-generic .ctf-header-link:hover .ctf-header-generic-icon{
194
+ background: #0c7abf;
195
+ }
196
+ #ctf .ctf-header-type-generic .ctf-header-no-bio{
197
+ padding-top: 9px !important;
198
+ font-size: 16px;
199
+ font-weight: bold;
200
+ }
201
+ #ctf .ctf-header-type-generic .ctf-header-generic-icon{
202
+ display: block;
203
+ color: #fff;
204
+ background: #1b95e0;
205
+
206
+ -moz-transition: background 0.2s ease-in-out;
207
+ -webkit-transition: background 0.2s ease-in-out;
208
+ -o-transition: background 0.2s ease-in-out;
209
+ transition: background 0.2s ease-in-out;
210
+ }
211
+ #ctf .ctf-header-type-generic:hover .ctf-header-generic-icon{
212
+ display: block;
213
+ opacity: 1;
214
+ }
215
+ #ctf .ctf-header .ctf-header-user{
216
+ font-size: 16px;
217
+ line-height: 1.3;
218
+ -ms-word-wrap: break-word;
219
+ word-break: break-word;
220
+ }
221
+ #ctf .ctf-header p,
222
+ #ctf .ctf-header .ctf-header-screenname{
223
+ font-size: 13px;
224
+ line-height: 1.3;
225
+ }
226
+
227
+ /* screenreader */
228
+
229
+ .ctf-screenreader{
230
+ text-indent: -9999px !important;
231
+ display: inline-block !important;
232
+ width: 0 !important;
233
+ height: 0 !important;
234
+ line-height: 0 !important;
235
+ text-align: left !important;
236
+ }
237
+
238
+ /* No bio */
239
+ #ctf .ctf-header-text.sbi-no-info .ctf-header-user{
240
+ padding-top: 9px !important;
241
+ }
242
+ #ctf .ctf-header-text.sbi-no-bio .ctf-header-counts{
243
+ clear: both;
244
+ }
245
+
246
+ /* Context */
247
+ #ctf .ctf-context {
248
+ float: left;
249
+ width: 100%;
250
+ margin-left: 57px;
251
+ margin-bottom: 2px;
252
+ }
253
+ #ctf .ctf-retweet-icon{
254
+ display: inline-block;
255
+ font-size: inherit;
256
+ background: #19cf86;
257
+ color: #fff !important;
258
+ padding: 1px 3px 3px 3px;
259
+ line-height: 1;
260
+ margin: 0 5px 0 0;
261
+
262
+ -moz-border-radius: 3px;
263
+ -webkit-border-radius: 3px;
264
+ border-radius: 3px;
265
+ }
266
+
267
+ /* Author */
268
+ #ctf .ctf-author-box{
269
+ margin-left: 56px;
270
+ }
271
+ #ctf .ctf-author-avatar{
272
+ width: 48px;
273
+ height: 48px;
274
+ margin: 3px 6px 0 -56px;
275
+ float: left;
276
+ overflow: hidden;
277
+ border: none;
278
+
279
+ -moz-border-radius: 5px;
280
+ -webkit-border-radius: 5px;
281
+ border-radius: 5px;
282
+ }
283
+ #ctf .ctf-author-avatar img{
284
+ margin: 0 !important;
285
+ padding: 0;
286
+ }
287
+ #ctf .ctf-author-name,
288
+ #ctf .ctf-author-screenname,
289
+ #ctf .ctf-verified,
290
+ #ctf .ctf-tweet-meta{
291
+ margin-left: 4px;
292
+ margin-right: 4px;
293
+ display: inline-block;
294
+ }
295
+ #ctf .ctf-screename-sep{
296
+ margin: 0 2px;
297
+ display: inline-block;
298
+ }
299
+ #ctf .ctf-author-name{
300
+ font-weight: bold !important;
301
+ margin-top: 4px;
302
+ }
303
+ #ctf .ctf-author-box-link{
304
+ color: black;
305
+ text-decoration: none;
306
+ display: inline;
307
+ line-height: 1.5 !important;
308
+ }
309
+ #ctf .ctf-verified,
310
+ #ctf .ctf-quoted-verified {
311
+ color: #77c7f7;
312
+ margin-left: 0;
313
+ }
314
+ #ctf .ctf-quoted-verified {
315
+ margin-right: 4px;
316
+ }
317
+ #ctf .ctf-tweet-meta{
318
+ display: inline-block;
319
+ }
320
+ #ctf .ctf-replied-to-text a {
321
+ font-size: inherit;
322
+ color: inherit;
323
+ font-weight: inherit;
324
+ }
325
+ #ctf p.ctf-tweet-text,
326
+ #ctf p.ctf-media-link {
327
+ padding: 0 !important;
328
+ margin: 0 !important;
329
+ line-height: 1.4;
330
+ }
331
+ #ctf .ctf-tweet-content,
332
+ #ctf .ctf-tweet-actions{
333
+ margin-left: 58px;
334
+ }
335
+ #ctf .ctf-corner-logo {
336
+ display: inline-block;
337
+ float: right;
338
+ color: #1b95e0;
339
+ font-size: 24px;
340
+ }
341
+ #ctf.ctf-narrow .ctf-corner-logo {
342
+ font-size: 20px;
343
+ }
344
+ /* No avatar */
345
+ #ctf .ctf-hide-avatar .ctf-context,
346
+ #ctf .ctf-hide-avatar .ctf-tweet-content,
347
+ #ctf .ctf-hide-avatar .ctf-tweet-actions,
348
+ #ctf .ctf-hide-avatar .ctf-quoted-tweet,
349
+ #ctf .ctf-hide-avatar .ctf-author-box,
350
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-context,
351
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-content,
352
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-tweet-actions,
353
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-quoted-tweet,
354
+ #ctf.ctf-narrow .ctf-hide-avatar .ctf-author-box,
355
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-context,
356
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-content,
357
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-tweet-actions,
358
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-quoted-tweet,
359
+ #ctf.ctf-super-narrow .ctf-hide-avatar .ctf-author-box{
360
+ margin-left: 0;
361
+ }
362
+
363
+ /*Quoted tweets*/
364
+ #ctf .ctf-quoted-tweet{
365
+ display: block;
366
+ /*float: left;*/
367
+ clear: both;
368
+ margin: 7px 0 5px 58px;
369
+ padding: 8px 12px;
370
+ border: 1px solid #ddd;
371
+ border: 1px solid rgba(0,0,0,0.1);
372
+ text-decoration: none;
373
+
374
+ -moz-border-radius: 5px;
375
+ -webkit-border-radius: 5px;
376
+ border-radius: 5px;
377
+
378
+ -moz-transition: background 0.1s ease-in-out;
379
+ -webkit-transition: background 0.1s ease-in-out;
380
+ -o-transition: background 0.1s ease-in-out;
381
+ transition: background 0.1s ease-in-out;
382
+ }
383
+ #ctf .ctf-quoted-tweet:hover,
384
+ #ctf .ctf-quoted-tweet:focus{
385
+ background: #eee;
386
+ background: rgba(0,0,0,0.05);
387
+ border: 1px solid #ddd;
388
+ border: 1px solid rgba(0,0,0,0.05);
389
+
390
+ -moz-transition: background 0.1s ease-in-out;
391
+ -webkit-transition: background 0.1s ease-in-out;
392
+ -o-transition: background 0.1s ease-in-out;
393
+ transition: background 0.1s ease-in-out;
394
+ }
395
+ #ctf .ctf-quoted-author-name{
396
+ font-weight: bold;
397
+ margin-right: 4px;
398
+ }
399
+ #ctf .ctf-quoted-tweet p{
400
+ margin: 2px 0;
401
+ }
402
+
403
+ /* Actions */
404
+ #ctf .ctf-tweet-actions a{
405
+ padding: 2px 10px 2px 5px;
406
+ float: left;
407
+ }
408
+ #ctf .ctf-tweet-actions a.ctf-reply,
409
+ #ctf .ctf-tweet-actions a.ctf-retweet,
410
+ #ctf .ctf-tweet-actions a.ctf-like{
411
+ color: #bbb;
412
+ color: rgba(0,0,0,0.30);
413
+ text-decoration: none;
414
+ border: none;
415
+ -moz-transition: color 0.1s ease-in-out;
416
+ -webkit-transition: color 0.1s ease-in-out;
417
+ -o-transition: color 0.1s ease-in-out;
418
+ transition: color 0.1s ease-in-out;
419
+ }
420
+ #ctf .ctf-tweet-actions .ctf-reply{
421
+ margin-left: -5px;
422
+ }
423
+ #ctf .ctf-tweet-actions a:hover,
424
+ #ctf .ctf-tweet-actions a:focus{
425
+ -moz-transition: color 0.1s ease-in-out;
426
+ -webkit-transition: color 0.1s ease-in-out;
427
+ -o-transition: color 0.1s ease-in-out;
428
+ transition: color 0.1s ease-in-out;
429
+ }
430
+ #ctf .ctf-tweet-actions .ctf-reply:hover,
431
+ #ctf .ctf-tweet-actions .ctf-reply:focus{
432
+ color: #E34F0E !important;
433
+ }
434
+ #ctf .ctf-tweet-actions .ctf-retweet:hover,
435
+ #ctf .ctf-tweet-actions .ctf-retweet:focus{
436
+ color: #19cf86 !important;
437
+ }
438
+ #ctf .ctf-tweet-actions .ctf-like:hover,
439
+ #ctf .ctf-tweet-actions .ctf-like:focus{
440
+ color: #e81c4f !important;
441
+ }
442
+ #ctf .ctf-action-count {
443
+ display: none;
444
+ float: right;
445
+ padding-left: 3px;
446
+ font-weight: bold;
447
+ font-size: 11px;
448
+ line-height: 14px;
449
+ }
450
+ #ctf .ctf-tweet-text-media-wrap,
451
+ #ctf .ctf-quoted-tweet-text-media-wrap{
452
+ margin-left: 5px;
453
+ padding: 4px;
454
+ display: inline-block;
455
+ border: 1px solid #ddd;
456
+ border: 1px solid rgba(0,0,0,0.15);
457
+
458
+ color: inherit;
459
+ text-decoration: none;
460
+ line-height: 1;
461
+
462
+ -moz-border-radius: 2px;
463
+ -webkit-border-radius: 2px;
464
+ border-radius: 2px;
465
+
466
+ -moz-transition: background 0.1s ease-in-out;
467
+ -webkit-transition: background 0.1s ease-in-out;
468
+ -o-transition: background 0.1s ease-in-out;
469
+ transition: background 0.1s ease-in-out;
470
+ }
471
+ #ctf .ctf-tweet-text-media-wrap:hover,
472
+ #ctf .ctf-tweet-text-media-wrap:focus{
473
+ background: #eee;
474
+ background: rgba(0,0,0,0.05);
475
+ border: 1px solid #ddd;
476
+ border: 1px solid rgba(0,0,0,0.1);
477
+
478
+ -moz-transition: background 0.1s ease-in-out;
479
+ -webkit-transition: background 0.1s ease-in-out;
480
+ -o-transition: background 0.1s ease-in-out;
481
+ transition: background 0.1s ease-in-out;
482
+ }
483
+ .ctf-multi-media-icon .ctf-tweet-text-media,
484
+ .ctf-quoted-tweet-text .ctf-tweet-text-media{
485
+ margin-left: 3px;
486
+ }
487
+
488
+ /* Load more */
489
+ #ctf #ctf-more{
490
+ display: block;
491
+ border: none;
492
+ background: #eee;
493
+ background: rgba(0,0,0,0.05);
494
+ width: 100%;
495
+ min-height: 30px;
496
+ padding: 5px 0;
497
+ margin: 0;
498
+ position: relative;
499
+ text-align: center;
500
+ box-sizing: border-box;
501
+ outline: none;
502
+ text-decoration: none;
503
+
504
+ -moz-border-radius: 5px;
505
+ -webkit-border-radius: 5px;
506
+ border-radius: 5px;
507
+
508
+ -moz-transition: background 0.1s ease-in-out;
509
+ -webkit-transition: background 0.1s ease-in-out;
510
+ -o-transition: background 0.1s ease-in-out;
511
+ transition: background 0.1s ease-in-out;
512
+ }
513
+ #ctf #ctf-more:hover{
514
+ background: #ddd;
515
+ background: rgba(0,0,0,0.1);
516
+ -moz-transition: background 0.1s ease-in-out;
517
+ -webkit-transition: background 0.1s ease-in-out;
518
+ -o-transition: background 0.1s ease-in-out;
519
+ transition: background 0.1s ease-in-out;
520
+ }
521
+ #ctf #ctf-more.ctf-loading span{
522
+ -moz-transition: opacity 0.5s ease-in-out;
523
+ -webkit-transition: opacity 0.5s ease-in-out;
524
+ -o-transition: opacity 0.5s ease-in-out;
525
+ transition: opacity 0.5s ease-in-out;
526
+ }
527
+ #ctf #ctf-more.ctf-loading span{
528
+ filter: alpha(opacity=0);
529
+ opacity: 0;
530
+ }
531
+ #ctf .ctf-loader {
532
+ width: 20px;
533
+ height: 20px;
534
+
535
+ position: absolute;
536
+ top: 50%;
537
+ left: 50%;
538
+ margin: -10px 0 0 -10px;
539
+ background-color: unset;
540
+ background-color: rgba(0,0,0,0.5);
541
+
542
+ border-radius: 100%;
543
+ -webkit-animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
544
+ animation: ctf-sk-scaleout 1.0s infinite ease-in-out;
545
+ }
546
+ @-webkit-keyframes ctf-sk-scaleout {
547
+ 0% { -webkit-transform: scale(0) }
548
+ 100% {
549
+ -webkit-transform: scale(1.0);
550
+ opacity: 0;
551
+ }
552
+ }
553
+ @keyframes ctf-sk-scaleout {
554
+ 0% {
555
+ -webkit-transform: scale(0);
556
+ transform: scale(0);
557
+ } 100% {
558
+ -webkit-transform: scale(1.0);
559
+ transform: scale(1.0);
560
+ opacity: 0;
561
+ }
562
+ }
563
+
564
+ /* Credit link */
565
+ .ctf-credit-link{
566
+ width: 100%;
567
+ clear: both;
568
+ padding: 5px 0;
569
+ margin-top: 5px;
570
+ text-align: center;
571
+ font-size: 12px;
572
+ }
573
+ .ctf-credit-link .fa,
574
+ .ctf-credit-link svg {
575
+ margin-right: 4px;
576
+ }
577
+
578
+ /* SVGs */
579
+ #ctf svg { box-sizing: unset; }
580
+ #ctf svg:not(:root).svg-inline--fa,
581
+ #ctf_lightbox svg:not(:root).svg-inline--fa{
582
+ box-sizing: unset;
583
+ overflow: visible;
584
+ width: 1em;
585
+ }
586
+ #ctf .svg-inline--fa.fa-w-16,
587
+ #ctf .svg-inline--fa.fa-w-16{
588
+ width: 16px;
589
+ }
590
+ #ctf .svg-inline--fa.fa-w-20 {
591
+ width: 20px;
592
+ }
593
+ #ctf .ctf-tweet-actions a.ctf-retweet svg,
594
+ #ctf .ctf-context .ctf-retweet-icon svg{
595
+ width: 1.2em;
596
+ }
597
+ #ctf .svg-inline--fa,
598
+ #ctf_lightbox .svg-inline--fa{
599
+ display: inline-block;
600
+ font-size: inherit;
601
+ height: 1em;
602
+ overflow: visible;
603
+ vertical-align: -.125em;
604
+ }
605
+
606
+ #ctf #ctf .ctf-photo-hover span.fa-arrows-alt,
607
+ #ctf .ctf-photo-hover svg.fa-arrows-alt {
608
+ display: none;
609
+ }
610
+
611
+ /* Default styles */
612
+ #ctf.ctf-styles .ctf-author-screenname,
613
+ #ctf.ctf-styles .ctf-context,
614
+ #ctf.ctf-styles .ctf-twitterlink,
615
+ #ctf.ctf-styles .ctf-tweet-meta{
616
+ font-size: 12px;
617
+ }
618
+
619
+ /* Errors */
620
+ #ctf .ctf-error{
621
+ width: 100%;
622
+ text-align: center;
623
+ }
624
+ #ctf .ctf-error p,
625
+ #ctf .ctf-error iframe,
626
+ #ctf .ctf-error code{
627
+ padding: 0;
628
+ margin: 0 2px;
629
+ }
630
+ #ctf .ctf-error-user p{
631
+ padding-bottom: 5px;
632
+ }
633
+ #ctf .ctf-error-user{
634
+ width: 100%;
635
+ }
636
+ #ctf .ctf-error-admin{
637
+ margin-top: 15px;
638
+ display: inline-block;
639
+ }
640
+ #ctf .ctf-error code{
641
+ display: block;
642
+ padding: 4px;
643
+ }
644
+ .ctf_smash_error{
645
+ border: 1px solid #ddd;
646
+ background: #eee;
647
+ color: #333;
648
+ margin: 10px 0 0;
649
+ padding: 10px 15px;
650
+ font-size: 13px;
651
+ text-align: center;
652
+ clear: both;
653
+
654
+ -moz-border-radius: 4px;
655
+ -webkit-border-radius: 4px;
656
+ border-radius: 4px;
657
+ }
658
+ .ctf_smash_error p{
659
+ padding: 5px 0 !important;
660
+ margin: 0 !important;
661
+ line-height: 1.3 !important;
662
+ }
663
+ .ctf_smash_error span{
664
+ font-size: 12px;
665
+ }
666
+
667
+ /* Out of tweets */
668
+ #ctf .ctf-out-of-tweets{
669
+ width: 100%;
670
+ }
671
+ #ctf .ctf-out-of-tweets{
672
+ display: block;
673
+ border: 1px solid rgba(0,0,0,0.1);
674
+ background: none;
675
+ background: none;
676
+ width: 100%;
677
+ min-height: 30px;
678
+ padding: 5px 0;
679
+ margin: 0;
680
+ position: relative;
681
+ text-align: center;
682
+ box-sizing: border-box;
683
+
684
+ -moz-border-radius: 5px;
685
+ -webkit-border-radius: 5px;
686
+ border-radius: 5px;
687
+ }
688
+ #ctf .ctf-out-of-tweets p,
689
+ #ctf .ctf-out-of-tweets iframe{
690
+ margin: 0 2px;
691
+ padding: 0;
692
+ }
693
+ #ctf .ctf-out-of-tweets p{
694
+ padding: 3px;
695
+ display: none;
696
+ }
697
+
698
+ /* Mobile / Narrow */
699
+ #ctf.ctf-narrow .ctf-author-avatar,
700
+ #ctf.ctf-narrow .ctf-author-avatar img{
701
+ width: 38px;
702
+ height: 38px;
703
+ }
704
+ #ctf.ctf-narrow .ctf-tweet-content,
705
+ #ctf.ctf-narrow .ctf-tweet-actions,
706
+ #ctf.ctf-narrow .ctf-quoted-tweet{
707
+ margin-left: 48px;
708
+ }
709
+ #ctf.ctf-narrow .ctf-context {
710
+ margin-left: 49px;
711
+ }
712
+ #ctf.ctf-narrow .ctf-author-box{
713
+ margin-left: 46px;
714
+ }
715
+ #ctf.ctf-narrow .ctf-author-avatar {
716
+ margin-left: -46px;
717
+ }
718
+ #ctf.ctf-narrow .ctf-tweet-content{
719
+ padding: 3px 0 5px 0;
720
+ }
721
+ #ctf.ctf-narrow .ctf-tweet-actions {
722
+ float: left;
723
+ clear: both;
724
+ }
725
+
726
+ /* Super narrow */
727
+ #ctf.ctf-super-narrow .ctf-context,
728
+ #ctf.ctf-super-narrow .ctf-tweet-content,
729
+ #ctf.ctf-super-narrow .ctf-tweet-actions,
730
+ #ctf.ctf-super-narrow .ctf-quoted-tweet {
731
+ margin-left: 0;
732
+ }
733
+ #ctf.ctf-super-narrow .ctf-author-box{
734
+ min-height: 32px;
735
+ margin-bottom: 2px;
736
+ }
737
+ #ctf.ctf-super-narrow .ctf-author-avatar,
738
+ #ctf.ctf-super-narrow .ctf-author-avatar img{
739
+ width: 32px;
740
+ height: 32px;
741
+ }
742
+ #ctf.ctf-super-narrow .ctf-author-box{
743
+ margin-left: 38px;
744
+ float: left;
745
+ clear: both;
746
+ }
747
+ #ctf.ctf-super-narrow .ctf-author-avatar {
748
+ margin-left: -38px;
749
+ margin-top: 0;
750
+ }
751
+ #ctf.ctf-super-narrow .ctf-author-screenname,
752
+ #ctf.ctf-super-narrow .ctf-screename-sep {
753
+ display: none;
754
+ }
755
+ #ctf.ctf-super-narrow .ctf-context {
756
+ padding-bottom: 5px;
757
+ }
758
+ #ctf.ctf-super-narrow .ctf-author-name{
759
+ display: inline-block;
760
+ }
761
+ #ctf.ctf-super-narrow .ctf-tweet-content{
762
+ float: left;
763
+ clear: both;
764
+ }
765
+ #ctf.ctf-super-narrow .ctf-credit-link{
766
+ font-size: 10px;
767
+ }
768
+ #ctf.ctf-super-narrow .ctf-header-bio{
769
+ margin: 0 !important;
770
+ padding-top: 5px;
771
+ }
772
+ #ctf.ctf-super-narrow .ctf-header-user{
773
+ min-height: 48px;
774
+ }
775
+ #ctf.ctf-super-narrow .ctf-header-text{
776
+ padding-top: 10px;
777
+ }
778
+ #ctf.ctf-super-narrow .ctf-header-follow{
779
+ margin-top: 5px;
780
+ }
781
+ #ctf.ctf-super-narrow .ctf-more span{
782
+ display: block;
783
+ margin-top: 3px;
784
+ }
785
+
786
+ /* On mobile make the min-width 100% */
787
+ @media all and (max-width: 640px){
788
+ #ctf.ctf-width-resp{
789
+ width: 100% !important;
790
+ }
791
  }
custom-twitter-feed.php CHANGED
@@ -1,566 +1,582 @@
1
- <?php
2
- /*
3
- Plugin Name: Custom Twitter Feeds
4
- Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
- Description: Customizable Twitter feeds for your website
6
- Version: 1.4.1
7
- Author: Smash Balloon
8
- Author URI: http://smashballoon.com/
9
- Text Domain: custom-twitter-feeds
10
- */
11
- /*
12
- Copyright 2020 Smash Balloon LLC (email : hey@smashballoon.com)
13
- This program is free software; you can redistribute it and/or modify
14
- it under the terms of the GNU General Public License as published by
15
- the Free Software Foundation; either version 2 of the License, or
16
- (at your option) any later version.
17
- This program is distributed in the hope that it will be useful,
18
- but WITHOUT ANY WARRANTY; without even the implied warranty of
19
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
- GNU General Public License for more details.
21
- You should have received a copy of the GNU General Public License
22
- along with this program; if not, write to the Free Software
23
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
- */
25
-
26
- define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
- define( 'CTF_VERSION', '1.4.1.1' );
28
- define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
- define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
- define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
31
- // Plugin Folder Path.
32
- if ( ! defined( 'CTF_PLUGIN_DIR' ) ) {
33
- define( 'CTF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
34
- }
35
- // Plugin Folder URL.
36
- if ( ! defined( 'CTF_PLUGIN_URL' ) ) {
37
- define( 'CTF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
38
- }
39
- // Db version.
40
- if ( ! defined( 'CTF_DBVERSION' ) ) {
41
- define( 'CTF_DBVERSION', '1.0' );
42
- }
43
-
44
- if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
45
-
46
- require_once( CTF_URL . '/inc/widget.php' );
47
-
48
- require_once( CTF_URL . '/inc/admin-hooks.php' );
49
-
50
- function ctf_update_settings() {
51
- $existing_deprecated_options = get_option( 'ctf_configure' );
52
- $existing_options = get_option( 'ctf_options' );
53
-
54
- update_option( 'ctf_version', CTF_VERSION );
55
-
56
- if ( ! empty( $existing_deprecated_options ) && empty( $existing_options ) ) {
57
- $merged_options = $existing_deprecated_options;
58
- $merged_options = array_merge( $merged_options, get_option( 'ctf_customize', array() ) );
59
- $merged_options = array_merge( $merged_options, get_option( 'ctf_style', array() ) );
60
-
61
- update_option( 'ctf_options', $merged_options );
62
- }
63
- }
64
-
65
- function ctf_check_for_db_updates() {
66
-
67
- $db_ver = get_option( 'ctf_db_version', 0 );
68
-
69
- if ( (float) $db_ver < 1.0 ) {
70
-
71
- global $wp_roles;
72
- $wp_roles->add_cap( 'administrator', 'manage_custom_twitter_feeds_options' );
73
-
74
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
75
-
76
- if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
77
-
78
- $options_set = get_option( 'ctf_options', false );
79
-
80
- if ( $options_set ) {
81
- $ctf_statuses_option['first_install'] = 'from_update';
82
- } else {
83
- $ctf_statuses_option['first_install'] = time();
84
- }
85
-
86
- $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
87
-
88
- if ( $ctf_rating_notice_option === 'dismissed' ) {
89
- $ctf_statuses_option['rating_notice_dismissed'] = time();
90
- }
91
-
92
- $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
93
-
94
- if ( $ctf_rating_notice_waiting === false
95
- && $ctf_rating_notice_option === false ) {
96
- $time = 2 * WEEK_IN_SECONDS;
97
- set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
98
- update_option( 'ctf_rating_notice', 'pending', false );
99
- }
100
-
101
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
102
-
103
- }
104
-
105
- update_option( 'ctf_db_version', CTF_DBVERSION );
106
- }
107
-
108
- }
109
- add_action( 'wp_loaded', 'ctf_check_for_db_updates' );
110
-
111
-
112
- /**
113
- * include the admin files only if in the admin area
114
- */
115
- if ( is_admin() ) {
116
-
117
- $ctf_version = get_option( 'ctf_version', false );
118
-
119
- if ( ! $ctf_version ) {
120
- ctf_update_settings();
121
- }
122
- require_once( CTF_URL . '/inc/CtfAdmin.php' );
123
- require_once( CTF_URL . '/inc/notices.php' );
124
-
125
- $admin = new CtfAdmin;
126
- }
127
-
128
- /**
129
- * Generates the Twitter feed wherever the shortcode is placed
130
- *
131
- * @param $atts array shortcode arguments
132
- *
133
- * @return string
134
- */
135
- function ctf_init( $atts ) {
136
-
137
- include_once( CTF_URL . '/inc/CtfFeed.php' );
138
- wp_enqueue_script( 'ctf_scripts' );
139
-
140
- $twitter_feed = CtfFeed::init( $atts );
141
- /*
142
- echo '<pre>';
143
- var_dump( $twitter_feed->tweet_set);
144
- echo '</pre>'; */
145
- // if there is an error, display the error html, otherwise the feed
146
- if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
147
- return $twitter_feed->getErrorHtml();
148
- } else {
149
- $twitter_feed->maybeCacheTweets();
150
-
151
- $feed_html = $twitter_feed->getFeedOpeningHtml();
152
- $feed_html .= $twitter_feed->getTweetSetHtml();
153
- $feed_html .= $twitter_feed->getFeedClosingHtml();
154
-
155
- return $feed_html;
156
- }
157
- }
158
- add_shortcode( 'custom-twitter-feed', 'ctf_init' );
159
- add_shortcode( 'custom-twitter-feeds', 'ctf_init' );
160
-
161
- /**
162
- * Called via ajax to get more posts after the "load more" button is clicked
163
- */
164
- function ctf_get_more_posts() {
165
- $shortcode_data = json_decode( str_replace( '\"', '"', sanitize_text_field( $_POST['shortcode_data'] ) ), true ); // necessary to unescape quotes
166
- $last_id_data = isset( $_POST['last_id_data'] ) ? sanitize_text_field( $_POST['last_id_data'] ) : '';
167
- $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
168
- $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
169
- $is_pagination = empty( $last_id_data ) ? 0 : 1;
170
- $persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
171
-
172
- include_once( CTF_URL . '/inc/CtfFeed.php' );
173
-
174
- $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
175
-
176
- if ( ! $twitter_feed->feed_options['persistentcache'] ) {
177
- $twitter_feed->maybeCacheTweets();
178
- }
179
-
180
- echo $twitter_feed->getTweetSetHtml( $is_pagination );
181
-
182
- die();
183
- }
184
- add_action( 'wp_ajax_nopriv_ctf_get_more_posts', 'ctf_get_more_posts' );
185
- add_action( 'wp_ajax_ctf_get_more_posts', 'ctf_get_more_posts' );
186
-
187
- function ctf_plugin_action_links( $links ) {
188
- $links[] = '<a href="'. esc_url( get_admin_url( null, 'admin.php?page=custom-twitter-feeds' ) ) .'">' . __( 'Settings' ) . '</a>';
189
- return $links;
190
- }
191
- add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'ctf_plugin_action_links' );
192
-
193
- /**
194
- * the html output is controlled by the user selecting which portions of tweets to show
195
- *
196
- * @param $part string part of the feed in the html
197
- * @param $feed_options array options that contain what parts of the tweet to show
198
- * @return bool whether or not to show the tweet
199
- */
200
- function ctf_show( $part, $feed_options ) {
201
- $tweet_excludes = isset( $feed_options['tweet_excludes'] ) ? $feed_options['tweet_excludes'] : '';
202
- $tweet_includes = isset( $feed_options['tweet_includes'] ) ? $feed_options['tweet_includes'] : '';
203
-
204
- // if part is in the array of excluded parts or not in the array of included parts, don't show
205
- if ( ! empty( $tweet_excludes ) ) {
206
- return ( in_array( $part, $tweet_excludes ) === false );
207
- } else {
208
- return ( in_array( $part, $tweet_includes ) === true );
209
- }
210
- }
211
-
212
- /**
213
- * this function returns the properly formatted date string based on user input
214
- *
215
- * @param $raw_date string the date from the Twitter api
216
- * @param $feed_options array options for the feed that contain date formatting settings
217
- * @param $utc_offset int offset in seconds for the time display based on timezone
218
- * @return string formatted date
219
- */
220
- function ctf_get_formatted_date( $raw_date, $feed_options, $utc_offset ) {
221
- include_once( CTF_URL . '/inc/CtfDateTime.php' );
222
-
223
- $options = get_option( 'ctf_options' );
224
- $timezone = isset( $options['timezone'] ) ? $options['timezone'] : 'default';
225
- // use php DateTimeZone class to handle the date formatting and offsets
226
- $date_obj = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
227
-
228
- if( $timezone != 'default' ) {
229
- $date_obj->setTimeZone( new DateTimeZone( $timezone ) );
230
- $utc_offset = $date_obj->getOffset();
231
- }
232
-
233
- $tz_offset_timestamp = $date_obj->getTimestamp() + $utc_offset;
234
-
235
- // use the custom date format if set, otherwise use from the selected defaults
236
- if ( ! empty( $feed_options['datecustom'] ) ){
237
- $date_str = date_i18n( $feed_options['datecustom'], $tz_offset_timestamp );
238
- } else {
239
-
240
- switch ( $feed_options['dateformat'] ) {
241
-
242
- case '2':
243
- $date_str = date_i18n( 'F j', $tz_offset_timestamp );
244
- break;
245
- case '3':
246
- $date_str = date_i18n( 'F j, Y', $tz_offset_timestamp );
247
- break;
248
- case '4':
249
- $date_str = date_i18n( 'm.d', $tz_offset_timestamp );
250
- break;
251
- case '5':
252
- $date_str = date_i18n( 'm.d.y', $tz_offset_timestamp );
253
- break;
254
- default:
255
-
256
- // default format is similar to Twitter
257
- $ctf_minute = ! empty( $feed_options['mtime'] ) ? $feed_options['mtime'] : 'm';
258
- $ctf_hour = ! empty( $feed_options['htime'] ) ? $feed_options['htime'] : 'h';
259
- $ctf_now_str = ! empty( $feed_options['nowtime'] ) ? $feed_options['nowtime'] : 'now';
260
-
261
- $now = time() + $utc_offset;
262
-
263
- $difference = $now - $tz_offset_timestamp;
264
-
265
- if ( $difference < 60 ) {
266
- $date_str = $ctf_now_str;
267
- } elseif ( $difference < 60*60 ) {
268
- $date_str = round( $difference/60 ) . $ctf_minute;
269
- } elseif ( $difference < 60*60*24 ) {
270
- $date_str = round( $difference/3600 ) . $ctf_hour;
271
- } else {
272
- $one_year_from_date = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
273
- $one_year_from_date->modify('+1 year');
274
- $one_year_from_date_timestamp = $one_year_from_date->getTimestamp();
275
- if ( $now > $one_year_from_date_timestamp ) {
276
- $date_str = date_i18n( 'j M Y', $tz_offset_timestamp );
277
- } else {
278
- $date_str = date_i18n( 'j M', $tz_offset_timestamp );
279
- }
280
- }
281
- break;
282
- }
283
-
284
- }
285
-
286
- return $date_str;
287
- }
288
-
289
- function ctf_maybe_shorten_text( $string, $feed_settings ) {
290
- $limit = $feed_settings['textlength'];
291
- if ( strlen( $string ) <= $limit || $limit == 280 ) {
292
- return $string;
293
- }
294
- $parts = preg_split( '/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE );
295
- $parts_count = count( $parts );
296
- $length = 0;
297
- $last_part = 0;
298
- for ( ; $last_part < $parts_count; ++$last_part ) {
299
- $length += strlen( $parts[ $last_part ] );
300
- if ( $length > $limit ) { break; }
301
- }
302
- $last_part = $last_part !== 0 ? $last_part - 1 : 0;
303
- $parts[ $last_part ] = $parts[ $last_part ] . '<a href="#" class="ctf_more">...</a><span class="ctf_remaining">';
304
- $return = implode( ' ', $parts ).'</span>';
305
- return $return;
306
- }
307
- add_filter( 'ctf_tweet_text', 'ctf_maybe_shorten_text', 10, 2 );
308
-
309
- function ctf_replace_urls( $string, $feed_settings, $post ) {
310
-
311
- if ( $feed_settings['shorturls'] ) {
312
- return $string;
313
- }
314
-
315
- if ( isset( $post['entities']['urls'][0] ) ) {
316
- foreach ( $post['entities']['urls'] as $url ) {
317
- if ( isset( $url['url'] ) ) {
318
- $string = str_replace( $url['url'], $url['expanded_url'], $string );
319
- }
320
- }
321
- }
322
-
323
- return $string;
324
- }
325
- add_filter( 'ctf_tweet_text', 'ctf_replace_urls', 9, 3 );
326
- add_filter( 'ctf_quoted_tweet_text', 'ctf_replace_urls', 9, 3 );
327
-
328
- function ctf_get_fa_el( $icon ) {
329
- $options = get_option( 'ctf_options' );
330
- $font_method = isset( $options['font_method'] ) ? $options['font_method'] : 'svg';
331
-
332
- $elems = array(
333
- 'fa-arrows-alt' => array(
334
- 'icon' => '<span class="fa fa-arrows-alt"></span>',
335
- 'svg' => '<svg class="svg-inline--fa fa-arrows-alt fa-w-16" aria-hidden="true" aria-label="expand" data-fa-processed="" data-prefix="fa" data-icon="arrows-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M352.201 425.775l-79.196 79.196c-9.373 9.373-24.568 9.373-33.941 0l-79.196-79.196c-15.119-15.119-4.411-40.971 16.971-40.97h51.162L228 284H127.196v51.162c0 21.382-25.851 32.09-40.971 16.971L7.029 272.937c-9.373-9.373-9.373-24.569 0-33.941L86.225 159.8c15.119-15.119 40.971-4.411 40.971 16.971V228H228V127.196h-51.23c-21.382 0-32.09-25.851-16.971-40.971l79.196-79.196c9.373-9.373 24.568-9.373 33.941 0l79.196 79.196c15.119 15.119 4.411 40.971-16.971 40.971h-51.162V228h100.804v-51.162c0-21.382 25.851-32.09 40.97-16.971l79.196 79.196c9.373 9.373 9.373 24.569 0 33.941L425.773 352.2c-15.119 15.119-40.971 4.411-40.97-16.971V284H284v100.804h51.23c21.382 0 32.09 25.851 16.971 40.971z"></path></svg>'
336
- ),
337
- 'fa-check-circle' => array(
338
- 'icon' => '<span class="fa fa-check-circle"></span>',
339
- 'svg' => '<svg class="svg-inline--fa fa-check-circle fa-w-16" aria-hidden="true" aria-label="verified" data-fa-processed="" data-prefix="fa" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>'
340
- ),
341
- 'fa-reply' => array(
342
- 'icon' => '<span class="fa fa-reply"></span>',
343
- 'svg' => '<svg class="svg-inline--fa fa-reply fa-w-16" aria-hidden="true" aria-label="reply" data-fa-processed="" data-prefix="fa" data-icon="reply" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M8.309 189.836L184.313 37.851C199.719 24.546 224 35.347 224 56.015v80.053c160.629 1.839 288 34.032 288 186.258 0 61.441-39.581 122.309-83.333 154.132-13.653 9.931-33.111-2.533-28.077-18.631 45.344-145.012-21.507-183.51-176.59-185.742V360c0 20.7-24.3 31.453-39.687 18.164l-176.004-152c-11.071-9.562-11.086-26.753 0-36.328z"></path></svg>'
344
- ),
345
- 'fa-retweet' => array(
346
- 'icon' => '<span class="fa fa-retweet"></span>',
347
- 'svg' => '<svg class="svg-inline--fa fa-retweet fa-w-20" aria-hidden="true" aria-label="retweet" data-fa-processed="" data-prefix="fa" data-icon="retweet" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M629.657 343.598L528.971 444.284c-9.373 9.372-24.568 9.372-33.941 0L394.343 343.598c-9.373-9.373-9.373-24.569 0-33.941l10.823-10.823c9.562-9.562 25.133-9.34 34.419.492L480 342.118V160H292.451a24.005 24.005 0 0 1-16.971-7.029l-16-16C244.361 121.851 255.069 96 276.451 96H520c13.255 0 24 10.745 24 24v222.118l40.416-42.792c9.285-9.831 24.856-10.054 34.419-.492l10.823 10.823c9.372 9.372 9.372 24.569-.001 33.941zm-265.138 15.431A23.999 23.999 0 0 0 347.548 352H160V169.881l40.416 42.792c9.286 9.831 24.856 10.054 34.419.491l10.822-10.822c9.373-9.373 9.373-24.569 0-33.941L144.971 67.716c-9.373-9.373-24.569-9.373-33.941 0L10.343 168.402c-9.373 9.373-9.373 24.569 0 33.941l10.822 10.822c9.562 9.562 25.133 9.34 34.419-.491L96 169.881V392c0 13.255 10.745 24 24 24h243.549c21.382 0 32.09-25.851 16.971-40.971l-16.001-16z"></path></svg>'
348
- ),
349
- 'fa-heart' => array(
350
- 'icon' => '<span class="fa fa-heart"></span>',
351
- 'svg' => '<svg class="svg-inline--fa fa-heart fa-w-18" aria-hidden="true" aria-label="like" data-fa-processed="" data-prefix="fa" data-icon="heart" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M414.9 24C361.8 24 312 65.7 288 89.3 264 65.7 214.2 24 161.1 24 70.3 24 16 76.9 16 165.5c0 72.6 66.8 133.3 69.2 135.4l187 180.8c8.8 8.5 22.8 8.5 31.6 0l186.7-180.2c2.7-2.7 69.5-63.5 69.5-136C560 76.9 505.7 24 414.9 24z"></path></svg>'
352
- ),
353
- 'fa-twitter' => array(
354
- 'icon' => '<span class="fa fab fa-twitter"></span>',
355
- 'svg' => '<svg class="svg-inline--fa fa-twitter fa-w-16" aria-hidden="true" aria-label="twitter logo" data-fa-processed="" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>'
356
- ),
357
- 'fa-user' => array(
358
- 'icon' => '<span class="fa fa-user"></span>',
359
- 'svg' => '<svg class="svg-inline--fa fa-user fa-w-16" aria-hidden="true" aria-label="followers" data-fa-processed="" data-prefix="fa" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 160C96 71.634 167.635 0 256 0s160 71.634 160 160-71.635 160-160 160S96 248.366 96 160zm304 192h-28.556c-71.006 42.713-159.912 42.695-230.888 0H112C50.144 352 0 402.144 0 464v24c0 13.255 10.745 24 24 24h464c13.255 0 24-10.745 24-24v-24c0-61.856-50.144-112-112-112z"></path></svg>'
360
- ),
361
- 'ctf_playbtn' => array(
362
- 'icon' => '',
363
- 'svg' => '<svg aria-label="play button" style="color: rgba(255,255,255,1)" class="svg-inline--fa fa-play fa-w-14 ctf_playbtn" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="play" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>'
364
- ),
365
- 'fa-file-video-o' => array(
366
- 'icon' => '<span class="fa fa-file-video-o ctf-tweet-text-media" aria-hidden="true"></span>',
367
- 'svg' => '<svg aria-hidden="true" aria-label="video in tweet" focusable="false" data-prefix="far" data-icon="file-video" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-file-video fa-w-12 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M369.941 97.941l-83.882-83.882A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v416c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V131.882a48 48 0 0 0-14.059-33.941zM332.118 128H256V51.882L332.118 128zM48 464V48h160v104c0 13.255 10.745 24 24 24h104v288H48zm228.687-211.303L224 305.374V268c0-11.046-8.954-20-20-20H100c-11.046 0-20 8.954-20 20v104c0 11.046 8.954 20 20 20h104c11.046 0 20-8.954 20-20v-37.374l52.687 52.674C286.704 397.318 304 390.28 304 375.986V264.011c0-14.311-17.309-21.319-27.313-11.314z" class=""></path></svg>'
368
- ),
369
- 'fa-picture-o' => array(
370
- 'icon' => '<span class="fa fa-picture-o ctf-tweet-text-media" aria-hidden="true"></span>',
371
- 'svg' => '<svg aria-hidden="true" aria-label="images in tweet" focusable="false" data-prefix="far" data-icon="image" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-image fa-w-16 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z" class=""></path></svg>'
372
- ),
373
- );
374
-
375
- if ( $font_method !== 'fontfile' ){
376
- return $elems[ $icon ]['svg'];
377
- }
378
-
379
- return $elems[ $icon ]['icon'];
380
- }
381
-
382
- /**
383
- * Called via ajax to automatically save access token and access token secret
384
- * retrieved with the big blue button
385
- */
386
- function ctf_auto_save_tokens() {
387
- if ( current_user_can( 'edit_posts' ) ) {
388
- wp_cache_delete ( 'alloptions', 'options' );
389
-
390
- $options = get_option( 'ctf_options', array() );
391
-
392
- $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
393
- $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
394
-
395
- update_option( 'ctf_options', $options );
396
- delete_transient( 'ctf_reauthenticate' );
397
- die();
398
- }
399
- die();
400
- }
401
- add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
402
-
403
- /**
404
- * manually clears the cached tweets in case of error or user preference
405
- *
406
- * @return mixed bool whether or not it was successful
407
- */
408
- function ctf_clear_cache() {
409
-
410
- //Delete all transients
411
- global $wpdb;
412
- $table_name = $wpdb->prefix . "options";
413
- $result = $wpdb->query("
414
- DELETE
415
- FROM $table_name
416
- WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
417
- ");
418
- $wpdb->query("
419
- DELETE
420
- FROM $table_name
421
- WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
422
- ");
423
-
424
- }
425
- add_action( 'ctf_cron_job', 'ctf_clear_cache' );
426
-
427
- function ctf_clear_cache_admin() {
428
-
429
- //Delete all transients
430
- global $wpdb;
431
- $table_name = $wpdb->prefix . "options";
432
- $result = $wpdb->query("
433
- DELETE
434
- FROM $table_name
435
- WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
436
- ");
437
- $wpdb->query("
438
- DELETE
439
- FROM $table_name
440
- WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
441
- ");
442
-
443
- }
444
- add_action( 'wp_ajax_ctf_clear_cache_admin', 'ctf_clear_cache_admin' );
445
-
446
- /**
447
- * manually clears the persistent cached tweets
448
- *
449
- * @return mixed bool whether or not it was successful
450
- */
451
-
452
- function ctf_clear_persistent_cache() {
453
- if ( current_user_can( 'edit_posts' ) ) {
454
- //Delete all persistent caches (start with ctf_!)
455
- global $wpdb;
456
- $table_name = $wpdb->prefix . "options";
457
- $result = $wpdb->query("
458
- DELETE
459
- FROM $table_name
460
- WHERE `option_name` LIKE ('%ctf\_\!%')
461
- ");
462
- delete_option( 'ctf_cache_list' );
463
- return $result;
464
- } else {
465
- return false;
466
- }
467
-
468
- die();
469
- }
470
- add_action( 'wp_ajax_ctf_clear_persistent_cache', 'ctf_clear_persistent_cache' );
471
-
472
- /**
473
- * clear the cache and unschedule an cron jobs when deactivated
474
- */
475
- function ctf_deactivate() {
476
- ctf_clear_cache();
477
-
478
- wp_clear_scheduled_hook( 'ctf_cron_job' );
479
- }
480
- register_deactivation_hook( __FILE__, 'ctf_deactivate' );
481
-
482
- /**
483
- * Loads the javascript for the plugin front-end. Also localizes the admin-ajax file location for use in ajax calls
484
- */
485
- function ctf_scripts_and_styles() {
486
- $options = get_option( 'ctf_options' );
487
- $not_ajax_theme = (! isset( $options['ajax_theme'] ) || ! $options['ajax_theme']);
488
- $font_method = isset( $options['font_method'] ) ? $options['font_method'] : 'svg';
489
- $disable_awesome = isset( $options['disableawesome'] ) ? $options['disableawesome'] : false;
490
-
491
- wp_enqueue_style( 'ctf_styles', plugins_url( '/css/ctf-styles.min.css', __FILE__ ), array(), CTF_VERSION );
492
-
493
- if ( $font_method === 'fontfile' && ! $disable_awesome ) {
494
- wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
495
- }
496
-
497
- if ( $not_ajax_theme ) {
498
- wp_register_script( 'ctf_scripts', plugins_url( '/js/ctf-scripts.min.js', __FILE__ ), array( 'jquery' ), CTF_VERSION, true );
499
- wp_localize_script( 'ctf_scripts', 'ctf', array(
500
- 'ajax_url' => admin_url( 'admin-ajax.php' )
501
- )
502
- );
503
- } else {
504
- wp_localize_script( 'jquery', 'ctf', array(
505
- 'ajax_url' => admin_url( 'admin-ajax.php' )
506
- )
507
- );
508
- }
509
-
510
- }
511
- add_action( 'wp_enqueue_scripts', 'ctf_scripts_and_styles' );
512
-
513
- /**
514
- * outputs the custom js from the "Customize" tab on the Settings page
515
- */
516
- function ctf_custom_js() {
517
- $options = get_option( 'ctf_options' );
518
- $ctf_custom_js = isset( $options[ 'custom_js' ] ) ? $options[ 'custom_js' ] : '';
519
-
520
- if ( ! empty( $ctf_custom_js ) ) {
521
- ?>
522
- <!-- Custom Twitter Feeds JS -->
523
- <script type="text/javascript">
524
- <?php echo "window.ctf_custom_js = function($){" . stripslashes( $ctf_custom_js ) . "}\r\n"; ?>
525
- </script>
526
- <?php
527
- }
528
- }
529
- add_action( 'wp_footer', 'ctf_custom_js' );
530
-
531
- /**
532
- * outputs the custom css from the "Customize" tab on the Settings page
533
- */
534
- function ctf_custom_css() {
535
- $options = get_option( 'ctf_options' );
536
- $ctf_custom_css = isset( $options[ 'custom_css' ] ) ? $options[ 'custom_css' ] : '';
537
-
538
- if ( ! empty( $ctf_custom_css ) ) {
539
- ?>
540
- <!-- Custom Twitter Feeds CSS -->
541
- <style type="text/css">
542
- <?php echo stripslashes( $ctf_custom_css ) . "\r\n"; ?>
543
- </style>
544
- <?php
545
- }
546
- }
547
- add_action( 'wp_head', 'ctf_custom_css' );
548
-
549
- /**
550
- * Some CSS and JS needed in the admin area as well
551
- */
552
- function ctf_admin_scripts_and_styles() {
553
- wp_enqueue_style( 'ctf_admin_styles', plugins_url( '/css/ctf-admin-styles.css', __FILE__ ), array(), CTF_VERSION );
554
- wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
555
- wp_enqueue_script( 'ctf_admin_scripts', plugins_url( '/js/ctf-admin-scripts.js', __FILE__ ) , array( 'jquery' ), CTF_VERSION, false );
556
- wp_localize_script( 'ctf_admin_scripts', 'ctf', array(
557
- 'ajax_url' => admin_url( 'admin-ajax.php' ),
558
- 'sb_nonce' => wp_create_nonce( 'ctf-smash-balloon' )
559
- )
560
- );
561
- wp_enqueue_style( 'wp-color-picker' );
562
- wp_enqueue_script( 'wp-color-picker' );
563
- }
564
- add_action( 'admin_enqueue_scripts', 'ctf_admin_scripts_and_styles' );
565
-
566
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: Custom Twitter Feeds
4
+ Plugin URI: http://smashballoon.com/custom-twitter-feeds
5
+ Description: Customizable Twitter feeds for your website
6
+ Version: 1.5
7
+ Author: Smash Balloon
8
+ Author URI: http://smashballoon.com/
9
+ Text Domain: custom-twitter-feeds
10
+ */
11
+ /*
12
+ Copyright 2020 Smash Balloon LLC (email : hey@smashballoon.com)
13
+ This program is free software; you can redistribute it and/or modify
14
+ it under the terms of the GNU General Public License as published by
15
+ the Free Software Foundation; either version 2 of the License, or
16
+ (at your option) any later version.
17
+ This program is distributed in the hope that it will be useful,
18
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ GNU General Public License for more details.
21
+ You should have received a copy of the GNU General Public License
22
+ along with this program; if not, write to the Free Software
23
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
24
+ */
25
+
26
+ define( 'CTF_URL', plugin_dir_path( __FILE__ ) );
27
+ define( 'CTF_VERSION', '1.5' );
28
+ define( 'CTF_TITLE', 'Custom Twitter Feeds' );
29
+ define( 'CTF_JS_URL', plugins_url( '/js/ctf-scripts.min.js?ver=' . CTF_VERSION , __FILE__ ) );
30
+ define( 'OAUTH_PROCESSOR_URL', 'https://api.smashballoon.com/twitter-login.php?return_uri=' );
31
+ // Plugin Folder Path.
32
+ if ( ! defined( 'CTF_PLUGIN_DIR' ) ) {
33
+ define( 'CTF_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
34
+ }
35
+ // Plugin Folder URL.
36
+ if ( ! defined( 'CTF_PLUGIN_URL' ) ) {
37
+ define( 'CTF_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
38
+ }
39
+ // Db version.
40
+ if ( ! defined( 'CTF_DBVERSION' ) ) {
41
+ define( 'CTF_DBVERSION', '1.0' );
42
+ }
43
+
44
+ if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
45
+
46
+ require_once( CTF_URL . '/inc/widget.php' );
47
+
48
+ require_once( CTF_URL . '/inc/admin-hooks.php' );
49
+
50
+ function ctf_plugin_init() {
51
+ require_once trailingslashit( CTF_PLUGIN_DIR ) . 'inc/blocks/class-ctf-blocks.php';
52
+
53
+ $ctf_blocks = new CTF_Blocks();
54
+
55
+ if ( $ctf_blocks->allow_load() ) {
56
+ $ctf_blocks->load();
57
+ }
58
+ }
59
+
60
+ add_action( 'plugins_loaded', 'ctf_plugin_init' );
61
+
62
+ function ctf_update_settings() {
63
+ $existing_deprecated_options = get_option( 'ctf_configure' );
64
+ $existing_options = get_option( 'ctf_options' );
65
+
66
+ update_option( 'ctf_version', CTF_VERSION );
67
+
68
+ if ( ! empty( $existing_deprecated_options ) && empty( $existing_options ) ) {
69
+ $merged_options = $existing_deprecated_options;
70
+ $merged_options = array_merge( $merged_options, get_option( 'ctf_customize', array() ) );
71
+ $merged_options = array_merge( $merged_options, get_option( 'ctf_style', array() ) );
72
+
73
+ update_option( 'ctf_options', $merged_options );
74
+ }
75
+ }
76
+
77
+ function ctf_check_for_db_updates() {
78
+
79
+ $db_ver = get_option( 'ctf_db_version', 0 );
80
+
81
+ if ( (float) $db_ver < 1.0 ) {
82
+
83
+ global $wp_roles;
84
+ $wp_roles->add_cap( 'administrator', 'manage_custom_twitter_feeds_options' );
85
+
86
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
87
+
88
+ if ( ! isset( $ctf_statuses_option['first_install'] ) ) {
89
+
90
+ $options_set = get_option( 'ctf_options', false );
91
+
92
+ if ( $options_set ) {
93
+ $ctf_statuses_option['first_install'] = 'from_update';
94
+ } else {
95
+ $ctf_statuses_option['first_install'] = time();
96
+ }
97
+
98
+ $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
99
+
100
+ if ( $ctf_rating_notice_option === 'dismissed' ) {
101
+ $ctf_statuses_option['rating_notice_dismissed'] = time();
102
+ }
103
+
104
+ $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
105
+
106
+ if ( $ctf_rating_notice_waiting === false
107
+ && $ctf_rating_notice_option === false ) {
108
+ $time = 2 * WEEK_IN_SECONDS;
109
+ set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', $time );
110
+ update_option( 'ctf_rating_notice', 'pending', false );
111
+ }
112
+
113
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
114
+
115
+ }
116
+
117
+ update_option( 'ctf_db_version', CTF_DBVERSION );
118
+ }
119
+
120
+ }
121
+ add_action( 'wp_loaded', 'ctf_check_for_db_updates' );
122
+
123
+
124
+ /**
125
+ * include the admin files only if in the admin area
126
+ */
127
+ if ( is_admin() ) {
128
+
129
+ $ctf_version = get_option( 'ctf_version', false );
130
+
131
+ if ( ! $ctf_version ) {
132
+ ctf_update_settings();
133
+ }
134
+ require_once( CTF_URL . '/inc/CtfAdmin.php' );
135
+ require_once( CTF_URL . '/inc/notices.php' );
136
+
137
+ $admin = new CtfAdmin;
138
+ }
139
+
140
+ /**
141
+ * Generates the Twitter feed wherever the shortcode is placed
142
+ *
143
+ * @param $atts array shortcode arguments
144
+ *
145
+ * @return string
146
+ */
147
+ function ctf_init( $atts ) {
148
+
149
+ include_once( CTF_URL . '/inc/CtfFeed.php' );
150
+ wp_enqueue_script( 'ctf_scripts' );
151
+
152
+ $twitter_feed = CtfFeed::init( $atts );
153
+ /*
154
+ echo '<pre>';
155
+ var_dump( $twitter_feed->tweet_set);
156
+ echo '</pre>'; */
157
+ // if there is an error, display the error html, otherwise the feed
158
+ if ( ! $twitter_feed->tweet_set || $twitter_feed->missing_credentials ) {
159
+ return $twitter_feed->getErrorHtml();
160
+ } else {
161
+ $twitter_feed->maybeCacheTweets();
162
+
163
+ $feed_html = $twitter_feed->getFeedOpeningHtml();
164
+ $feed_html .= $twitter_feed->getTweetSetHtml();
165
+ $feed_html .= $twitter_feed->getFeedClosingHtml();
166
+
167
+ return $feed_html;
168
+ }
169
+ }
170
+ add_shortcode( 'custom-twitter-feed', 'ctf_init' );
171
+ add_shortcode( 'custom-twitter-feeds', 'ctf_init' );
172
+
173
+ /**
174
+ * Called via ajax to get more posts after the "load more" button is clicked
175
+ */
176
+ function ctf_get_more_posts() {
177
+ $shortcode_data = json_decode( str_replace( '\"', '"', sanitize_text_field( $_POST['shortcode_data'] ) ), true ); // necessary to unescape quotes
178
+ $last_id_data = isset( $_POST['last_id_data'] ) ? sanitize_text_field( $_POST['last_id_data'] ) : '';
179
+ $num_needed = isset( $_POST['num_needed'] ) ? (int)$_POST['num_needed'] : 0;
180
+ $ids_to_remove = isset( $_POST['ids_to_remove'] ) ? $_POST['ids_to_remove'] : array();
181
+ $is_pagination = empty( $last_id_data ) ? 0 : 1;
182
+ $persistent_index = isset( $_POST['persistent_index'] ) ? sanitize_text_field( $_POST['persistent_index'] ) : '';
183
+
184
+ include_once( CTF_URL . '/inc/CtfFeed.php' );
185
+
186
+ $twitter_feed = CtfFeed::init( $shortcode_data, $last_id_data, $num_needed, $ids_to_remove, $persistent_index );
187
+
188
+ if ( ! $twitter_feed->feed_options['persistentcache'] ) {
189
+ $twitter_feed->maybeCacheTweets();
190
+ }
191
+
192
+ echo $twitter_feed->getTweetSetHtml( $is_pagination );
193
+
194
+ die();
195
+ }
196
+ add_action( 'wp_ajax_nopriv_ctf_get_more_posts', 'ctf_get_more_posts' );
197
+ add_action( 'wp_ajax_ctf_get_more_posts', 'ctf_get_more_posts' );
198
+
199
+ function ctf_plugin_action_links( $links ) {
200
+ $links[] = '<a href="'. esc_url( get_admin_url( null, 'admin.php?page=custom-twitter-feeds' ) ) .'">' . __( 'Settings' ) . '</a>';
201
+ return $links;
202
+ }
203
+ add_filter( 'plugin_action_links_' . plugin_basename(__FILE__), 'ctf_plugin_action_links' );
204
+
205
+ /**
206
+ * the html output is controlled by the user selecting which portions of tweets to show
207
+ *
208
+ * @param $part string part of the feed in the html
209
+ * @param $feed_options array options that contain what parts of the tweet to show
210
+ * @return bool whether or not to show the tweet
211
+ */
212
+ function ctf_show( $part, $feed_options ) {
213
+ $tweet_excludes = isset( $feed_options['tweet_excludes'] ) ? $feed_options['tweet_excludes'] : '';
214
+ $tweet_includes = isset( $feed_options['tweet_includes'] ) ? $feed_options['tweet_includes'] : '';
215
+
216
+ // if part is in the array of excluded parts or not in the array of included parts, don't show
217
+ if ( ! empty( $tweet_excludes ) ) {
218
+ return ( in_array( $part, $tweet_excludes ) === false );
219
+ } else {
220
+ return ( in_array( $part, $tweet_includes ) === true );
221
+ }
222
+ }
223
+
224
+ /**
225
+ * this function returns the properly formatted date string based on user input
226
+ *
227
+ * @param $raw_date string the date from the Twitter api
228
+ * @param $feed_options array options for the feed that contain date formatting settings
229
+ * @param $utc_offset int offset in seconds for the time display based on timezone
230
+ * @return string formatted date
231
+ */
232
+ function ctf_get_formatted_date( $raw_date, $feed_options, $utc_offset ) {
233
+ include_once( CTF_URL . '/inc/CtfDateTime.php' );
234
+
235
+ $options = get_option( 'ctf_options' );
236
+ $timezone = isset( $options['timezone'] ) ? $options['timezone'] : 'default';
237
+ // use php DateTimeZone class to handle the date formatting and offsets
238
+ $date_obj = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
239
+
240
+ if( $timezone != 'default' ) {
241
+ $date_obj->setTimeZone( new DateTimeZone( $timezone ) );
242
+ $utc_offset = $date_obj->getOffset();
243
+ }
244
+
245
+ $tz_offset_timestamp = $date_obj->getTimestamp() + $utc_offset;
246
+
247
+ // use the custom date format if set, otherwise use from the selected defaults
248
+ if ( ! empty( $feed_options['datecustom'] ) ){
249
+ $date_str = date_i18n( $feed_options['datecustom'], $tz_offset_timestamp );
250
+ } else {
251
+
252
+ switch ( $feed_options['dateformat'] ) {
253
+
254
+ case '2':
255
+ $date_str = date_i18n( 'F j', $tz_offset_timestamp );
256
+ break;
257
+ case '3':
258
+ $date_str = date_i18n( 'F j, Y', $tz_offset_timestamp );
259
+ break;
260
+ case '4':
261
+ $date_str = date_i18n( 'm.d', $tz_offset_timestamp );
262
+ break;
263
+ case '5':
264
+ $date_str = date_i18n( 'm.d.y', $tz_offset_timestamp );
265
+ break;
266
+ default:
267
+
268
+ // default format is similar to Twitter
269
+ $ctf_minute = ! empty( $feed_options['mtime'] ) ? $feed_options['mtime'] : 'm';
270
+ $ctf_hour = ! empty( $feed_options['htime'] ) ? $feed_options['htime'] : 'h';
271
+ $ctf_now_str = ! empty( $feed_options['nowtime'] ) ? $feed_options['nowtime'] : 'now';
272
+
273
+ $now = time() + $utc_offset;
274
+
275
+ $difference = $now - $tz_offset_timestamp;
276
+
277
+ if ( $difference < 60 ) {
278
+ $date_str = $ctf_now_str;
279
+ } elseif ( $difference < 60*60 ) {
280
+ $date_str = round( $difference/60 ) . $ctf_minute;
281
+ } elseif ( $difference < 60*60*24 ) {
282
+ $date_str = round( $difference/3600 ) . $ctf_hour;
283
+ } else {
284
+ $one_year_from_date = new CtfDateTime( $raw_date, new DateTimeZone( "UTC" ) );
285
+ $one_year_from_date->modify('+1 year');
286
+ $one_year_from_date_timestamp = $one_year_from_date->getTimestamp();
287
+ if ( $now > $one_year_from_date_timestamp ) {
288
+ $date_str = date_i18n( 'j M Y', $tz_offset_timestamp );
289
+ } else {
290
+ $date_str = date_i18n( 'j M', $tz_offset_timestamp );
291
+ }
292
+ }
293
+ break;
294
+ }
295
+
296
+ }
297
+
298
+ return $date_str;
299
+ }
300
+
301
+ function ctf_maybe_shorten_text( $string, $feed_settings ) {
302
+ $limit = $feed_settings['textlength'];
303
+ if ( strlen( $string ) <= $limit || $limit == 280 ) {
304
+ return $string;
305
+ }
306
+ $parts = preg_split( '/([\s\n\r]+)/', $string, null, PREG_SPLIT_DELIM_CAPTURE );
307
+ $parts_count = count( $parts );
308
+ $length = 0;
309
+ $last_part = 0;
310
+ for ( ; $last_part < $parts_count; ++$last_part ) {
311
+ $length += strlen( $parts[ $last_part ] );
312
+ if ( $length > $limit ) { break; }
313
+ }
314
+ $last_part = $last_part !== 0 ? $last_part - 1 : 0;
315
+ $parts[ $last_part ] = $parts[ $last_part ] . '<a href="#" class="ctf_more">...</a><span class="ctf_remaining">';
316
+ $return = implode( ' ', $parts ).'</span>';
317
+ return $return;
318
+ }
319
+ add_filter( 'ctf_tweet_text', 'ctf_maybe_shorten_text', 10, 2 );
320
+
321
+ function ctf_replace_urls( $string, $feed_settings, $post ) {
322
+
323
+ if ( $feed_settings['shorturls'] ) {
324
+ return $string;
325
+ }
326
+
327
+ if ( isset( $post['entities']['urls'][0] ) ) {
328
+ foreach ( $post['entities']['urls'] as $url ) {
329
+ if ( isset( $url['url'] ) ) {
330
+ $string = str_replace( $url['url'], $url['expanded_url'], $string );
331
+ }
332
+ }
333
+ }
334
+
335
+ return $string;
336
+ }
337
+ add_filter( 'ctf_tweet_text', 'ctf_replace_urls', 9, 3 );
338
+ add_filter( 'ctf_quoted_tweet_text', 'ctf_replace_urls', 9, 3 );
339
+
340
+ function ctf_get_fa_el( $icon ) {
341
+ $options = get_option( 'ctf_options' );
342
+ $font_method = isset( $options['font_method'] ) ? $options['font_method'] : 'svg';
343
+
344
+ $elems = array(
345
+ 'fa-arrows-alt' => array(
346
+ 'icon' => '<span class="fa fa-arrows-alt"></span>',
347
+ 'svg' => '<svg class="svg-inline--fa fa-arrows-alt fa-w-16" aria-hidden="true" aria-label="expand" data-fa-processed="" data-prefix="fa" data-icon="arrows-alt" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M352.201 425.775l-79.196 79.196c-9.373 9.373-24.568 9.373-33.941 0l-79.196-79.196c-15.119-15.119-4.411-40.971 16.971-40.97h51.162L228 284H127.196v51.162c0 21.382-25.851 32.09-40.971 16.971L7.029 272.937c-9.373-9.373-9.373-24.569 0-33.941L86.225 159.8c15.119-15.119 40.971-4.411 40.971 16.971V228H228V127.196h-51.23c-21.382 0-32.09-25.851-16.971-40.971l79.196-79.196c9.373-9.373 24.568-9.373 33.941 0l79.196 79.196c15.119 15.119 4.411 40.971-16.971 40.971h-51.162V228h100.804v-51.162c0-21.382 25.851-32.09 40.97-16.971l79.196 79.196c9.373 9.373 9.373 24.569 0 33.941L425.773 352.2c-15.119 15.119-40.971 4.411-40.97-16.971V284H284v100.804h51.23c21.382 0 32.09 25.851 16.971 40.971z"></path></svg>'
348
+ ),
349
+ 'fa-check-circle' => array(
350
+ 'icon' => '<span class="fa fa-check-circle"></span>',
351
+ 'svg' => '<svg class="svg-inline--fa fa-check-circle fa-w-16" aria-hidden="true" aria-label="verified" data-fa-processed="" data-prefix="fa" data-icon="check-circle" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M504 256c0 136.967-111.033 248-248 248S8 392.967 8 256 119.033 8 256 8s248 111.033 248 248zM227.314 387.314l184-184c6.248-6.248 6.248-16.379 0-22.627l-22.627-22.627c-6.248-6.249-16.379-6.249-22.628 0L216 308.118l-70.059-70.059c-6.248-6.248-16.379-6.248-22.628 0l-22.627 22.627c-6.248 6.248-6.248 16.379 0 22.627l104 104c6.249 6.249 16.379 6.249 22.628.001z"></path></svg>'
352
+ ),
353
+ 'fa-reply' => array(
354
+ 'icon' => '<span class="fa fa-reply"></span>',
355
+ 'svg' => '<svg class="svg-inline--fa fa-reply fa-w-16" aria-hidden="true" aria-label="reply" data-fa-processed="" data-prefix="fa" data-icon="reply" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M8.309 189.836L184.313 37.851C199.719 24.546 224 35.347 224 56.015v80.053c160.629 1.839 288 34.032 288 186.258 0 61.441-39.581 122.309-83.333 154.132-13.653 9.931-33.111-2.533-28.077-18.631 45.344-145.012-21.507-183.51-176.59-185.742V360c0 20.7-24.3 31.453-39.687 18.164l-176.004-152c-11.071-9.562-11.086-26.753 0-36.328z"></path></svg>'
356
+ ),
357
+ 'fa-retweet' => array(
358
+ 'icon' => '<span class="fa fa-retweet"></span>',
359
+ 'svg' => '<svg class="svg-inline--fa fa-retweet fa-w-20" aria-hidden="true" aria-label="retweet" data-fa-processed="" data-prefix="fa" data-icon="retweet" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M629.657 343.598L528.971 444.284c-9.373 9.372-24.568 9.372-33.941 0L394.343 343.598c-9.373-9.373-9.373-24.569 0-33.941l10.823-10.823c9.562-9.562 25.133-9.34 34.419.492L480 342.118V160H292.451a24.005 24.005 0 0 1-16.971-7.029l-16-16C244.361 121.851 255.069 96 276.451 96H520c13.255 0 24 10.745 24 24v222.118l40.416-42.792c9.285-9.831 24.856-10.054 34.419-.492l10.823 10.823c9.372 9.372 9.372 24.569-.001 33.941zm-265.138 15.431A23.999 23.999 0 0 0 347.548 352H160V169.881l40.416 42.792c9.286 9.831 24.856 10.054 34.419.491l10.822-10.822c9.373-9.373 9.373-24.569 0-33.941L144.971 67.716c-9.373-9.373-24.569-9.373-33.941 0L10.343 168.402c-9.373 9.373-9.373 24.569 0 33.941l10.822 10.822c9.562 9.562 25.133 9.34 34.419-.491L96 169.881V392c0 13.255 10.745 24 24 24h243.549c21.382 0 32.09-25.851 16.971-40.971l-16.001-16z"></path></svg>'
360
+ ),
361
+ 'fa-heart' => array(
362
+ 'icon' => '<span class="fa fa-heart"></span>',
363
+ 'svg' => '<svg class="svg-inline--fa fa-heart fa-w-18" aria-hidden="true" aria-label="like" data-fa-processed="" data-prefix="fa" data-icon="heart" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M414.9 24C361.8 24 312 65.7 288 89.3 264 65.7 214.2 24 161.1 24 70.3 24 16 76.9 16 165.5c0 72.6 66.8 133.3 69.2 135.4l187 180.8c8.8 8.5 22.8 8.5 31.6 0l186.7-180.2c2.7-2.7 69.5-63.5 69.5-136C560 76.9 505.7 24 414.9 24z"></path></svg>'
364
+ ),
365
+ 'fa-twitter' => array(
366
+ 'icon' => '<span class="fa fab fa-twitter"></span>',
367
+ 'svg' => '<svg class="svg-inline--fa fa-twitter fa-w-16" aria-hidden="true" aria-label="twitter logo" data-fa-processed="" data-prefix="fab" data-icon="twitter" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z"></path></svg>'
368
+ ),
369
+ 'fa-user' => array(
370
+ 'icon' => '<span class="fa fa-user"></span>',
371
+ 'svg' => '<svg class="svg-inline--fa fa-user fa-w-16" aria-hidden="true" aria-label="followers" data-fa-processed="" data-prefix="fa" data-icon="user" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="currentColor" d="M96 160C96 71.634 167.635 0 256 0s160 71.634 160 160-71.635 160-160 160S96 248.366 96 160zm304 192h-28.556c-71.006 42.713-159.912 42.695-230.888 0H112C50.144 352 0 402.144 0 464v24c0 13.255 10.745 24 24 24h464c13.255 0 24-10.745 24-24v-24c0-61.856-50.144-112-112-112z"></path></svg>'
372
+ ),
373
+ 'ctf_playbtn' => array(
374
+ 'icon' => '',
375
+ 'svg' => '<svg aria-label="play button" style="color: rgba(255,255,255,1)" class="svg-inline--fa fa-play fa-w-14 ctf_playbtn" aria-hidden="true" data-fa-processed="" data-prefix="fa" data-icon="play" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><path fill="currentColor" d="M424.4 214.7L72.4 6.6C43.8-10.3 0 6.1 0 47.9V464c0 37.5 40.7 60.1 72.4 41.3l352-208c31.4-18.5 31.5-64.1 0-82.6z"></path></svg>'
376
+ ),
377
+ 'fa-file-video-o' => array(
378
+ 'icon' => '<span class="fa fa-file-video-o ctf-tweet-text-media" aria-hidden="true"></span>',
379
+ 'svg' => '<svg aria-hidden="true" aria-label="video in tweet" focusable="false" data-prefix="far" data-icon="file-video" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" class="svg-inline--fa fa-file-video fa-w-12 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M369.941 97.941l-83.882-83.882A48 48 0 0 0 252.118 0H48C21.49 0 0 21.49 0 48v416c0 26.51 21.49 48 48 48h288c26.51 0 48-21.49 48-48V131.882a48 48 0 0 0-14.059-33.941zM332.118 128H256V51.882L332.118 128zM48 464V48h160v104c0 13.255 10.745 24 24 24h104v288H48zm228.687-211.303L224 305.374V268c0-11.046-8.954-20-20-20H100c-11.046 0-20 8.954-20 20v104c0 11.046 8.954 20 20 20h104c11.046 0 20-8.954 20-20v-37.374l52.687 52.674C286.704 397.318 304 390.28 304 375.986V264.011c0-14.311-17.309-21.319-27.313-11.314z" class=""></path></svg>'
380
+ ),
381
+ 'fa-picture-o' => array(
382
+ 'icon' => '<span class="fa fa-picture-o ctf-tweet-text-media" aria-hidden="true"></span>',
383
+ 'svg' => '<svg aria-hidden="true" aria-label="images in tweet" focusable="false" data-prefix="far" data-icon="image" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" class="svg-inline--fa fa-image fa-w-16 fa-9x ctf-tweet-text-media"><path fill="currentColor" d="M464 64H48C21.49 64 0 85.49 0 112v288c0 26.51 21.49 48 48 48h416c26.51 0 48-21.49 48-48V112c0-26.51-21.49-48-48-48zm-6 336H54a6 6 0 0 1-6-6V118a6 6 0 0 1 6-6h404a6 6 0 0 1 6 6v276a6 6 0 0 1-6 6zM128 152c-22.091 0-40 17.909-40 40s17.909 40 40 40 40-17.909 40-40-17.909-40-40-40zM96 352h320v-80l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L192 304l-39.515-39.515c-4.686-4.686-12.284-4.686-16.971 0L96 304v48z" class=""></path></svg>'
384
+ ),
385
+ );
386
+
387
+ if ( $font_method !== 'fontfile' ){
388
+ return $elems[ $icon ]['svg'];
389
+ }
390
+
391
+ return $elems[ $icon ]['icon'];
392
+ }
393
+
394
+ /**
395
+ * Called via ajax to automatically save access token and access token secret
396
+ * retrieved with the big blue button
397
+ */
398
+ function ctf_auto_save_tokens() {
399
+ if ( current_user_can( 'edit_posts' ) ) {
400
+ wp_cache_delete ( 'alloptions', 'options' );
401
+
402
+ $options = get_option( 'ctf_options', array() );
403
+
404
+ $options['access_token'] = sanitize_text_field( $_POST['access_token'] );
405
+ $options['access_token_secret'] = sanitize_text_field( $_POST['access_token_secret'] );
406
+
407
+ update_option( 'ctf_options', $options );
408
+ delete_transient( 'ctf_reauthenticate' );
409
+ die();
410
+ }
411
+ die();
412
+ }
413
+ add_action( 'wp_ajax_ctf_auto_save_tokens', 'ctf_auto_save_tokens' );
414
+
415
+ /**
416
+ * manually clears the cached tweets in case of error or user preference
417
+ *
418
+ * @return mixed bool whether or not it was successful
419
+ */
420
+ function ctf_clear_cache() {
421
+
422
+ //Delete all transients
423
+ global $wpdb;
424
+ $table_name = $wpdb->prefix . "options";
425
+ $result = $wpdb->query("
426
+ DELETE
427
+ FROM $table_name
428
+ WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
429
+ ");
430
+ $wpdb->query("
431
+ DELETE
432
+ FROM $table_name
433
+ WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
434
+ ");
435
+
436
+ }
437
+ add_action( 'ctf_cron_job', 'ctf_clear_cache' );
438
+
439
+ function ctf_clear_cache_admin() {
440
+
441
+ //Delete all transients
442
+ global $wpdb;
443
+ $table_name = $wpdb->prefix . "options";
444
+ $result = $wpdb->query("
445
+ DELETE
446
+ FROM $table_name
447
+ WHERE `option_name` LIKE ('%\_transient\_ctf\_%')
448
+ ");
449
+ $wpdb->query("
450
+ DELETE
451
+ FROM $table_name
452
+ WHERE `option_name` LIKE ('%\_transient\_timeout\_ctf\_%')
453
+ ");
454
+
455
+ }
456
+ add_action( 'wp_ajax_ctf_clear_cache_admin', 'ctf_clear_cache_admin' );
457
+
458
+ /**
459
+ * manually clears the persistent cached tweets
460
+ *
461
+ * @return mixed bool whether or not it was successful
462
+ */
463
+
464
+ function ctf_clear_persistent_cache() {
465
+ if ( current_user_can( 'edit_posts' ) ) {
466
+ //Delete all persistent caches (start with ctf_!)
467
+ global $wpdb;
468
+ $table_name = $wpdb->prefix . "options";
469
+ $result = $wpdb->query("
470
+ DELETE
471
+ FROM $table_name
472
+ WHERE `option_name` LIKE ('%ctf\_\!%')
473
+ ");
474
+ delete_option( 'ctf_cache_list' );
475
+ return $result;
476
+ } else {
477
+ return false;
478
+ }
479
+
480
+ die();
481
+ }
482
+ add_action( 'wp_ajax_ctf_clear_persistent_cache', 'ctf_clear_persistent_cache' );
483
+
484
+ /**
485
+ * clear the cache and unschedule an cron jobs when deactivated
486
+ */
487
+ function ctf_deactivate() {
488
+ ctf_clear_cache();
489
+
490
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
491
+ }
492
+ register_deactivation_hook( __FILE__, 'ctf_deactivate' );
493
+
494
+ /**
495
+ * Loads the javascript for the plugin front-end. Also localizes the admin-ajax file location for use in ajax calls
496
+ */
497
+ function ctf_scripts_and_styles( $enqueue = false ) {
498
+ $options = get_option( 'ctf_options' );
499
+ $not_ajax_theme = (! isset( $options['ajax_theme'] ) || ! $options['ajax_theme']);
500
+ $font_method = isset( $options['font_method'] ) ? $options['font_method'] : 'svg';
501
+ $disable_awesome = isset( $options['disableawesome'] ) ? $options['disableawesome'] : false;
502
+
503
+ wp_enqueue_style( 'ctf_styles', plugins_url( '/css/ctf-styles.min.css', __FILE__ ), array(), CTF_VERSION );
504
+
505
+ if ( $font_method === 'fontfile' && ! $disable_awesome ) {
506
+ wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
507
+ }
508
+
509
+ if ( $not_ajax_theme ) {
510
+ wp_register_script( 'ctf_scripts', plugins_url( '/js/ctf-scripts.min.js', __FILE__ ), array( 'jquery' ), CTF_VERSION, true );
511
+ wp_localize_script( 'ctf_scripts', 'ctf', array(
512
+ 'ajax_url' => admin_url( 'admin-ajax.php' )
513
+ )
514
+ );
515
+ } else {
516
+ wp_localize_script( 'jquery', 'ctf', array(
517
+ 'ajax_url' => admin_url( 'admin-ajax.php' )
518
+ )
519
+ );
520
+ }
521
+
522
+ if ( $enqueue ) {
523
+ wp_enqueue_style( 'ctf_styles' );
524
+ wp_enqueue_script( 'ctf_scripts' );
525
+ }
526
+ }
527
+ add_action( 'wp_enqueue_scripts', 'ctf_scripts_and_styles' );
528
+
529
+ /**
530
+ * outputs the custom js from the "Customize" tab on the Settings page
531
+ */
532
+ function ctf_custom_js() {
533
+ $options = get_option( 'ctf_options' );
534
+ $ctf_custom_js = isset( $options[ 'custom_js' ] ) ? $options[ 'custom_js' ] : '';
535
+
536
+ if ( ! empty( $ctf_custom_js ) ) {
537
+ ?>
538
+ <!-- Custom Twitter Feeds JS -->
539
+ <script type="text/javascript">
540
+ <?php echo "window.ctf_custom_js = function($){" . stripslashes( $ctf_custom_js ) . "}\r\n"; ?>
541
+ </script>
542
+ <?php
543
+ }
544
+ }
545
+ add_action( 'wp_footer', 'ctf_custom_js' );
546
+
547
+ /**
548
+ * outputs the custom css from the "Customize" tab on the Settings page
549
+ */
550
+ function ctf_custom_css() {
551
+ $options = get_option( 'ctf_options' );
552
+ $ctf_custom_css = isset( $options[ 'custom_css' ] ) ? $options[ 'custom_css' ] : '';
553
+
554
+ if ( ! empty( $ctf_custom_css ) ) {
555
+ ?>
556
+ <!-- Custom Twitter Feeds CSS -->
557
+ <style type="text/css">
558
+ <?php echo stripslashes( $ctf_custom_css ) . "\r\n"; ?>
559
+ </style>
560
+ <?php
561
+ }
562
+ }
563
+ add_action( 'wp_head', 'ctf_custom_css' );
564
+
565
+ /**
566
+ * Some CSS and JS needed in the admin area as well
567
+ */
568
+ function ctf_admin_scripts_and_styles() {
569
+ wp_enqueue_style( 'ctf_admin_styles', plugins_url( '/css/ctf-admin-styles.css', __FILE__ ), array(), CTF_VERSION );
570
+ wp_enqueue_style( 'sb-font-awesome', 'https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css' );
571
+ wp_enqueue_script( 'ctf_admin_scripts', plugins_url( '/js/ctf-admin-scripts.js', __FILE__ ) , array( 'jquery' ), CTF_VERSION, false );
572
+ wp_localize_script( 'ctf_admin_scripts', 'ctf', array(
573
+ 'ajax_url' => admin_url( 'admin-ajax.php' ),
574
+ 'sb_nonce' => wp_create_nonce( 'ctf-smash-balloon' )
575
+ )
576
+ );
577
+ wp_enqueue_style( 'wp-color-picker' );
578
+ wp_enqueue_script( 'wp-color-picker' );
579
+ }
580
+ add_action( 'admin_enqueue_scripts', 'ctf_admin_scripts_and_styles' );
581
+
582
+
inc/CtfAdmin.php CHANGED
@@ -1,1913 +1,1912 @@
1
- <?php
2
- /**
3
- * Class CtfAdmin
4
- *
5
- * Uses the Settings API to create easily customizable settings pages and tabs
6
- */
7
-
8
- // Don't load directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- die( '-1' );
11
- }
12
-
13
- class CtfAdmin
14
- {
15
- public function __construct()
16
- {
17
- add_action( 'admin_menu', array( $this, 'add_menu' ) );
18
- add_action( 'admin_init', array( $this, 'options_page_init' ) );
19
- }
20
-
21
- public function add_menu()
22
- {
23
- add_menu_page(
24
- 'Twitter Feeds',
25
- 'Twitter Feeds',
26
- 'manage_options',
27
- 'custom-twitter-feeds',
28
- array( $this, 'create_options_page' ),
29
- '',
30
- 99
31
- );
32
-
33
- add_submenu_page(
34
- 'custom-twitter-feeds',
35
- 'Customize',
36
- 'Customize',
37
- 'manage_options',
38
- 'custom-twitter-feeds-customize',
39
- array( $this, 'create_submenu_page_customize' )
40
- );
41
-
42
- add_submenu_page(
43
- 'custom-twitter-feeds',
44
- 'Style',
45
- 'Style',
46
- 'manage_options',
47
- 'custom-twitter-feeds-style',
48
- array( $this, 'create_submenu_page_style' )
49
- );
50
- }
51
-
52
- public static function get_active_tab( $tab = '' )
53
- {
54
- switch ( $tab ) {
55
- case 'customize':
56
- return 'customize';
57
- case 'style':
58
- return 'style';
59
- case 'display':
60
- return 'display';
61
- case 'support':
62
- return 'support';
63
- default:
64
- return 'configure';
65
- }
66
- }
67
-
68
- public function create_options_page()
69
- {
70
- require_once CTF_URL . '/views/admin/main.php';
71
- }
72
-
73
- public function create_submenu_page_customize()
74
- {
75
- $tab = 'customize';
76
-
77
- require_once CTF_URL . '/views/admin/main.php';
78
- }
79
-
80
- public function create_submenu_page_style()
81
- {
82
- $tab = 'style';
83
-
84
- require_once CTF_URL . '/views/admin/main.php';
85
- }
86
-
87
- public function general_section_text()
88
- {
89
- // no explanation needed
90
- }
91
-
92
- public function access_token_button()
93
- {
94
- $this->the_admin_access_token_configure_html( $_GET );
95
- $options = get_option( 'ctf_options' );
96
- $option_checked = ( isset( $options['have_own_tokens'] ) ) ? $options['have_own_tokens'] : false;
97
- ?>
98
- <input name="<?php echo 'ctf_options'.'[have_own_tokens]'; ?>" id="ctf_have_own_tokens" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
99
- <label for="ctf_have_own_tokens" class="ctf_checkbox"><?php _e( 'Or, manually enter my own Twitter app information' ); ?></label>
100
- <span class="ctf-tooltip-wrap">
101
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
102
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Check this box if you would like to manually enter the information from your own <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">Twitter app</a>', 'custom-twitter-feeds' ); ?>.</p>
103
- </span>
104
- <?php
105
- }
106
-
107
- /**
108
- * generates the html for the access token retrieving button
109
- *
110
- * @param $access_token_data array the $_GET data if it exists
111
- */
112
- private function the_admin_access_token_configure_html( $access_token_data ) {
113
- ?>
114
-
115
- <div id="ctf_config">
116
-
117
- <?php if ( isset( $access_token_data['oauth_token'] ) ) : ?>
118
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
119
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
120
-
121
- <input type="hidden" id="ctf-retrieved-access-token" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token'] ) ); ?>">
122
- <input type="hidden" id="ctf-retrieved-access-token-secret" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token_secret'] ) ); ?>">
123
- <input type="hidden" id="ctf-retrieved-default-screen-name" value="<?php echo esc_html( sanitize_text_field( $access_token_data['screen_name'] ) ); ?>">
124
-
125
- <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
126
-
127
- <p class="ctf_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
128
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
129
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
130
-
131
- <?php else : ?>
132
-
133
- <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
134
- <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
135
-
136
- <?php endif; ?>
137
-
138
- </div>
139
- <?php
140
- }
141
-
142
- public function options_page_init()
143
- {
144
- /*
145
- * "Configure" Tab
146
- */
147
-
148
- register_setting(
149
- 'ctf_options', // name of the option that gets called in "get_option()"
150
- 'ctf_options', // matches the options name
151
- array( $this, 'validate_ctf_options' ) // callback function to validate and clean data
152
- );
153
-
154
- add_settings_section(
155
- 'ctf_options_connect', // matches the section name
156
- 'Configuration',
157
- array( $this, 'access_token_button' ), // callback function to explain the section
158
- 'ctf_options_connect' // matches the section name
159
- );
160
-
161
- // Consumer Key
162
- $this->create_settings_field( array(
163
- 'name' => 'consumer_key',
164
- 'title' => '<label for="ctf_consumer_key">Consumer Key</label>', // label for the input field
165
- 'callback' => 'default_text', // name of the function that outputs the html
166
- 'page' => 'ctf_options_connect', // matches the section name
167
- 'section' => 'ctf_options_connect', // matches the section name
168
- 'option' => 'ctf_options', // matches the options name
169
- 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
170
- 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">here</a>', // what is this? text
171
- 'size' => '27'
172
- ) );
173
-
174
- // Consumer Secret
175
- $this->create_settings_field( array(
176
- 'name' => 'consumer_secret',
177
- 'title' => '<label for="ctf_consumer_secret">Consumer Secret</label>', // label for the input field
178
- 'callback' => 'default_text', // name of the function that outputs the html
179
- 'page' => 'ctf_options_connect', // matches the section name
180
- 'section' => 'ctf_options_connect', // matches the section name
181
- 'option' => 'ctf_options', // matches the options name
182
- 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
183
- 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">here</a>', // what is this? text
184
- 'size' => '57'
185
- ) );
186
-
187
- // Access Token
188
- $this->create_settings_field( array(
189
- 'name' => 'access_token',
190
- 'title' => '<label for="ctf_access_token">Access Token</label>', // label for the input field
191
- 'callback' => 'default_text', // name of the function that outputs the html
192
- 'page' => 'ctf_options_connect', // matches the section name
193
- 'section' => 'ctf_options_connect', // matches the section name
194
- 'option' => 'ctf_options', // matches the options name
195
- 'class' => 'ctf-toggle-access', // class for the wrapper and input field
196
- 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
197
- 'size' => '57'
198
- ) );
199
-
200
- // Access Token Secret
201
- $this->create_settings_field( array(
202
- 'name' => 'access_token_secret',
203
- 'title' => '<label for="ctf_access_token_secret">Access Token Secret</label>', // label for the input field
204
- 'callback' => 'access_token_secret', // name of the function that outputs the html
205
- 'page' => 'ctf_options_connect', // matches the section name
206
- 'section' => 'ctf_options_connect', // matches the section name
207
- 'option' => 'ctf_options', // matches the options name
208
- 'class' => 'ctf-toggle-access', // class for the wrapper and input field
209
- 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
210
- 'size' => '57'
211
- ));
212
-
213
- add_settings_section(
214
- 'ctf_options_feed_settings', // matches the section name
215
- 'Feed Settings',
216
- array( $this, 'general_section_text' ), // callback function to explain the section
217
- 'ctf_options_feed_settings' // matches the section name
218
- );
219
-
220
- // User Timeline Radio
221
- $this->create_settings_field( array(
222
- 'name' => 'usertimeline',
223
- 'title' => '<label for="ctf_feed_type">Feed Type</label><code class="ctf_shortcode">type
224
- Eg: screenname=gopro
225
- Eg: home=true
226
- Eg: hashtag=#cats</code>', // label for the input field
227
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
228
- 'page' => 'ctf_options_feed_settings', // matches the section name
229
- 'section' => 'ctf_options_feed_settings', // matches the section name
230
- 'option' => 'ctf_options', // matches the options name
231
- 'class' => 'ctf-radio', // class for the wrapper and input field
232
- 'whatis' => "Select this option and enter any screen name to create a user timeline feed", // what is this? text
233
- 'label' => "User Timeline:",
234
- 'has_input' => true,
235
- 'has_replies' => true
236
- ));
237
-
238
- // Search Radio
239
- $search_label = apply_filters( 'ctf_admin_search_label', '' );
240
- $search_whatis = apply_filters( 'ctf_admin_search_whatis', '' );
241
- $this->create_settings_field( array(
242
- 'name' => 'search',
243
- 'title' => '<label></label>', // label for the input field
244
- 'callback' => 'feed_settings_radio_search', // name of the function that outputs the html
245
- 'page' => 'ctf_options_feed_settings', // matches the section name
246
- 'section' => 'ctf_options_feed_settings', // matches the section name
247
- 'option' => 'ctf_options', // matches the options name
248
- 'class' => 'ctf-radio', // class for the wrapper and input field
249
- 'whatis' => $search_whatis, // what is this? text
250
- 'label' => $search_label,
251
- 'has_input' => true,
252
- 'note_after_input' => __( '(only recent tweets initially)', 'custom-twitter-feeds' ),
253
- 'extra' => true
254
- ) );
255
-
256
- // Home Timeline Radio
257
- $this->create_settings_field( array(
258
- 'name' => 'hometimeline',
259
- 'title' => '<label></label>', // label for the input field
260
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
261
- 'page' => 'ctf_options_feed_settings', // matches the section name
262
- 'section' => 'ctf_options_feed_settings', // matches the section name
263
- 'option' => 'ctf_options', // matches the options name
264
- 'class' => 'ctf-radio', // class for the wrapper and input field
265
- 'whatis' => 'Select this option to display tweets from yourself and those you follow', // what is this? text
266
- 'label' => "Home Timeline",
267
- 'has_input' => false,
268
- 'has_replies' => true
269
- ));
270
-
271
- do_action( 'ctf_admin_endpoints', $this );
272
-
273
- // Number of Tweets
274
- $this->create_settings_field( array(
275
- 'name' => 'num',
276
- 'title' => '<label for="ctf_num">How Many Tweets to Display</label><code class="ctf_shortcode">num
277
- Eg: num=10</code>', // label for the input field
278
- 'callback' => 'default_text', // name of the function that outputs the html
279
- 'page' => 'ctf_options_feed_settings', // matches the section name
280
- 'section' => 'ctf_options_feed_settings', // matches the section name
281
- 'option' => 'ctf_options', // matches the options name
282
- 'class' => 'small-text', // class for the wrapper and input field
283
- 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
284
- 'type' => 'number', // input field "type" attribute
285
- 'default' => 5
286
- ));
287
-
288
- // time unit for cache
289
- $this->create_settings_field( array(
290
- 'name' => 'cache_time',
291
- 'title' => '<label for="ctf_cache_time">How Many Tweets to Display</label>', // label for the input field
292
- 'callback' => 'default_text', // name of the function that outputs the html
293
- 'page' => 'ctf_options_feed_settings', // matches the section name
294
- 'section' => 'ctf_options_feed_settings', // matches the section name
295
- 'option' => 'ctf_options', // matches the options name
296
- 'class' => 'small-text', // class for the wrapper and input field
297
- 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
298
- 'type' => 'number' // input field "type" attribute
299
- ));
300
-
301
- // check for new tweets
302
- $this->create_settings_field( array(
303
- 'name' => 'cache_time',
304
- 'title' => '<label for="ctf_cache_time">Check for new tweets every</label>', // label for the input field
305
- 'callback' => 'cache_time', // name of the function that outputs the html
306
- 'page' => 'ctf_options_feed_settings', // matches the section name
307
- 'section' => 'ctf_options_feed_settings', // matches the section name
308
- 'option' => 'ctf_options', // matches the options name
309
- 'class' => 'short-text', // class for the wrapper and input field
310
- 'whatis' => "Your Tweets are temporarily cached by the plugin in your WordPress database. You can choose how long the posts should be cached for. If you set the time to 1 hour then the plugin will clear the cache after that length of time and check Instagram for posts again" // what is this? text
311
- ) );
312
-
313
- // preserve settings
314
- $this->create_settings_field( array(
315
- 'name' => 'preserve_settings',
316
- 'title' => '<label for="ctf_preserve_settings">Preserve settings when plugin is removed</label>', // label for the input field
317
- 'callback' => 'default_checkbox', // name of the function that outputs the html
318
- 'page' => 'ctf_options_feed_settings', // matches the section name
319
- 'section' => 'ctf_options_feed_settings', // matches the section name
320
- 'option' => 'ctf_options', // matches the options name
321
- 'class' => '',
322
- 'whatis' => "When removing the plugin your settings are automatically erased. Checking this box will prevent any settings from being deleted. This means that you can uninstall and reinstall the plugin without losing your settings"
323
- ));
324
-
325
- // ajax theme
326
- $this->create_settings_field( array(
327
- 'name' => 'ajax_theme',
328
- 'title' => '<label for="ctf_ajax_theme">Are you using an Ajax powered theme?</label>', // label for the input field
329
- 'callback' => 'default_checkbox', // name of the function that outputs the html
330
- 'page' => 'ctf_options_feed_settings', // matches the section name
331
- 'section' => 'ctf_options_feed_settings', // matches the section name
332
- 'option' => 'ctf_options', // matches the options name
333
- 'class' => '',
334
- 'whatis' => "When navigating your site, if your theme uses Ajax to load content into your pages (meaning your page doesn't refresh) then check this setting. If you're not sure then please check with the theme author"
335
- ));
336
-
337
- /*
338
- * "Customize" tab
339
- */
340
-
341
- add_settings_section(
342
- 'ctf_options_general', // matches the section name
343
- 'General',
344
- array( $this, 'general_section_text' ), // callback function to explain the section
345
- 'ctf_options_general' // matches the section name
346
- );
347
-
348
- // width
349
- $this->create_settings_field( array(
350
- 'name' => 'width',
351
- 'title' => '<label for="ctf_width">Width of Feed</label><code class="ctf_shortcode">width
352
- Eg: width=500</code>', // label for the input field
353
- 'callback' => 'width_and_height_settings', // name of the function that outputs the html
354
- 'page' => 'ctf_options_general', // matches the section name
355
- 'section' => 'ctf_options_general', // matches the section name
356
- 'option' => 'ctf_options', // matches the options name
357
- 'class' => 'small-text',
358
- 'default' => '100',
359
- 'default_unit' => '%'
360
- ));
361
-
362
- // height
363
- $this->create_settings_field( array(
364
- 'name' => 'height',
365
- 'title' => '<label for="ctf_height">Height of Feed</label><code class="ctf_shortcode">height
366
- Eg: height=1000</code>', // label for the input field
367
- 'callback' => 'width_and_height_settings', // name of the function that outputs the html
368
- 'page' => 'ctf_options_general', // matches the section name
369
- 'section' => 'ctf_options_general', // matches the section name
370
- 'option' => 'ctf_options', // matches the options name
371
- 'class' => 'small-text',
372
- 'default_unit' => 'px'
373
- ));
374
-
375
- // class
376
- $this->create_settings_field( array(
377
- 'name' => 'class',
378
- 'title' => '<label for="ctf_class">Add Custom CSS Class</label><code class="ctf_shortcode">class
379
- Eg: class="my-class"</code>', // label for the input field
380
- 'callback' => 'default_text', // name of the function that outputs the html
381
- 'page' => 'ctf_options_general', // matches the section name
382
- 'section' => 'ctf_options_general', // matches the section name
383
- 'option' => 'ctf_options', // matches the options name
384
- 'class' => 'default-text',
385
- 'type' => 'text',
386
- 'whatis' => "You can add your own CSS classes to the feed here. To add multiple classes separate each with a space, Eg. classone classtwo classthree"
387
- ));
388
-
389
- add_settings_section(
390
- 'ctf_options_layout', // matches the section name
391
- 'Layout',
392
- array( $this, 'general_section_text' ), // callback function to explain the section
393
- 'ctf_options_layout' // matches the section name
394
- );
395
-
396
- $settings = get_option( 'ctf_options', array() );
397
- $layout = 'list';
398
- $layout_selections = array(
399
- 'layout' => $layout,
400
- 'carouselcols' => isset( $settings['carouselcols'] ) ? $settings['carouselcols'] : 3,
401
- 'carouselmobilecols' => isset( $settings['carouselmobilecols'] ) ? $settings['carouselmobilecols'] : 1,
402
- 'carouselarrows' => isset( $settings['carouselarrows'] ) ? $settings['carouselarrows'] : 'onhover',
403
- 'carouselpag' => isset( $settings['carouselpag'] ) ? $settings['carouselpag'] : true,
404
- 'carouselheight' => isset( $settings['carouselheight'] ) ? $settings['carouselheight'] : 'tallest',
405
- 'carouselautoplay' => isset( $settings['carouselautoplay'] ) ? $settings['carouselautoplay'] : false,
406
- 'carouseltime' => isset( $settings['carouseltime'] ) ? $settings['carouseltime'] : '5000',
407
- 'carouselloop' => isset( $settings['carouselloop'] ) ? $settings['carouselloop'] : 'infinite',
408
- 'masonrycols' => isset( $settings['masonrycols'] ) ? $settings['masonrycols'] : 3,
409
- 'masonrymobilecols' => isset( $settings['masonrymobilecols'] ) ? $settings['masonrymobilecols'] : 1,
410
- );
411
-
412
- $this->create_settings_field( array(
413
- 'name' => 'class',
414
- 'title' => '<label for="ctf_layout">Layout Type</label><code class="ctf_shortcode">layout
415
- Eg: layout="masonry"</code>', // label for the input field
416
- 'callback' => 'layout', // name of the function that outputs the html
417
- 'page' => 'ctf_options_layout', // matches the section name
418
- 'section' => 'ctf_options_layout', // matches the section name
419
- 'option' => 'ctf_options', // matches the options name
420
- 'class' => 'default-text ctf_pro',
421
- 'type' => 'text',
422
- 'layout_selections' => $layout_selections,
423
- 'whatis' => ""
424
- ));
425
-
426
- add_settings_section(
427
- 'ctf_options_showandhide', // matches the section name
428
- 'Show/Hide',
429
- array( $this, 'general_section_text' ), // callback function to explain the section
430
- 'ctf_options_showandhide' // matches the section name
431
- );
432
-
433
- // show/hide
434
- $show_hide_list = array(
435
- array( 'include_retweeter', 'Retweeted text' ),
436
- array( 'include_avatar', 'Avatar image' ),
437
- array( 'include_author', 'Author name' ),
438
- array( 'include_logo', 'Twitter logo' ),
439
- array( 'include_text', 'Tweet text' ),
440
- array( 'include_media_placeholder', 'Media placeholder' ),
441
- array( 'include_date', 'Date' ),
442
- array( 'include_actions', 'Tweet actions (reply, retweet, like)' ),
443
- array( 'include_twitterlink', '"Twitter" link' ),
444
- array( 'include_linkbox', 'Quoted tweet box' )
445
- );
446
- $show_hide_list = apply_filters( 'ctf_admin_show_hide_list', $show_hide_list );
447
-
448
- $this->create_settings_field( array(
449
- 'name' => 'showandhide',
450
- 'title' => '<label>Include the Following in Tweets <em>(when applicable)</em></label><code class="ctf_shortcode">include exclude
451
- Eg: include=author,date
452
- Eg: exclude=actions
453
- Options: avatar, author,
454
- logo, text, placeholder,
455
- date, actions, linkbox </code>', // label for the input field
456
- 'callback' => 'include_exclude_checkbox', // name of the function that outputs the html
457
- 'page' => 'ctf_options_showandhide', // matches the section name
458
- 'section' => 'ctf_options_showandhide', // matches the section name
459
- 'option' => 'ctf_options', // matches the options name
460
- 'fields' => $show_hide_list,
461
- 'class' => ''
462
- ));
463
-
464
- // show header
465
- $this->create_settings_field( array(
466
- 'name' => 'showheader',
467
- 'title' => '<label for="ctf_showheader">Show Header</label><code class="ctf_shortcode">showheader
468
- Eg: showheader=true</code>', // label for the input field
469
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
470
- 'page' => 'ctf_options_showandhide', // matches the section name
471
- 'section' => 'ctf_options_showandhide', // matches the section name
472
- 'option' => 'ctf_options', // matches the options name
473
- 'class' => '',
474
- 'whatis' => "The header is displayed above your tweets with some basic information about the feed"
475
- ));
476
-
477
- // load more button
478
- $this->create_settings_field( array(
479
- 'name' => 'showbutton',
480
- 'title' => '<label for="ctf_showbutton">Show the "Load More" Button</label><code class="ctf_shortcode">showbutton
481
- Eg: showbutton=true</code>', // label for the input field
482
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
483
- 'page' => 'ctf_options_showandhide', // matches the section name
484
- 'section' => 'ctf_options_showandhide', // matches the section name
485
- 'option' => 'ctf_options', // matches the options name
486
- 'class' => '',
487
- 'whatis' => "Show the Load More Button",
488
- ));
489
-
490
-
491
- // credit ctf
492
- $this->create_settings_field( array(
493
- 'name' => 'creditctf',
494
- 'title' => '<label for="ctf_creditctf">Add Custom Twitter Feeds Credit</label><code class="ctf_shortcode">creditctf
495
- Eg: creditctf=true</code>', // label for the input field
496
- 'callback' => 'default_checkbox', // name of the function that outputs the html
497
- 'page' => 'ctf_options_showandhide', // matches the section name
498
- 'section' => 'ctf_options_showandhide', // matches the section name
499
- 'option' => 'ctf_options', // matches the options name
500
- 'class' => '',
501
- 'whatis' => "Help us keep this plugin great! Add a link below your feed to credit Custom Twitter Feeds by Smash Balloon"
502
- ));
503
-
504
- do_action( 'ctf_admin_customize_option', $this );
505
-
506
- add_settings_section(
507
- 'ctf_options_misc', // matches the section name
508
- 'Misc',
509
- array( $this, 'general_section_text' ), // callback function to explain the section
510
- 'ctf_options_misc' // matches the section name
511
- );
512
-
513
- // Custom CSS
514
- $this->create_settings_field( array(
515
- 'name' => 'custom_css',
516
- 'title' => '<label for="ctf_custom_css">Custom CSS</label>', // label for the input field
517
- 'callback' => 'custom_code', // name of the function that outputs the html
518
- 'page' => 'ctf_options_misc', // matches the section name
519
- 'section' => 'ctf_options_misc', // matches the section name
520
- 'option' => 'ctf_options', // matches the options name
521
- 'class' => 'default-text', // class for the wrapper and input field
522
- 'description' => 'Enter your own custom CSS in the box below'
523
- ));
524
-
525
- // Custom JS
526
- $this->create_settings_field( array(
527
- 'name' => 'custom_js',
528
- 'title' => '<label for="ctf_custom_js">Custom Javascript*</label>', // label for the input field
529
- 'callback' => 'custom_code', // name of the function that outputs the html
530
- 'page' => 'ctf_options_misc', // matches the section name
531
- 'section' => 'ctf_options_misc', // matches the section name
532
- 'option' => 'ctf_options', // matches the options name
533
- 'class' => 'default-text', // class for the wrapper and input field
534
- 'description' => 'Enter your own custom Javascript/JQuery in the box below',
535
- 'extra' => '*will be fired every time more tweets are loaded'
536
- ));
537
-
538
- add_settings_section(
539
- 'ctf_options_advanced', // matches the section name
540
- 'Advanced',
541
- array( $this, 'general_section_text' ), // callback function to explain the section
542
- 'ctf_options_advanced' // matches the section name
543
- );
544
-
545
- // Request Method
546
- $this->create_settings_field( array(
547
- 'name' => 'request_method',
548
- 'title' => '<label for="ctf_request_method">Request Method</label>', // label for the input field
549
- 'callback' => 'default_select', // name of the function that outputs the html
550
- 'page' => 'ctf_options_advanced', // matches the section name
551
- 'section' => 'ctf_options_advanced', // matches the section name
552
- 'option' => 'ctf_options', // matches the options name
553
- 'class' => 'default-text', // class for the wrapper and input field
554
- 'fields' => array(
555
- 1 => array( 'auto', 'Auto' ),
556
- 2 => array( 'curl', 'cURL' ),
557
- 3 => array( 'file_get_contents', 'file_get_contents()' ),
558
- 4 => array( 'wp_http', 'WP_Http' )
559
- ),
560
- 'whatis' => "Explicitly set the request method. You would only want to change this if you are unable to connect to the Twitter API" // what is this? text
561
- ) );
562
-
563
- // force cache to clear on interval
564
- $this->create_settings_field( array(
565
- 'name' => 'cron_cache_clear',
566
- 'title' => '<label for="ctf_cron_cache_clear">Force cache to clear on interval</label>', // label for the input field
567
- 'callback' => 'default_select', // name of the function that outputs the html
568
- 'page' => 'ctf_options_advanced', // matches the section name
569
- 'section' => 'ctf_options_advanced', // matches the section name
570
- 'option' => 'ctf_options', // matches the options name
571
- 'class' => 'default-text', // class for the wrapper and input field
572
- 'fields' => array(
573
- 1 => array( 'unset', '-' ),
574
- 2 => array( 'yes', 'Yes' ),
575
- 3 => array( 'no', 'No' )
576
- ),
577
- 'whatis' => "If you're experiencing an issue with the plugin not auto-updating then you can set this to 'Yes' to run a scheduled event behind the scenes which forces the plugin cache to clear on a regular basis and retrieve new data from Twitter" // what is this? text
578
- ) );
579
-
580
- // tweet multiplier
581
- $this->create_settings_field( array(
582
- 'name' => 'multiplier',
583
- 'title' => '<label for="ctf_multiplier">Tweet Multiplier</label><code class="ctf_shortcode">multiplier
584
- Eg: multiplier=1.5</code>', // label for the input field
585
- 'callback' => 'default_text', // name of the function that outputs the html
586
- 'page' => 'ctf_options_advanced', // matches the section name
587
- 'section' => 'ctf_options_advanced', // matches the section name
588
- 'option' => 'ctf_options', // matches the options name
589
- 'class' => 'small-text', // class for the wrapper and input field
590
- 'whatis' => "If your feed excludes reply tweets (this is automatic in hashtag/search feeds), the correct number of tweets may not show up. Increasing this number will increase the number of tweets retrieved but will also increase the load time for the feed as well", // what is this? text
591
- 'type' => 'number', // input field "type" attribute
592
- 'min' => 1,
593
- 'max' => 3,
594
- 'step' => 'any',
595
- 'default' => 1.25
596
- ));
597
-
598
- $this->create_settings_field( array(
599
- 'name' => 'persistent',
600
- 'title' => '<label for="ctf_multiplier">Clear Persistent Cache</label>', // label for the input field
601
- 'callback' => 'clear_persistent_cache_button', // name of the function that outputs the html
602
- 'page' => 'ctf_options_advanced', // matches the section name
603
- 'section' => 'ctf_options_advanced', // matches the section name
604
- 'option' => 'ctf_options', // matches the options name
605
- 'class' => 'small-text' // class for the wrapper and input field
606
- ));
607
-
608
- // persistent cache
609
- $this->create_settings_field( array(
610
- 'name' => 'persistentcache',
611
- 'title' => '<label for="ctf_persistentcache">Persistent cache enabled by default</label><code class="ctf_shortcode">persistentcache
612
- Eg: persistentcache=false</code>', // label for the input field
613
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
614
- 'page' => 'ctf_options_advanced', // matches the section name
615
- 'section' => 'ctf_options_advanced', // matches the section name
616
- 'option' => 'ctf_options', // matches the options name
617
- 'class' => '',
618
- 'whatis' => "Checking this box will make all Search and Hashtag feeds have a permanent cache saved in the database by default of up to 150 tweets. Tweets will be available for the feed even after the 7 day limit though numbers of retweets and likes will not update"
619
- ));
620
-
621
- $this->create_settings_field( array(
622
- 'name' => 'selfreplies',
623
- 'title' => '<label for="ctf_selfreplies">Always include replies to self in the feed</label><code class="ctf_shortcode">autores
624
- Eg: selfreplies=true</code>', // label for the input field
625
- 'callback' => 'default_checkbox', // name of the function that outputs the html
626
- 'page' => 'ctf_options_advanced', // matches the section name
627
- 'section' => 'ctf_options_advanced', // matches the section name
628
- 'option' => 'ctf_options', // matches the options name
629
- 'class' => '',
630
- 'whatis' => "Twitter considers @mentions of your own account and replies to your own account's tweets as \"reply\" tweets. Enable this setting to include these type of tweets in your feed"
631
- ));
632
-
633
- $this->create_settings_field( array(
634
- 'name' => 'disableintents',
635
- 'title' => '<label for="ctf_disableintents">Disable Twitter intents JS</label><code class="ctf_shortcode">disableintents
636
- Eg: disableintents=true</code>', // label for the input field
637
- 'callback' => 'default_checkbox', // name of the function that outputs the html
638
- 'page' => 'ctf_options_advanced', // matches the section name
639
- 'section' => 'ctf_options_advanced', // matches the section name
640
- 'option' => 'ctf_options', // matches the options name
641
- 'class' => '',
642
- 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
643
- ));
644
-
645
- $this->create_settings_field( array(
646
- 'name' => 'font_method',
647
- 'title' => '<label for="ctf_font_method">Icon Method</label>', // label for the input field
648
- 'callback' => 'default_select', // name of the function that outputs the html
649
- 'page' => 'ctf_options_advanced', // matches the section name
650
- 'section' => 'ctf_options_advanced', // matches the section name
651
- 'option' => 'ctf_options', // matches the options name
652
- 'class' => 'default-text', // class for the wrapper and input field
653
- 'fields' => array(
654
- array( 'svg', 'SVG' ),
655
- array( 'fontfile', 'Font File' )
656
- ),
657
- 'whatis' => "This plugin uses SVGs for all icons in the feed. Use this setting to switch to font icons" // what is this? text
658
- ) );
659
-
660
- $this->create_settings_field( array(
661
- 'name' => 'disableawesome',
662
- 'title' => '<label for="ctf_disableawesome">Disable icon font</label>', // label for the input field
663
- 'callback' => 'default_checkbox', // name of the function that outputs the html
664
- 'page' => 'ctf_options_advanced', // matches the section name
665
- 'section' => 'ctf_options_advanced', // matches the section name
666
- 'option' => 'ctf_options', // matches the options name
667
- 'class' => '',
668
- 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
669
- ));
670
-
671
- $this->create_settings_field( array(
672
- 'name' => 'shorturls',
673
- 'title' => '<label for="ctf_shorturls">Use shortened urls</label>', // label for the input field
674
- 'callback' => 'default_checkbox', // name of the function that outputs the html
675
- 'page' => 'ctf_options_advanced', // matches the section name
676
- 'section' => 'ctf_options_advanced', // matches the section name
677
- 'option' => 'ctf_options', // matches the options name
678
- 'class' => '',
679
- 'whatis' => "Twitter provides shortened versions of links in tweets. Enable this setting to use them instead of the full URLs"
680
- ));
681
-
682
- /**
683
- * "Style" tab
684
- */
685
-
686
- add_settings_section(
687
- 'ctf_options_general_style', // matches the section name
688
- 'General',
689
- array( $this, 'general_section_text' ), // callback function to explain the section
690
- 'ctf_options_general_style' // matches the section name
691
- );
692
-
693
- // background color
694
- $this->create_settings_field( array(
695
- 'name' => 'bgcolor',
696
- 'title' => '<label for="ctf_bgcolor">Feed Background Color</label><code class="ctf_shortcode">bgcolor
697
- Eg: bgcolor=#eee</code>', // label for the input field
698
- 'callback' => 'default_color', // name of the function that outputs the html
699
- 'page' => 'ctf_options_general_style', // matches the section name
700
- 'section' => 'ctf_options_general_style', // matches the section name
701
- 'option' => 'ctf_options', // matches the options name
702
- 'class' => '',
703
- 'whatis' => "The background color of the feed"
704
- ));
705
-
706
- // tweet background color
707
- $this->create_settings_field( array(
708
- 'name' => 'tweetbgcolor',
709
- 'title' => '<label for="ctf_tweetbgcolor">Tweet Background Color</label><code class="ctf_shortcode">tweetbgcolor
710
- Eg: tweetbgcolor=#eee</code>', // label for the input field
711
- 'callback' => 'default_color', // name of the function that outputs the html
712
- 'page' => 'ctf_options_general_style', // matches the section name
713
- 'section' => 'ctf_options_general_style', // matches the section name
714
- 'option' => 'ctf_options', // matches the options name
715
- 'class' => '',
716
- 'whatis' => "The background color of each tweet"
717
- ));
718
-
719
- add_settings_section(
720
- 'ctf_options_header', // matches the section name
721
- 'Header',
722
- array( $this, 'general_section_text' ), // callback function to explain the section
723
- 'ctf_options_header' // matches the section name
724
- );
725
-
726
- // show bio
727
- $this->create_settings_field( array(
728
- 'name' => 'showbio',
729
- 'title' => '<label for="ctf_showbio">Show Bio</label><code class="ctf_shortcode">showbio
730
- Eg: showbio=false</code>', // label for the input field
731
- 'callback' => 'reverse_checkbox', // name of the function that outputs the html
732
- 'page' => 'ctf_options_header', // matches the section name
733
- 'section' => 'ctf_options_header', // matches the section name
734
- 'option' => 'ctf_options', // matches the options name
735
- 'class' => 'default-text', // class for the wrapper and input field
736
- 'whatis' => "Show the bio text description on the header of the feed"
737
- ));
738
-
739
- // header background color
740
- $this->create_settings_field( array(
741
- 'name' => 'headerbgcolor',
742
- 'title' => '<label for="ctf_headerbgcolor">Header Background Color</label><code class="ctf_shortcode">headerbgcolor
743
- Eg: headerbgcolor=#ee0</code>', // label for the input field
744
- 'callback' => 'default_color', // name of the function that outputs the html
745
- 'page' => 'ctf_options_header', // matches the section name
746
- 'section' => 'ctf_options_header', // matches the section name
747
- 'option' => 'ctf_options', // matches the options name
748
- 'class' => ''
749
- ));
750
-
751
- // header text color
752
- $this->create_settings_field( array(
753
- 'name' => 'headertextcolor',
754
- 'title' => '<label for="ctf_headertextcolor">Header Text Color</label><code class="ctf_shortcode">headertextcolor
755
- Eg: headertextcolor=#444</code>', // label for the input field
756
- 'callback' => 'default_color', // name of the function that outputs the html
757
- 'page' => 'ctf_options_header', // matches the section name
758
- 'section' => 'ctf_options_header', // matches the section name
759
- 'option' => 'ctf_options', // matches the options name
760
- 'class' => ''
761
- ));
762
-
763
-
764
- // custom header text
765
- $this->create_settings_field( array(
766
- 'name' => 'headertext',
767
- 'title' => '<label for="ctf_headertext">Custom Header Text</label><code class="ctf_shortcode">headertext
768
- Eg: headertext="Tweets from @SmashBalloon"</code>', // label for the input field
769
- 'callback' => 'default_text', // name of the function that outputs the html
770
- 'page' => 'ctf_options_header', // matches the section name
771
- 'section' => 'ctf_options_header', // matches the section name
772
- 'option' => 'ctf_options', // matches the options name
773
- 'class' => 'default-text', // class for the wrapper and input field
774
- 'whatis' => 'This will replace the default text displayed inside the optional header of the feed' // "what is this?" text
775
- ));
776
-
777
- add_settings_section(
778
- 'ctf_options_date', // matches the section name
779
- 'Date',
780
- array( $this, 'general_section_text' ), // callback function to explain the section
781
- 'ctf_options_date' // matches the section name
782
- );
783
-
784
- // Timezone
785
- $this->create_settings_field( array(
786
- 'name' => 'timezone',
787
- 'title' => '<label for="ctf_timezone">Timezone</label>', // label for the input field
788
- 'callback' => 'feed_settings_timezone', // name of the function that outputs the html
789
- 'page' => 'ctf_options_date', // matches the section name
790
- 'section' => 'ctf_options_date', // matches the section name
791
- 'option' => 'ctf_options', // matches the options name
792
- 'class' => 'default-text', // class for the wrapper and input field
793
- 'whatis' => "Select a timezone for displaying date and timestamps of tweets" // what is this? text
794
- ));
795
-
796
- // Date Format
797
- $this->create_settings_field( array(
798
- 'name' => 'dateformat',
799
- 'title' => '<label for="ctf_date_format">Date Format</label><code class="ctf_shortcode">dateformat
800
- Eg: dateformat=3</code>', // label for the input field
801
- 'callback' => 'customize_date_format', // name of the function that outputs the html
802
- 'page' => 'ctf_options_date', // matches the section name
803
- 'section' => 'ctf_options_date', // matches the section name
804
- 'option' => 'ctf_options', // matches the options name
805
- 'class' => 'default-text', // class for the wrapper and input field
806
- 'whatis' => "Select the format you would like for dates in tweets" // what is this? text
807
- ));
808
-
809
- // Custom Date Format
810
- $this->create_settings_field( array(
811
- 'name' => 'datecustom',
812
- 'title' => '<label for="ctf_custom_date_format">Custom Format</label><code class="ctf_shortcode">datecustom
813
- Eg: datecustom="D M jS, Y"</code>', // label for the input field
814
- 'callback' => 'customize_custom_date_format', // name of the function that outputs the html
815
- 'page' => 'ctf_options_date', // matches the section name
816
- 'section' => 'ctf_options_date', // matches the section name
817
- 'option' => 'ctf_options', // matches the options name
818
- 'class' => 'default-text', // class for the wrapper and input field
819
- ));
820
-
821
- // Custom Time Translations
822
- $this->create_settings_field( array(
823
- 'name' => 'custom_time_translations',
824
- 'title' => '<label>Custom Time Translations</label><code class="ctf_shortcode">mtime, htime,
825
- nowtime
826
- Eg: mtime="M"
827
- Eg: htime="S"
828
- Eg: nowtime="Jetzt"</code>', // label for the input field
829
- 'callback' => 'customize_custom_time_translations', // name of the function that outputs the html
830
- 'page' => 'ctf_options_date', // matches the section name
831
- 'section' => 'ctf_options_date', // matches the section name
832
- 'option' => 'ctf_options', // matches the options name
833
- 'class' => 'default-text', // class for the wrapper and input field
834
- ));
835
-
836
- // date Text Size
837
- $this->create_settings_field( array(
838
- 'name' => 'datetextsize',
839
- 'title' => '<label for="ctf_datetextsize">Date Text Size</label><code class="ctf_shortcode">datetextsize
840
- Eg: datetextsize=16</code>', // label for the input field
841
- 'callback' => 'text_size', // name of the function that outputs the html
842
- 'page' => 'ctf_options_date', // matches the section name
843
- 'section' => 'ctf_options_date', // matches the section name
844
- 'option' => 'ctf_options', // matches the options name
845
- 'class' => 'default-text', // class for the wrapper and input field
846
- ));
847
-
848
- // date text weight
849
- $this->create_settings_field( array(
850
- 'name' => 'datetextweight',
851
- 'title' => '<label for="ctf_datetextweight">Date Text Weight</label><code class="ctf_shortcode">datetextweight
852
- Eg: datetextweight=bold</code>', // label for the input field
853
- 'callback' => 'text_weight', // name of the function that outputs the html
854
- 'page' => 'ctf_options_date', // matches the section name
855
- 'section' => 'ctf_options_date', // matches the section name
856
- 'option' => 'ctf_options', // matches the options name
857
- 'class' => '',
858
- ));
859
-
860
- add_settings_section(
861
- 'ctf_options_author', // matches the section name
862
- 'Author',
863
- array( $this, 'general_section_text' ), // callback function to explain the section
864
- 'ctf_options_author' // matches the section name
865
- );
866
-
867
- // Author Text Size
868
- $this->create_settings_field( array(
869
- 'name' => 'authortextsize',
870
- 'title' => '<label for="ctf_authortextsize">Author Text Size</label><code class="ctf_shortcode">authortextsize
871
- Eg: authortextsize=16</code>', // label for the input field
872
- 'callback' => 'text_size', // name of the function that outputs the html
873
- 'page' => 'ctf_options_author', // matches the section name
874
- 'section' => 'ctf_options_author', // matches the section name
875
- 'option' => 'ctf_options', // matches the options name
876
- 'class' => 'default-text', // class for the wrapper and input field
877
- ));
878
-
879
- // author text weight
880
- $this->create_settings_field( array(
881
- 'name' => 'authortextweight',
882
- 'title' => '<label for="ctf_authortextcolor">Author Text Weight</label><code class="ctf_shortcode">authortextweight
883
- Eg: authortextweight=bold</code>', // label for the input field
884
- 'callback' => 'text_weight', // name of the function that outputs the html
885
- 'page' => 'ctf_options_author', // matches the section name
886
- 'section' => 'ctf_options_author', // matches the section name
887
- 'option' => 'ctf_options', // matches the options name
888
- 'class' => '',
889
- ));
890
-
891
- $this->create_settings_field( array(
892
- 'name' => 'logosize',
893
- 'title' => '<label for="ctf_logotextsize">Twitter Logo Size</label><code class="ctf_shortcode">logosize
894
- Eg: logosize=16</code>', // label for the input field
895
- 'callback' => 'text_size', // name of the function that outputs the html
896
- 'page' => 'ctf_options_author', // matches the section name
897
- 'section' => 'ctf_options_author', // matches the section name
898
- 'option' => 'ctf_options', // matches the options name
899
- 'class' => 'default-text', // class for the wrapper and input field
900
- ));
901
-
902
- $this->create_settings_field( array(
903
- 'name' => 'logocolor',
904
- 'title' => '<label for="ctf_logocolor">Logo Color</label><code class="ctf_shortcode">logocolor
905
- Eg: logocolor=#333</code>', // label for the input field
906
- 'callback' => 'default_color', // name of the function that outputs the html
907
- 'page' => 'ctf_options_author', // matches the section name
908
- 'section' => 'ctf_options_author', // matches the section name
909
- 'option' => 'ctf_options', // matches the options name
910
- 'class' => '',
911
- ));
912
-
913
- add_settings_section(
914
- 'ctf_options_text', // matches the section name
915
- 'Tweet Text',
916
- array( $this, 'general_section_text' ), // callback function to explain the section
917
- 'ctf_options_text' // matches the section name
918
- );
919
-
920
- // Tweet Text Size
921
- $this->create_settings_field( array(
922
- 'name' => 'tweettextsize',
923
- 'title' => '<label for="ctf_tweettextsize">Tweet Text Size</label><code class="ctf_shortcode">tweettextsize
924
- Eg: tweettextsize=16</code>', // label for the input field
925
- 'callback' => 'text_size', // name of the function that outputs the html
926
- 'page' => 'ctf_options_text', // matches the section name
927
- 'section' => 'ctf_options_text', // matches the section name
928
- 'option' => 'ctf_options', // matches the options name
929
- 'class' => 'default-text', // class for the wrapper and input field
930
- ));
931
-
932
- // tweet text weight
933
- $this->create_settings_field( array(
934
- 'name' => 'tweettextweight',
935
- 'title' => '<label for="ctf_tweettextweight">Tweet Text Weight</label><code class="ctf_shortcode">tweettextweight
936
- Eg: tweettextweight=bold</code>', // label for the input field
937
- 'callback' => 'text_weight', // name of the function that outputs the html
938
- 'page' => 'ctf_options_text', // matches the section name
939
- 'section' => 'ctf_options_text', // matches the section name
940
- 'option' => 'ctf_options', // matches the options name
941
- 'class' => '',
942
- ));
943
-
944
- // text color
945
- $this->create_settings_field( array(
946
- 'name' => 'textcolor',
947
- 'title' => '<label for="ctf_textcolor">Text Color</label><code class="ctf_shortcode">textcolor
948
- Eg: textcolor=#333</code>', // label for the input field
949
- 'callback' => 'default_color', // name of the function that outputs the html
950
- 'page' => 'ctf_options_text', // matches the section name
951
- 'section' => 'ctf_options_text', // matches the section name
952
- 'option' => 'ctf_options', // matches the options name
953
- 'class' => '',
954
- ));
955
-
956
- $this->create_settings_field( array(
957
- 'name' => 'textlength',
958
- 'title' => '<label for="ctf_textlength">Text Length</label><code class="ctf_shortcode">textlength
959
- Eg: textlength=150</code>', // label for the input field
960
- 'callback' => 'default_text', // name of the function that outputs the html
961
- 'page' => 'ctf_options_text', // matches the section name
962
- 'section' => 'ctf_options_text', // matches the section name
963
- 'option' => 'ctf_options', // matches the options name
964
- 'class' => '',
965
- 'default' => 280,
966
- 'min' => 20,
967
- 'max' => 280,
968
- 'step' => 1,
969
- 'example' => 'characters',
970
- 'type' => 'number',
971
- 'whatis' => 'The number of characters of text to display in the tweet text. An ellipsis link will be added to allow the user to reveal more text if desired',
972
- ));
973
-
974
- // custom retweeted text
975
- $this->create_settings_field( array(
976
- 'name' => 'retweetedtext',
977
- 'title' => '<label for="ctf_retweetedtext">Translation for "Retweeted"</label><code class="ctf_shortcode">retweetedtext
978
- Eg: retweetedtext="retuiteó"</code>', // label for the input field
979
- 'callback' => 'default_text', // name of the function that outputs the html
980
- 'page' => 'ctf_options_text', // matches the section name
981
- 'section' => 'ctf_options_text', // matches the section name
982
- 'option' => 'ctf_options', // matches the options name
983
- 'class' => 'default-text', // class for the wrapper and input field
984
- 'whatis' => 'This will replace the default text displayed for retweeted texts',
985
- 'default' => 'Retweeted'// "what is this?" text
986
- ));
987
-
988
- add_settings_section(
989
- 'ctf_options_links', // matches the section name
990
- 'Links',
991
- array( $this, 'general_section_text' ), // callback function to explain the section
992
- 'ctf_options_links' // matches the section name
993
- );
994
-
995
- // disable links
996
- $this->create_settings_field( array(
997
- 'name' => 'disablelinks',
998
- 'title' => '<label for="ctf_disablelinks">Disable Links in Tweet Text</label><code class="ctf_shortcode">disablelinks
999
- Eg: disablelinks=true</code>', // label for the input field
1000
- 'callback' => 'default_checkbox', // name of the function that outputs the html
1001
- 'page' => 'ctf_options_links', // matches the section name
1002
- 'section' => 'ctf_options_links', // matches the section name
1003
- 'option' => 'ctf_options', // matches the options name
1004
- 'class' => '',
1005
- 'whatis' => "By default, links, hashtags, and mentions are turned into links inside the tweet text"
1006
- ));
1007
-
1008
- // link text to twitter
1009
- $this->create_settings_field( array(
1010
- 'name' => 'linktexttotwitter',
1011
- 'title' => '<label for="ctf_linktexttotwitter">Link Tweet Text to Twitter</label><code class="ctf_shortcode">linktexttotwitter
1012
- Eg: linktexttotwitter=true</code>', // label for the input field
1013
- 'callback' => 'default_checkbox', // name of the function that outputs the html
1014
- 'page' => 'ctf_options_links', // matches the section name
1015
- 'section' => 'ctf_options_links', // matches the section name
1016
- 'option' => 'ctf_options', // matches the options name
1017
- 'class' => '',
1018
- 'whatis' => "Clicking on the text of the tweet will link to the tweet on Twitter"
1019
- ));
1020
-
1021
- // link text color
1022
- $this->create_settings_field( array(
1023
- 'name' => 'linktextcolor',
1024
- 'title' => '<label for="ctf_linktextcolor">Links in Tweets Text Color</label><code class="ctf_shortcode">linktextcolor
1025
- Eg: linktextcolor=#00e</code>', // label for the input field
1026
- 'callback' => 'default_color', // name of the function that outputs the html
1027
- 'page' => 'ctf_options_links', // matches the section name
1028
- 'section' => 'ctf_options_links', // matches the section name
1029
- 'option' => 'ctf_options', // matches the options name
1030
- 'class' => '',
1031
- ));
1032
-
1033
- add_settings_section(
1034
- 'ctf_options_quoted', // matches the section name
1035
- 'Retweet Boxes',
1036
- array( $this, 'general_section_text' ), // callback function to explain the section
1037
- 'ctf_options_quoted' // matches the section name
1038
- );
1039
-
1040
- // quoted author Size
1041
- $this->create_settings_field( array(
1042
- 'name' => 'quotedauthorsize',
1043
- 'title' => '<label for="ctf_quotedauthorsize">Quoted Author Size</label><code class="ctf_shortcode">quotedauthorsize
1044
- Eg: quotedauthorsize=16</code>', // label for the input field
1045
- 'callback' => 'text_size', // name of the function that outputs the html
1046
- 'page' => 'ctf_options_quoted', // matches the section name
1047
- 'section' => 'ctf_options_quoted', // matches the section name
1048
- 'option' => 'ctf_options', // matches the options name
1049
- 'class' => 'default-text', // class for the wrapper and input field
1050
- ));
1051
-
1052
- // quoted author weight
1053
- $this->create_settings_field( array(
1054
- 'name' => 'quotedauthorweight',
1055
- 'title' => '<label for="ctf_quotedauthorweight">Quoted Author Weight</label><code class="ctf_shortcode">quotedauthorweight
1056
- Eg: quotedauthorweight=bold</code>', // label for the input field
1057
- 'callback' => 'text_weight', // name of the function that outputs the html
1058
- 'page' => 'ctf_options_quoted', // matches the section name
1059
- 'section' => 'ctf_options_quoted', // matches the section name
1060
- 'option' => 'ctf_options', // matches the options name
1061
- 'class' => '',
1062
- ));
1063
-
1064
- add_settings_section(
1065
- 'ctf_options_actions', // matches the section name
1066
- 'Tweets Actions',
1067
- array( $this, 'general_section_text' ), // callback function to explain the section
1068
- 'ctf_options_actions' // matches the section name
1069
- );
1070
-
1071
- // icon Size
1072
- $this->create_settings_field( array(
1073
- 'name' => 'iconsize',
1074
- 'title' => '<label for="ctf_iconsize">Icon Size</label><code class="ctf_shortcode">iconsize
1075
- Eg: iconsize=16</code>', // label for the input field
1076
- 'callback' => 'text_size', // name of the function that outputs the html
1077
- 'page' => 'ctf_options_actions', // matches the section name
1078
- 'section' => 'ctf_options_actions', // matches the section name
1079
- 'option' => 'ctf_options', // matches the options name
1080
- 'class' => 'default-text', // class for the wrapper and input field
1081
- ));
1082
-
1083
- // icon color
1084
- $this->create_settings_field( array(
1085
- 'name' => 'iconcolor',
1086
- 'title' => '<label for="ctf_iconcolor">Icon Color</label><code class="ctf_shortcode">iconcolor
1087
- Eg: iconcolor=green</code>', // label for the input field
1088
- 'callback' => 'default_color', // name of the function that outputs the html
1089
- 'page' => 'ctf_options_actions', // matches the section name
1090
- 'section' => 'ctf_options_actions', // matches the section name
1091
- 'option' => 'ctf_options', // matches the options name
1092
- 'class' => '',
1093
- ));
1094
-
1095
-
1096
- // view on twitter text
1097
- $this->create_settings_field( array(
1098
- 'name' => 'twitterlinktext',
1099
- 'title' => '<label for="ctf_twitterlinktext">Custom Text for "Twitter" Link</label><code class="ctf_shortcode">twitterlinktext
1100
- Eg: twitterlinktext="View this Tweet"</code>', // label for the input field
1101
- 'callback' => 'default_text', // name of the function that outputs the html
1102
- 'page' => 'ctf_options_actions', // matches the section name
1103
- 'section' => 'ctf_options_actions', // matches the section name
1104
- 'option' => 'ctf_options', // matches the options name
1105
- 'class' => 'default-text', // class for the wrapper and input field
1106
- 'default' => 'Twitter'
1107
- ));
1108
-
1109
- add_settings_section(
1110
- 'ctf_options_load', // matches the section name
1111
- '"Load More" Button',
1112
- array( $this, 'general_section_text' ), // callback function to explain the section
1113
- 'ctf_options_load' // matches the section name
1114
- );
1115
-
1116
- // button background color
1117
- $this->create_settings_field( array(
1118
- 'name' => 'buttoncolor',
1119
- 'title' => '<label for="ctf_buttoncolor">Button Background Color</label><code class="ctf_shortcode">buttoncolor
1120
- Eg: buttoncolor=#f33</code>', // label for the input field
1121
- 'callback' => 'default_color', // name of the function that outputs the html
1122
- 'page' => 'ctf_options_load', // matches the section name
1123
- 'section' => 'ctf_options_load', // matches the section name
1124
- 'option' => 'ctf_options', // matches the options name
1125
- 'class' => '',
1126
- 'whatis' => "The color of the background of the load more button"
1127
- ));
1128
-
1129
- // button text color
1130
- $this->create_settings_field( array(
1131
- 'name' => 'buttontextcolor',
1132
- 'title' => '<label for="ctf_buttontextcolor">Button Text Color</label><code class="ctf_shortcode">buttontextcolor
1133
- Eg: buttontextcolor=#444</code>', // label for the input field
1134
- 'callback' => 'default_color', // name of the function that outputs the html
1135
- 'page' => 'ctf_options_load', // matches the section name
1136
- 'section' => 'ctf_options_load', // matches the section name
1137
- 'option' => 'ctf_options', // matches the options name
1138
- 'class' => '',
1139
- 'whatis' => "The color of the text of the load more button"
1140
- ));
1141
-
1142
- // button text
1143
- $this->create_settings_field( array(
1144
- 'name' => 'buttontext',
1145
- 'title' => '<label for="ctf_buttontext">Button Text</label><code class="ctf_shortcode">buttontext
1146
- Eg: buttontext="More"</code>', // label for the input field
1147
- 'callback' => 'default_text', // name of the function that outputs the html
1148
- 'page' => 'ctf_options_load', // matches the section name
1149
- 'section' => 'ctf_options_load', // matches the section name
1150
- 'option' => 'ctf_options', // matches the options name
1151
- 'class' => 'default-text', // class for the wrapper and input field
1152
- 'default' => 'Load More...'
1153
- ));
1154
-
1155
- do_action( 'ctf_admin_style_option', $this );
1156
- }
1157
-
1158
- public function create_settings_field( $args=array() )
1159
- {
1160
- add_settings_field(
1161
- $args['name'],
1162
- $args['title'],
1163
- array( $this, $args['callback'] ),
1164
- $args['page'],
1165
- $args['section'],
1166
- $args
1167
- );
1168
- }
1169
-
1170
- public function default_text( $args )
1171
- {
1172
- $options = get_option( $args['option'] );
1173
- $default = isset( $args['default'] ) ? $args['default'] : '';
1174
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1175
- $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1176
- $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1177
- $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1178
- $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1179
- $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1180
- ?>
1181
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1182
- <?php if ( isset( $args['example'] ) ) : ?>
1183
- <span><?php echo $args['example']; ?></span>
1184
- <?php endif; ?>
1185
- <?php if ( isset( $args['whatis'] ) ) : ?>
1186
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1187
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1188
- <?php endif; ?>
1189
- <?php
1190
- }
1191
-
1192
- public function default_select( $args )
1193
- {
1194
- $options = get_option( $args['option'] );
1195
- $selected = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1196
- ?>
1197
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>">
1198
- <?php foreach ( $args['fields'] as $field ) : ?>
1199
- <option value="<?php echo $field[0]; ?>" id="ctf-<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php if( $selected == $field[0] ) { echo ' selected'; } ?>><?php _e( $field[1], 'custom-twitter-feeds' ); ?></option>
1200
- <?php endforeach; ?>
1201
- </select>
1202
- <?php if ( isset( $args['whatis'] ) ) : ?>
1203
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1204
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1205
- <?php endif; ?>
1206
- <?php
1207
- }
1208
-
1209
- public function default_color( $args )
1210
- {
1211
- $options = get_option( $args['option'] );
1212
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1213
- ?>
1214
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" value="#<?php esc_attr_e( str_replace('#', '', $option_string ) ); ?>" class="ctf-colorpicker" />
1215
- <?php
1216
- }
1217
-
1218
- public function default_checkbox( $args )
1219
- {
1220
- $options = get_option( $args['option'] );
1221
- $option_checked = ( isset( $options[ $args['name'] ] ) ) ? $options[ $args['name'] ] : false;
1222
- ?>
1223
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1224
- <?php if ( isset( $args['whatis'] ) ) : ?>
1225
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1226
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1227
- <?php endif; ?>
1228
- <?php
1229
- }
1230
-
1231
- public function reverse_checkbox( $args )
1232
- {
1233
- $options = get_option( $args['option'] );
1234
- $option_checked = isset( $options[ $args['name'] ] ) ? $options[ $args['name'] ] : true;
1235
- ?>
1236
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1237
- <?php if ( isset( $args['whatis'] ) ) : ?>
1238
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1239
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1240
- <?php endif; ?>
1241
- <?php
1242
- }
1243
-
1244
- public function access_token_secret( $args )
1245
- {
1246
- $options = get_option( $args['option'] );
1247
- $default = isset( $args['default'] ) ? $args['default'] : '';
1248
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1249
- $option_checked = ( isset( $options['use_own_consumer'] ) ) ? $options['use_own_consumer'] : false;
1250
- $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1251
- $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1252
- $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1253
- $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1254
- $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1255
- ?>
1256
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1257
- <?php if ( isset( $args['example'] ) ) : ?>
1258
- <span><?php echo $args['example']; ?></span>
1259
- <?php endif; ?>
1260
-
1261
- <?php if ( isset( $args['whatis'] ) ) : ?>
1262
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1263
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1264
- <?php endif; ?>
1265
-
1266
- <?php
1267
- }
1268
-
1269
- public function feed_settings_radio( $args )
1270
- {
1271
- $options = get_option( $args['option'] );
1272
- $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1273
- $show_replies = ( isset( $options[ $args['name'].'_includereplies' ] ) ) ? $options[ $args['name'].'_includereplies' ] : false;
1274
- $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1275
- ?>
1276
- <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1277
- <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php _e( $args['label'], 'custom-twitter-feeds' ); ?></label>
1278
- <?php if ( $args['has_input'] ) : ?>
1279
- <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1280
- <?php endif; ?>
1281
- <?php if ( isset( $args['whatis'] ) ) : ?>
1282
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1283
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1284
- <?php endif; ?>
1285
- <?php if ( $args['has_replies'] ) : ?>
1286
- <span class="ctf_include_replies_toggle ctf_pro">
1287
- <input name="<?php echo $args['option'].'['.$args['name'].'_includereplies]'; ?>" id="ctf_include_replies" type="checkbox" <?php if ( $show_replies ) echo "checked"; ?> />
1288
- <label class="ctf-radio-label" for="ctf_include_replies"><?php _e( 'Include replies', 'custom-twitter-feeds' ); ?></label>
1289
- <?php do_action( 'ctf_admin_upgrade_note' ); ?>
1290
- </span>
1291
- <?php endif; ?>
1292
- <?php
1293
- do_action( 'ctf_admin_feed_settings_radio_extra', $args );
1294
- }
1295
-
1296
- public function feed_settings_radio_search( $args )
1297
- {
1298
- $options = get_option( $args['option'] );
1299
- $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1300
- $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1301
- ?>
1302
- <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1303
- <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php echo $args['label']; ?></label>
1304
- <?php if ( $args['has_input'] ) : ?>
1305
- <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1306
- <?php endif; ?>
1307
- <?php if ( isset( $args['note_after_input'] ) ) : ?>
1308
- <span class="ctf-note-after-input"><?php echo esc_attr( $args['note_after_input'] ); ?></span>
1309
- <?php endif; ?>
1310
- <?php if ( isset( $args['whatis'] ) ) : ?>
1311
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1312
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1313
- <?php endif; ?>
1314
- <?php
1315
- do_action( 'ctf_admin_feed_settings_search_extra' );
1316
- }
1317
-
1318
- public function width_and_height_settings( $args )
1319
- {
1320
- $options = get_option( $args['option'] );
1321
- $default = isset( $args['default'] ) ? $args['default'] : '';
1322
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1323
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : $args['default_unit'];
1324
- ?>
1325
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1326
- <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>" id="ctf_<?php echo $args['name'].'_unit'; ?>">
1327
- <option value="px" <?php if ( $selected == "px" ) echo 'selected="selected"' ?> >px</option>
1328
- <option value="%" <?php if ( $selected == "%" ) echo 'selected="selected"' ?> >%</option>
1329
- </select>
1330
-
1331
- <?php if ( $args['name'] == 'width' ) :
1332
- $checked = ( isset( $options[ $args['name'] . '_mobile_no_fixed' ] ) ) ? esc_attr( $options[ $args['name'] . '_mobile_no_fixed' ] ) : false; ?>
1333
- <div id="ctf_width_options">
1334
- <input name="<?php echo $args['option'].'[width_mobile_no_fixed]'; ?>" type="checkbox" id="ctf_width_mobile_no_fixed" <?php if ( $checked == true ) { echo "checked"; }?> /><label for="ctf_width_mobile_no_fixed"><?php _e('Set to be 100% width on mobile?', 'custom-twitter-feeds'); ?></label>
1335
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-facebook-feed'); ?></a>
1336
- <p class="ctf-tooltip ctf-more-info"><?php _e("If you set a width on the feed then this will be used on mobile as well as desktop. Check this setting to set the feed width to be 100% on mobile so that it is responsive.", 'custom-twitter-feeds'); ?></p>
1337
- </div>
1338
- <?php endif; ?>
1339
- <?php
1340
- }
1341
-
1342
- public function cache_time( $args )
1343
- {
1344
- $min_cache_time = 3600;
1345
- $options = get_option( $args['option'] );
1346
- $unrestricted = isset( $options['have_own_tokens'] ) && $options['have_own_tokens'];
1347
- if ( $unrestricted ) {
1348
- $min_cache_time = 1;
1349
- }
1350
- $default = 3;
1351
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1352
-
1353
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1354
- $actual_time = (int)$option_string * (int)$selected;
1355
- $show_notice = false;
1356
- if ( get_transient( 'ct_feed_forced_cache_time_raise' ) ) {
1357
- $show_notice = true;
1358
- }
1359
- if ( $actual_time < $min_cache_time ) {
1360
- set_transient( 'ct_feed_forced_cache_time_raise', 'true', 60 * 60 * 48 );
1361
- $show_notice = true;
1362
- $option_string = max( 1, $min_cache_time / 3600 );
1363
- $selected = "3600";
1364
- } else {
1365
- $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1366
- }
1367
-
1368
- ?>
1369
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1370
- <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>">
1371
- <?php if ( $unrestricted ) : ?>
1372
- <option value="60" <?php if ( $selected == "60" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Minutes' ); ?></option>
1373
- <?php endif; ?>
1374
- <option value="3600" <?php if ( $selected == "3600" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Hours' ); ?></option>
1375
- <option value="86400" <?php if ( $selected == "86400" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Days' ); ?></option>
1376
- </select>&nbsp;
1377
- <input id="ctf-clear-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cache' ); ?>" />
1378
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1379
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your Twitter feeds', 'custom-twitter-feeds' ); ?>.</p>
1380
- <?php if ( $show_notice ) : ?>
1381
- <p class="ctf-more-info"><?php _e( 'Due to changes in Twitter\'s API usage policy, the minimum caching time for the plugin is 1 hour. To check for Tweets more often either create your own Twitter Developer app or upgrade to the Pro version.', 'custom-twitter-feeds' ); ?></p>
1382
- <?php endif; ?>
1383
- <?php
1384
- }
1385
-
1386
- public function customize_date_format( $args )
1387
- {
1388
- $options = get_option( $args['option'] );
1389
- $ctf_date_formatting = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1390
- $original = strtotime( '2016-02-25T17:30:00+0000' );
1391
- ?>
1392
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1393
- <option value="1" <?php if ( $ctf_date_formatting == "1" ) echo 'selected="selected"'; ?> ><?php _e( '2h / 25 Feb' ); ?></option>
1394
- <option value="2" <?php if ( $ctf_date_formatting == "2" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j', $original ); ?></option>
1395
- <option value="3" <?php if ( $ctf_date_formatting == "3" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j, Y', $original ); ?></option>
1396
- <option value="4" <?php if ( $ctf_date_formatting == "4" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d', $original ); ?></option>
1397
- <option value="5" <?php if ( $ctf_date_formatting == "5" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d.y', $original ); ?></option>
1398
- </select>
1399
- <?php if ( isset( $args['whatis'] ) ) : ?>
1400
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1401
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1402
- <?php endif; ?>
1403
- <?php
1404
- }
1405
-
1406
- public function customize_custom_date_format( $args )
1407
- {
1408
- $options = get_option( $args['option'] );
1409
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1410
- ?>
1411
- <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="10" placeholder="Eg. F jS, Y" />
1412
- <a href="https://smashballoon.com/custom-twitter-feeds/docs/date/" class="cff-external-link" target="_blank"><?php _e( 'Examples' , 'custom-twitter-feeds'); ?></a>
1413
- <?php
1414
- }
1415
-
1416
- public function customize_custom_time_translations( $args )
1417
- {
1418
- $options = get_option( $args['option'] );
1419
- $option_m = ( isset( $options['mtime'] ) ) ? esc_attr( $options['mtime'] ) : '';
1420
- $option_h = ( isset( $options['htime'] ) ) ? esc_attr( $options['htime'] ) : '';
1421
- $option_now = ( isset( $options['nowtime'] ) ) ? esc_attr( $options['nowtime'] ) : '';
1422
-
1423
- ?>
1424
- <input name="<?php echo $args['option'].'[mtime]'; ?>" id="ctf_translate_minute" type="text" value="<?php esc_attr_e( $option_m ); ?>" size="5" />
1425
- <label for=ctf_translate_minute"><?php _e( 'translation for "m" (minutes)', 'custom-twitter-feeds' ); ?></label><br>
1426
- <input name="<?php echo $args['option'].'[htime]'; ?>" id="ctf_translate_hour" type="text" value="<?php esc_attr_e( $option_h ); ?>" size="5" />
1427
- <label for=ctf_translate_hour"><?php _e( 'translation for "h" (hours)', 'custom-twitter-feeds' ); ?></label><br>
1428
- <input name="<?php echo $args['option'].'[nowtime]'; ?>" id="ctf_translate_now" type="text" value="<?php esc_attr_e( $option_now ); ?>" size="5" />
1429
- <label for=ctf_translate_now"><?php _e( 'translation for "now"', 'custom-twitter-feeds' ); ?></label><br>
1430
- <?php
1431
- }
1432
-
1433
- public function include_exclude_checkbox( $args )
1434
- {
1435
- $options = get_option( $args['option'] );
1436
- foreach ( $args['fields'] as $field ) {
1437
- $option_checked = isset( $options[$field[0]] ) ? $options[$field[0]] : true;
1438
- ?>
1439
- <input name="<?php echo $args['option'] . '[' . $field[0] . ']'; ?>"
1440
- id="ctf_<?php echo $field[0]; ?>" type="checkbox"
1441
- <?php if ( $option_checked ) {
1442
- echo "checked";
1443
- } ?> />
1444
- <label for=ctf_<?php echo $field[0]; ?>"><?php _e( $field[1], 'custom-twitter-feeds' ); ?></label><br>
1445
- <?php
1446
- } // end foreach
1447
- }
1448
-
1449
- public function text_size( $args )
1450
- {
1451
- $options = get_option( $args['option'] );
1452
- $ctf_text_size = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1453
- ?>
1454
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1455
- <option value="inherit" <?php if ( $ctf_text_size == "inherit" ) echo 'selected="selected"' ?> >Inherit</option>
1456
- <option value="10" <?php if ( $ctf_text_size == "10" ) echo 'selected="selected"' ?> >10px</option>
1457
- <option value="11" <?php if ( $ctf_text_size == "11" ) echo 'selected="selected"' ?> >11px</option>
1458
- <option value="12" <?php if ( $ctf_text_size == "12" ) echo 'selected="selected"' ?> >12px</option>
1459
- <option value="13" <?php if ( $ctf_text_size == "13" ) echo 'selected="selected"' ?> >13px</option>
1460
- <option value="14" <?php if ( $ctf_text_size == "14" ) echo 'selected="selected"' ?> >14px</option>
1461
- <option value="16" <?php if ( $ctf_text_size == "16" ) echo 'selected="selected"' ?> >16px</option>
1462
- <option value="18" <?php if ( $ctf_text_size == "18" ) echo 'selected="selected"' ?> >18px</option>
1463
- <option value="20" <?php if ( $ctf_text_size == "20" ) echo 'selected="selected"' ?> >20px</option>
1464
- <option value="24" <?php if ( $ctf_text_size == "24" ) echo 'selected="selected"' ?> >24px</option>
1465
- <option value="28" <?php if ( $ctf_text_size == "28" ) echo 'selected="selected"' ?> >28px</option>
1466
- <option value="32" <?php if ( $ctf_text_size == "32" ) echo 'selected="selected"' ?> >32px</option>
1467
- <option value="36" <?php if ( $ctf_text_size == "36" ) echo 'selected="selected"' ?> >36px</option>
1468
- <option value="42" <?php if ( $ctf_text_size == "42" ) echo 'selected="selected"' ?> >42px</option>
1469
- <option value="48" <?php if ( $ctf_text_size == "48" ) echo 'selected="selected"' ?> >48px</option>
1470
- <option value="54" <?php if ( $ctf_text_size == "54" ) echo 'selected="selected"' ?> >54px</option>
1471
- <option value="60" <?php if ( $ctf_text_size == "60" ) echo 'selected="selected"' ?> >60px</option>
1472
- </select>
1473
- <?php if ( isset( $args['whatis'] ) ) : ?>
1474
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1475
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1476
- <?php endif; ?>
1477
- <?php
1478
- }
1479
-
1480
- public function text_weight( $args )
1481
- {
1482
- $options = get_option( $args['option'] );
1483
- $ctf_text_weight = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1484
- ?>
1485
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1486
- <option value="inherit" <?php if ( $ctf_text_weight == "inherit" ) echo 'selected="selected"'; ?> >Inherit</option>
1487
- <option value="normal" <?php if ( $ctf_text_weight == "normal" ) echo 'selected="selected"'; ?> >Normal</option>
1488
- <option value="bold" <?php if ( $ctf_text_weight == "bold" ) echo 'selected="selected"'; ?> >Bold</option>
1489
- </select>
1490
- <?php if ( isset( $args['whatis'] ) ) : ?>
1491
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1492
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1493
- <?php endif; ?>
1494
- <?php
1495
- }
1496
-
1497
- public function feed_settings_timezone( $args )
1498
- {
1499
- $options = get_option( $args['option'] );
1500
- $ctf_timezone = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1501
- ?>
1502
- <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 300px;">
1503
- <option value="default" <?php if( $ctf_timezone == "default" ) echo 'selected="selected"' ?> ><?php _e( 'default from Twitter' ) ?></option>
1504
- <option value="Pacific/Midway" <?php if( $ctf_timezone == "Pacific/Midway" ) echo 'selected="selected"' ?> ><?php _e( '(GMT11:00) Midway Island, Samoa' ) ?></option>
1505
- <option value="America/Adak" <?php if( $ctf_timezone == "America/Adak" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) HawaiiAleutian' ) ?></option>
1506
- <option value="Etc/GMT+10" <?php if( $ctf_timezone == "Etc/GMT+10" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) Hawaii' ) ?></option>
1507
- <option value="Pacific/Marquesas" <?php if( $ctf_timezone == "Pacific/Marquesas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:30) Marquesas Islands' ) ?></option>
1508
- <option value="Pacific/Gambier" <?php if( $ctf_timezone == "Pacific/Gambier" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Gambier Islands' ) ?></option>
1509
- <option value="America/Anchorage" <?php if( $ctf_timezone == "America/Anchorage" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Alaska' ) ?></option>
1510
- <option value="America/Ensenada" <?php if( $ctf_timezone == "America/Ensenada" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Tijuana, Baja California' ) ?></option>
1511
- <option value="Etc/GMT+8" <?php if( $ctf_timezone == "Etc/GMT+8" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pitcairn Islands' ) ?></option>
1512
- <option value="America/Los_Angeles" <?php if( $ctf_timezone == "America/Los_Angeles" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pacific Time (US & Canada)' ) ?></option>
1513
- <option value="America/Denver" <?php if( $ctf_timezone == "America/Denver" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Mountain Time (US & Canada)' ) ?></option>
1514
- <option value="America/Chihuahua" <?php if( $ctf_timezone == "America/Chihuahua" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Chihuahua, La Paz, Mazatlan' ) ?></option>
1515
- <option value="America/Dawson_Creek" <?php if( $ctf_timezone == "America/Dawson_Creek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Arizona' ) ?></option>
1516
- <option value="America/Belize" <?php if( $ctf_timezone == "America/Belize" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Saskatchewan, Central America' ) ?></option>
1517
- <option value="America/Cancun" <?php if( $ctf_timezone == "America/Cancun" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Guadalajara, Mexico City, Monterrey' ) ?></option>
1518
- <option value="Chile/EasterIsland" <?php if( $ctf_timezone == "Chile/EasterIsland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Easter Island' ) ?></option>
1519
- <option value="America/Chicago" <?php if( $ctf_timezone == "America/Chicago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Central Time (US & Canada)' ) ?></option>
1520
- <option value="America/New_York" <?php if( $ctf_timezone == "America/New_York" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Eastern Time (US & Canada)' ) ?></option>
1521
- <option value="America/Havana" <?php if( $ctf_timezone == "America/Havana" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Cuba' ) ?></option>
1522
- <option value="America/Bogota" <?php if( $ctf_timezone == "America/Bogota" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Bogota, Lima, Quito, Rio Branco' ) ?></option>
1523
- <option value="America/Caracas" <?php if( $ctf_timezone == "America/Caracas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:30) Caracas' ) ?></option>
1524
- <option value="America/Santiago" <?php if( $ctf_timezone == "America/Santiago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Santiago' ) ?></option>
1525
- <option value="America/La_Paz" <?php if( $ctf_timezone == "America/La_Paz" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) La Paz' ) ?></option>
1526
- <option value="Atlantic/Stanley" <?php if( $ctf_timezone == "Atlantic/Stanley" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Faukland Islands' ) ?></option>
1527
- <option value="America/Campo_Grande" <?php if( $ctf_timezone == "America/Campo_Grande" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Brazil' ) ?></option>
1528
- <option value="America/Goose_Bay" <?php if( $ctf_timezone == "America/Goose_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Goose Bay)' ) ?></option>
1529
- <option value="America/Glace_Bay" <?php if( $ctf_timezone == "America/Glace_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Canada)' ) ?></option>
1530
- <option value="America/St_Johns" <?php if( $ctf_timezone == "America/St_Johns" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:30) Newfoundland' ) ?></option>
1531
- <option value="America/Araguaina" <?php if( $ctf_timezone == "America/Araguaina" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) UTC3' ) ?></option>
1532
- <option value="America/Montevideo" <?php if( $ctf_timezone == "America/Montevideo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Montevideo' ) ?></option>
1533
- <option value="America/Miquelon" <?php if( $ctf_timezone == "America/Miquelon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Miquelon, St. Pierre' ) ?></option>
1534
- <option value="America/Godthab" <?php if( $ctf_timezone == "America/Godthab" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Greenland' ) ?></option>
1535
- <option value="America/Argentina/Buenos_Aires" <?php if( $ctf_timezone == "America/Argentina/Buenos_Aires" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Buenos Aires' ) ?></option>
1536
- <option value="America/Sao_Paulo" <?php if( $ctf_timezone == "America/Sao_Paulo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Brasilia' ) ?></option>
1537
- <option value="America/Noronha" <?php if( $ctf_timezone == "America/Noronha" ) echo 'selected="selected"' ?> ><?php _e( '(GMT02:00) MidAtlantic' ) ?></option>
1538
- <option value="Atlantic/Cape_Verde" <?php if( $ctf_timezone == "Atlantic/Cape_Verde" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Cape Verde Is.' ) ?></option>
1539
- <option value="Atlantic/Azores" <?php if( $ctf_timezone == "Atlantic/Azores" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Azores' ) ?></option>
1540
- <option value="Europe/Belfast" <?php if( $ctf_timezone == "Europe/Belfast" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Belfast' ) ?></option>
1541
- <option value="Europe/Dublin" <?php if( $ctf_timezone == "Europe/Dublin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Dublin' ) ?></option>
1542
- <option value="Europe/Lisbon" <?php if( $ctf_timezone == "Europe/Lisbon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Lisbon' ) ?></option>
1543
- <option value="Europe/London" <?php if( $ctf_timezone == "Europe/London" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : London' ) ?></option>
1544
- <option value="Africa/Abidjan" <?php if( $ctf_timezone == "Africa/Abidjan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Monrovia, Reykjavik' ) ?></option>
1545
- <option value="Europe/Amsterdam" <?php if( $ctf_timezone == "Europe/Amsterdam" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' ) ?></option>
1546
- <option value="Europe/Belgrade" <?php if( $ctf_timezone == "Europe/Belgrade" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' ) ?></option>
1547
- <option value="Europe/Brussels" <?php if( $ctf_timezone == "Europe/Brussels" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris' ) ?></option>
1548
- <option value="Africa/Algiers" <?php if( $ctf_timezone == "Africa/Algiers" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) West Central Africa' ) ?></option>
1549
- <option value="Africa/Windhoek" <?php if( $ctf_timezone == "Africa/Windhoek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Windhoek' ) ?></option>
1550
- <option value="Asia/Beirut" <?php if( $ctf_timezone == "Asia/Beirut" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Beirut' ) ?></option>
1551
- <option value="Africa/Cairo" <?php if( $ctf_timezone == "Africa/Cairo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Cairo' ) ?></option>
1552
- <option value="Asia/Gaza" <?php if( $ctf_timezone == "Asia/Gaza" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Gaza' ) ?></option>
1553
- <option value="Africa/Blantyre" <?php if( $ctf_timezone == "Africa/Blantyre" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Harare, Pretoria' ) ?></option>
1554
- <option value="Asia/Jerusalem" <?php if( $ctf_timezone == "Asia/Jerusalem" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Jerusalem' ) ?></option>
1555
- <option value="Europe/Minsk" <?php if( $ctf_timezone == "Europe/Minsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Minsk' ) ?></option>
1556
- <option value="Asia/Damascus" <?php if( $ctf_timezone == "Asia/Damascus" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Syria' ) ?></option>
1557
- <option value="Europe/Moscow" <?php if( $ctf_timezone == "Europe/Moscow" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Moscow, St. Petersburg, Volgograd' ) ?></option>
1558
- <option value="Africa/Addis_Ababa" <?php if( $ctf_timezone == "Africa/Addis_Ababa" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Nairobi' ) ?></option>
1559
- <option value="Asia/Tehran" <?php if( $ctf_timezone == "Asia/Tehran" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:30) Tehran' ) ?></option>
1560
- <option value="Asia/Dubai" <?php if( $ctf_timezone == "Asia/Dubai" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Abu Dhabi, Muscat' ) ?></option>
1561
- <option value="Asia/Yerevan" <?php if( $ctf_timezone == "Asia/Yerevan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Yerevan' ) ?></option>
1562
- <option value="Asia/Kabul" <?php if( $ctf_timezone == "Asia/Kabul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:30) Kabul' ) ?></option>
1563
- <option value="Asia/Yekaterinburg" <?php if( $ctf_timezone == "Asia/Yekaterinburg" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Ekaterinburg' ) ?></option>
1564
- <option value="Asia/Tashkent" <?php if( $ctf_timezone == "Asia/Tashkent" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Tashkent' ) ?></option>
1565
- <option value="Asia/Kolkata" <?php if( $ctf_timezone == "Asia/Kolkata" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi' ) ?></option>
1566
- <option value="Asia/Katmandu" <?php if( $ctf_timezone == "Asia/Katmandu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:45) Kathmandu' ) ?></option>
1567
- <option value="Asia/Dhaka" <?php if( $ctf_timezone == "Asia/Dhaka" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Astana, Dhaka' ) ?></option>
1568
- <option value="Asia/Novosibirsk" <?php if( $ctf_timezone == "Asia/Novosibirsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Novosibirsk' ) ?></option>
1569
- <option value="Asia/Rangoon" <?php if( $ctf_timezone == "Asia/Rangoon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:30) Yangon (Rangoon)' ) ?></option>
1570
- <option value="Asia/Bangkok" <?php if( $ctf_timezone == "Asia/Bangkok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Bangkok, Hanoi, Jakarta' ) ?></option>
1571
- <option value="Asia/Krasnoyarsk" <?php if( $ctf_timezone == "Asia/Krasnoyarsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Krasnoyarsk' ) ?></option>
1572
- <option value="Asia/Hong_Kong" <?php if( $ctf_timezone == "Asia/Hong_Kong" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi' ) ?></option>
1573
- <option value="Asia/Irkutsk" <?php if( $ctf_timezone == "Asia/Irkutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Irkutsk, Ulaan Bataar' ) ?></option>
1574
- <option value="Australia/Perth" <?php if( $ctf_timezone == "Australia/Perth" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Perth' ) ?></option>
1575
- <option value="Australia/Eucla" <?php if( $ctf_timezone == "Australia/Eucla" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:45) Eucla' ) ?></option>
1576
- <option value="Asia/Tokyo" <?php if( $ctf_timezone == "Asia/Tokyo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Osaka, Sapporo, Tokyo' ) ?></option>
1577
- <option value="Asia/Seoul" <?php if( $ctf_timezone == "Asia/Seoul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Seoul' ) ?></option>
1578
- <option value="Asia/Yakutsk" <?php if( $ctf_timezone == "Asia/Yakutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Yakutsk' ) ?></option>
1579
- <option value="Australia/Adelaide" <?php if( $ctf_timezone == "Australia/Adelaide" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Adelaide' ) ?></option>
1580
- <option value="Australia/Darwin" <?php if( $ctf_timezone == "Australia/Darwin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Darwin' ) ?></option>
1581
- <option value="Australia/Brisbane" <?php if( $ctf_timezone == "Australia/Brisbane" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Brisbane' ) ?></option>
1582
- <option value="Australia/Hobart" <?php if( $ctf_timezone == "Australia/Hobart" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Sydney' ) ?></option>
1583
- <option value="Asia/Vladivostok" <?php if( $ctf_timezone == "Asia/Vladivostok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Vladivostok' ) ?></option>
1584
- <option value="Australia/Lord_Howe" <?php if( $ctf_timezone == "Australia/Lord_Howe" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:30) Lord Howe Island' ) ?></option>
1585
- <option value="Etc/GMT11" <?php if( $ctf_timezone == "Etc/GMT11" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Solomon Is., New Caledonia' ) ?></option>
1586
- <option value="Asia/Magadan" <?php if( $ctf_timezone == "Asia/Magadan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Magadan' ) ?></option>
1587
- <option value="Pacific/Norfolk" <?php if( $ctf_timezone == "Pacific/Norfolk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:30) Norfolk Island' ) ?></option>
1588
- <option value="Asia/Anadyr" <?php if( $ctf_timezone == "Asia/Anadyr" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Anadyr, Kamchatka' ) ?></option>
1589
- <option value="Pacific/Auckland" <?php if( $ctf_timezone == "Pacific/Auckland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Auckland, Wellington' ) ?></option>
1590
- <option value="Etc/GMT12" <?php if( $ctf_timezone == "Etc/GMT12" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Fiji, Kamchatka, Marshall Is.' ) ?></option>
1591
- <option value="Pacific/Chatham" <?php if( $ctf_timezone == "Pacific/Chatham" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:45) Chatham Islands' ) ?></option>
1592
- <option value="Pacific/Tongatapu" <?php if( $ctf_timezone == "Pacific/Tongatapu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+13:00) Nuku\'alofa' ) ?></option>
1593
- <option value="Pacific/Kiritimati" <?php if( $ctf_timezone == "Pacific/Kiritimati" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+14:00) Kiritimati' ) ?></option>
1594
- </select>
1595
- <?php if ( isset( $args['whatis'] ) ) : ?>
1596
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1597
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1598
- <?php endif; ?>
1599
- <?php
1600
- }
1601
-
1602
- public function layout( $args ) {
1603
- $selected_type = $args['layout_selections']['layout'];
1604
- $layout_types = array(
1605
- 'list' => __( 'List', 'instagram-feed' ),
1606
- 'carousel' => __( 'Carousel', 'instagram-feed' ),
1607
- 'masonry' => __( 'Masonry', 'instagram-feed' ),
1608
- );
1609
- $layout_images = array(
1610
- 'list' => CTF_PLUGIN_URL . 'img/list.png',
1611
- 'carousel' => CTF_PLUGIN_URL . 'img/carousel.png',
1612
- 'masonry' => CTF_PLUGIN_URL . 'img/masonry.png',
1613
- );
1614
- ?>
1615
-
1616
- <?php foreach( $layout_types as $layout_type => $label ) : ?>
1617
- <div class="ctf_layout_cell <?php if($selected_type === $layout_type) echo "ctf_layout_selected"; ?>">
1618
- <input class="ctf_layout_type" id="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>" name="<?php echo $args['option'].'[layout]'; ?>" type="radio" value="<?php esc_attr_e( $layout_type ); ?>" <?php if ( $selected_type === $layout_type ) echo 'checked'; ?>/><label for="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>"><span class="ctf_label"><?php echo esc_html( $label ); ?></span><img src="<?php echo $layout_images[ $layout_type ]; ?>" /></label>
1619
-
1620
- <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&amp;utm_campaign=ctf" target="_blank" class="ctf_lock"><i class="fa fa-rocket"></i>Pro</a>
1621
- </div>
1622
- <?php endforeach; ?>
1623
- <div class="ctf_layout_options_wrap">
1624
- <div class="ctf_layout_settings ctf_layout_type_list">
1625
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('A full-width list of tweets.'); ?></span>
1626
- </div>
1627
- <div class="ctf_layout_settings ctf_layout_type_masonry">
1628
- <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Masonry layout option</a></p>
1629
- <div class="ctf_layout_setting">
1630
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Tweets in columns with no empty space between them.'); ?></span>
1631
- </div>
1632
- <div class="ctf_layout_setting">
1633
- <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrycols
1634
- Eg: masonrycols=4</code>
1635
- <br />
1636
- <select name="<?php echo $args['option'].'[masonrycols]'; ?>" id="ctf_masonrycols">
1637
- <?php
1638
- $cols_options = array(1,2,3,4,5,6);
1639
- foreach ( $cols_options as $option ) :
1640
- ?>
1641
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrycols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1642
- <?php endforeach; ?>
1643
- </select>
1644
- </div>
1645
- <div class="ctf_layout_setting">
1646
- <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrymobilecols
1647
- Eg: masonrymobilecols=2</code>
1648
- <br />
1649
- <select name="<?php echo $args['option'].'[masonrymobilecols]'; ?>" id="ctf_masonrymobilecols">
1650
- <?php
1651
- $cols_options = array(1,2);
1652
- foreach ( $cols_options as $option ) :
1653
- ?>
1654
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrymobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1655
- <?php endforeach; ?>
1656
- </select>
1657
- </div>
1658
- </div>
1659
- <div class="ctf_layout_settings ctf_layout_type_carousel">
1660
- <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Carousel layout option</a></p>
1661
- <div class="ctf_layout_setting">
1662
- <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Posts are displayed in a slideshow carousel.', 'instagram-feed'); ?></span>
1663
- </div>
1664
- <div class="ctf_layout_setting">
1665
- <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselcols
1666
- Eg: carouselcols=4</code>
1667
- <br />
1668
- <select name="<?php echo $args['option'].'[carouselcols]'; ?>" id="ctf_carouselcols">
1669
- <?php
1670
- $cols_options = array(1,2,3,4,5,6);
1671
- foreach ( $cols_options as $option ) :
1672
- ?>
1673
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselcols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1674
- <?php endforeach; ?>
1675
- </select>
1676
- </div>
1677
- <div class="ctf_layout_setting">
1678
- <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselmobilecols
1679
- Eg: carouselmobilecols=2</code>
1680
- <br />
1681
- <select name="<?php echo $args['option'].'[carouselmobilecols]'; ?>" id="ctf_carouselmobilecols">
1682
- <?php
1683
- $cols_options = array(1,2);
1684
- foreach ( $cols_options as $option ) :
1685
- ?>
1686
- <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselmobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1687
- <?php endforeach; ?>
1688
- </select>
1689
- </div>
1690
- <div class="ctf_layout_setting">
1691
- <label><?php _e('Loop Type', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselloop
1692
- Eg: carouselloop=rewind
1693
- carouselloop=infinity</code>
1694
- <br />
1695
- <select name="<?php echo $args['option'].'[carouselloop]'; ?>" id="ctf_carousel_loop">
1696
- <option value="none" <?php if($args['layout_selections']['carouselloop'] == "none") echo 'selected="selected"' ?> ><?php _e( 'None', 'instagram-feed'); ?></option>
1697
- <option value="rewind" <?php if($args['layout_selections']['carouselloop'] == "rewind") echo 'selected="selected"' ?> ><?php _e( 'Rewind', 'instagram-feed'); ?></option>
1698
- <option value="infinity" <?php if($args['layout_selections']['carouselloop'] == "infinity") echo 'selected="selected"' ?> ><?php _e( 'Infinity', 'instagram-feed'); ?></option>
1699
- </select>
1700
- </div>
1701
- <div class="ctf_layout_setting">
1702
- <label><?php _e('Navigation Arrows', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1703
- Eg: carouselarrows=below</code>
1704
- <br />
1705
- <select name="<?php echo $args['option'].'[carouselarrows]'; ?>" id="ctf_carousel_loop">
1706
- <option value="onhover" <?php if($args['layout_selections']['carouselarrows'] == "onhover") echo 'selected="selected"' ?> ><?php _e( 'Show on Hover', 'instagram-feed'); ?></option>
1707
- <option value="below" <?php if($args['layout_selections']['carouselarrows'] == "below") echo 'selected="selected"' ?> ><?php _e( 'Show below feed', 'instagram-feed'); ?></option>
1708
- <option value="hide" <?php if($args['layout_selections']['carouselarrows'] == "hide") echo 'selected="selected"' ?> ><?php _e( 'Hide arrows', 'instagram-feed'); ?></option>
1709
- </select>
1710
- </div>
1711
- <div class="ctf_layout_setting">
1712
- <label><?php _e('Carousel Height', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1713
- Eg: carouselarrows=below</code>
1714
- <br />
1715
- <select name="<?php echo $args['option'].'[carouselheight]'; ?>" id="ctf_carousel_loop">
1716
- <option value="tallest" <?php if($args['layout_selections']['carouselheight'] == "tallest") echo 'selected="selected"' ?> ><?php _e( 'Always set to tallest post', 'instagram-feed'); ?></option>
1717
- <option value="clickexpand" <?php if($args['layout_selections']['carouselheight'] == "clickexpand") echo 'selected="selected"' ?> ><?php _e( 'Set to shortest post, button to expand', 'instagram-feed'); ?></option>
1718
- <option value="auto'" <?php if($args['layout_selections']['carouselheight'] == "auto'") echo 'selected="selected"' ?> ><?php _e( 'Automatically set to post height (forces single column)', 'instagram-feed'); ?></option>
1719
- </select>
1720
- </div>
1721
- <div class="ctf_layout_setting">
1722
- <input type="checkbox" name="<?php echo $args['option'].'[carouselautoplay]'; ?>" id="ctf_carousel_autoplay" <?php if($args['layout_selections']['carouselautoplay'] == true) echo 'checked="checked"' ?> />
1723
- <label><?php _e("Enable Autoplay", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselautoplay
1724
- Eg: carouselautoplay=true</code>
1725
- </div>
1726
- <div class="ctf_layout_setting">
1727
- <label><?php _e("Interval Time", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouseltime
1728
- Eg: carouseltime=8000</code>
1729
- <br />
1730
- <input name="<?php echo $args['option'].'[carouseltime]'; ?>" type="text" value="<?php esc_attr_e( $args['layout_selections']['carouseltime'] ); ?>" size="6" /><?php _e("miliseconds", 'instagram-feed'); ?>
1731
- </div>
1732
- </div>
1733
-
1734
- </div>
1735
- <?php
1736
- }
1737
-
1738
- public function custom_code( $args )
1739
- {
1740
- $options = get_option( $args['option'] );
1741
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1742
- ?>
1743
- <p><?php _e( $args['description'], 'custom-twitter-feeds' ) ; ?></p>
1744
- <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="7"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
1745
- <?php if ( isset( $args['extra'] ) ) { _e( '<p class="ctf_note">'.$args['extra'].'</p>', 'custom-twitter-feeds' ); } ?>
1746
- <?php
1747
- }
1748
-
1749
- public function clear_persistent_cache_button( $args ) {
1750
- ?>
1751
- <input id="ctf-clear-persistent-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Persistent Caches' ); ?>" />
1752
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1753
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for search and hashtag feeds that have persistent caches', 'custom-twitter-feeds' ); ?>.</p>
1754
- <?php
1755
- }
1756
-
1757
- public function validate_ctf_options( $input )
1758
- {
1759
- if ( isset( $input['tab'] ) && ! isset( $_POST['just_tokens'] ) ) {
1760
- wp_cache_delete ( 'alloptions', 'options' );
1761
-
1762
- $ctf_options = get_option( 'ctf_options', array() );
1763
-
1764
- if ( $input['tab'] === 'configure' && isset( $input['usertimeline_text'] ) ) {
1765
-
1766
- $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1767
- $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1768
- $ctf_options['ajax_theme'] = false;
1769
- $ctf_options['have_own_tokens'] = false;
1770
- $ctf_options['use_own_consumer'] = false;
1771
- $ctf_options['preserve_settings'] = false;
1772
- $ctf_options['usertimeline_includereplies'] = false;
1773
- $ctf_options['hometimeline_includereplies'] = false;
1774
- $ctf_options['mentionstimeline_includereplies'] = false;
1775
-
1776
- foreach ( $input as $key => $val ) {
1777
- if ( $key == 'search_text' || $key == 'usertimeline_text' || $key == 'hashtag_text' ) {
1778
- $ctf_options[$key] = apply_filters( 'ctf_admin_validate_' . $key, $val );
1779
- } elseif ( $key == 'ajax_theme' || $key == 'use_own_consumer' || $key == 'have_own_tokens' || $key == 'preserve_settings' ||
1780
- $key == 'usertimeline_includereplies' || $key == 'hometimeline_includereplies' || $key == 'mentionstimeline_includereplies' ) {
1781
- if ( $val != 'on' ) {
1782
- $ctf_options[$key] = false;
1783
- } else {
1784
- $ctf_options[$key] = true;
1785
- }
1786
- } else {
1787
- $ctf_options[$key] = sanitize_text_field( $val );
1788
- }
1789
- }
1790
-
1791
- $ctf_options['includereplies'] = apply_filters( 'ctf_admin_set_include_replies', $ctf_options );
1792
-
1793
- // delete feeds cached in transients
1794
- ctf_clear_cache();
1795
- delete_transient( 'ctf_reauthenticate' );
1796
-
1797
- // process force cache to clear on interval
1798
- $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1799
- $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1800
-
1801
- if ( $cron_clear_cache == 'no' ) {
1802
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1803
- } elseif ( $cron_clear_cache == 'yes' ) {
1804
- //Clear the existing cron event
1805
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1806
-
1807
- //Set the event schedule based on what the caching time is set to
1808
- if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1809
- $ctf_cron_schedule = 'twicedaily';
1810
- } elseif ( $cache_time_unit == 86400 ) {
1811
- $ctf_cron_schedule = 'daily';
1812
- } else {
1813
- $ctf_cron_schedule = 'hourly';
1814
- }
1815
-
1816
- wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1817
- }
1818
- } elseif ( $input['tab'] === 'customize' && isset( $input['class'] ) ) {
1819
-
1820
- $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1821
- $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_logo', 'include_text', 'include_media_placeholder',
1822
- 'include_date', 'include_actions', 'include_twitterlink', 'include_linkbox', 'creditctf', 'showbutton', 'showheader', 'persistentcache', 'selfreplies',
1823
- 'disableintents', 'disableawesome', 'shorturls' );
1824
- $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1825
- $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour', 'custom_css', 'custom_js' );
1826
-
1827
- foreach ( $checkbox_settings as $checkbox_setting ) {
1828
- $ctf_options[$checkbox_setting] = 0;
1829
- }
1830
-
1831
- foreach ( $input as $key => $val ) {
1832
- if ( in_array( $key, $checkbox_settings ) ) {
1833
- if ( $val != 'on' ) {
1834
- $ctf_options[$key] = false;
1835
- } else {
1836
- $ctf_options[$key] = true;
1837
- }
1838
- } else {
1839
- if ( in_array( $key, $leave_spaces ) ) {
1840
- $ctf_options[$key] = $val;
1841
- } else {
1842
- $ctf_options[$key] = sanitize_text_field( $val );
1843
- }
1844
- }
1845
- }
1846
-
1847
- // delete feeds cached in transients
1848
- ctf_clear_cache();
1849
-
1850
- // process force cache to clear on interval
1851
- $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1852
- $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1853
-
1854
- if ( $cron_clear_cache == 'no' ) {
1855
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1856
- } elseif ( $cron_clear_cache == 'yes' ) {
1857
- //Clear the existing cron event
1858
- wp_clear_scheduled_hook( 'ctf_cron_job' );
1859
-
1860
- //Set the event schedule based on what the caching time is set to
1861
- if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1862
- $ctf_cron_schedule = 'twicedaily';
1863
- } elseif ( $cache_time_unit == 86400 ) {
1864
- $ctf_cron_schedule = 'daily';
1865
- } else {
1866
- $ctf_cron_schedule = 'hourly';
1867
- }
1868
-
1869
- wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1870
- }
1871
- } elseif ( $input['tab'] === 'style' && isset( $input['headertext'] ) ) {
1872
- $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
1873
- $leave_spaces = array( 'headertext' );
1874
-
1875
- foreach ( $checkbox_settings as $checkbox_setting ) {
1876
- $ctf_options[$checkbox_setting] = false;
1877
- }
1878
-
1879
- foreach ( $input as $key => $val ) {
1880
- if ( in_array( $key, $checkbox_settings ) ) {
1881
- if ( $val != 'on' ) {
1882
- $ctf_options[$key] = false;
1883
- } else {
1884
- $ctf_options[$key] = true;
1885
- }
1886
- } else {
1887
- if ( in_array( $key, $leave_spaces ) ) {
1888
- $ctf_options[$key] = $val;
1889
- } else {
1890
- $ctf_options[$key] = sanitize_text_field( $val );
1891
- }
1892
- }
1893
- }
1894
- }
1895
-
1896
- return $ctf_options;
1897
- } elseif ( isset( $input['access_token'] ) ) {
1898
- wp_cache_delete ( 'alloptions', 'options' );
1899
-
1900
- $new = get_option( 'ctf_options', array() );
1901
- $new['access_token'] = $input['access_token'];
1902
- $new['access_token_secret'] = $input['access_token_secret'];
1903
-
1904
- return $new;
1905
-
1906
- }
1907
- wp_cache_delete ( 'alloptions', 'options' );
1908
-
1909
- $new = get_option( 'ctf_options', array() );
1910
-
1911
- return $new;
1912
- }
1913
  }
1
+ <?php
2
+ /**
3
+ * Class CtfAdmin
4
+ *
5
+ * Uses the Settings API to create easily customizable settings pages and tabs
6
+ */
7
+
8
+ // Don't load directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ die( '-1' );
11
+ }
12
+
13
+ class CtfAdmin
14
+ {
15
+ public function __construct()
16
+ {
17
+ add_action( 'admin_menu', array( $this, 'add_menu' ) );
18
+ add_action( 'admin_init', array( $this, 'options_page_init' ) );
19
+ }
20
+
21
+ public function add_menu()
22
+ {
23
+ add_menu_page(
24
+ 'Twitter Feeds',
25
+ 'Twitter Feeds',
26
+ 'manage_options',
27
+ 'custom-twitter-feeds',
28
+ array( $this, 'create_options_page' ),
29
+ '',
30
+ 99
31
+ );
32
+
33
+ add_submenu_page(
34
+ 'custom-twitter-feeds',
35
+ 'Customize',
36
+ 'Customize',
37
+ 'manage_options',
38
+ 'custom-twitter-feeds-customize',
39
+ array( $this, 'create_submenu_page_customize' )
40
+ );
41
+
42
+ add_submenu_page(
43
+ 'custom-twitter-feeds',
44
+ 'Style',
45
+ 'Style',
46
+ 'manage_options',
47
+ 'custom-twitter-feeds-style',
48
+ array( $this, 'create_submenu_page_style' )
49
+ );
50
+ }
51
+
52
+ public static function get_active_tab( $tab = '' )
53
+ {
54
+ switch ( $tab ) {
55
+ case 'customize':
56
+ return 'customize';
57
+ case 'style':
58
+ return 'style';
59
+ case 'display':
60
+ return 'display';
61
+ case 'support':
62
+ return 'support';
63
+ default:
64
+ return 'configure';
65
+ }
66
+ }
67
+
68
+ public function create_options_page()
69
+ {
70
+ require_once CTF_URL . '/views/admin/main.php';
71
+ }
72
+
73
+ public function create_submenu_page_customize()
74
+ {
75
+ $tab = 'customize';
76
+
77
+ require_once CTF_URL . '/views/admin/main.php';
78
+ }
79
+
80
+ public function create_submenu_page_style()
81
+ {
82
+ $tab = 'style';
83
+
84
+ require_once CTF_URL . '/views/admin/main.php';
85
+ }
86
+
87
+ public function general_section_text()
88
+ {
89
+ // no explanation needed
90
+ }
91
+
92
+ public function access_token_button()
93
+ {
94
+ $this->the_admin_access_token_configure_html( $_GET );
95
+ $options = get_option( 'ctf_options' );
96
+ $option_checked = ( isset( $options['have_own_tokens'] ) ) ? $options['have_own_tokens'] : false;
97
+ ?>
98
+ <input name="<?php echo 'ctf_options'.'[have_own_tokens]'; ?>" id="ctf_have_own_tokens" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
99
+ <label for="ctf_have_own_tokens" class="ctf_checkbox"><?php _e( 'Or, manually enter my own Twitter app information' ); ?></label>
100
+ <span class="ctf-tooltip-wrap">
101
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
102
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Check this box if you would like to manually enter the information from your own <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">Twitter app</a>', 'custom-twitter-feeds' ); ?>.</p>
103
+ </span>
104
+ <?php
105
+ }
106
+
107
+ /**
108
+ * generates the html for the access token retrieving button
109
+ *
110
+ * @param $access_token_data array the $_GET data if it exists
111
+ */
112
+ private function the_admin_access_token_configure_html( $access_token_data ) {
113
+ ?>
114
+
115
+ <div id="ctf_config">
116
+
117
+ <?php if ( isset( $access_token_data['oauth_token'] ) ) : ?>
118
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
119
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
120
+
121
+ <input type="hidden" id="ctf-retrieved-access-token" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token'] ) ); ?>">
122
+ <input type="hidden" id="ctf-retrieved-access-token-secret" value="<?php echo esc_html( sanitize_text_field( $access_token_data['oauth_token_secret'] ) ); ?>">
123
+ <input type="hidden" id="ctf-retrieved-default-screen-name" value="<?php echo esc_html( sanitize_text_field( $access_token_data['screen_name'] ) ); ?>">
124
+
125
+ <?php elseif ( isset( $access_token_data['error'] ) && ! isset( $access_token_data['oauth_token'] ) ) : ?>
126
+
127
+ <p class="ctf_notice"><?php _e( 'There was an error with retrieving your access tokens. Please <a href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank">use this tool</a> to get your access token and secret.' ); ?></p><br>
128
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
129
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
130
+
131
+ <?php else : ?>
132
+
133
+ <a href="<?php echo OAUTH_PROCESSOR_URL . admin_url( 'admin.php?page=custom-twitter-feeds' ); ?>" id="ctf-get-token"><i class="fa fa-twitter"></i><?php _e( 'Log in to Twitter and get my Access Token and Secret' ); ?></a>
134
+ <a class="ctf-tooltip-link" href="https://smashballoon.com/custom-twitter-feeds/token/" target="_blank"><?php _e( "Button not working?", 'custom-twitter-feeds' ); ?></a>
135
+
136
+ <?php endif; ?>
137
+
138
+ </div>
139
+ <?php
140
+ }
141
+
142
+ public function options_page_init()
143
+ {
144
+ /*
145
+ * "Configure" Tab
146
+ */
147
+
148
+ register_setting(
149
+ 'ctf_options', // name of the option that gets called in "get_option()"
150
+ 'ctf_options', // matches the options name
151
+ array( $this, 'validate_ctf_options' ) // callback function to validate and clean data
152
+ );
153
+
154
+ add_settings_section(
155
+ 'ctf_options_connect', // matches the section name
156
+ 'Configuration',
157
+ array( $this, 'access_token_button' ), // callback function to explain the section
158
+ 'ctf_options_connect' // matches the section name
159
+ );
160
+
161
+ // Consumer Key
162
+ $this->create_settings_field( array(
163
+ 'name' => 'consumer_key',
164
+ 'title' => '<label for="ctf_consumer_key">Consumer Key</label>', // label for the input field
165
+ 'callback' => 'default_text', // name of the function that outputs the html
166
+ 'page' => 'ctf_options_connect', // matches the section name
167
+ 'section' => 'ctf_options_connect', // matches the section name
168
+ 'option' => 'ctf_options', // matches the options name
169
+ 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
170
+ 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">here</a>', // what is this? text
171
+ 'size' => '27'
172
+ ) );
173
+
174
+ // Consumer Secret
175
+ $this->create_settings_field( array(
176
+ 'name' => 'consumer_secret',
177
+ 'title' => '<label for="ctf_consumer_secret">Consumer Secret</label>', // label for the input field
178
+ 'callback' => 'default_text', // name of the function that outputs the html
179
+ 'page' => 'ctf_options_connect', // matches the section name
180
+ 'section' => 'ctf_options_connect', // matches the section name
181
+ 'option' => 'ctf_options', // matches the options name
182
+ 'class' => 'ctf-toggle-consumer', // class for the wrapper and input field
183
+ 'whatis' => 'A Consumer Key and a Consumer Secret are both needed if you want to use credentials from your own Twitter App. You can create these <a href="https://smashballoon.com/custom-twitter-feeds/docs/create-twitter-app/" target="_blank">here</a>', // what is this? text
184
+ 'size' => '57'
185
+ ) );
186
+
187
+ // Access Token
188
+ $this->create_settings_field( array(
189
+ 'name' => 'access_token',
190
+ 'title' => '<label for="ctf_access_token">Access Token</label>', // label for the input field
191
+ 'callback' => 'default_text', // name of the function that outputs the html
192
+ 'page' => 'ctf_options_connect', // matches the section name
193
+ 'section' => 'ctf_options_connect', // matches the section name
194
+ 'option' => 'ctf_options', // matches the options name
195
+ 'class' => 'ctf-toggle-access', // class for the wrapper and input field
196
+ 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
197
+ 'size' => '57'
198
+ ) );
199
+
200
+ // Access Token Secret
201
+ $this->create_settings_field( array(
202
+ 'name' => 'access_token_secret',
203
+ 'title' => '<label for="ctf_access_token_secret">Access Token Secret</label>', // label for the input field
204
+ 'callback' => 'access_token_secret', // name of the function that outputs the html
205
+ 'page' => 'ctf_options_connect', // matches the section name
206
+ 'section' => 'ctf_options_connect', // matches the section name
207
+ 'option' => 'ctf_options', // matches the options name
208
+ 'class' => 'ctf-toggle-access', // class for the wrapper and input field
209
+ 'whatis' => "This will allow the plugin to connect to the Twitter API", // "what is this?" text
210
+ 'size' => '57'
211
+ ));
212
+
213
+ add_settings_section(
214
+ 'ctf_options_feed_settings', // matches the section name
215
+ 'Feed Settings',
216
+ array( $this, 'general_section_text' ), // callback function to explain the section
217
+ 'ctf_options_feed_settings' // matches the section name
218
+ );
219
+
220
+ // User Timeline Radio
221
+ $this->create_settings_field( array(
222
+ 'name' => 'usertimeline',
223
+ 'title' => '<label for="ctf_feed_type">Feed Type</label><code class="ctf_shortcode">type
224
+ Eg: screenname=gopro
225
+ Eg: home=true
226
+ Eg: hashtag=#cats</code>', // label for the input field
227
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
228
+ 'page' => 'ctf_options_feed_settings', // matches the section name
229
+ 'section' => 'ctf_options_feed_settings', // matches the section name
230
+ 'option' => 'ctf_options', // matches the options name
231
+ 'class' => 'ctf-radio', // class for the wrapper and input field
232
+ 'whatis' => "Select this option and enter any screen name to create a user timeline feed", // what is this? text
233
+ 'label' => "User Timeline:",
234
+ 'has_input' => true,
235
+ 'has_replies' => true
236
+ ));
237
+
238
+ // Search Radio
239
+ $search_label = apply_filters( 'ctf_admin_search_label', '' );
240
+ $search_whatis = apply_filters( 'ctf_admin_search_whatis', '' );
241
+ $this->create_settings_field( array(
242
+ 'name' => 'search',
243
+ 'title' => '<label></label>', // label for the input field
244
+ 'callback' => 'feed_settings_radio_search', // name of the function that outputs the html
245
+ 'page' => 'ctf_options_feed_settings', // matches the section name
246
+ 'section' => 'ctf_options_feed_settings', // matches the section name
247
+ 'option' => 'ctf_options', // matches the options name
248
+ 'class' => 'ctf-radio', // class for the wrapper and input field
249
+ 'whatis' => $search_whatis, // what is this? text
250
+ 'label' => $search_label,
251
+ 'has_input' => true,
252
+ 'note_after_input' => __( '(only recent tweets initially)', 'custom-twitter-feeds' ),
253
+ 'extra' => true
254
+ ) );
255
+
256
+ // Home Timeline Radio
257
+ $this->create_settings_field( array(
258
+ 'name' => 'hometimeline',
259
+ 'title' => '<label></label>', // label for the input field
260
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
261
+ 'page' => 'ctf_options_feed_settings', // matches the section name
262
+ 'section' => 'ctf_options_feed_settings', // matches the section name
263
+ 'option' => 'ctf_options', // matches the options name
264
+ 'class' => 'ctf-radio', // class for the wrapper and input field
265
+ 'whatis' => 'Select this option to display tweets from yourself and those you follow', // what is this? text
266
+ 'label' => "Home Timeline",
267
+ 'has_input' => false,
268
+ 'has_replies' => true
269
+ ));
270
+
271
+ do_action( 'ctf_admin_endpoints', $this );
272
+
273
+ // Number of Tweets
274
+ $this->create_settings_field( array(
275
+ 'name' => 'num',
276
+ 'title' => '<label for="ctf_num">How Many Tweets to Display</label><code class="ctf_shortcode">num
277
+ Eg: num=10</code>', // label for the input field
278
+ 'callback' => 'default_text', // name of the function that outputs the html
279
+ 'page' => 'ctf_options_feed_settings', // matches the section name
280
+ 'section' => 'ctf_options_feed_settings', // matches the section name
281
+ 'option' => 'ctf_options', // matches the options name
282
+ 'class' => 'small-text', // class for the wrapper and input field
283
+ 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
284
+ 'type' => 'number', // input field "type" attribute
285
+ 'default' => 5
286
+ ));
287
+
288
+ // time unit for cache
289
+ $this->create_settings_field( array(
290
+ 'name' => 'cache_time',
291
+ 'title' => '<label for="ctf_cache_time">How Many Tweets to Display</label>', // label for the input field
292
+ 'callback' => 'default_text', // name of the function that outputs the html
293
+ 'page' => 'ctf_options_feed_settings', // matches the section name
294
+ 'section' => 'ctf_options_feed_settings', // matches the section name
295
+ 'option' => 'ctf_options', // matches the options name
296
+ 'class' => 'small-text', // class for the wrapper and input field
297
+ 'whatis' => "Enter the number of tweets you would like to display when the feed first loads", // what is this? text
298
+ 'type' => 'number' // input field "type" attribute
299
+ ));
300
+
301
+ // check for new tweets
302
+ $this->create_settings_field( array(
303
+ 'name' => 'cache_time',
304
+ 'title' => '<label for="ctf_cache_time">Check for new tweets every</label>', // label for the input field
305
+ 'callback' => 'cache_time', // name of the function that outputs the html
306
+ 'page' => 'ctf_options_feed_settings', // matches the section name
307
+ 'section' => 'ctf_options_feed_settings', // matches the section name
308
+ 'option' => 'ctf_options', // matches the options name
309
+ 'class' => 'short-text', // class for the wrapper and input field
310
+ 'whatis' => "Your Tweets are temporarily cached by the plugin in your WordPress database. You can choose how long the posts should be cached for. If you set the time to 1 hour then the plugin will clear the cache after that length of time and check Instagram for posts again" // what is this? text
311
+ ) );
312
+
313
+ // preserve settings
314
+ $this->create_settings_field( array(
315
+ 'name' => 'preserve_settings',
316
+ 'title' => '<label for="ctf_preserve_settings">Preserve settings when plugin is removed</label>', // label for the input field
317
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
318
+ 'page' => 'ctf_options_feed_settings', // matches the section name
319
+ 'section' => 'ctf_options_feed_settings', // matches the section name
320
+ 'option' => 'ctf_options', // matches the options name
321
+ 'class' => '',
322
+ 'whatis' => "When removing the plugin your settings are automatically erased. Checking this box will prevent any settings from being deleted. This means that you can uninstall and reinstall the plugin without losing your settings"
323
+ ));
324
+
325
+ /*
326
+ * "Customize" tab
327
+ */
328
+
329
+ add_settings_section(
330
+ 'ctf_options_general', // matches the section name
331
+ 'General',
332
+ array( $this, 'general_section_text' ), // callback function to explain the section
333
+ 'ctf_options_general' // matches the section name
334
+ );
335
+
336
+ // width
337
+ $this->create_settings_field( array(
338
+ 'name' => 'width',
339
+ 'title' => '<label for="ctf_width">Width of Feed</label><code class="ctf_shortcode">width
340
+ Eg: width=500</code>', // label for the input field
341
+ 'callback' => 'width_and_height_settings', // name of the function that outputs the html
342
+ 'page' => 'ctf_options_general', // matches the section name
343
+ 'section' => 'ctf_options_general', // matches the section name
344
+ 'option' => 'ctf_options', // matches the options name
345
+ 'class' => 'small-text',
346
+ 'default' => '100',
347
+ 'default_unit' => '%'
348
+ ));
349
+
350
+ // height
351
+ $this->create_settings_field( array(
352
+ 'name' => 'height',
353
+ 'title' => '<label for="ctf_height">Height of Feed</label><code class="ctf_shortcode">height
354
+ Eg: height=1000</code>', // label for the input field
355
+ 'callback' => 'width_and_height_settings', // name of the function that outputs the html
356
+ 'page' => 'ctf_options_general', // matches the section name
357
+ 'section' => 'ctf_options_general', // matches the section name
358
+ 'option' => 'ctf_options', // matches the options name
359
+ 'class' => 'small-text',
360
+ 'default_unit' => 'px'
361
+ ));
362
+
363
+ // class
364
+ $this->create_settings_field( array(
365
+ 'name' => 'class',
366
+ 'title' => '<label for="ctf_class">Add Custom CSS Class</label><code class="ctf_shortcode">class
367
+ Eg: class="my-class"</code>', // label for the input field
368
+ 'callback' => 'default_text', // name of the function that outputs the html
369
+ 'page' => 'ctf_options_general', // matches the section name
370
+ 'section' => 'ctf_options_general', // matches the section name
371
+ 'option' => 'ctf_options', // matches the options name
372
+ 'class' => 'default-text',
373
+ 'type' => 'text',
374
+ 'whatis' => "You can add your own CSS classes to the feed here. To add multiple classes separate each with a space, Eg. classone classtwo classthree"
375
+ ));
376
+
377
+ add_settings_section(
378
+ 'ctf_options_layout', // matches the section name
379
+ 'Layout',
380
+ array( $this, 'general_section_text' ), // callback function to explain the section
381
+ 'ctf_options_layout' // matches the section name
382
+ );
383
+
384
+ $settings = get_option( 'ctf_options', array() );
385
+ $layout = 'list';
386
+ $layout_selections = array(
387
+ 'layout' => $layout,
388
+ 'carouselcols' => isset( $settings['carouselcols'] ) ? $settings['carouselcols'] : 3,
389
+ 'carouselmobilecols' => isset( $settings['carouselmobilecols'] ) ? $settings['carouselmobilecols'] : 1,
390
+ 'carouselarrows' => isset( $settings['carouselarrows'] ) ? $settings['carouselarrows'] : 'onhover',
391
+ 'carouselpag' => isset( $settings['carouselpag'] ) ? $settings['carouselpag'] : true,
392
+ 'carouselheight' => isset( $settings['carouselheight'] ) ? $settings['carouselheight'] : 'tallest',
393
+ 'carouselautoplay' => isset( $settings['carouselautoplay'] ) ? $settings['carouselautoplay'] : false,
394
+ 'carouseltime' => isset( $settings['carouseltime'] ) ? $settings['carouseltime'] : '5000',
395
+ 'carouselloop' => isset( $settings['carouselloop'] ) ? $settings['carouselloop'] : 'infinite',
396
+ 'masonrycols' => isset( $settings['masonrycols'] ) ? $settings['masonrycols'] : 3,
397
+ 'masonrymobilecols' => isset( $settings['masonrymobilecols'] ) ? $settings['masonrymobilecols'] : 1,
398
+ );
399
+
400
+ $this->create_settings_field( array(
401
+ 'name' => 'class',
402
+ 'title' => '<label for="ctf_layout">Layout Type</label><code class="ctf_shortcode">layout
403
+ Eg: layout="masonry"</code>', // label for the input field
404
+ 'callback' => 'layout', // name of the function that outputs the html
405
+ 'page' => 'ctf_options_layout', // matches the section name
406
+ 'section' => 'ctf_options_layout', // matches the section name
407
+ 'option' => 'ctf_options', // matches the options name
408
+ 'class' => 'default-text ctf_pro',
409
+ 'type' => 'text',
410
+ 'layout_selections' => $layout_selections,
411
+ 'whatis' => ""
412
+ ));
413
+
414
+ add_settings_section(
415
+ 'ctf_options_showandhide', // matches the section name
416
+ 'Show/Hide',
417
+ array( $this, 'general_section_text' ), // callback function to explain the section
418
+ 'ctf_options_showandhide' // matches the section name
419
+ );
420
+
421
+ // show/hide
422
+ $show_hide_list = array(
423
+ array( 'include_retweeter', 'Retweeted text' ),
424
+ array( 'include_avatar', 'Avatar image' ),
425
+ array( 'include_author', 'Author name' ),
426
+ array( 'include_logo', 'Twitter logo' ),
427
+ array( 'include_text', 'Tweet text' ),
428
+ array( 'include_media_placeholder', 'Media placeholder' ),
429
+ array( 'include_date', 'Date' ),
430
+ array( 'include_actions', 'Tweet actions (reply, retweet, like)' ),
431
+ array( 'include_twitterlink', '"Twitter" link' ),
432
+ array( 'include_linkbox', 'Quoted tweet box' )
433
+ );
434
+ $show_hide_list = apply_filters( 'ctf_admin_show_hide_list', $show_hide_list );
435
+
436
+ $this->create_settings_field( array(
437
+ 'name' => 'showandhide',
438
+ 'title' => '<label>Include the Following in Tweets <em>(when applicable)</em></label><code class="ctf_shortcode">include exclude
439
+ Eg: include=author,date
440
+ Eg: exclude=actions
441
+ Options: avatar, author,
442
+ logo, text, placeholder,
443
+ date, actions, linkbox </code>', // label for the input field
444
+ 'callback' => 'include_exclude_checkbox', // name of the function that outputs the html
445
+ 'page' => 'ctf_options_showandhide', // matches the section name
446
+ 'section' => 'ctf_options_showandhide', // matches the section name
447
+ 'option' => 'ctf_options', // matches the options name
448
+ 'fields' => $show_hide_list,
449
+ 'class' => ''
450
+ ));
451
+
452
+ // show header
453
+ $this->create_settings_field( array(
454
+ 'name' => 'showheader',
455
+ 'title' => '<label for="ctf_showheader">Show Header</label><code class="ctf_shortcode">showheader
456
+ Eg: showheader=true</code>', // label for the input field
457
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
458
+ 'page' => 'ctf_options_showandhide', // matches the section name
459
+ 'section' => 'ctf_options_showandhide', // matches the section name
460
+ 'option' => 'ctf_options', // matches the options name
461
+ 'class' => '',
462
+ 'whatis' => "The header is displayed above your tweets with some basic information about the feed"
463
+ ));
464
+
465
+ // load more button
466
+ $this->create_settings_field( array(
467
+ 'name' => 'showbutton',
468
+ 'title' => '<label for="ctf_showbutton">Show the "Load More" Button</label><code class="ctf_shortcode">showbutton
469
+ Eg: showbutton=true</code>', // label for the input field
470
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
471
+ 'page' => 'ctf_options_showandhide', // matches the section name
472
+ 'section' => 'ctf_options_showandhide', // matches the section name
473
+ 'option' => 'ctf_options', // matches the options name
474
+ 'class' => '',
475
+ 'whatis' => "Show the Load More Button",
476
+ ));
477
+
478
+
479
+ // credit ctf
480
+ $this->create_settings_field( array(
481
+ 'name' => 'creditctf',
482
+ 'title' => '<label for="ctf_creditctf">Add Custom Twitter Feeds Credit</label><code class="ctf_shortcode">creditctf
483
+ Eg: creditctf=true</code>', // label for the input field
484
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
485
+ 'page' => 'ctf_options_showandhide', // matches the section name
486
+ 'section' => 'ctf_options_showandhide', // matches the section name
487
+ 'option' => 'ctf_options', // matches the options name
488
+ 'class' => '',
489
+ 'whatis' => "Help us keep this plugin great! Add a link below your feed to credit Custom Twitter Feeds by Smash Balloon"
490
+ ));
491
+
492
+ do_action( 'ctf_admin_customize_option', $this );
493
+
494
+ add_settings_section(
495
+ 'ctf_options_misc', // matches the section name
496
+ 'Misc',
497
+ array( $this, 'general_section_text' ), // callback function to explain the section
498
+ 'ctf_options_misc' // matches the section name
499
+ );
500
+
501
+ // Custom CSS
502
+ $this->create_settings_field( array(
503
+ 'name' => 'custom_css',
504
+ 'title' => '<label for="ctf_custom_css">Custom CSS</label>', // label for the input field
505
+ 'callback' => 'custom_code', // name of the function that outputs the html
506
+ 'page' => 'ctf_options_misc', // matches the section name
507
+ 'section' => 'ctf_options_misc', // matches the section name
508
+ 'option' => 'ctf_options', // matches the options name
509
+ 'class' => 'default-text', // class for the wrapper and input field
510
+ 'description' => 'Enter your own custom CSS in the box below'
511
+ ));
512
+
513
+ // Custom JS
514
+ $this->create_settings_field( array(
515
+ 'name' => 'custom_js',
516
+ 'title' => '<label for="ctf_custom_js">Custom Javascript*</label>', // label for the input field
517
+ 'callback' => 'custom_code', // name of the function that outputs the html
518
+ 'page' => 'ctf_options_misc', // matches the section name
519
+ 'section' => 'ctf_options_misc', // matches the section name
520
+ 'option' => 'ctf_options', // matches the options name
521
+ 'class' => 'default-text', // class for the wrapper and input field
522
+ 'description' => 'Enter your own custom Javascript/JQuery in the box below',
523
+ 'extra' => '*will be fired every time more tweets are loaded'
524
+ ));
525
+
526
+ add_settings_section(
527
+ 'ctf_options_advanced', // matches the section name
528
+ 'Advanced',
529
+ array( $this, 'general_section_text' ), // callback function to explain the section
530
+ 'ctf_options_advanced' // matches the section name
531
+ );
532
+
533
+ // ajax theme
534
+ $this->create_settings_field( array(
535
+ 'name' => 'ajax_theme',
536
+ 'title' => '<label for="ctf_ajax_theme">Are you using an Ajax powered theme?</label>', // label for the input field
537
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
538
+ 'page' => 'ctf_options_advanced', // matches the section name
539
+ 'section' => 'ctf_options_advanced', // matches the section name
540
+ 'option' => 'ctf_options', // matches the options name
541
+ 'class' => '',
542
+ 'whatis' => "When navigating your site, if your theme uses Ajax to load content into your pages (meaning your page doesn't refresh) then check this setting. If you're not sure then please check with the theme author"
543
+ ));
544
+
545
+ // Request Method
546
+ $this->create_settings_field( array(
547
+ 'name' => 'request_method',
548
+ 'title' => '<label for="ctf_request_method">Request Method</label>', // label for the input field
549
+ 'callback' => 'default_select', // name of the function that outputs the html
550
+ 'page' => 'ctf_options_advanced', // matches the section name
551
+ 'section' => 'ctf_options_advanced', // matches the section name
552
+ 'option' => 'ctf_options', // matches the options name
553
+ 'class' => 'default-text', // class for the wrapper and input field
554
+ 'fields' => array(
555
+ 1 => array( 'auto', 'Auto' ),
556
+ 2 => array( 'curl', 'cURL' ),
557
+ 3 => array( 'file_get_contents', 'file_get_contents()' ),
558
+ 4 => array( 'wp_http', 'WP_Http' )
559
+ ),
560
+ 'whatis' => "Explicitly set the request method. You would only want to change this if you are unable to connect to the Twitter API" // what is this? text
561
+ ) );
562
+
563
+ // force cache to clear on interval
564
+ $this->create_settings_field( array(
565
+ 'name' => 'cron_cache_clear',
566
+ 'title' => '<label for="ctf_cron_cache_clear">Force cache to clear on interval</label>', // label for the input field
567
+ 'callback' => 'default_select', // name of the function that outputs the html
568
+ 'page' => 'ctf_options_advanced', // matches the section name
569
+ 'section' => 'ctf_options_advanced', // matches the section name
570
+ 'option' => 'ctf_options', // matches the options name
571
+ 'class' => 'default-text', // class for the wrapper and input field
572
+ 'fields' => array(
573
+ 1 => array( 'unset', '-' ),
574
+ 2 => array( 'yes', 'Yes' ),
575
+ 3 => array( 'no', 'No' )
576
+ ),
577
+ 'whatis' => "If you're experiencing an issue with the plugin not auto-updating then you can set this to 'Yes' to run a scheduled event behind the scenes which forces the plugin cache to clear on a regular basis and retrieve new data from Twitter" // what is this? text
578
+ ) );
579
+
580
+ // tweet multiplier
581
+ $this->create_settings_field( array(
582
+ 'name' => 'multiplier',
583
+ 'title' => '<label for="ctf_multiplier">Tweet Multiplier</label><code class="ctf_shortcode">multiplier
584
+ Eg: multiplier=1.5</code>', // label for the input field
585
+ 'callback' => 'default_text', // name of the function that outputs the html
586
+ 'page' => 'ctf_options_advanced', // matches the section name
587
+ 'section' => 'ctf_options_advanced', // matches the section name
588
+ 'option' => 'ctf_options', // matches the options name
589
+ 'class' => 'small-text', // class for the wrapper and input field
590
+ 'whatis' => "If your feed excludes reply tweets (this is automatic in hashtag/search feeds), the correct number of tweets may not show up. Increasing this number will increase the number of tweets retrieved but will also increase the load time for the feed as well", // what is this? text
591
+ 'type' => 'number', // input field "type" attribute
592
+ 'min' => 1,
593
+ 'max' => 3,
594
+ 'step' => 'any',
595
+ 'default' => 1.25
596
+ ));
597
+
598
+ $this->create_settings_field( array(
599
+ 'name' => 'persistent',
600
+ 'title' => '<label for="ctf_multiplier">Clear Persistent Cache</label>', // label for the input field
601
+ 'callback' => 'clear_persistent_cache_button', // name of the function that outputs the html
602
+ 'page' => 'ctf_options_advanced', // matches the section name
603
+ 'section' => 'ctf_options_advanced', // matches the section name
604
+ 'option' => 'ctf_options', // matches the options name
605
+ 'class' => 'small-text' // class for the wrapper and input field
606
+ ));
607
+
608
+ // persistent cache
609
+ $this->create_settings_field( array(
610
+ 'name' => 'persistentcache',
611
+ 'title' => '<label for="ctf_persistentcache">Persistent cache enabled by default</label><code class="ctf_shortcode">persistentcache
612
+ Eg: persistentcache=false</code>', // label for the input field
613
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
614
+ 'page' => 'ctf_options_advanced', // matches the section name
615
+ 'section' => 'ctf_options_advanced', // matches the section name
616
+ 'option' => 'ctf_options', // matches the options name
617
+ 'class' => '',
618
+ 'whatis' => "Checking this box will make all Search and Hashtag feeds have a permanent cache saved in the database by default of up to 150 tweets. Tweets will be available for the feed even after the 7 day limit though numbers of retweets and likes will not update"
619
+ ));
620
+
621
+ $this->create_settings_field( array(
622
+ 'name' => 'selfreplies',
623
+ 'title' => '<label for="ctf_selfreplies">Always include replies to self in the feed</label><code class="ctf_shortcode">autores
624
+ Eg: selfreplies=true</code>', // label for the input field
625
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
626
+ 'page' => 'ctf_options_advanced', // matches the section name
627
+ 'section' => 'ctf_options_advanced', // matches the section name
628
+ 'option' => 'ctf_options', // matches the options name
629
+ 'class' => '',
630
+ 'whatis' => "Twitter considers @mentions of your own account and replies to your own account's tweets as \"reply\" tweets. Enable this setting to include these type of tweets in your feed"
631
+ ));
632
+
633
+ $this->create_settings_field( array(
634
+ 'name' => 'disableintents',
635
+ 'title' => '<label for="ctf_disableintents">Disable Twitter intents JS</label><code class="ctf_shortcode">disableintents
636
+ Eg: disableintents=true</code>', // label for the input field
637
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
638
+ 'page' => 'ctf_options_advanced', // matches the section name
639
+ 'section' => 'ctf_options_advanced', // matches the section name
640
+ 'option' => 'ctf_options', // matches the options name
641
+ 'class' => '',
642
+ 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
643
+ ));
644
+
645
+ $this->create_settings_field( array(
646
+ 'name' => 'font_method',
647
+ 'title' => '<label for="ctf_font_method">Icon Method</label>', // label for the input field
648
+ 'callback' => 'default_select', // name of the function that outputs the html
649
+ 'page' => 'ctf_options_advanced', // matches the section name
650
+ 'section' => 'ctf_options_advanced', // matches the section name
651
+ 'option' => 'ctf_options', // matches the options name
652
+ 'class' => 'default-text', // class for the wrapper and input field
653
+ 'fields' => array(
654
+ array( 'svg', 'SVG' ),
655
+ array( 'fontfile', 'Font File' )
656
+ ),
657
+ 'whatis' => "This plugin uses SVGs for all icons in the feed. Use this setting to switch to font icons" // what is this? text
658
+ ) );
659
+
660
+ $this->create_settings_field( array(
661
+ 'name' => 'disableawesome',
662
+ 'title' => '<label for="ctf_disableawesome">Disable icon font</label>', // label for the input field
663
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
664
+ 'page' => 'ctf_options_advanced', // matches the section name
665
+ 'section' => 'ctf_options_advanced', // matches the section name
666
+ 'option' => 'ctf_options', // matches the options name
667
+ 'class' => '',
668
+ 'whatis' => "Twitter provides JavaScript that allows visitors of your site to reply to, retweet, and like tweets without leaving your site. This can be disabled using this setting"
669
+ ));
670
+
671
+ $this->create_settings_field( array(
672
+ 'name' => 'shorturls',
673
+ 'title' => '<label for="ctf_shorturls">Use shortened urls</label>', // label for the input field
674
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
675
+ 'page' => 'ctf_options_advanced', // matches the section name
676
+ 'section' => 'ctf_options_advanced', // matches the section name
677
+ 'option' => 'ctf_options', // matches the options name
678
+ 'class' => '',
679
+ 'whatis' => "Twitter provides shortened versions of links in tweets. Enable this setting to use them instead of the full URLs"
680
+ ));
681
+
682
+ /**
683
+ * "Style" tab
684
+ */
685
+
686
+ add_settings_section(
687
+ 'ctf_options_general_style', // matches the section name
688
+ 'General',
689
+ array( $this, 'general_section_text' ), // callback function to explain the section
690
+ 'ctf_options_general_style' // matches the section name
691
+ );
692
+
693
+ // background color
694
+ $this->create_settings_field( array(
695
+ 'name' => 'bgcolor',
696
+ 'title' => '<label for="ctf_bgcolor">Feed Background Color</label><code class="ctf_shortcode">bgcolor
697
+ Eg: bgcolor=#eee</code>', // label for the input field
698
+ 'callback' => 'default_color', // name of the function that outputs the html
699
+ 'page' => 'ctf_options_general_style', // matches the section name
700
+ 'section' => 'ctf_options_general_style', // matches the section name
701
+ 'option' => 'ctf_options', // matches the options name
702
+ 'class' => '',
703
+ 'whatis' => "The background color of the feed"
704
+ ));
705
+
706
+ // tweet background color
707
+ $this->create_settings_field( array(
708
+ 'name' => 'tweetbgcolor',
709
+ 'title' => '<label for="ctf_tweetbgcolor">Tweet Background Color</label><code class="ctf_shortcode">tweetbgcolor
710
+ Eg: tweetbgcolor=#eee</code>', // label for the input field
711
+ 'callback' => 'default_color', // name of the function that outputs the html
712
+ 'page' => 'ctf_options_general_style', // matches the section name
713
+ 'section' => 'ctf_options_general_style', // matches the section name
714
+ 'option' => 'ctf_options', // matches the options name
715
+ 'class' => '',
716
+ 'whatis' => "The background color of each tweet"
717
+ ));
718
+
719
+ add_settings_section(
720
+ 'ctf_options_header', // matches the section name
721
+ 'Header',
722
+ array( $this, 'general_section_text' ), // callback function to explain the section
723
+ 'ctf_options_header' // matches the section name
724
+ );
725
+
726
+ // show bio
727
+ $this->create_settings_field( array(
728
+ 'name' => 'showbio',
729
+ 'title' => '<label for="ctf_showbio">Show Bio</label><code class="ctf_shortcode">showbio
730
+ Eg: showbio=false</code>', // label for the input field
731
+ 'callback' => 'reverse_checkbox', // name of the function that outputs the html
732
+ 'page' => 'ctf_options_header', // matches the section name
733
+ 'section' => 'ctf_options_header', // matches the section name
734
+ 'option' => 'ctf_options', // matches the options name
735
+ 'class' => 'default-text', // class for the wrapper and input field
736
+ 'whatis' => "Show the bio text description on the header of the feed"
737
+ ));
738
+
739
+ // header background color
740
+ $this->create_settings_field( array(
741
+ 'name' => 'headerbgcolor',
742
+ 'title' => '<label for="ctf_headerbgcolor">Header Background Color</label><code class="ctf_shortcode">headerbgcolor
743
+ Eg: headerbgcolor=#ee0</code>', // label for the input field
744
+ 'callback' => 'default_color', // name of the function that outputs the html
745
+ 'page' => 'ctf_options_header', // matches the section name
746
+ 'section' => 'ctf_options_header', // matches the section name
747
+ 'option' => 'ctf_options', // matches the options name
748
+ 'class' => ''
749
+ ));
750
+
751
+ // header text color
752
+ $this->create_settings_field( array(
753
+ 'name' => 'headertextcolor',
754
+ 'title' => '<label for="ctf_headertextcolor">Header Text Color</label><code class="ctf_shortcode">headertextcolor
755
+ Eg: headertextcolor=#444</code>', // label for the input field
756
+ 'callback' => 'default_color', // name of the function that outputs the html
757
+ 'page' => 'ctf_options_header', // matches the section name
758
+ 'section' => 'ctf_options_header', // matches the section name
759
+ 'option' => 'ctf_options', // matches the options name
760
+ 'class' => ''
761
+ ));
762
+
763
+
764
+ // custom header text
765
+ $this->create_settings_field( array(
766
+ 'name' => 'headertext',
767
+ 'title' => '<label for="ctf_headertext">Custom Header Text</label><code class="ctf_shortcode">headertext
768
+ Eg: headertext="Tweets from @SmashBalloon"</code>', // label for the input field
769
+ 'callback' => 'default_text', // name of the function that outputs the html
770
+ 'page' => 'ctf_options_header', // matches the section name
771
+ 'section' => 'ctf_options_header', // matches the section name
772
+ 'option' => 'ctf_options', // matches the options name
773
+ 'class' => 'default-text', // class for the wrapper and input field
774
+ 'whatis' => 'This will replace the default text displayed inside the optional header of the feed' // "what is this?" text
775
+ ));
776
+
777
+ add_settings_section(
778
+ 'ctf_options_date', // matches the section name
779
+ 'Date',
780
+ array( $this, 'general_section_text' ), // callback function to explain the section
781
+ 'ctf_options_date' // matches the section name
782
+ );
783
+
784
+ // Timezone
785
+ $this->create_settings_field( array(
786
+ 'name' => 'timezone',
787
+ 'title' => '<label for="ctf_timezone">Timezone</label>', // label for the input field
788
+ 'callback' => 'feed_settings_timezone', // name of the function that outputs the html
789
+ 'page' => 'ctf_options_date', // matches the section name
790
+ 'section' => 'ctf_options_date', // matches the section name
791
+ 'option' => 'ctf_options', // matches the options name
792
+ 'class' => 'default-text', // class for the wrapper and input field
793
+ 'whatis' => "Select a timezone for displaying date and timestamps of tweets" // what is this? text
794
+ ));
795
+
796
+ // Date Format
797
+ $this->create_settings_field( array(
798
+ 'name' => 'dateformat',
799
+ 'title' => '<label for="ctf_date_format">Date Format</label><code class="ctf_shortcode">dateformat
800
+ Eg: dateformat=3</code>', // label for the input field
801
+ 'callback' => 'customize_date_format', // name of the function that outputs the html
802
+ 'page' => 'ctf_options_date', // matches the section name
803
+ 'section' => 'ctf_options_date', // matches the section name
804
+ 'option' => 'ctf_options', // matches the options name
805
+ 'class' => 'default-text', // class for the wrapper and input field
806
+ 'whatis' => "Select the format you would like for dates in tweets" // what is this? text
807
+ ));
808
+
809
+ // Custom Date Format
810
+ $this->create_settings_field( array(
811
+ 'name' => 'datecustom',
812
+ 'title' => '<label for="ctf_custom_date_format">Custom Format</label><code class="ctf_shortcode">datecustom
813
+ Eg: datecustom="D M jS, Y"</code>', // label for the input field
814
+ 'callback' => 'customize_custom_date_format', // name of the function that outputs the html
815
+ 'page' => 'ctf_options_date', // matches the section name
816
+ 'section' => 'ctf_options_date', // matches the section name
817
+ 'option' => 'ctf_options', // matches the options name
818
+ 'class' => 'default-text', // class for the wrapper and input field
819
+ ));
820
+
821
+ // Custom Time Translations
822
+ $this->create_settings_field( array(
823
+ 'name' => 'custom_time_translations',
824
+ 'title' => '<label>Custom Time Translations</label><code class="ctf_shortcode">mtime, htime,
825
+ nowtime
826
+ Eg: mtime="M"
827
+ Eg: htime="S"
828
+ Eg: nowtime="Jetzt"</code>', // label for the input field
829
+ 'callback' => 'customize_custom_time_translations', // name of the function that outputs the html
830
+ 'page' => 'ctf_options_date', // matches the section name
831
+ 'section' => 'ctf_options_date', // matches the section name
832
+ 'option' => 'ctf_options', // matches the options name
833
+ 'class' => 'default-text', // class for the wrapper and input field
834
+ ));
835
+
836
+ // date Text Size
837
+ $this->create_settings_field( array(
838
+ 'name' => 'datetextsize',
839
+ 'title' => '<label for="ctf_datetextsize">Date Text Size</label><code class="ctf_shortcode">datetextsize
840
+ Eg: datetextsize=16</code>', // label for the input field
841
+ 'callback' => 'text_size', // name of the function that outputs the html
842
+ 'page' => 'ctf_options_date', // matches the section name
843
+ 'section' => 'ctf_options_date', // matches the section name
844
+ 'option' => 'ctf_options', // matches the options name
845
+ 'class' => 'default-text', // class for the wrapper and input field
846
+ ));
847
+
848
+ // date text weight
849
+ $this->create_settings_field( array(
850
+ 'name' => 'datetextweight',
851
+ 'title' => '<label for="ctf_datetextweight">Date Text Weight</label><code class="ctf_shortcode">datetextweight
852
+ Eg: datetextweight=bold</code>', // label for the input field
853
+ 'callback' => 'text_weight', // name of the function that outputs the html
854
+ 'page' => 'ctf_options_date', // matches the section name
855
+ 'section' => 'ctf_options_date', // matches the section name
856
+ 'option' => 'ctf_options', // matches the options name
857
+ 'class' => '',
858
+ ));
859
+
860
+ add_settings_section(
861
+ 'ctf_options_author', // matches the section name
862
+ 'Author',
863
+ array( $this, 'general_section_text' ), // callback function to explain the section
864
+ 'ctf_options_author' // matches the section name
865
+ );
866
+
867
+ // Author Text Size
868
+ $this->create_settings_field( array(
869
+ 'name' => 'authortextsize',
870
+ 'title' => '<label for="ctf_authortextsize">Author Text Size</label><code class="ctf_shortcode">authortextsize
871
+ Eg: authortextsize=16</code>', // label for the input field
872
+ 'callback' => 'text_size', // name of the function that outputs the html
873
+ 'page' => 'ctf_options_author', // matches the section name
874
+ 'section' => 'ctf_options_author', // matches the section name
875
+ 'option' => 'ctf_options', // matches the options name
876
+ 'class' => 'default-text', // class for the wrapper and input field
877
+ ));
878
+
879
+ // author text weight
880
+ $this->create_settings_field( array(
881
+ 'name' => 'authortextweight',
882
+ 'title' => '<label for="ctf_authortextcolor">Author Text Weight</label><code class="ctf_shortcode">authortextweight
883
+ Eg: authortextweight=bold</code>', // label for the input field
884
+ 'callback' => 'text_weight', // name of the function that outputs the html
885
+ 'page' => 'ctf_options_author', // matches the section name
886
+ 'section' => 'ctf_options_author', // matches the section name
887
+ 'option' => 'ctf_options', // matches the options name
888
+ 'class' => '',
889
+ ));
890
+
891
+ $this->create_settings_field( array(
892
+ 'name' => 'logosize',
893
+ 'title' => '<label for="ctf_logotextsize">Twitter Logo Size</label><code class="ctf_shortcode">logosize
894
+ Eg: logosize=16</code>', // label for the input field
895
+ 'callback' => 'text_size', // name of the function that outputs the html
896
+ 'page' => 'ctf_options_author', // matches the section name
897
+ 'section' => 'ctf_options_author', // matches the section name
898
+ 'option' => 'ctf_options', // matches the options name
899
+ 'class' => 'default-text', // class for the wrapper and input field
900
+ ));
901
+
902
+ $this->create_settings_field( array(
903
+ 'name' => 'logocolor',
904
+ 'title' => '<label for="ctf_logocolor">Logo Color</label><code class="ctf_shortcode">logocolor
905
+ Eg: logocolor=#333</code>', // label for the input field
906
+ 'callback' => 'default_color', // name of the function that outputs the html
907
+ 'page' => 'ctf_options_author', // matches the section name
908
+ 'section' => 'ctf_options_author', // matches the section name
909
+ 'option' => 'ctf_options', // matches the options name
910
+ 'class' => '',
911
+ ));
912
+
913
+ add_settings_section(
914
+ 'ctf_options_text', // matches the section name
915
+ 'Tweet Text',
916
+ array( $this, 'general_section_text' ), // callback function to explain the section
917
+ 'ctf_options_text' // matches the section name
918
+ );
919
+
920
+ // Tweet Text Size
921
+ $this->create_settings_field( array(
922
+ 'name' => 'tweettextsize',
923
+ 'title' => '<label for="ctf_tweettextsize">Tweet Text Size</label><code class="ctf_shortcode">tweettextsize
924
+ Eg: tweettextsize=16</code>', // label for the input field
925
+ 'callback' => 'text_size', // name of the function that outputs the html
926
+ 'page' => 'ctf_options_text', // matches the section name
927
+ 'section' => 'ctf_options_text', // matches the section name
928
+ 'option' => 'ctf_options', // matches the options name
929
+ 'class' => 'default-text', // class for the wrapper and input field
930
+ ));
931
+
932
+ // tweet text weight
933
+ $this->create_settings_field( array(
934
+ 'name' => 'tweettextweight',
935
+ 'title' => '<label for="ctf_tweettextweight">Tweet Text Weight</label><code class="ctf_shortcode">tweettextweight
936
+ Eg: tweettextweight=bold</code>', // label for the input field
937
+ 'callback' => 'text_weight', // name of the function that outputs the html
938
+ 'page' => 'ctf_options_text', // matches the section name
939
+ 'section' => 'ctf_options_text', // matches the section name
940
+ 'option' => 'ctf_options', // matches the options name
941
+ 'class' => '',
942
+ ));
943
+
944
+ // text color
945
+ $this->create_settings_field( array(
946
+ 'name' => 'textcolor',
947
+ 'title' => '<label for="ctf_textcolor">Text Color</label><code class="ctf_shortcode">textcolor
948
+ Eg: textcolor=#333</code>', // label for the input field
949
+ 'callback' => 'default_color', // name of the function that outputs the html
950
+ 'page' => 'ctf_options_text', // matches the section name
951
+ 'section' => 'ctf_options_text', // matches the section name
952
+ 'option' => 'ctf_options', // matches the options name
953
+ 'class' => '',
954
+ ));
955
+
956
+ $this->create_settings_field( array(
957
+ 'name' => 'textlength',
958
+ 'title' => '<label for="ctf_textlength">Text Length</label><code class="ctf_shortcode">textlength
959
+ Eg: textlength=150</code>', // label for the input field
960
+ 'callback' => 'default_text', // name of the function that outputs the html
961
+ 'page' => 'ctf_options_text', // matches the section name
962
+ 'section' => 'ctf_options_text', // matches the section name
963
+ 'option' => 'ctf_options', // matches the options name
964
+ 'class' => '',
965
+ 'default' => 280,
966
+ 'min' => 20,
967
+ 'max' => 280,
968
+ 'step' => 1,
969
+ 'example' => 'characters',
970
+ 'type' => 'number',
971
+ 'whatis' => 'The number of characters of text to display in the tweet text. An ellipsis link will be added to allow the user to reveal more text if desired',
972
+ ));
973
+
974
+ // custom retweeted text
975
+ $this->create_settings_field( array(
976
+ 'name' => 'retweetedtext',
977
+ 'title' => '<label for="ctf_retweetedtext">Translation for "Retweeted"</label><code class="ctf_shortcode">retweetedtext
978
+ Eg: retweetedtext="retuiteó"</code>', // label for the input field
979
+ 'callback' => 'default_text', // name of the function that outputs the html
980
+ 'page' => 'ctf_options_text', // matches the section name
981
+ 'section' => 'ctf_options_text', // matches the section name
982
+ 'option' => 'ctf_options', // matches the options name
983
+ 'class' => 'default-text', // class for the wrapper and input field
984
+ 'whatis' => 'This will replace the default text displayed for retweeted texts',
985
+ 'default' => 'Retweeted'// "what is this?" text
986
+ ));
987
+
988
+ add_settings_section(
989
+ 'ctf_options_links', // matches the section name
990
+ 'Links',
991
+ array( $this, 'general_section_text' ), // callback function to explain the section
992
+ 'ctf_options_links' // matches the section name
993
+ );
994
+
995
+ // disable links
996
+ $this->create_settings_field( array(
997
+ 'name' => 'disablelinks',
998
+ 'title' => '<label for="ctf_disablelinks">Disable Links in Tweet Text</label><code class="ctf_shortcode">disablelinks
999
+ Eg: disablelinks=true</code>', // label for the input field
1000
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
1001
+ 'page' => 'ctf_options_links', // matches the section name
1002
+ 'section' => 'ctf_options_links', // matches the section name
1003
+ 'option' => 'ctf_options', // matches the options name
1004
+ 'class' => '',
1005
+ 'whatis' => "By default, links, hashtags, and mentions are turned into links inside the tweet text"
1006
+ ));
1007
+
1008
+ // link text to twitter
1009
+ $this->create_settings_field( array(
1010
+ 'name' => 'linktexttotwitter',
1011
+ 'title' => '<label for="ctf_linktexttotwitter">Link Tweet Text to Twitter</label><code class="ctf_shortcode">linktexttotwitter
1012
+ Eg: linktexttotwitter=true</code>', // label for the input field
1013
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
1014
+ 'page' => 'ctf_options_links', // matches the section name
1015
+ 'section' => 'ctf_options_links', // matches the section name
1016
+ 'option' => 'ctf_options', // matches the options name
1017
+ 'class' => '',
1018
+ 'whatis' => "Clicking on the text of the tweet will link to the tweet on Twitter"
1019
+ ));
1020
+
1021
+ // link text color
1022
+ $this->create_settings_field( array(
1023
+ 'name' => 'linktextcolor',
1024
+ 'title' => '<label for="ctf_linktextcolor">Links in Tweets Text Color</label><code class="ctf_shortcode">linktextcolor
1025
+ Eg: linktextcolor=#00e</code>', // label for the input field
1026
+ 'callback' => 'default_color', // name of the function that outputs the html
1027
+ 'page' => 'ctf_options_links', // matches the section name
1028
+ 'section' => 'ctf_options_links', // matches the section name
1029
+ 'option' => 'ctf_options', // matches the options name
1030
+ 'class' => '',
1031
+ ));
1032
+
1033
+ add_settings_section(
1034
+ 'ctf_options_quoted', // matches the section name
1035
+ 'Retweet Boxes',
1036
+ array( $this, 'general_section_text' ), // callback function to explain the section
1037
+ 'ctf_options_quoted' // matches the section name
1038
+ );
1039
+
1040
+ // quoted author Size
1041
+ $this->create_settings_field( array(
1042
+ 'name' => 'quotedauthorsize',
1043
+ 'title' => '<label for="ctf_quotedauthorsize">Quoted Author Size</label><code class="ctf_shortcode">quotedauthorsize
1044
+ Eg: quotedauthorsize=16</code>', // label for the input field
1045
+ 'callback' => 'text_size', // name of the function that outputs the html
1046
+ 'page' => 'ctf_options_quoted', // matches the section name
1047
+ 'section' => 'ctf_options_quoted', // matches the section name
1048
+ 'option' => 'ctf_options', // matches the options name
1049
+ 'class' => 'default-text', // class for the wrapper and input field
1050
+ ));
1051
+
1052
+ // quoted author weight
1053
+ $this->create_settings_field( array(
1054
+ 'name' => 'quotedauthorweight',
1055
+ 'title' => '<label for="ctf_quotedauthorweight">Quoted Author Weight</label><code class="ctf_shortcode">quotedauthorweight
1056
+ Eg: quotedauthorweight=bold</code>', // label for the input field
1057
+ 'callback' => 'text_weight', // name of the function that outputs the html
1058
+ 'page' => 'ctf_options_quoted', // matches the section name
1059
+ 'section' => 'ctf_options_quoted', // matches the section name
1060
+ 'option' => 'ctf_options', // matches the options name
1061
+ 'class' => '',
1062
+ ));
1063
+
1064
+ add_settings_section(
1065
+ 'ctf_options_actions', // matches the section name
1066
+ 'Tweets Actions',
1067
+ array( $this, 'general_section_text' ), // callback function to explain the section
1068
+ 'ctf_options_actions' // matches the section name
1069
+ );
1070
+
1071
+ // icon Size
1072
+ $this->create_settings_field( array(
1073
+ 'name' => 'iconsize',
1074
+ 'title' => '<label for="ctf_iconsize">Icon Size</label><code class="ctf_shortcode">iconsize
1075
+ Eg: iconsize=16</code>', // label for the input field
1076
+ 'callback' => 'text_size', // name of the function that outputs the html
1077
+ 'page' => 'ctf_options_actions', // matches the section name
1078
+ 'section' => 'ctf_options_actions', // matches the section name
1079
+ 'option' => 'ctf_options', // matches the options name
1080
+ 'class' => 'default-text', // class for the wrapper and input field
1081
+ ));
1082
+
1083
+ // icon color
1084
+ $this->create_settings_field( array(
1085
+ 'name' => 'iconcolor',
1086
+ 'title' => '<label for="ctf_iconcolor">Icon Color</label><code class="ctf_shortcode">iconcolor
1087
+ Eg: iconcolor=green</code>', // label for the input field
1088
+ 'callback' => 'default_color', // name of the function that outputs the html
1089
+ 'page' => 'ctf_options_actions', // matches the section name
1090
+ 'section' => 'ctf_options_actions', // matches the section name
1091
+ 'option' => 'ctf_options', // matches the options name
1092
+ 'class' => '',
1093
+ ));
1094
+
1095
+
1096
+ // view on twitter text
1097
+ $this->create_settings_field( array(
1098
+ 'name' => 'twitterlinktext',
1099
+ 'title' => '<label for="ctf_twitterlinktext">Custom Text for "Twitter" Link</label><code class="ctf_shortcode">twitterlinktext
1100
+ Eg: twitterlinktext="View this Tweet"</code>', // label for the input field
1101
+ 'callback' => 'default_text', // name of the function that outputs the html
1102
+ 'page' => 'ctf_options_actions', // matches the section name
1103
+ 'section' => 'ctf_options_actions', // matches the section name
1104
+ 'option' => 'ctf_options', // matches the options name
1105
+ 'class' => 'default-text', // class for the wrapper and input field
1106
+ 'default' => 'Twitter'
1107
+ ));
1108
+
1109
+ add_settings_section(
1110
+ 'ctf_options_load', // matches the section name
1111
+ '"Load More" Button',
1112
+ array( $this, 'general_section_text' ), // callback function to explain the section
1113
+ 'ctf_options_load' // matches the section name
1114
+ );
1115
+
1116
+ // button background color
1117
+ $this->create_settings_field( array(
1118
+ 'name' => 'buttoncolor',
1119
+ 'title' => '<label for="ctf_buttoncolor">Button Background Color</label><code class="ctf_shortcode">buttoncolor
1120
+ Eg: buttoncolor=#f33</code>', // label for the input field
1121
+ 'callback' => 'default_color', // name of the function that outputs the html
1122
+ 'page' => 'ctf_options_load', // matches the section name
1123
+ 'section' => 'ctf_options_load', // matches the section name
1124
+ 'option' => 'ctf_options', // matches the options name
1125
+ 'class' => '',
1126
+ 'whatis' => "The color of the background of the load more button"
1127
+ ));
1128
+
1129
+ // button text color
1130
+ $this->create_settings_field( array(
1131
+ 'name' => 'buttontextcolor',
1132
+ 'title' => '<label for="ctf_buttontextcolor">Button Text Color</label><code class="ctf_shortcode">buttontextcolor
1133
+ Eg: buttontextcolor=#444</code>', // label for the input field
1134
+ 'callback' => 'default_color', // name of the function that outputs the html
1135
+ 'page' => 'ctf_options_load', // matches the section name
1136
+ 'section' => 'ctf_options_load', // matches the section name
1137
+ 'option' => 'ctf_options', // matches the options name
1138
+ 'class' => '',
1139
+ 'whatis' => "The color of the text of the load more button"
1140
+ ));
1141
+
1142
+ // button text
1143
+ $this->create_settings_field( array(
1144
+ 'name' => 'buttontext',
1145
+ 'title' => '<label for="ctf_buttontext">Button Text</label><code class="ctf_shortcode">buttontext
1146
+ Eg: buttontext="More"</code>', // label for the input field
1147
+ 'callback' => 'default_text', // name of the function that outputs the html
1148
+ 'page' => 'ctf_options_load', // matches the section name
1149
+ 'section' => 'ctf_options_load', // matches the section name
1150
+ 'option' => 'ctf_options', // matches the options name
1151
+ 'class' => 'default-text', // class for the wrapper and input field
1152
+ 'default' => 'Load More...'
1153
+ ));
1154
+
1155
+ do_action( 'ctf_admin_style_option', $this );
1156
+ }
1157
+
1158
+ public function create_settings_field( $args=array() )
1159
+ {
1160
+ add_settings_field(
1161
+ $args['name'],
1162
+ $args['title'],
1163
+ array( $this, $args['callback'] ),
1164
+ $args['page'],
1165
+ $args['section'],
1166
+ $args
1167
+ );
1168
+ }
1169
+
1170
+ public function default_text( $args )
1171
+ {
1172
+ $options = get_option( $args['option'] );
1173
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1174
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1175
+ $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1176
+ $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1177
+ $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1178
+ $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1179
+ $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1180
+ ?>
1181
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1182
+ <?php if ( isset( $args['example'] ) ) : ?>
1183
+ <span><?php echo $args['example']; ?></span>
1184
+ <?php endif; ?>
1185
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1186
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1187
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1188
+ <?php endif; ?>
1189
+ <?php
1190
+ }
1191
+
1192
+ public function default_select( $args )
1193
+ {
1194
+ $options = get_option( $args['option'] );
1195
+ $selected = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1196
+ ?>
1197
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>">
1198
+ <?php foreach ( $args['fields'] as $field ) : ?>
1199
+ <option value="<?php echo $field[0]; ?>" id="ctf-<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php if( $selected == $field[0] ) { echo ' selected'; } ?>><?php _e( $field[1], 'custom-twitter-feeds' ); ?></option>
1200
+ <?php endforeach; ?>
1201
+ </select>
1202
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1203
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1204
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1205
+ <?php endif; ?>
1206
+ <?php
1207
+ }
1208
+
1209
+ public function default_color( $args )
1210
+ {
1211
+ $options = get_option( $args['option'] );
1212
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1213
+ ?>
1214
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" value="#<?php esc_attr_e( str_replace('#', '', $option_string ) ); ?>" class="ctf-colorpicker" />
1215
+ <?php
1216
+ }
1217
+
1218
+ public function default_checkbox( $args )
1219
+ {
1220
+ $options = get_option( $args['option'] );
1221
+ $option_checked = ( isset( $options[ $args['name'] ] ) ) ? $options[ $args['name'] ] : false;
1222
+ ?>
1223
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1224
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1225
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1226
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1227
+ <?php endif; ?>
1228
+ <?php
1229
+ }
1230
+
1231
+ public function reverse_checkbox( $args )
1232
+ {
1233
+ $options = get_option( $args['option'] );
1234
+ $option_checked = isset( $options[ $args['name'] ] ) ? $options[ $args['name'] ] : true;
1235
+ ?>
1236
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="checkbox" <?php if ( $option_checked ) echo "checked"; ?> />
1237
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1238
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1239
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1240
+ <?php endif; ?>
1241
+ <?php
1242
+ }
1243
+
1244
+ public function access_token_secret( $args )
1245
+ {
1246
+ $options = get_option( $args['option'] );
1247
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1248
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1249
+ $option_checked = ( isset( $options['use_own_consumer'] ) ) ? $options['use_own_consumer'] : false;
1250
+ $type = ( isset( $args['type'] ) ) ? ' type="'. $args['type'].'"' : ' type="text"';
1251
+ $size = ( isset( $args['size'] ) ) ? ' size="'. $args['size'].'"' : '';
1252
+ $min = ( isset( $args['min'] ) ) ? ' min="'. $args['min'].'"' : '';
1253
+ $max = ( isset( $args['max'] ) ) ? ' max="'. $args['max'].'"' : '';
1254
+ $step = ( isset( $args['step'] ) ) ? ' step="'. $args['step'].'"' : '';
1255
+ ?>
1256
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>"<?php echo $type; ?><?php echo $size; ?><?php echo $min; ?><?php echo $max; ?><?php echo $step; ?> value="<?php echo $option_string; ?>" />
1257
+ <?php if ( isset( $args['example'] ) ) : ?>
1258
+ <span><?php echo $args['example']; ?></span>
1259
+ <?php endif; ?>
1260
+
1261
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1262
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1263
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1264
+ <?php endif; ?>
1265
+
1266
+ <?php
1267
+ }
1268
+
1269
+ public function feed_settings_radio( $args )
1270
+ {
1271
+ $options = get_option( $args['option'] );
1272
+ $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1273
+ $show_replies = ( isset( $options[ $args['name'].'_includereplies' ] ) ) ? $options[ $args['name'].'_includereplies' ] : false;
1274
+ $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1275
+ ?>
1276
+ <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1277
+ <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php _e( $args['label'], 'custom-twitter-feeds' ); ?></label>
1278
+ <?php if ( $args['has_input'] ) : ?>
1279
+ <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1280
+ <?php endif; ?>
1281
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1282
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1283
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1284
+ <?php endif; ?>
1285
+ <?php if ( $args['has_replies'] ) : ?>
1286
+ <span class="ctf_include_replies_toggle ctf_pro">
1287
+ <input name="<?php echo $args['option'].'['.$args['name'].'_includereplies]'; ?>" id="ctf_include_replies" type="checkbox" <?php if ( $show_replies ) echo "checked"; ?> />
1288
+ <label class="ctf-radio-label" for="ctf_include_replies"><?php _e( 'Include replies', 'custom-twitter-feeds' ); ?></label>
1289
+ <?php do_action( 'ctf_admin_upgrade_note' ); ?>
1290
+ </span>
1291
+ <?php endif; ?>
1292
+ <?php
1293
+ do_action( 'ctf_admin_feed_settings_radio_extra', $args );
1294
+ }
1295
+
1296
+ public function feed_settings_radio_search( $args )
1297
+ {
1298
+ $options = get_option( $args['option'] );
1299
+ $option_checked = ( ( ! isset( $options[ 'type' ] ) && $args['name'] == 'usertimeline' ) || ( isset( $options[ 'type' ] ) && $options[ 'type' ] == $args['name'] ) ) ? true : false;
1300
+ $option_string = ( isset( $options[ $args['name'].'_text' ] ) ) ? esc_attr( $options[ $args['name'].'_text' ] ) : '';
1301
+ ?>
1302
+ <input type="radio" name="<?php echo $args['option'].'[type]'; ?>" class="ctf-feed-settings-radio" id="ctf_<?php echo $args['name'].'_radio'; ?>" value="<?php echo $args['name']; ?>" <?php if ( $option_checked ) echo "checked"; ?> />
1303
+ <label class="ctf-radio-label" for="ctf_<?php echo $args['name'].'_radio'; ?>"><?php echo $args['label']; ?></label>
1304
+ <?php if ( $args['has_input'] ) : ?>
1305
+ <input name="<?php echo $args['option'].'['.$args['name'].'_text'.']'; ?>" id="ctf_<?php echo $args['name'].'_text'; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="25" />
1306
+ <?php endif; ?>
1307
+ <?php if ( isset( $args['note_after_input'] ) ) : ?>
1308
+ <span class="ctf-note-after-input"><?php echo esc_attr( $args['note_after_input'] ); ?></span>
1309
+ <?php endif; ?>
1310
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1311
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1312
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1313
+ <?php endif; ?>
1314
+ <?php
1315
+ do_action( 'ctf_admin_feed_settings_search_extra' );
1316
+ }
1317
+
1318
+ public function width_and_height_settings( $args )
1319
+ {
1320
+ $options = get_option( $args['option'] );
1321
+ $default = isset( $args['default'] ) ? $args['default'] : '';
1322
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1323
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : $args['default_unit'];
1324
+ ?>
1325
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1326
+ <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>" id="ctf_<?php echo $args['name'].'_unit'; ?>">
1327
+ <option value="px" <?php if ( $selected == "px" ) echo 'selected="selected"' ?> >px</option>
1328
+ <option value="%" <?php if ( $selected == "%" ) echo 'selected="selected"' ?> >%</option>
1329
+ </select>
1330
+
1331
+ <?php if ( $args['name'] == 'width' ) :
1332
+ $checked = ( isset( $options[ $args['name'] . '_mobile_no_fixed' ] ) ) ? esc_attr( $options[ $args['name'] . '_mobile_no_fixed' ] ) : false; ?>
1333
+ <div id="ctf_width_options">
1334
+ <input name="<?php echo $args['option'].'[width_mobile_no_fixed]'; ?>" type="checkbox" id="ctf_width_mobile_no_fixed" <?php if ( $checked == true ) { echo "checked"; }?> /><label for="ctf_width_mobile_no_fixed"><?php _e('Set to be 100% width on mobile?', 'custom-twitter-feeds'); ?></label>
1335
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><?php _e('What does this mean?', 'custom-facebook-feed'); ?></a>
1336
+ <p class="ctf-tooltip ctf-more-info"><?php _e("If you set a width on the feed then this will be used on mobile as well as desktop. Check this setting to set the feed width to be 100% on mobile so that it is responsive.", 'custom-twitter-feeds'); ?></p>
1337
+ </div>
1338
+ <?php endif; ?>
1339
+ <?php
1340
+ }
1341
+
1342
+ public function cache_time( $args )
1343
+ {
1344
+ $min_cache_time = 3600;
1345
+ $options = get_option( $args['option'] );
1346
+ $unrestricted = isset( $options['have_own_tokens'] ) && $options['have_own_tokens'];
1347
+ if ( $unrestricted ) {
1348
+ $min_cache_time = 1;
1349
+ }
1350
+ $default = 3;
1351
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : $default;
1352
+
1353
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1354
+ $actual_time = (int)$option_string * (int)$selected;
1355
+ $show_notice = false;
1356
+ if ( get_transient( 'ct_feed_forced_cache_time_raise' ) ) {
1357
+ $show_notice = true;
1358
+ }
1359
+ if ( $actual_time < $min_cache_time ) {
1360
+ set_transient( 'ct_feed_forced_cache_time_raise', 'true', 60 * 60 * 48 );
1361
+ $show_notice = true;
1362
+ $option_string = max( 1, $min_cache_time / 3600 );
1363
+ $selected = "3600";
1364
+ } else {
1365
+ $selected = ( isset( $options[ $args['name'] . '_unit' ] ) ) ? esc_attr( $options[ $args['name'] . '_unit' ] ) : '3600';
1366
+ }
1367
+
1368
+ ?>
1369
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" class="<?php echo $args['class']; ?>" type="number" value="<?php echo $option_string; ?>" />
1370
+ <select name="<?php echo $args['option'].'['.$args['name'].'_unit]'; ?>">
1371
+ <?php if ( $unrestricted ) : ?>
1372
+ <option value="60" <?php if ( $selected == "60" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Minutes' ); ?></option>
1373
+ <?php endif; ?>
1374
+ <option value="3600" <?php if ( $selected == "3600" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Hours' ); ?></option>
1375
+ <option value="86400" <?php if ( $selected == "86400" ) echo 'selected="selected"' ?> ><?php esc_attr_e( 'Days' ); ?></option>
1376
+ </select>&nbsp;
1377
+ <input id="ctf-clear-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cache' ); ?>" />
1378
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1379
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your Twitter feeds', 'custom-twitter-feeds' ); ?>.</p>
1380
+ <?php if ( $show_notice ) : ?>
1381
+ <p class="ctf-more-info"><?php _e( 'Due to changes in Twitter\'s API usage policy, the minimum caching time for the plugin is 1 hour. To check for Tweets more often either create your own Twitter Developer app or upgrade to the Pro version.', 'custom-twitter-feeds' ); ?></p>
1382
+ <?php endif; ?>
1383
+ <?php
1384
+ }
1385
+
1386
+ public function customize_date_format( $args )
1387
+ {
1388
+ $options = get_option( $args['option'] );
1389
+ $ctf_date_formatting = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1390
+ $original = strtotime( '2016-02-25T17:30:00+0000' );
1391
+ ?>
1392
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1393
+ <option value="1" <?php if ( $ctf_date_formatting == "1" ) echo 'selected="selected"'; ?> ><?php _e( '2h / 25 Feb' ); ?></option>
1394
+ <option value="2" <?php if ( $ctf_date_formatting == "2" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j', $original ); ?></option>
1395
+ <option value="3" <?php if ( $ctf_date_formatting == "3" ) echo 'selected="selected"'; ?> ><?php echo date( 'F j, Y', $original ); ?></option>
1396
+ <option value="4" <?php if ( $ctf_date_formatting == "4" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d', $original ); ?></option>
1397
+ <option value="5" <?php if ( $ctf_date_formatting == "5" ) echo 'selected="selected"'; ?> ><?php echo date( 'm.d.y', $original ); ?></option>
1398
+ </select>
1399
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1400
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1401
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1402
+ <?php endif; ?>
1403
+ <?php
1404
+ }
1405
+
1406
+ public function customize_custom_date_format( $args )
1407
+ {
1408
+ $options = get_option( $args['option'] );
1409
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1410
+ ?>
1411
+ <input name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" type="text" value="<?php esc_attr_e( $option_string ); ?>" size="10" placeholder="Eg. F jS, Y" />
1412
+ <a href="https://smashballoon.com/custom-twitter-feeds/docs/date/" class="cff-external-link" target="_blank"><?php _e( 'Examples' , 'custom-twitter-feeds'); ?></a>
1413
+ <?php
1414
+ }
1415
+
1416
+ public function customize_custom_time_translations( $args )
1417
+ {
1418
+ $options = get_option( $args['option'] );
1419
+ $option_m = ( isset( $options['mtime'] ) ) ? esc_attr( $options['mtime'] ) : '';
1420
+ $option_h = ( isset( $options['htime'] ) ) ? esc_attr( $options['htime'] ) : '';
1421
+ $option_now = ( isset( $options['nowtime'] ) ) ? esc_attr( $options['nowtime'] ) : '';
1422
+
1423
+ ?>
1424
+ <input name="<?php echo $args['option'].'[mtime]'; ?>" id="ctf_translate_minute" type="text" value="<?php esc_attr_e( $option_m ); ?>" size="5" />
1425
+ <label for=ctf_translate_minute"><?php _e( 'translation for "m" (minutes)', 'custom-twitter-feeds' ); ?></label><br>
1426
+ <input name="<?php echo $args['option'].'[htime]'; ?>" id="ctf_translate_hour" type="text" value="<?php esc_attr_e( $option_h ); ?>" size="5" />
1427
+ <label for=ctf_translate_hour"><?php _e( 'translation for "h" (hours)', 'custom-twitter-feeds' ); ?></label><br>
1428
+ <input name="<?php echo $args['option'].'[nowtime]'; ?>" id="ctf_translate_now" type="text" value="<?php esc_attr_e( $option_now ); ?>" size="5" />
1429
+ <label for=ctf_translate_now"><?php _e( 'translation for "now"', 'custom-twitter-feeds' ); ?></label><br>
1430
+ <?php
1431
+ }
1432
+
1433
+ public function include_exclude_checkbox( $args )
1434
+ {
1435
+ $options = get_option( $args['option'] );
1436
+ foreach ( $args['fields'] as $field ) {
1437
+ $option_checked = isset( $options[$field[0]] ) ? $options[$field[0]] : true;
1438
+ ?>
1439
+ <input name="<?php echo $args['option'] . '[' . $field[0] . ']'; ?>"
1440
+ id="ctf_<?php echo $field[0]; ?>" type="checkbox"
1441
+ <?php if ( $option_checked ) {
1442
+ echo "checked";
1443
+ } ?> />
1444
+ <label for=ctf_<?php echo $field[0]; ?>"><?php _e( $field[1], 'custom-twitter-feeds' ); ?></label><br>
1445
+ <?php
1446
+ } // end foreach
1447
+ }
1448
+
1449
+ public function text_size( $args )
1450
+ {
1451
+ $options = get_option( $args['option'] );
1452
+ $ctf_text_size = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1453
+ ?>
1454
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1455
+ <option value="inherit" <?php if ( $ctf_text_size == "inherit" ) echo 'selected="selected"' ?> >Inherit</option>
1456
+ <option value="10" <?php if ( $ctf_text_size == "10" ) echo 'selected="selected"' ?> >10px</option>
1457
+ <option value="11" <?php if ( $ctf_text_size == "11" ) echo 'selected="selected"' ?> >11px</option>
1458
+ <option value="12" <?php if ( $ctf_text_size == "12" ) echo 'selected="selected"' ?> >12px</option>
1459
+ <option value="13" <?php if ( $ctf_text_size == "13" ) echo 'selected="selected"' ?> >13px</option>
1460
+ <option value="14" <?php if ( $ctf_text_size == "14" ) echo 'selected="selected"' ?> >14px</option>
1461
+ <option value="16" <?php if ( $ctf_text_size == "16" ) echo 'selected="selected"' ?> >16px</option>
1462
+ <option value="18" <?php if ( $ctf_text_size == "18" ) echo 'selected="selected"' ?> >18px</option>
1463
+ <option value="20" <?php if ( $ctf_text_size == "20" ) echo 'selected="selected"' ?> >20px</option>
1464
+ <option value="24" <?php if ( $ctf_text_size == "24" ) echo 'selected="selected"' ?> >24px</option>
1465
+ <option value="28" <?php if ( $ctf_text_size == "28" ) echo 'selected="selected"' ?> >28px</option>
1466
+ <option value="32" <?php if ( $ctf_text_size == "32" ) echo 'selected="selected"' ?> >32px</option>
1467
+ <option value="36" <?php if ( $ctf_text_size == "36" ) echo 'selected="selected"' ?> >36px</option>
1468
+ <option value="42" <?php if ( $ctf_text_size == "42" ) echo 'selected="selected"' ?> >42px</option>
1469
+ <option value="48" <?php if ( $ctf_text_size == "48" ) echo 'selected="selected"' ?> >48px</option>
1470
+ <option value="54" <?php if ( $ctf_text_size == "54" ) echo 'selected="selected"' ?> >54px</option>
1471
+ <option value="60" <?php if ( $ctf_text_size == "60" ) echo 'selected="selected"' ?> >60px</option>
1472
+ </select>
1473
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1474
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1475
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1476
+ <?php endif; ?>
1477
+ <?php
1478
+ }
1479
+
1480
+ public function text_weight( $args )
1481
+ {
1482
+ $options = get_option( $args['option'] );
1483
+ $ctf_text_weight = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1484
+ ?>
1485
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>">
1486
+ <option value="inherit" <?php if ( $ctf_text_weight == "inherit" ) echo 'selected="selected"'; ?> >Inherit</option>
1487
+ <option value="normal" <?php if ( $ctf_text_weight == "normal" ) echo 'selected="selected"'; ?> >Normal</option>
1488
+ <option value="bold" <?php if ( $ctf_text_weight == "bold" ) echo 'selected="selected"'; ?> >Bold</option>
1489
+ </select>
1490
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1491
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1492
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1493
+ <?php endif; ?>
1494
+ <?php
1495
+ }
1496
+
1497
+ public function feed_settings_timezone( $args )
1498
+ {
1499
+ $options = get_option( $args['option'] );
1500
+ $ctf_timezone = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1501
+ ?>
1502
+ <select name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 300px;">
1503
+ <option value="default" <?php if( $ctf_timezone == "default" ) echo 'selected="selected"' ?> ><?php _e( 'default from Twitter' ) ?></option>
1504
+ <option value="Pacific/Midway" <?php if( $ctf_timezone == "Pacific/Midway" ) echo 'selected="selected"' ?> ><?php _e( '(GMT11:00) Midway Island, Samoa' ) ?></option>
1505
+ <option value="America/Adak" <?php if( $ctf_timezone == "America/Adak" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) HawaiiAleutian' ) ?></option>
1506
+ <option value="Etc/GMT+10" <?php if( $ctf_timezone == "Etc/GMT+10" ) echo 'selected="selected"' ?> ><?php _e( '(GMT10:00) Hawaii' ) ?></option>
1507
+ <option value="Pacific/Marquesas" <?php if( $ctf_timezone == "Pacific/Marquesas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:30) Marquesas Islands' ) ?></option>
1508
+ <option value="Pacific/Gambier" <?php if( $ctf_timezone == "Pacific/Gambier" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Gambier Islands' ) ?></option>
1509
+ <option value="America/Anchorage" <?php if( $ctf_timezone == "America/Anchorage" ) echo 'selected="selected"' ?> ><?php _e( '(GMT09:00) Alaska' ) ?></option>
1510
+ <option value="America/Ensenada" <?php if( $ctf_timezone == "America/Ensenada" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Tijuana, Baja California' ) ?></option>
1511
+ <option value="Etc/GMT+8" <?php if( $ctf_timezone == "Etc/GMT+8" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pitcairn Islands' ) ?></option>
1512
+ <option value="America/Los_Angeles" <?php if( $ctf_timezone == "America/Los_Angeles" ) echo 'selected="selected"' ?> ><?php _e( '(GMT08:00) Pacific Time (US & Canada)' ) ?></option>
1513
+ <option value="America/Denver" <?php if( $ctf_timezone == "America/Denver" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Mountain Time (US & Canada)' ) ?></option>
1514
+ <option value="America/Chihuahua" <?php if( $ctf_timezone == "America/Chihuahua" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Chihuahua, La Paz, Mazatlan' ) ?></option>
1515
+ <option value="America/Dawson_Creek" <?php if( $ctf_timezone == "America/Dawson_Creek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT07:00) Arizona' ) ?></option>
1516
+ <option value="America/Belize" <?php if( $ctf_timezone == "America/Belize" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Saskatchewan, Central America' ) ?></option>
1517
+ <option value="America/Cancun" <?php if( $ctf_timezone == "America/Cancun" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Guadalajara, Mexico City, Monterrey' ) ?></option>
1518
+ <option value="Chile/EasterIsland" <?php if( $ctf_timezone == "Chile/EasterIsland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Easter Island' ) ?></option>
1519
+ <option value="America/Chicago" <?php if( $ctf_timezone == "America/Chicago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT06:00) Central Time (US & Canada)' ) ?></option>
1520
+ <option value="America/New_York" <?php if( $ctf_timezone == "America/New_York" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Eastern Time (US & Canada)' ) ?></option>
1521
+ <option value="America/Havana" <?php if( $ctf_timezone == "America/Havana" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Cuba' ) ?></option>
1522
+ <option value="America/Bogota" <?php if( $ctf_timezone == "America/Bogota" ) echo 'selected="selected"' ?> ><?php _e( '(GMT05:00) Bogota, Lima, Quito, Rio Branco' ) ?></option>
1523
+ <option value="America/Caracas" <?php if( $ctf_timezone == "America/Caracas" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:30) Caracas' ) ?></option>
1524
+ <option value="America/Santiago" <?php if( $ctf_timezone == "America/Santiago" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Santiago' ) ?></option>
1525
+ <option value="America/La_Paz" <?php if( $ctf_timezone == "America/La_Paz" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) La Paz' ) ?></option>
1526
+ <option value="Atlantic/Stanley" <?php if( $ctf_timezone == "Atlantic/Stanley" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Faukland Islands' ) ?></option>
1527
+ <option value="America/Campo_Grande" <?php if( $ctf_timezone == "America/Campo_Grande" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Brazil' ) ?></option>
1528
+ <option value="America/Goose_Bay" <?php if( $ctf_timezone == "America/Goose_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Goose Bay)' ) ?></option>
1529
+ <option value="America/Glace_Bay" <?php if( $ctf_timezone == "America/Glace_Bay" ) echo 'selected="selected"' ?> ><?php _e( '(GMT04:00) Atlantic Time (Canada)' ) ?></option>
1530
+ <option value="America/St_Johns" <?php if( $ctf_timezone == "America/St_Johns" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:30) Newfoundland' ) ?></option>
1531
+ <option value="America/Araguaina" <?php if( $ctf_timezone == "America/Araguaina" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) UTC3' ) ?></option>
1532
+ <option value="America/Montevideo" <?php if( $ctf_timezone == "America/Montevideo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Montevideo' ) ?></option>
1533
+ <option value="America/Miquelon" <?php if( $ctf_timezone == "America/Miquelon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Miquelon, St. Pierre' ) ?></option>
1534
+ <option value="America/Godthab" <?php if( $ctf_timezone == "America/Godthab" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Greenland' ) ?></option>
1535
+ <option value="America/Argentina/Buenos_Aires" <?php if( $ctf_timezone == "America/Argentina/Buenos_Aires" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Buenos Aires' ) ?></option>
1536
+ <option value="America/Sao_Paulo" <?php if( $ctf_timezone == "America/Sao_Paulo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT03:00) Brasilia' ) ?></option>
1537
+ <option value="America/Noronha" <?php if( $ctf_timezone == "America/Noronha" ) echo 'selected="selected"' ?> ><?php _e( '(GMT02:00) MidAtlantic' ) ?></option>
1538
+ <option value="Atlantic/Cape_Verde" <?php if( $ctf_timezone == "Atlantic/Cape_Verde" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Cape Verde Is.' ) ?></option>
1539
+ <option value="Atlantic/Azores" <?php if( $ctf_timezone == "Atlantic/Azores" ) echo 'selected="selected"' ?> ><?php _e( '(GMT01:00) Azores' ) ?></option>
1540
+ <option value="Europe/Belfast" <?php if( $ctf_timezone == "Europe/Belfast" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Belfast' ) ?></option>
1541
+ <option value="Europe/Dublin" <?php if( $ctf_timezone == "Europe/Dublin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Dublin' ) ?></option>
1542
+ <option value="Europe/Lisbon" <?php if( $ctf_timezone == "Europe/Lisbon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : Lisbon' ) ?></option>
1543
+ <option value="Europe/London" <?php if( $ctf_timezone == "Europe/London" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Greenwich Mean Time : London' ) ?></option>
1544
+ <option value="Africa/Abidjan" <?php if( $ctf_timezone == "Africa/Abidjan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT) Monrovia, Reykjavik' ) ?></option>
1545
+ <option value="Europe/Amsterdam" <?php if( $ctf_timezone == "Europe/Amsterdam" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' ) ?></option>
1546
+ <option value="Europe/Belgrade" <?php if( $ctf_timezone == "Europe/Belgrade" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague' ) ?></option>
1547
+ <option value="Europe/Brussels" <?php if( $ctf_timezone == "Europe/Brussels" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Brussels, Copenhagen, Madrid, Paris' ) ?></option>
1548
+ <option value="Africa/Algiers" <?php if( $ctf_timezone == "Africa/Algiers" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) West Central Africa' ) ?></option>
1549
+ <option value="Africa/Windhoek" <?php if( $ctf_timezone == "Africa/Windhoek" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+01:00) Windhoek' ) ?></option>
1550
+ <option value="Asia/Beirut" <?php if( $ctf_timezone == "Asia/Beirut" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Beirut' ) ?></option>
1551
+ <option value="Africa/Cairo" <?php if( $ctf_timezone == "Africa/Cairo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Cairo' ) ?></option>
1552
+ <option value="Asia/Gaza" <?php if( $ctf_timezone == "Asia/Gaza" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Gaza' ) ?></option>
1553
+ <option value="Africa/Blantyre" <?php if( $ctf_timezone == "Africa/Blantyre" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Harare, Pretoria' ) ?></option>
1554
+ <option value="Asia/Jerusalem" <?php if( $ctf_timezone == "Asia/Jerusalem" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Jerusalem' ) ?></option>
1555
+ <option value="Europe/Minsk" <?php if( $ctf_timezone == "Europe/Minsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Minsk' ) ?></option>
1556
+ <option value="Asia/Damascus" <?php if( $ctf_timezone == "Asia/Damascus" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+02:00) Syria' ) ?></option>
1557
+ <option value="Europe/Moscow" <?php if( $ctf_timezone == "Europe/Moscow" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Moscow, St. Petersburg, Volgograd' ) ?></option>
1558
+ <option value="Africa/Addis_Ababa" <?php if( $ctf_timezone == "Africa/Addis_Ababa" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:00) Nairobi' ) ?></option>
1559
+ <option value="Asia/Tehran" <?php if( $ctf_timezone == "Asia/Tehran" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+03:30) Tehran' ) ?></option>
1560
+ <option value="Asia/Dubai" <?php if( $ctf_timezone == "Asia/Dubai" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Abu Dhabi, Muscat' ) ?></option>
1561
+ <option value="Asia/Yerevan" <?php if( $ctf_timezone == "Asia/Yerevan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:00) Yerevan' ) ?></option>
1562
+ <option value="Asia/Kabul" <?php if( $ctf_timezone == "Asia/Kabul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+04:30) Kabul' ) ?></option>
1563
+ <option value="Asia/Yekaterinburg" <?php if( $ctf_timezone == "Asia/Yekaterinburg" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Ekaterinburg' ) ?></option>
1564
+ <option value="Asia/Tashkent" <?php if( $ctf_timezone == "Asia/Tashkent" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:00) Tashkent' ) ?></option>
1565
+ <option value="Asia/Kolkata" <?php if( $ctf_timezone == "Asia/Kolkata" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:30) Chennai, Kolkata, Mumbai, New Delhi' ) ?></option>
1566
+ <option value="Asia/Katmandu" <?php if( $ctf_timezone == "Asia/Katmandu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+05:45) Kathmandu' ) ?></option>
1567
+ <option value="Asia/Dhaka" <?php if( $ctf_timezone == "Asia/Dhaka" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Astana, Dhaka' ) ?></option>
1568
+ <option value="Asia/Novosibirsk" <?php if( $ctf_timezone == "Asia/Novosibirsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:00) Novosibirsk' ) ?></option>
1569
+ <option value="Asia/Rangoon" <?php if( $ctf_timezone == "Asia/Rangoon" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+06:30) Yangon (Rangoon)' ) ?></option>
1570
+ <option value="Asia/Bangkok" <?php if( $ctf_timezone == "Asia/Bangkok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Bangkok, Hanoi, Jakarta' ) ?></option>
1571
+ <option value="Asia/Krasnoyarsk" <?php if( $ctf_timezone == "Asia/Krasnoyarsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+07:00) Krasnoyarsk' ) ?></option>
1572
+ <option value="Asia/Hong_Kong" <?php if( $ctf_timezone == "Asia/Hong_Kong" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Beijing, Chongqing, Hong Kong, Urumqi' ) ?></option>
1573
+ <option value="Asia/Irkutsk" <?php if( $ctf_timezone == "Asia/Irkutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Irkutsk, Ulaan Bataar' ) ?></option>
1574
+ <option value="Australia/Perth" <?php if( $ctf_timezone == "Australia/Perth" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:00) Perth' ) ?></option>
1575
+ <option value="Australia/Eucla" <?php if( $ctf_timezone == "Australia/Eucla" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+08:45) Eucla' ) ?></option>
1576
+ <option value="Asia/Tokyo" <?php if( $ctf_timezone == "Asia/Tokyo" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Osaka, Sapporo, Tokyo' ) ?></option>
1577
+ <option value="Asia/Seoul" <?php if( $ctf_timezone == "Asia/Seoul" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Seoul' ) ?></option>
1578
+ <option value="Asia/Yakutsk" <?php if( $ctf_timezone == "Asia/Yakutsk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:00) Yakutsk' ) ?></option>
1579
+ <option value="Australia/Adelaide" <?php if( $ctf_timezone == "Australia/Adelaide" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Adelaide' ) ?></option>
1580
+ <option value="Australia/Darwin" <?php if( $ctf_timezone == "Australia/Darwin" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+09:30) Darwin' ) ?></option>
1581
+ <option value="Australia/Brisbane" <?php if( $ctf_timezone == "Australia/Brisbane" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Brisbane' ) ?></option>
1582
+ <option value="Australia/Hobart" <?php if( $ctf_timezone == "Australia/Hobart" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Sydney' ) ?></option>
1583
+ <option value="Asia/Vladivostok" <?php if( $ctf_timezone == "Asia/Vladivostok" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:00) Vladivostok' ) ?></option>
1584
+ <option value="Australia/Lord_Howe" <?php if( $ctf_timezone == "Australia/Lord_Howe" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+10:30) Lord Howe Island' ) ?></option>
1585
+ <option value="Etc/GMT11" <?php if( $ctf_timezone == "Etc/GMT11" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Solomon Is., New Caledonia' ) ?></option>
1586
+ <option value="Asia/Magadan" <?php if( $ctf_timezone == "Asia/Magadan" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:00) Magadan' ) ?></option>
1587
+ <option value="Pacific/Norfolk" <?php if( $ctf_timezone == "Pacific/Norfolk" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+11:30) Norfolk Island' ) ?></option>
1588
+ <option value="Asia/Anadyr" <?php if( $ctf_timezone == "Asia/Anadyr" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Anadyr, Kamchatka' ) ?></option>
1589
+ <option value="Pacific/Auckland" <?php if( $ctf_timezone == "Pacific/Auckland" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Auckland, Wellington' ) ?></option>
1590
+ <option value="Etc/GMT12" <?php if( $ctf_timezone == "Etc/GMT12" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:00) Fiji, Kamchatka, Marshall Is.' ) ?></option>
1591
+ <option value="Pacific/Chatham" <?php if( $ctf_timezone == "Pacific/Chatham" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+12:45) Chatham Islands' ) ?></option>
1592
+ <option value="Pacific/Tongatapu" <?php if( $ctf_timezone == "Pacific/Tongatapu" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+13:00) Nuku\'alofa' ) ?></option>
1593
+ <option value="Pacific/Kiritimati" <?php if( $ctf_timezone == "Pacific/Kiritimati" ) echo 'selected="selected"' ?> ><?php _e( '(GMT+14:00) Kiritimati' ) ?></option>
1594
+ </select>
1595
+ <?php if ( isset( $args['whatis'] ) ) : ?>
1596
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1597
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
1598
+ <?php endif; ?>
1599
+ <?php
1600
+ }
1601
+
1602
+ public function layout( $args ) {
1603
+ $selected_type = $args['layout_selections']['layout'];
1604
+ $layout_types = array(
1605
+ 'list' => __( 'List', 'instagram-feed' ),
1606
+ 'carousel' => __( 'Carousel', 'instagram-feed' ),
1607
+ 'masonry' => __( 'Masonry', 'instagram-feed' ),
1608
+ );
1609
+ $layout_images = array(
1610
+ 'list' => CTF_PLUGIN_URL . 'img/list.png',
1611
+ 'carousel' => CTF_PLUGIN_URL . 'img/carousel.png',
1612
+ 'masonry' => CTF_PLUGIN_URL . 'img/masonry.png',
1613
+ );
1614
+ ?>
1615
+
1616
+ <?php foreach( $layout_types as $layout_type => $label ) : ?>
1617
+ <div class="ctf_layout_cell <?php if($selected_type === $layout_type) echo "ctf_layout_selected"; ?>">
1618
+ <input class="ctf_layout_type" id="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>" name="<?php echo $args['option'].'[layout]'; ?>" type="radio" value="<?php esc_attr_e( $layout_type ); ?>" <?php if ( $selected_type === $layout_type ) echo 'checked'; ?>/><label for="ctf_layout_type_<?php esc_attr_e( $layout_type ); ?>"><span class="ctf_label"><?php echo esc_html( $label ); ?></span><img src="<?php echo $layout_images[ $layout_type ]; ?>" /></label>
1619
+
1620
+ <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&amp;utm_campaign=ctf" target="_blank" class="ctf_lock"><i class="fa fa-rocket"></i>Pro</a>
1621
+ </div>
1622
+ <?php endforeach; ?>
1623
+ <div class="ctf_layout_options_wrap">
1624
+ <div class="ctf_layout_settings ctf_layout_type_list">
1625
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('A full-width list of tweets.'); ?></span>
1626
+ </div>
1627
+ <div class="ctf_layout_settings ctf_layout_type_masonry">
1628
+ <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Masonry layout option</a></p>
1629
+ <div class="ctf_layout_setting">
1630
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Tweets in columns with no empty space between them.'); ?></span>
1631
+ </div>
1632
+ <div class="ctf_layout_setting">
1633
+ <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrycols
1634
+ Eg: masonrycols=4</code>
1635
+ <br />
1636
+ <select name="<?php echo $args['option'].'[masonrycols]'; ?>" id="ctf_masonrycols">
1637
+ <?php
1638
+ $cols_options = array(1,2,3,4,5,6);
1639
+ foreach ( $cols_options as $option ) :
1640
+ ?>
1641
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrycols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1642
+ <?php endforeach; ?>
1643
+ </select>
1644
+ </div>
1645
+ <div class="ctf_layout_setting">
1646
+ <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> masonrymobilecols
1647
+ Eg: masonrymobilecols=2</code>
1648
+ <br />
1649
+ <select name="<?php echo $args['option'].'[masonrymobilecols]'; ?>" id="ctf_masonrymobilecols">
1650
+ <?php
1651
+ $cols_options = array(1,2);
1652
+ foreach ( $cols_options as $option ) :
1653
+ ?>
1654
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['masonrymobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1655
+ <?php endforeach; ?>
1656
+ </select>
1657
+ </div>
1658
+ </div>
1659
+ <div class="ctf_layout_settings ctf_layout_type_carousel">
1660
+ <p class="ctf_note" style="margin: 0 0 15px 0;"><a href="">Upgrade to the Pro version to use the Carousel layout option</a></p>
1661
+ <div class="ctf_layout_setting">
1662
+ <i class="fa fa-info-circle" aria-hidden="true" style="margin-right: 8px;"></i><span class="ctf_note" style="margin-left: 0;"><?php _e('Posts are displayed in a slideshow carousel.', 'instagram-feed'); ?></span>
1663
+ </div>
1664
+ <div class="ctf_layout_setting">
1665
+ <label><?php _e('Desktop Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselcols
1666
+ Eg: carouselcols=4</code>
1667
+ <br />
1668
+ <select name="<?php echo $args['option'].'[carouselcols]'; ?>" id="ctf_carouselcols">
1669
+ <?php
1670
+ $cols_options = array(1,2,3,4,5,6);
1671
+ foreach ( $cols_options as $option ) :
1672
+ ?>
1673
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselcols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1674
+ <?php endforeach; ?>
1675
+ </select>
1676
+ </div>
1677
+ <div class="ctf_layout_setting">
1678
+ <label><?php _e('Moblie Columns', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselmobilecols
1679
+ Eg: carouselmobilecols=2</code>
1680
+ <br />
1681
+ <select name="<?php echo $args['option'].'[carouselmobilecols]'; ?>" id="ctf_carouselmobilecols">
1682
+ <?php
1683
+ $cols_options = array(1,2);
1684
+ foreach ( $cols_options as $option ) :
1685
+ ?>
1686
+ <option value="<?php echo esc_attr( $option ); ?>" <?php if((int)$args['layout_selections']['carouselmobilecols'] == (int)$option) echo 'selected="selected"' ?> ><?php echo esc_html( $option ); ?></option>
1687
+ <?php endforeach; ?>
1688
+ </select>
1689
+ </div>
1690
+ <div class="ctf_layout_setting">
1691
+ <label><?php _e('Loop Type', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselloop
1692
+ Eg: carouselloop=rewind
1693
+ carouselloop=infinity</code>
1694
+ <br />
1695
+ <select name="<?php echo $args['option'].'[carouselloop]'; ?>" id="ctf_carousel_loop">
1696
+ <option value="none" <?php if($args['layout_selections']['carouselloop'] == "none") echo 'selected="selected"' ?> ><?php _e( 'None', 'instagram-feed'); ?></option>
1697
+ <option value="rewind" <?php if($args['layout_selections']['carouselloop'] == "rewind") echo 'selected="selected"' ?> ><?php _e( 'Rewind', 'instagram-feed'); ?></option>
1698
+ <option value="infinity" <?php if($args['layout_selections']['carouselloop'] == "infinity") echo 'selected="selected"' ?> ><?php _e( 'Infinity', 'instagram-feed'); ?></option>
1699
+ </select>
1700
+ </div>
1701
+ <div class="ctf_layout_setting">
1702
+ <label><?php _e('Navigation Arrows', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1703
+ Eg: carouselarrows=below</code>
1704
+ <br />
1705
+ <select name="<?php echo $args['option'].'[carouselarrows]'; ?>" id="ctf_carousel_loop">
1706
+ <option value="onhover" <?php if($args['layout_selections']['carouselarrows'] == "onhover") echo 'selected="selected"' ?> ><?php _e( 'Show on Hover', 'instagram-feed'); ?></option>
1707
+ <option value="below" <?php if($args['layout_selections']['carouselarrows'] == "below") echo 'selected="selected"' ?> ><?php _e( 'Show below feed', 'instagram-feed'); ?></option>
1708
+ <option value="hide" <?php if($args['layout_selections']['carouselarrows'] == "hide") echo 'selected="selected"' ?> ><?php _e( 'Hide arrows', 'instagram-feed'); ?></option>
1709
+ </select>
1710
+ </div>
1711
+ <div class="ctf_layout_setting">
1712
+ <label><?php _e('Carousel Height', 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselarrows
1713
+ Eg: carouselarrows=below</code>
1714
+ <br />
1715
+ <select name="<?php echo $args['option'].'[carouselheight]'; ?>" id="ctf_carousel_loop">
1716
+ <option value="tallest" <?php if($args['layout_selections']['carouselheight'] == "tallest") echo 'selected="selected"' ?> ><?php _e( 'Always set to tallest post', 'instagram-feed'); ?></option>
1717
+ <option value="clickexpand" <?php if($args['layout_selections']['carouselheight'] == "clickexpand") echo 'selected="selected"' ?> ><?php _e( 'Set to shortest post, button to expand', 'instagram-feed'); ?></option>
1718
+ <option value="auto'" <?php if($args['layout_selections']['carouselheight'] == "auto'") echo 'selected="selected"' ?> ><?php _e( 'Automatically set to post height (forces single column)', 'instagram-feed'); ?></option>
1719
+ </select>
1720
+ </div>
1721
+ <div class="ctf_layout_setting">
1722
+ <input type="checkbox" name="<?php echo $args['option'].'[carouselautoplay]'; ?>" id="ctf_carousel_autoplay" <?php if($args['layout_selections']['carouselautoplay'] == true) echo 'checked="checked"' ?> />
1723
+ <label><?php _e("Enable Autoplay", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouselautoplay
1724
+ Eg: carouselautoplay=true</code>
1725
+ </div>
1726
+ <div class="ctf_layout_setting">
1727
+ <label><?php _e("Interval Time", 'instagram-feed'); ?></label><code class="ctf_shortcode"> carouseltime
1728
+ Eg: carouseltime=8000</code>
1729
+ <br />
1730
+ <input name="<?php echo $args['option'].'[carouseltime]'; ?>" type="text" value="<?php esc_attr_e( $args['layout_selections']['carouseltime'] ); ?>" size="6" /><?php _e("miliseconds", 'instagram-feed'); ?>
1731
+ </div>
1732
+ </div>
1733
+
1734
+ </div>
1735
+ <?php
1736
+ }
1737
+
1738
+ public function custom_code( $args )
1739
+ {
1740
+ $options = get_option( $args['option'] );
1741
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
1742
+ ?>
1743
+ <p><?php _e( $args['description'], 'custom-twitter-feeds' ) ; ?></p>
1744
+ <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="7"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
1745
+ <?php if ( isset( $args['extra'] ) ) { _e( '<p class="ctf_note">'.$args['extra'].'</p>', 'custom-twitter-feeds' ); } ?>
1746
+ <?php
1747
+ }
1748
+
1749
+ public function clear_persistent_cache_button( $args ) {
1750
+ ?>
1751
+ <input id="ctf-clear-persistent-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Persistent Caches' ); ?>" />
1752
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
1753
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for search and hashtag feeds that have persistent caches', 'custom-twitter-feeds' ); ?>.</p>
1754
+ <?php
1755
+ }
1756
+
1757
+ public function validate_ctf_options( $input )
1758
+ {
1759
+ if ( isset( $input['tab'] ) && ! isset( $_POST['just_tokens'] ) ) {
1760
+ wp_cache_delete ( 'alloptions', 'options' );
1761
+
1762
+ $ctf_options = get_option( 'ctf_options', array() );
1763
+
1764
+ if ( $input['tab'] === 'configure' && isset( $input['usertimeline_text'] ) ) {
1765
+
1766
+ $feed_types = apply_filters( 'ctf_admin_feed_type_list', '' );
1767
+ $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1768
+ $ctf_options['have_own_tokens'] = false;
1769
+ $ctf_options['use_own_consumer'] = false;
1770
+ $ctf_options['preserve_settings'] = false;
1771
+ $ctf_options['usertimeline_includereplies'] = false;
1772
+ $ctf_options['hometimeline_includereplies'] = false;
1773
+ $ctf_options['mentionstimeline_includereplies'] = false;
1774
+
1775
+ foreach ( $input as $key => $val ) {
1776
+ if ( $key == 'search_text' || $key == 'usertimeline_text' || $key == 'hashtag_text' ) {
1777
+ $ctf_options[$key] = apply_filters( 'ctf_admin_validate_' . $key, $val );
1778
+ } elseif ( $key == 'ajax_theme' || $key == 'use_own_consumer' || $key == 'have_own_tokens' || $key == 'preserve_settings' ||
1779
+ $key == 'usertimeline_includereplies' || $key == 'hometimeline_includereplies' || $key == 'mentionstimeline_includereplies' ) {
1780
+ if ( $val != 'on' ) {
1781
+ $ctf_options[$key] = false;
1782
+ } else {
1783
+ $ctf_options[$key] = true;
1784
+ }
1785
+ } else {
1786
+ $ctf_options[$key] = sanitize_text_field( $val );
1787
+ }
1788
+ }
1789
+
1790
+ $ctf_options['includereplies'] = apply_filters( 'ctf_admin_set_include_replies', $ctf_options );
1791
+
1792
+ // delete feeds cached in transients
1793
+ ctf_clear_cache();
1794
+ delete_transient( 'ctf_reauthenticate' );
1795
+
1796
+ // process force cache to clear on interval
1797
+ $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1798
+ $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1799
+
1800
+ if ( $cron_clear_cache == 'no' ) {
1801
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1802
+ } elseif ( $cron_clear_cache == 'yes' ) {
1803
+ //Clear the existing cron event
1804
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1805
+
1806
+ //Set the event schedule based on what the caching time is set to
1807
+ if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1808
+ $ctf_cron_schedule = 'twicedaily';
1809
+ } elseif ( $cache_time_unit == 86400 ) {
1810
+ $ctf_cron_schedule = 'daily';
1811
+ } else {
1812
+ $ctf_cron_schedule = 'hourly';
1813
+ }
1814
+
1815
+ wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1816
+ }
1817
+ } elseif ( $input['tab'] === 'customize' && isset( $input['class'] ) ) {
1818
+
1819
+ $cron_clear_cache = isset( $input['cron_cache_clear'] ) ? $input['cron_cache_clear'] : 'no';
1820
+ $checkbox_settings = array( 'width_mobile_no_fixed', 'include_retweeter', 'include_avatar', 'include_author', 'include_logo', 'include_text', 'include_media_placeholder',
1821
+ 'include_date', 'include_actions', 'include_twitterlink', 'ajax_theme', 'include_linkbox', 'creditctf', 'showbutton', 'showheader', 'persistentcache', 'selfreplies',
1822
+ 'disableintents', 'disableawesome', 'shorturls' );
1823
+ $checkbox_settings = apply_filters( 'ctf_admin_customize_checkbox_settings', $checkbox_settings );
1824
+ $leave_spaces = array( 'headertext', 'translate_minute', 'translate_hour', 'custom_css', 'custom_js' );
1825
+
1826
+ foreach ( $checkbox_settings as $checkbox_setting ) {
1827
+ $ctf_options[$checkbox_setting] = 0;
1828
+ }
1829
+
1830
+ foreach ( $input as $key => $val ) {
1831
+ if ( in_array( $key, $checkbox_settings ) ) {
1832
+ if ( $val != 'on' ) {
1833
+ $ctf_options[$key] = false;
1834
+ } else {
1835
+ $ctf_options[$key] = true;
1836
+ }
1837
+ } else {
1838
+ if ( in_array( $key, $leave_spaces ) ) {
1839
+ $ctf_options[$key] = $val;
1840
+ } else {
1841
+ $ctf_options[$key] = sanitize_text_field( $val );
1842
+ }
1843
+ }
1844
+ }
1845
+
1846
+ // delete feeds cached in transients
1847
+ ctf_clear_cache();
1848
+
1849
+ // process force cache to clear on interval
1850
+ $cache_time = isset( $input['cache_time'] ) ? (int) $input['cache_time'] : 1;
1851
+ $cache_time_unit = isset( $input['cache_time_unit'] ) ? (int) $input['cache_time_unit'] : 3600;
1852
+
1853
+ if ( $cron_clear_cache == 'no' ) {
1854
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1855
+ } elseif ( $cron_clear_cache == 'yes' ) {
1856
+ //Clear the existing cron event
1857
+ wp_clear_scheduled_hook( 'ctf_cron_job' );
1858
+
1859
+ //Set the event schedule based on what the caching time is set to
1860
+ if ( $cache_time_unit == 3600 && $cache_time > 5 ) {
1861
+ $ctf_cron_schedule = 'twicedaily';
1862
+ } elseif ( $cache_time_unit == 86400 ) {
1863
+ $ctf_cron_schedule = 'daily';
1864
+ } else {
1865
+ $ctf_cron_schedule = 'hourly';
1866
+ }
1867
+
1868
+ wp_schedule_event( time(), $ctf_cron_schedule, 'ctf_cron_job' );
1869
+ }
1870
+ } elseif ( $input['tab'] === 'style' && isset( $input['headertext'] ) ) {
1871
+ $checkbox_settings = array( 'showbio', 'disablelinks', 'linktexttotwitter' );
1872
+ $leave_spaces = array( 'headertext' );
1873
+
1874
+ foreach ( $checkbox_settings as $checkbox_setting ) {
1875
+ $ctf_options[$checkbox_setting] = false;
1876
+ }
1877
+
1878
+ foreach ( $input as $key => $val ) {
1879
+ if ( in_array( $key, $checkbox_settings ) ) {
1880
+ if ( $val != 'on' ) {
1881
+ $ctf_options[$key] = false;
1882
+ } else {
1883
+ $ctf_options[$key] = true;
1884
+ }
1885
+ } else {
1886
+ if ( in_array( $key, $leave_spaces ) ) {
1887
+ $ctf_options[$key] = $val;
1888
+ } else {
1889
+ $ctf_options[$key] = sanitize_text_field( $val );
1890
+ }
1891
+ }
1892
+ }
1893
+ }
1894
+
1895
+ return $ctf_options;
1896
+ } elseif ( isset( $input['access_token'] ) ) {
1897
+ wp_cache_delete ( 'alloptions', 'options' );
1898
+
1899
+ $new = get_option( 'ctf_options', array() );
1900
+ $new['access_token'] = $input['access_token'];
1901
+ $new['access_token_secret'] = $input['access_token_secret'];
1902
+
1903
+ return $new;
1904
+
1905
+ }
1906
+ wp_cache_delete ( 'alloptions', 'options' );
1907
+
1908
+ $new = get_option( 'ctf_options', array() );
1909
+
1910
+ return $new;
1911
+ }
 
1912
  }
inc/CtfDateTime.php CHANGED
@@ -1,25 +1,25 @@
1
- <?php
2
- /**
3
- * Class CtfDateTime
4
- *
5
- * Workaround for PHP 5.2
6
- */
7
- // Don't load directly
8
- if ( ! defined( 'ABSPATH' ) ) {
9
- die( '-1' );
10
- }
11
-
12
- class CtfDateTime extends DateTime
13
- {
14
- public function setTimestamp( $timestamp )
15
- {
16
- $date = getdate( ( int ) $timestamp );
17
- $this->setDate( $date['year'] , $date['mon'] , $date['mday'] );
18
- $this->setTime( $date['hours'] , $date['minutes'] , $date['seconds'] );
19
- }
20
-
21
- public function getTimestamp()
22
- {
23
- return $this->format( 'U' );
24
- }
25
- }
1
+ <?php
2
+ /**
3
+ * Class CtfDateTime
4
+ *
5
+ * Workaround for PHP 5.2
6
+ */
7
+ // Don't load directly
8
+ if ( ! defined( 'ABSPATH' ) ) {
9
+ die( '-1' );
10
+ }
11
+
12
+ class CtfDateTime extends DateTime
13
+ {
14
+ public function setTimestamp( $timestamp )
15
+ {
16
+ $date = getdate( ( int ) $timestamp );
17
+ $this->setDate( $date['year'] , $date['mon'] , $date['mday'] );
18
+ $this->setTime( $date['hours'] , $date['minutes'] , $date['seconds'] );
19
+ }
20
+
21
+ public function getTimestamp()
22
+ {
23
+ return $this->format( 'U' );
24
+ }
25
+ }
inc/CtfFeed.php CHANGED
@@ -1,1768 +1,1768 @@
1
- <?php
2
- /**
3
- * Class CtfFeed
4
- *
5
- * Creates the settings for the feed and outputs the html
6
- */
7
-
8
- // Don't load directly
9
- if ( ! defined( 'ABSPATH' ) ) {
10
- die( '-1' );
11
- }
12
-
13
- class CtfFeed
14
- {
15
- /**
16
- * @var array
17
- */
18
- public $errors = array();
19
-
20
- /**
21
- * @var array
22
- */
23
- protected $atts;
24
-
25
- /**
26
- * @var string
27
- */
28
- protected $last_id_data;
29
-
30
- private $num_needed_input;
31
-
32
- /**
33
- * @var mixed|void
34
- */
35
- protected $db_options;
36
-
37
- /**
38
- * @var array
39
- */
40
- public $feed_options = array();
41
-
42
- /**
43
- * @var mixed|void
44
- */
45
- public $missing_credentials;
46
-
47
- /**
48
- * @var string
49
- */
50
- public $transient_name;
51
-
52
- /**
53
- * @var bool
54
- */
55
- protected $transient_data = false;
56
-
57
- /**
58
- * @var int
59
- */
60
- private $num_tweets_needed;
61
-
62
- private $check_for_duplicates = false;
63
-
64
- /**
65
- * @var array
66
- */
67
- public $tweet_set;
68
-
69
- /**
70
- * @var object
71
- */
72
- public $api_obj;
73
-
74
- /**
75
- * @var string
76
- */
77
- public $feed_html;
78
-
79
- private $persistent_index;
80
-
81
-
82
- /**
83
- * retrieves and sets options that apply to the feed
84
- *
85
- * @param array $atts data from the shortcode
86
- * @param string $last_id_data the last visible tweet on the feed, empty string if first set
87
- * @param int $num_needed_input this number represents the number left to retrieve after the first set
88
- */
89
- public function __construct( $atts, $last_id_data, $num_needed_input )
90
- {
91
- $this->atts = $atts;
92
- $this->last_id_data = $last_id_data;
93
- $this->num_needed_input = $num_needed_input;
94
- $this->db_options = get_option( 'ctf_options', array() );
95
- }
96
-
97
- /**
98
- * creates and returns all of the data needed to generate the output for the feed
99
- *
100
- * @param array $atts data from the shortcode
101
- * @param string $last_id_data the last visible tweet on the feed, empty string if first set
102
- * @param int $num_needed_input this number represents the number left to retrieve after the first set
103
- * @return CtfFeed the complete object for the feed
104
- */
105
- public static function init( $atts, $last_id_data = '', $num_needed_input = 0, $ids_to_remove = array(), $persistent_index = 1 )
106
- {
107
- $feed = new CtfFeed( $atts, $last_id_data, $num_needed_input );
108
- $feed->setFeedOptions();
109
-
110
- $feed->setCacheTypeOption();
111
- if ( $feed->feed_options['persistentcache'] ) {
112
- $feed->persistent_index = $persistent_index;
113
- }
114
-
115
- $feed->setTweetSet();
116
- return $feed;
117
- }
118
-
119
- /**
120
- * creates all of the feed options with shortcode settings having the highest priority
121
- */
122
- protected function setFeedOptions()
123
- {
124
- $this->setFeedTypeAndTermOptions();
125
-
126
- $bool_false = array (
127
- 'have_own_tokens',
128
- 'includereplies',
129
- 'ajax_theme',
130
- 'width_mobile_no_fixed',
131
- 'disablelinks',
132
- 'linktexttotwitter',
133
- 'creditctf',
134
- 'selfreplies',
135
- 'disableintents',
136
- 'shorturls'
137
- );
138
- $this->setStandardBoolOptions( $bool_false, false );
139
-
140
- $this->setAccessTokenAndSecretOptions();
141
- $this->setConsumerKeyAndSecretOptions();
142
-
143
- $db_only = array(
144
- 'request_method'
145
- );
146
- $this->setDatabaseOnlyOptions( $db_only );
147
-
148
- $this->setStandardTextOptions( 'num', 5 );
149
-
150
- $standard_text = array(
151
- 'class',
152
- 'headertext',
153
- 'dateformat',
154
- 'datecustom',
155
- 'mtime',
156
- 'htime',
157
- 'nowtime'
158
- );
159
- $this->setStandardTextOptions( $standard_text, '' );
160
-
161
- $this->setStandardTextOptions( 'retweetedtext', __( 'Retweeted', 'custom-twitter-feeds' ) );
162
- $this->setStandardTextOptions( 'font_method', 'svg' );
163
- $this->setStandardTextOptions( 'multiplier', 1.25 );
164
- $this->setStandardTextOptions( 'twitterlinktext', 'Twitter' );
165
-
166
- $this->setStandardTextOptions( 'buttontext', __( 'Load More...', 'custom-twitter-feeds' ) );
167
- $this->setStandardTextOptions( 'textlength', 280 );
168
- $text_size = array(
169
- 'authortextsize',
170
- 'tweettextsize',
171
- 'datetextsize',
172
- 'quotedauthorsize',
173
- 'iconsize',
174
- 'logosize'
175
- );
176
- $this->setTextSizeOptions( $text_size );
177
-
178
- $text_weight = array(
179
- 'authortextweight',
180
- 'tweettextweight',
181
- 'datetextweight',
182
- 'quotedauthorweight'
183
- );
184
- $this->setStandardStyleProperty( $text_weight, 'font-weight' );
185
-
186
- $text_color = array(
187
- 'headertextcolor',
188
- 'textcolor',
189
- 'linktextcolor',
190
- 'iconcolor',
191
- 'logocolor',
192
- 'buttontextcolor'
193
- );
194
- $this->setStandardStyleProperty( $text_color, 'color' );
195
-
196
- $bg_color = array(
197
- 'bgcolor',
198
- 'tweetbgcolor',
199
- 'headerbgcolor',
200
- 'buttoncolor'
201
- );
202
- $this->setStandardStyleProperty( $bg_color, 'background-color' );
203
-
204
- $bool_true = array(
205
- 'persistentcache',
206
- 'showbutton',
207
- 'showbio',
208
- 'showheader'
209
- );
210
- $this->setStandardBoolOptions( $bool_true, true );
211
-
212
- $this->setDimensionOptions();
213
- $this->setCacheTimeOptions();
214
- $this->setIncludeExcludeOptions();
215
- }
216
-
217
- /**
218
- * uses the feed options to set the the tweets in the feed by using
219
- * an existing set in a cache or by retrieving them from Twitter
220
- */
221
- protected function setTweetSet()
222
- {
223
- $this->setTransientName();
224
- $success = $this->maybeSetTweetsFromCache();
225
-
226
- if ( ! $success ) {
227
- $this->maybeSetTweetsFromTwitter();
228
- }
229
-
230
- $this->num_tweets_needed = $this->numTweetsNeeded();
231
- }
232
-
233
- /**
234
- * the access token and secret must be set in order for the feed to work
235
- * this function processes the user input and sets a flag if none are entered
236
- */
237
- private function setAccessTokenAndSecretOptions()
238
- {
239
- $this->feed_options['access_token'] = isset( $this->db_options['access_token'] ) && strlen( $this->db_options['access_token'] ) > 30 ? $this->db_options['access_token'] : 'missing';
240
- $this->feed_options['access_token_secret'] = isset( $this->db_options['access_token_secret'] ) && strlen( $this->db_options['access_token_secret'] ) > 30 ? $this->db_options['access_token_secret'] : 'missing';
241
-
242
- // verify that access token and secret have been entered
243
- $this->setMissingCredentials();
244
- }
245
-
246
- /**
247
- * generates the flag if there are missing access tokens
248
- */
249
- private function setMissingCredentials() {
250
- if ( $this->feed_options['access_token'] == 'missing' || $this->feed_options['access_token_secret'] == 'missing' ) {
251
- $this->missing_credentials = true;
252
- } else {
253
- $this->missing_credentials = false;
254
- }
255
- }
256
-
257
- /**
258
- * processes the consumer key and secret options
259
- */
260
- protected function setConsumerKeyAndSecretOptions()
261
- {
262
- if ( $this->feed_options['have_own_tokens'] ) {
263
- $this->feed_options['consumer_key'] = isset( $this->db_options['consumer_key'] ) && strlen( $this->db_options['consumer_key'] ) > 15 ? $this->db_options['consumer_key'] : 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
264
- $this->feed_options['consumer_secret'] = isset( $this->db_options['consumer_secret'] ) && strlen( $this->db_options['consumer_secret'] ) > 30 ? $this->db_options['consumer_secret'] : 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
265
- } else {
266
- $this->feed_options['consumer_key'] ='FPYSYWIdyUIQ76Yz5hdYo5r7y';
267
- $this->feed_options['consumer_secret'] = 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
268
- }
269
- }
270
-
271
- /**
272
- * determines what value to use and saves it for the appropriate key in the feed_options array
273
- *
274
- * @param $options mixed the key or array of keys to be set
275
- * @param $options_page string options page this setting is set on
276
- * @param string $default default value to use if there is no user input
277
- */
278
- public function setDatabaseOnlyOptions( $options, $default = '' )
279
- {
280
- if ( is_array( $options ) ) {
281
- foreach ( $options as $option ) {
282
- $this->feed_options[$option] = isset( $this->db_options[$option] ) && ! empty( $this->db_options[$option] ) ? $this->db_options[$option] : $default;
283
- }
284
- } else {
285
- $this->feed_options[$options] = isset( $this->db_options[$options] ) && ! empty( $this->db_options[$options] ) ? $this->db_options[$options] : $default;
286
- }
287
- }
288
-
289
- /**
290
- * determines what value to use and saves it for the appropriate key in the feed_options array
291
- *
292
- * @param $options mixed the key or array of keys to be set
293
- * @param $options_page string options page this setting is set on
294
- * @param string $default default value to use if there is no user input
295
- */
296
- public function setStandardTextOptions( $options, $default = '' )
297
- {
298
- if ( is_array( $options ) ) {
299
- foreach ( $options as $option ) {
300
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? esc_attr( __( $this->atts[$option], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$option] ) ? esc_attr( $this->db_options[$option] ) : $default );
301
- }
302
- } else {
303
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( __( $this->atts[$options], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
304
- }
305
- }
306
-
307
- /**
308
- * creates the appropriate style attribute string for the text size setting
309
- *
310
- * @param $value mixed pixel size or other that the user has selected
311
- * @return string string for the style attribute
312
- */
313
- public static function processTextSizeStyle( $value )
314
- {
315
- if ( $value == '' ) {
316
- return '';
317
- }
318
- $processed_value = $value == 'inherit' ? '' : 'font-size: ' . $value . 'px;';
319
-
320
- return $processed_value;
321
- }
322
-
323
- /**
324
- * determines what value to use and saves it for the appropriate key in the feed_options array
325
- *
326
- * @param $options mixed the key or array of keys to be set
327
- * @param string $default default value to use if there is no user input
328
- */
329
- public function setTextSizeOptions( $options, $default = '' )
330
- {
331
- if ( is_array( $options ) ) {
332
- foreach ( $options as $option ) {
333
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$option] ) ) : ( isset( $this->db_options[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$option] ) ) : $default );
334
- }
335
- } else {
336
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$options] ) ) : ( isset( $this->db_options[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$options] ) ) : $default );
337
- }
338
- }
339
-
340
- /**
341
- * determines what value to use and saves it for the appropriate key in the feed_options array
342
- *
343
- * @param $options mixed the key or array of keys to be set
344
- * @param $property string name of the property to be set
345
- * @param string $default default value to use if there is no user input
346
- */
347
- public function setStandardStyleProperty( $options, $property, $default = '' )
348
- {
349
- if ( is_array( $options ) ) {
350
- foreach ( $options as $option ) {
351
- $this->feed_options[$option] = isset( $this->atts[$option] ) && $this->atts[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$option] ) . ';' : ( isset( $this->db_options[$option] ) && $this->db_options[$option] != '#' && $this->db_options[$option] != '' && $this->db_options[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$option] ) . ';' : $default );
352
- }
353
- } else {
354
- $this->feed_options[$options] = isset( $this->atts[$options] ) && $this->atts[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$options] ) . ';' : ( isset( $this->db_options[$options] ) && $this->db_options[$options] != '#' && $this->db_options[$options] != '' && $this->db_options[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$options] ) . ';' : $default );
355
- }
356
- }
357
-
358
- /**
359
- * determines what value to use and saves it for the appropriate key in the feed_options array
360
- *
361
- * @param $options mixed the key or array of keys to be set
362
- * @param bool|true $default default value to use if there is no user input
363
- */
364
- public function setStandardBoolOptions( $options, $default = true )
365
- {
366
- if ( is_array( $options ) ) {
367
- foreach ( $options as $option ) {
368
- $this->feed_options[$option] = isset( $this->atts[$option] ) ? ( $this->atts[$option] === 'true' ) : ( isset( $this->db_options[$option] ) ? (bool) $this->db_options[$option] : (bool) $default );
369
- }
370
- } else {
371
- $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( $this->atts[$options] ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
372
- }
373
- }
374
-
375
- /**
376
- * sets the width and height of the feed based on user input
377
- */
378
- public function setDimensionOptions()
379
- {
380
- $this->feed_options['width'] = isset( $this->atts['width'] ) ? 'width: '. esc_attr( $this->atts['width'] ) .';' : ( ( isset( $this->db_options['width'] ) && $this->db_options['width'] != '' ) ? 'width: '. esc_attr( $this->db_options['width'] ) . ( isset( $this->db_options['width_unit'] ) ? esc_attr( $this->db_options['width_unit'] ) : '%' ) . ';' : '' );
381
- $this->feed_options['height'] = isset( $this->atts['height'] ) ? 'height: '. esc_attr( $this->atts['height'] ) .';' : ( ( isset( $this->db_options['height'] ) && $this->db_options['height'] != '' ) ? 'height: '. esc_attr( $this->db_options['height'] ) . ( isset( $this->db_options['height_unit'] ) ? esc_attr( $this->db_options['height_unit'] ) : 'px' ) . ';' : '' );
382
- }
383
-
384
- /**
385
- * sets the cache time based on user input
386
- */
387
- public function setCacheTimeOptions()
388
- {
389
- $user_cache = isset( $this->db_options['cache_time'] ) ? ( $this->db_options['cache_time'] * $this->db_options['cache_time_unit'] ) : HOUR_IN_SECONDS;
390
-
391
- if ( $this->feed_options['have_own_tokens'] ) {
392
- $this->feed_options['cache_time'] = max( $user_cache, 60 );
393
- } else {
394
- $this->feed_options['cache_time'] = max( $user_cache, 3600 );
395
- }
396
- }
397
-
398
-
399
- /**
400
- * sets the number of tweets to retrieve
401
- */
402
- public function setTweetsToRetrieve()
403
- {
404
- $min_tweets_to_retrieve = 10;
405
-
406
- if ( $this->num_needed_input < 1 ) {
407
- if ( $this->feed_options['includereplies'] ) {
408
- $this->feed_options['count'] = $this->feed_options['num'];
409
- } else {
410
- if ( $this->feed_options['num'] < 10 ) {
411
- $this->feed_options['count'] = max( round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
412
- } elseif ( $this->feed_options['num'] < 30 ) {
413
- $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.2 );
414
- } else {
415
- $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] );
416
- }
417
- }
418
- } else {
419
- $this->feed_options['count'] = max( $this->num_needed_input, 50 );
420
- $this->feed_options['num'] = $this->num_needed_input;
421
- }
422
-
423
- }
424
-
425
- /**
426
- * sets the feed type and associated parameter
427
- */
428
- public function setFeedTypeAndTermOptions()
429
- {
430
- $this->feed_options['type'] = '';
431
- $this->feed_options['feed_term'] = '';
432
- $this->feed_options['screenname'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
433
-
434
- if ( isset( $this->atts['home'] ) && $this->atts['home'] == 'true' ) {
435
- $this->feed_options['type'] = 'hometimeline';
436
- }
437
- if ( isset( $this->atts['screenname'] ) ) {
438
- $this->feed_options['type'] = 'usertimeline';
439
- $this->feed_options['feed_term'] = isset( $this->atts['screenname'] ) ? ctf_validate_usertimeline_text( $this->atts['screenname'] ) : ( ( isset( $this->db_options['usertimeline_text'] ) ) ? $this->db_options['usertimeline_text'] : '' );
440
- $this->feed_options['screenname'] = $this->feed_options['feed_term'];
441
- }
442
- if ( isset( $this->atts['search'] ) || isset( $this->atts['hashtag'] ) ) {
443
- $this->feed_options['type'] = 'search';
444
- $this->working_term = isset( $this->atts['hashtag'] ) ? $this->atts['hashtag'] : ( isset( $this->atts['search'] ) ? $this->atts['search'] : '' );
445
- $this->feed_options['feed_term'] = isset( $this->working_term ) ? ctf_validate_search_text( $this->working_term ) : ( ( isset( $this->db_options['search_text'] ) ) ? $this->db_options['search_text'] : '' );
446
- $this->check_for_duplicates = true;
447
- }
448
-
449
- if ( $this->feed_options['type'] == '' ) {
450
- $this->feed_options['type'] = isset( $this->db_options['type'] ) ? $this->db_options['type'] : 'usertimeline';
451
- switch ( $this->feed_options['type'] ) {
452
- case 'usertimeline':
453
- $this->feed_options['feed_term'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
454
- break;
455
- case 'hometimeline':
456
- $this->feed_options['type'] = 'hometimeline';
457
- break;
458
- case 'search':
459
- $this->feed_options['feed_term'] = isset( $this->db_options['search_text'] ) ? $this->db_options['search_text'] : '';
460
- $this->check_for_duplicates = true;
461
- break;
462
- }
463
- }
464
- }
465
-
466
- /**
467
- * sets the visible parts of each tweet for the feed
468
- */
469
- public function setIncludeExcludeOptions()
470
- {
471
- $this->feed_options['tweet_excludes'] = array();
472
- $this->feed_options['tweet_includes'] = isset( $this->atts['include'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['include'] ) ) ) : array();
473
-
474
- if ( empty( $this->feed_options['tweet_includes'][0] ) ) {
475
- $this->feed_options['tweet_excludes'] = isset( $this->atts['exclude'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['exclude'] ) ) ) : array();
476
- }
477
- if ( empty( $this->feed_options['tweet_excludes'][0] ) && empty( $this->feed_options['tweet_includes'][0] ) ) {
478
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_retweeter'] ) && $this->db_options['include_retweeter'] == false ? null : 'retweeter';
479
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_avatar'] ) && $this->db_options['include_avatar'] == false ? null : 'avatar';
480
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_author'] ) && $this->db_options['include_author'] == false ? null : 'author';
481
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_text'] ) && $this->db_options['include_text'] == false ? null : 'text';
482
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_media_placeholder'] ) && $this->db_options['include_media_placeholder'] == false ? null : 'placeholder';
483
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_date'] ) && $this->db_options['include_date'] == false ? null : 'date';
484
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_actions'] ) && $this->db_options['include_actions'] == false ? null : 'actions';
485
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_twitterlink'] ) && $this->db_options['include_twitterlink'] == false ? null : 'twitterlink';
486
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_linkbox'] ) && $this->db_options['include_linkbox'] == false ? null : 'linkbox';
487
- $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_logo'] ) && $this->db_options['include_logo'] == false ? null : 'logo';
488
- }
489
-
490
- }
491
-
492
- /**
493
- * sets the transient name for the caching system
494
- */
495
- public function setTransientName()
496
- {
497
- $last_id_data = $this->last_id_data;
498
- $num = isset( $this->feed_options['num'] ) ? $this->feed_options['num'] : '';
499
-
500
- switch ( $this->feed_options['type'] ) {
501
- case 'hometimeline' :
502
- $this->transient_name = 'ctf_' . $last_id_data . 'hometimeline'. $num;
503
- break;
504
- case 'usertimeline' :
505
- $screenname = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
506
- $this->transient_name = substr( 'ctf__' . $last_id_data . $screenname . $num, 0, 45 );
507
- break;
508
- case 'search' :
509
- $hashtag = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
510
- $this->transient_name = substr( 'ctf_' . $last_id_data . $hashtag . $num, 0, 45 );
511
- break;
512
- }
513
- }
514
-
515
- public function setCacheTypeOption() {
516
- if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
517
- $this->feed_options['persistentcache'] = true;
518
- } else {
519
- $this->feed_options['persistentcache'] = false;
520
- }
521
- }
522
-
523
- /**
524
- * checks the data available in the cache to make sure it seems to be valid
525
- *
526
- * @return bool|string false if the cache is valid, error otherwise
527
- */
528
- private function validateCache()
529
- {
530
- if ( isset( $this->transient_data[0] ) ) {
531
- return false;
532
- } else {
533
- return 'invalid cache';
534
- }
535
- }
536
-
537
- /**
538
- * will use the cached data in the feed if data seems to be valid and user
539
- * wants to use caching
540
- *
541
- * @return bool|mixed false if none is set, tweet set otherwise
542
- */
543
- public function maybeSetTweetsFromCache()
544
- {
545
- if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
546
- $persistent_cache_tweets = $this->persistentCacheTweets();
547
- if ( is_array( $persistent_cache_tweets ) ) {
548
- $this->transient_data = array_slice( $persistent_cache_tweets, ( $this->persistent_index - $this->feed_options['num'] - 1 ) , $this->persistent_index );
549
- } else {
550
- $this->transient_data = $persistent_cache_tweets;
551
- }
552
- } else {
553
- $this->transient_data = get_transient( $this->transient_name );
554
- if ( ! is_array( $this->transient_data ) ) {
555
- $this->transient_data = json_decode( $this->transient_data, $assoc = true );
556
- }
557
-
558
- if ( $this->feed_options['cache_time'] <= 0 ) {
559
- return $this->tweet_set = false;
560
- }
561
- }
562
- // validate the transient data
563
- if ( $this->transient_data ) {
564
- $this->errors['cache_status'] = $this->validateCache();
565
- if ( $this->errors['cache_status'] === false ) {
566
- return $this->tweet_set = $this->transient_data;
567
- } else {
568
- return $this->tweet_set = false;
569
- }
570
- } else {
571
- $this->errors['cache_status'] = 'none found';
572
- return $this->tweet_set = false;
573
- }
574
- }
575
-
576
- private function persistentCacheTweets()
577
- {
578
- // if cache exists get cached data
579
- $includewords = ! empty( $this->feed_options['includewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['includewords'] ), 0, 10 ) : '';
580
- $excludewords = ! empty( $this->feed_options['excludewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['excludewords'] ), 0, 5 ) : '';
581
- $cache_name = substr( 'ctf_!_' . $this->feed_options['feed_term'] . $includewords . $excludewords, 0, 45 );
582
- $cache_time_limit_reached = get_transient( $cache_name ) ? false : true;
583
-
584
- $existing_cache = get_option( $cache_name, false );
585
- if ( $existing_cache && ! is_array( $existing_cache ) ) {
586
- $existing_cache = json_decode( $existing_cache, $assoc = true );
587
- }
588
-
589
- $this->persistent_index = $this->persistent_index + $this->feed_options['num'];
590
-
591
- $this->feed_options['count'] = 200;
592
-
593
- if ( ! empty( $this->last_id_data ) || ( ! $cache_time_limit_reached && $existing_cache ) ) {
594
- return $existing_cache;
595
- } elseif ( $existing_cache ) {
596
- // use "since-id" to look for more in an api request
597
- $since_id = $existing_cache[0]['id_str'];
598
- $api_obj = $this->getTweetsSinceID( $since_id, 'search', $this->feed_options['feed_term'], $this->feed_options['count'] );
599
- // add any new tweets to the cache
600
- $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
601
-
602
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : array();
603
-
604
- // add a transient to delay another api retrieval
605
- set_transient( $cache_name, true, $this->feed_options['cache_time'] );
606
-
607
- if ( empty( $tweets ) ) {
608
- if ( ! is_array( $existing_cache ) ) {
609
- return false;
610
- } else {
611
- return $existing_cache;
612
- }
613
- } else {
614
- $tweet_set = $this->reduceTweetSetData( $tweets, false );
615
- }
616
- $tweet_set = $this->appendPersistentCacheTweets( $existing_cache, $tweet_set );
617
- $cache_set = json_encode( $tweet_set );
618
-
619
- update_option( $cache_name, $cache_set );
620
-
621
- return $tweet_set;
622
- // else if cached data doesn't exist
623
- } else {
624
- // make a request for last 200 tweets
625
- $api_obj = $this->apiConnectionResponse( 'search', $this->feed_options['feed_term'] );
626
- // cache them in a regular option
627
- $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
628
-
629
- // check for errors/tweets present
630
- if ( isset( $this->tweet_set['errors'][0] ) ) {
631
- if ( empty( $this->api_obj ) ) {
632
- $this->api_obj = new stdClass();
633
- }
634
- $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
635
- $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
636
-
637
- $this->tweet_set = false;
638
- }
639
-
640
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
641
-
642
- if ( empty( $tweets ) ) {
643
- $this->errors['error_message'] = 'No Tweets returned';
644
- $this->tweet_set = false;
645
- } else {
646
- $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
647
- }
648
-
649
- // create a new persistent cache
650
- if ( $this->tweet_set && isset( $this->tweet_set[0] ) ) {
651
- $tweet_set = json_encode( $this->tweet_set );
652
-
653
- update_option( $cache_name, $tweet_set );
654
-
655
- // update list of persistent cache
656
- $cache_list = get_option( 'ctf_cache_list', array() );
657
-
658
- $cache_list[] = $cache_name;
659
-
660
- update_option( 'ctf_cache_list', $cache_list );
661
- }
662
-
663
- return $this->tweet_set;
664
- }
665
-
666
- // add the search parameter to another option that contains a list of all persistent caches available
667
- }
668
-
669
- private function reduceTweetSetData( $tweet_set, $limit = true ) {
670
- if ( $this->check_for_duplicates ) {
671
- $this->tweet_set = $this->removeDuplicates( $tweet_set, $limit );
672
- }
673
-
674
- if ( $this->feed_options['selfreplies'] ) {
675
- $this->tweet_set = $this->filterTweetSet( $tweet_set, $limit );
676
- }
677
-
678
- $this->tweet_set = $tweet_set;
679
- $this->trimTweetData( false );
680
- return $this->tweet_set;
681
- }
682
-
683
- /**
684
- * this takes the current set of tweets and processes them until there are
685
- * enough filtered tweets to create the feed from
686
- */
687
- private function filterTweetSet( $tweet_set, $limit = true )
688
- {
689
- $working_tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
690
- $usable_tweets = 0;
691
- if ( $limit ) {
692
- $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
693
- } else {
694
- $tweets_needed = 200;
695
- }
696
- $i = 0; // index of working_tweet_set
697
- $still_setting_filtered_tweets = true;
698
-
699
- while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
700
- if ( isset ( $working_tweet_set[$i] ) ) { // if there is another tweet available
701
- if ( !$this->feed_options['selfreplies'] && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] ) ) {
702
- unset( $working_tweet_set[$i] );
703
- } elseif ( $this->feed_options['selfreplies']
704
- && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] )
705
- && $working_tweet_set[$i]['in_reply_to_screen_name'] !== $working_tweet_set[$i]['user']['screen_name']) {
706
- unset( $working_tweet_set[$i] );
707
- } else {
708
- $usable_tweets++;
709
- }
710
- } else {
711
- $still_setting_filtered_tweets = false;
712
- }
713
-
714
- // if there are no more tweets needed
715
- if ( $usable_tweets >= $tweets_needed ) {
716
- $still_setting_filtered_tweets = false;
717
- } else {
718
- $i++;
719
- }
720
-
721
- }
722
-
723
- if ( is_array( $working_tweet_set ) ) {
724
- return array_values( $working_tweet_set );
725
- } else {
726
- return false;
727
- }
728
- }
729
-
730
- private function appendPersistentCacheTweets( $existing_cache )
731
- {
732
- if ( is_array( $this->tweet_set ) ) {
733
- $tweet_set = array_merge( $this->tweet_set, $existing_cache );
734
- } else {
735
- $tweet_set = $existing_cache;
736
- }
737
-
738
- $tweet_set = array_slice( $tweet_set, 0, 150 );
739
-
740
- return $tweet_set;
741
- }
742
-
743
-
744
- private function removeDuplicates( $tweet_set, $limit = true )
745
- {
746
- $tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
747
- $usable_tweets = 0;
748
- if ( $limit ) {
749
- $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
750
- } else {
751
- $tweets_needed = 200;
752
- }
753
- $ids_of_tweets_to_remove = array();
754
-
755
- $i = 0; // index of tweet_set
756
- $still_setting_filtered_tweets = true;
757
- while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
758
- if ( isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ) {
759
- unset( $tweet_set[$i] );
760
- } elseif ( isset( $tweet_set[$i] ) ) {
761
- $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
762
- if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
763
- unset( $tweet_set[$i] );
764
- } else {
765
- $usable_tweets++;
766
- $ids_of_tweets_to_remove[] = $id;
767
- }
768
- } else {
769
- $still_setting_filtered_tweets = false;
770
- }
771
-
772
- // if there are no more tweets needed
773
- if ( $usable_tweets >= $tweets_needed ) {
774
- $still_setting_filtered_tweets = false;
775
- } else {
776
- $i++;
777
- }
778
-
779
- }
780
-
781
- if ( is_array( $tweet_set ) ) {
782
- return array_values( $tweet_set );
783
- } else {
784
- return false;
785
- }
786
- }
787
-
788
- /**
789
- * will attempt to connect to the api to retrieve current tweets
790
- */
791
- public function maybeSetTweetsFromTwitter()
792
- {
793
- $this->setTweetsToRetrieve();
794
- $this->api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
795
- $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
796
-
797
- // check for errors/tweets present
798
- if ( isset( $this->tweet_set['errors'][0] ) ) {
799
- $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
800
- $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
801
- $this->tweet_set = false;
802
- }
803
-
804
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
805
-
806
- if ( empty( $tweets ) ) {
807
- $this->errors['error_message'] = 'No Tweets returned';
808
- $this->tweet_set = false;
809
- }
810
-
811
- if ( $this->check_for_duplicates ) {
812
- $this->tweet_set = $this->removeDuplicates( $this->tweet_set );
813
- }
814
- }
815
-
816
-
817
- /**
818
- * calculates how many tweets short the feed is so more can be retrieved via ajax
819
- *
820
- * @return int number of tweets needed
821
- */
822
- protected function numTweetsNeeded() {
823
- $tweet_count = 0;
824
- if ( isset( $this->tweet_set['statuses'] ) && is_array( $this->tweet_set['statuses'] ) ) {
825
- $tweet_count = count( $this->tweet_set['statuses'] );
826
- } elseif ( isset( $this->tweet_set ) && is_array( $this->tweet_set ) ) {
827
- $tweet_count = count( $this->tweet_set );
828
- }
829
-
830
- return $this->feed_options['num'] - $tweet_count;
831
- }
832
-
833
- /**
834
- * trims the unused data retrieved for more efficient caching
835
- */
836
- protected function trimTweetData( $limit = true )
837
- {
838
- $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
839
- $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
840
- if ( $limit ) {
841
- $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
842
- } else {
843
- $len = count( $tweets );
844
- }
845
- $trimmed_tweets = array();
846
-
847
- // for header
848
- if ( $this->last_id_data == '' && isset( $tweets[0] ) ) { // if this is the first set of tweets
849
- $trimmed_tweets[0]['user']['name']= $tweets[0]['user']['name'];
850
- $trimmed_tweets[0]['user']['description']= $tweets[0]['user']['description'];
851
- $trimmed_tweets[0]['user']['statuses_count']= $tweets[0]['user']['statuses_count'];
852
- $trimmed_tweets[0]['user']['followers_count']= $tweets[0]['user']['followers_count'];
853
- }
854
-
855
- for ( $i = 0; $i < $len; $i++ ) {
856
- $trimmed_tweets[$i]['user']['name'] = $tweets[$i]['user']['name'];
857
- $trimmed_tweets[$i]['user']['screen_name'] = $tweets[$i]['user']['screen_name'];
858
- $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
859
- $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
860
- $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
861
- $trimmed_tweets[$i]['text'] = isset( $tweets[$i]['text'] ) ? $tweets[$i]['text'] : $tweets[$i]['full_text'];
862
- $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
863
- $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
864
- $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
865
- $trimmed_tweets[$i]['favorite_count']= $tweets[$i]['favorite_count'];
866
-
867
- if ( isset( $tweets[$i]['entities']['urls'][0] ) ) {
868
- foreach ( $tweets[$i]['entities']['urls'] as $url ) {
869
- $trimmed_tweets[$i]['entities']['urls'][] = array(
870
- 'url' => $url['url'],
871
- 'expanded_url' => $url['expanded_url'],
872
- 'display_url' => $url['display_url'],
873
-
874
- );
875
- }
876
- }
877
-
878
- if ( isset( $tweets[$i]['retweeted_status'] ) ) {
879
- $trimmed_tweets[$i]['retweeted_status']['user']['name'] = $tweets[$i]['retweeted_status']['user']['name'];
880
- $trimmed_tweets[$i]['retweeted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['user']['screen_name'];
881
- $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
882
- $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
883
- $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
884
- $trimmed_tweets[$i]['retweeted_status']['text'] = isset( $tweets[$i]['retweeted_status']['text'] ) ? $tweets[$i]['retweeted_status']['text'] : $tweets[$i]['retweeted_status']['full_text'];
885
- $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
886
- $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
887
- $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
888
- $trimmed_tweets[$i]['retweeted_status']['favorite_count']= $tweets[$i]['retweeted_status']['favorite_count'];
889
- if ( isset( $tweets[$i]['retweeted_status']['entities']['urls'][0] ) ) {
890
- foreach ( $tweets[$i]['retweeted_status']['entities']['urls'] as $url ) {
891
- $trimmed_tweets[$i]['retweeted_status']['entities']['urls'][] = array(
892
- 'url' => $url['url'],
893
- 'expanded_url' => $url['expanded_url'],
894
- 'display_url' => $url['display_url'],
895
-
896
- );
897
- }
898
- }
899
- }
900
-
901
- if ( isset( $tweets[$i]['retweeted_status']['quoted_status'] ) ) {
902
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['name'];
903
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'];
904
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['verified'];
905
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['text'] = isset( $tweets[$i]['retweeted_status']['quoted_status']['text'] ) ? $tweets[$i]['retweeted_status']['quoted_status']['text'] : $tweets[$i]['retweeted_status']['quoted_status']['full_text'];
906
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['id_str'] = $tweets[$i]['retweeted_status']['quoted_status']['id_str'];
907
- if ( isset( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][0] ) ) {
908
- foreach ( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'] as $url ) {
909
- $trimmed_tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][] = array(
910
- 'url' => $url['url'],
911
- 'expanded_url' => $url['expanded_url'],
912
- 'display_url' => $url['display_url'],
913
- );
914
- }
915
- }
916
- }
917
-
918
- if ( isset( $tweets[$i]['quoted_status'] ) ) {
919
- $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
920
- $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
921
- $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
922
- $trimmed_tweets[$i]['quoted_status']['text'] = isset( $tweets[$i]['quoted_status']['text'] ) ? $tweets[$i]['quoted_status']['text'] : $tweets[$i]['quoted_status']['full_text'];
923
- $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
924
- if ( isset( $tweets[$i]['quoted_status']['entities']['urls'][0] ) ) {
925
- foreach ( $tweets[$i]['quoted_status']['entities']['urls'] as $url ) {
926
- $trimmed_tweets[$i]['quoted_status']['entities']['urls'][] = array(
927
- 'url' => $url['url'],
928
- 'expanded_url' => $url['expanded_url'],
929
- 'display_url' => $url['display_url'],
930
- );
931
- }
932
- }
933
- }
934
-
935
- $trimmed_tweets[$i] = $this->filterTrimmedTweets( $trimmed_tweets[$i], $tweets[$i] );
936
- }
937
-
938
- $this->tweet_set = $trimmed_tweets;
939
- }
940
-
941
- protected function removeStringFromText( $string, $text) {
942
- return str_replace( $string, '', $text );
943
- }
944
-
945
- /**
946
- * captures additional data for "Pro" features
947
- *
948
- * @param $trimmed array current set of trimmed tweets
949
- * @param $tweet array raw tweet data from api
950
- * @return array
951
- */
952
- protected function filterTrimmedTweets( $trimmed, $tweet )
953
- {
954
- if ( isset( $tweet['in_reply_to_screen_name'] ) ) {
955
- $trimmed['in_reply_to_screen_name'] = $tweet['in_reply_to_screen_name'];
956
- $trimmed['entities']['user_mentions'][0]['name'] = isset( $tweet['entities']['user_mentions'][0]['name'] ) ? $tweet['entities']['user_mentions'][0]['name'] : '';
957
- $trimmed['in_reply_to_status_id_str'] = $tweet['in_reply_to_status_id_str'];
958
- }
959
-
960
- if ( isset( $tweet['extended_entities']['media'] ) ) {
961
- // if there is media, we need to remove the media url from the tweet text
962
- $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
963
- if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
964
- $trimmed['text'] = $this->removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $text );
965
- }
966
- $num_media = count( $tweet['extended_entities']['media'] );
967
- for ( $i = 0; $i < $num_media; $i++ ) {
968
- $trimmed['extended_entities']['media'][$i]['media_url_https'] = $tweet['extended_entities']['media'][$i]['media_url_https'];
969
- $trimmed['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
970
- if ( isset( $tweet['extended_entities']['media'][$i]['sizes'] ) ) {
971
- $trimmed['extended_entities']['media'][$i]['sizes'] = $tweet['extended_entities']['media'][$i]['sizes'];
972
- }
973
- if ( $tweet['extended_entities']['media'][$i]['type'] == 'video' || $tweet['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
974
- foreach ( $tweet['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
975
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
976
- $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
977
- }
978
- }
979
- if ( ! isset( $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
980
- $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
981
- }
982
- }
983
- }
984
-
985
- } elseif ( isset( $tweet['entities']['media'] ) ) {
986
- // if there is media, we need to remove the media url from the tweet text
987
- $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
988
- if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
989
- $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['media'][0]['url'], $text );
990
- }
991
-
992
- $num_media = count( $tweet['entities']['media'] );
993
- for ( $i = 0; $i < $num_media; $i++ ) {
994
- $trimmed['entities']['media'][$i]['media_url_https'] = $tweet['entities']['media'][$i]['media_url_https'];
995
- $trimmed['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
996
- if ( isset( $tweet['entities']['media'][$i]['sizes'] ) ) {
997
- $trimmed['entities']['media'][$i]['sizes'] = $tweet['entities']['media'][$i]['sizes'];
998
- }
999
- if ( $tweet['entities']['media'][$i]['type'] == 'video' || $tweet['entities']['media'][$i]['type'] == 'animated_gif' ) {
1000
- foreach ( $tweet['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1001
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1002
- $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1003
- }
1004
- }
1005
- if ( ! isset( $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1006
- $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['entities']['media'][$i]['video_info']['variants'][0]['url'];
1007
- }
1008
- }
1009
- }
1010
-
1011
- }
1012
-
1013
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
1014
- // if there is media, we need to remove the media url from the tweet text
1015
- $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1016
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
1017
- $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1018
- }
1019
-
1020
- $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
1021
- for ( $i = 0; $i < $num_media; $i++ ) {
1022
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'];
1023
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
1024
- if ( isset( $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1025
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'];
1026
- }
1027
- if ( $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1028
- foreach ( $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1029
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1030
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1031
- }
1032
- }
1033
- if ( ! isset( $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1034
- $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1035
- }
1036
- }
1037
- }
1038
-
1039
- } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
1040
- // if there is media, we need to remove the media url from the tweet text
1041
- $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1042
- if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
1043
- $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
1044
- }
1045
-
1046
- $num_media = count( $tweet['retweeted_status']['entities']['media'] );
1047
- for( $i = 0; $i < $num_media; $i++ ) {
1048
- $trimmed['retweeted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['entities']['media'][$i]['media_url_https'];
1049
- $trimmed['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
1050
- if ( isset( $tweet['retweeted_status']['entities']['media'][$i]['sizes'] ) ) {
1051
- $trimmed['retweeted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['entities']['media'][$i]['sizes'];
1052
- }
1053
- if ( $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1054
- foreach ( $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1055
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1056
- $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1057
- }
1058
- }
1059
- if ( ! isset( $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1060
- $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1061
- }
1062
- }
1063
- }
1064
-
1065
- } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
1066
- // if there is media, we need to remove the media url from the tweet text
1067
- $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1068
- if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1069
- $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
1070
- }
1071
-
1072
- $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
1073
- for( $i = 0; $i < $num_media; $i++ ) {
1074
- $trimmed['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1075
- $trimmed['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
1076
- if ( $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1077
- foreach ( $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1078
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1079
- $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1080
- }
1081
- }
1082
- if ( ! isset( $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1083
- $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1084
- }
1085
- }
1086
- }
1087
-
1088
- } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
1089
- // if there is media, we need to remove the media url from the tweet text
1090
- $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1091
- if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
1092
- $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
1093
- }
1094
-
1095
- $num_media = count( $tweet['quoted_status']['entities']['media'] );
1096
- for( $i = 0; $i < $num_media; $i++ ) {
1097
- $trimmed['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['entities']['media'][$i]['media_url_https'];
1098
- $trimmed['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
1099
- if ( $tweet['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1100
- foreach ( $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1101
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1102
- $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1103
- }
1104
- }
1105
- if ( ! isset( $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1106
- $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1107
- }
1108
- }
1109
- }
1110
-
1111
- }
1112
-
1113
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] ) ) {
1114
- // if there is media, we need to remove the media url from the tweet text
1115
- $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1116
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1117
- $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1118
- }
1119
- $num_media = count( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] );
1120
- for ( $i = 0; $i < $num_media; $i++ ) {
1121
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1122
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'];
1123
- if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1124
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'];
1125
- }
1126
- if ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1127
- foreach ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1128
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1129
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1130
- }
1131
- }
1132
- if ( ! isset( $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1133
- $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1134
- }
1135
- }
1136
- }
1137
- } elseif ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'] ) ) {
1138
- // if there is media, we need to remove the media url from the tweet text
1139
- $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1140
- if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'] ) ) {
1141
- $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'], $retweeted_text );
1142
- }
1143
- $num_media = count( $tweet['retweeted_status']['quoted_status']['entities']['media'] );
1144
- for( $i = 0; $i < $num_media; $i++ ) {
1145
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'];
1146
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'];
1147
- if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] ) ) {
1148
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'];
1149
- }
1150
- if ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1151
- foreach ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1152
- if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1153
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1154
- }
1155
- }
1156
- if ( ! isset( $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1157
- $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1158
- }
1159
- }
1160
- }
1161
- }
1162
-
1163
- //remove the url from the text if it links to a quoted tweet that is already linked to
1164
- if ( isset( $tweet['quoted_status'] ) ) {
1165
- $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
1166
- if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
1167
- $text = isset( $trimmed['full_text'] ) ? $trimmed['full_text'] : $trimmed['text'];
1168
- $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
1169
- }
1170
- }
1171
-
1172
-
1173
- // used to generate twitter cards
1174
- if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
1175
- $trimmed['entities']['urls'][0]['expanded_url'] = $tweet['entities']['urls'][0]['expanded_url'];
1176
- }
1177
-
1178
- if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
1179
- $trimmed['retweeted_status']['entities']['urls'][0]['expanded_url'] = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
1180
- }
1181
-
1182
- return $trimmed;
1183
- }
1184
-
1185
- /**
1186
- * will create a transient with the tweet cache if one doesn't exist, the data seems valid, and caching is active
1187
- */
1188
- public function maybeCacheTweets()
1189
- {
1190
- if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
1191
- $this->trimTweetData();
1192
- $cache = json_encode( $this->tweet_set );
1193
- set_transient( $this->transient_name, $cache, $this->feed_options['cache_time'] );
1194
- }
1195
- }
1196
-
1197
- /**
1198
- * returns a JSON string to be used in the data attribute that contains the shortcode data
1199
- */
1200
- public function getShortCodeJSON()
1201
- {
1202
- $json_data = '{';
1203
- $i = 0;
1204
- $len = is_array( $this->atts ) ? count( $this->atts ) : 0;
1205
-
1206
- if ( ! empty( $this->atts ) ) {
1207
- foreach ( $this->atts as $key => $value) {
1208
- if ( $i == $len - 1 ) {
1209
- $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;';
1210
- } else {
1211
- $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;, ';
1212
- }
1213
- $i++;
1214
- }
1215
- }
1216
-
1217
- $json_data .= '}';
1218
-
1219
- return $json_data;
1220
- }
1221
-
1222
- /**
1223
- * uses the endpoint to determing what get fields need to be set
1224
- *
1225
- * @param $end_point api endpoint needed
1226
- * @param $feed_term term associated with the endpoint, user name or search term
1227
- * @return array the get fields for the request
1228
- */
1229
- protected function setGetFieldsArray( $end_point, $feed_term )
1230
- {
1231
- $get_fields = array();
1232
-
1233
- $get_fields['tweet_mode'] = 'extended';
1234
-
1235
- if ( $end_point === 'usertimeline' ) {
1236
- if ( ! empty ( $feed_term ) ) {
1237
- $get_fields['screen_name'] = $feed_term;
1238
- }
1239
- if ( !$this->feed_options['selfreplies'] ) {
1240
- $get_fields['exclude_replies'] = 'true';
1241
- }
1242
- }
1243
- if ( $end_point === 'hometimeline' ) {
1244
- $get_fields['exclude_replies'] = 'true';
1245
- if ( !$this->feed_options['selfreplies'] ) {
1246
- $get_fields['exclude_replies'] = 'true';
1247
- }
1248
- }
1249
- if ( $end_point === 'search' ) {
1250
- $get_fields['q'] = $feed_term;
1251
- }
1252
-
1253
- return $get_fields;
1254
- }
1255
-
1256
- /**
1257
- * attempts to connect and retrieve tweets from the Twitter api
1258
- *
1259
- * @return mixed|string object containing the response
1260
- */
1261
- public function apiConnectionResponse( $end_point, $feed_term )
1262
- {
1263
- // Only can be set in the options page
1264
- $request_settings = array(
1265
- 'consumer_key' => $this->feed_options['consumer_key'],
1266
- 'consumer_secret' => $this->feed_options['consumer_secret'],
1267
- 'access_token' => $this->feed_options['access_token'],
1268
- 'access_token_secret' => $this->feed_options['access_token_secret'],
1269
- );
1270
-
1271
- // For pagination, an extra post needs to be retrieved since the last post is
1272
- // included in the next set
1273
- $count = $this->feed_options['count'];
1274
-
1275
- $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1276
-
1277
- if ( ! empty( $this->last_id_data ) ) {
1278
- $count++;
1279
- $max_id = $this->last_id_data;
1280
- }
1281
- $get_fields['count'] = $count;
1282
-
1283
- // max_id parameter should only be included for the second set of posts
1284
- if ( isset( $max_id ) ) {
1285
- $get_fields['max_id'] = $max_id;
1286
- }
1287
-
1288
- include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1289
-
1290
- // actual connection
1291
- $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1292
- $twitter_connect->setUrlBase();
1293
- $twitter_connect->setGetFields( $get_fields );
1294
- $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1295
-
1296
- return $twitter_connect->performRequest();
1297
- }
1298
-
1299
- private function getTweetsSinceID( $since_id, $end_point = 'search', $feed_term, $count )
1300
- {
1301
- // Only can be set in the options page
1302
- $request_settings = array(
1303
- 'consumer_key' => $this->feed_options['consumer_key'],
1304
- 'consumer_secret' => $this->feed_options['consumer_secret'],
1305
- 'access_token' => $this->feed_options['access_token'],
1306
- 'access_token_secret' => $this->feed_options['access_token_secret'],
1307
- );
1308
-
1309
- $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1310
-
1311
- $get_fields['since_id'] = $since_id;
1312
-
1313
- $get_fields['count'] = $count;
1314
-
1315
- include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1316
-
1317
- // actual connection
1318
- $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1319
- $twitter_connect->setUrlBase();
1320
- $twitter_connect->setGetFields( $get_fields );
1321
- $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1322
-
1323
- return $twitter_connect->performRequest();
1324
- }
1325
-
1326
- /**
1327
- * If the feed runs out of tweets to display for some reason,
1328
- * this function creates a graceful failure message
1329
- *
1330
- * @param $feed_options
1331
- * @return string html for "out of tweets" message
1332
- */
1333
- protected function getOutOfTweetsHtml( $feed_options )
1334
- {
1335
- $html = '';
1336
-
1337
- $html .= '<div class="ctf-out-of-tweets">';
1338
- $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1339
- $html .= '<p>';
1340
- $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" data-size="large" data-url="'.get_home_url().'">Share</a>';
1341
- if ( !empty( $feed_options['screenname'] ) ) {
1342
- $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1343
- }
1344
- $html .= '</p>';
1345
- if ( !$feed_options['disableintents'] ) {
1346
- $html .= "<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>";
1347
- }
1348
- $html .= '</div>';
1349
-
1350
- return $html;
1351
- }
1352
-
1353
- /**
1354
- * creates opening html for the feed
1355
- *
1356
- * @return string opening html that creates the feed
1357
- */
1358
- public function getFeedOpeningHtml()
1359
- {
1360
- $feed_options = $this->feed_options;
1361
- $ctf_data_disablelinks = ($feed_options['disablelinks'] == 'true') ? ' data-ctfdisablelinks="true"' : '';
1362
- $ctf_data_linktextcolor = $feed_options['linktextcolor'] != '' ? ' data-ctflinktextcolor="'.$feed_options['linktextcolor'].'"' : '';
1363
- $ctf_enable_intents = $feed_options['disableintents'] === false && ctf_show( 'actions', $feed_options ) ? ' data-ctfintents="1"' : '';
1364
- $ctf_data_needed = $this->num_tweets_needed;
1365
- $ctf_feed_type = ! empty ( $feed_options['type'] ) ? esc_attr( $feed_options['type'] ) : 'multiple';
1366
- $ctf_feed_classes = 'ctf ctf-type-' . $ctf_feed_type;
1367
- $ctf_feed_classes .= ' ' . $feed_options['class'] . ' ctf-styles';
1368
- $ctf_feed_classes .= $feed_options['width_mobile_no_fixed'] ? ' ctf-width-resp' : '';
1369
- if ( $this->check_for_duplicates ) { $ctf_feed_classes .= ' ctf-no-duplicates'; }
1370
- $ctf_feed_classes = apply_filters( 'ctf_feed_classes', $ctf_feed_classes ); //add_filter( 'ctf_feed_classes', function( $ctf_feed_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1371
- $ctf_feed_html = '';
1372
-
1373
- $ctf_feed_html .= '<!-- Custom Twitter Feeds by Smash Balloon -->';
1374
- $ctf_feed_html .= '<div id="ctf" class="' . $ctf_feed_classes . '" style="' . $feed_options['width'] . $feed_options['height'] . $feed_options['bgcolor'] . '" data-ctfshortcode="' . $this->getShortCodeJSON() . '"' .$ctf_data_disablelinks . $ctf_data_linktextcolor . $ctf_enable_intents . ' data-ctfneeded="'. $ctf_data_needed .'">';
1375
- $tweet_set = $this->tweet_set;
1376
-
1377
- // dynamically include header
1378
- if ( $feed_options['showheader'] ) {
1379
- $ctf_feed_html .= $this->getFeedHeaderHtml( $tweet_set, $this->feed_options );
1380
- }
1381
-
1382
- $ctf_feed_html .= '<div class="ctf-tweets">';
1383
-
1384
- return $ctf_feed_html;
1385
- }
1386
-
1387
- /**
1388
- * creates opening html for the feed
1389
- *
1390
- * @return string opening html that creates the feed
1391
- */
1392
- public function getFeedClosingHtml()
1393
- {
1394
- $feed_options = $this->feed_options;
1395
- $ctf_feed_html = '';
1396
-
1397
- $ctf_feed_html .= '</div>'; // closing div for ctf-tweets
1398
-
1399
- if ( $feed_options['showbutton'] ) {
1400
- $ctf_feed_html .= '<a href="javascript:void(0);" id="ctf-more" class="ctf-more" style="' . $feed_options['buttoncolor'] . $feed_options['buttontextcolor'] . '"><span>' . $feed_options['buttontext'] . '</span></a>';
1401
- }
1402
-
1403
- if ( $feed_options['creditctf'] ) {
1404
- $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1405
- }
1406
-
1407
- $ctf_feed_html .= '</div>'; // closing div tag for #ctf
1408
-
1409
- if ( $feed_options['ajax_theme'] ) {
1410
- $ctf_feed_html .= '<script type="text/javascript" src="' . CTF_JS_URL . '"></script>';
1411
- }
1412
-
1413
- return $ctf_feed_html;
1414
- }
1415
-
1416
- /**
1417
- * creates html for header of the feed
1418
- *
1419
- * @param $tweet_set string trimmed tweets to be added to the feed
1420
- * @param $feed_options options for the feed
1421
- * @return string html that creates the header of the feed
1422
- */
1423
- protected function getFeedHeaderHtml( $tweet_set, $feed_options )
1424
- {
1425
- $ctf_header_html = '';
1426
- $ctf_no_bio = ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) ? '' : ' ctf-no-bio';
1427
-
1428
- // temporary workaround for cached http images
1429
- $tweet_set[0]['user']['profile_image_url_https'] = isset( $tweet_set[0]['user']['profile_image_url_https'] ) ? $tweet_set[0]['user']['profile_image_url_https'] : $tweet_set[0]['user']['profile_image_url'];
1430
-
1431
-
1432
- if ( $feed_options['type'] === 'usertimeline' ) {
1433
- $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1434
- $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1435
- $ctf_header_html .= '<div class="ctf-header-text">';
1436
- $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1437
- $ctf_header_html .= '<span class="ctf-header-name">';
1438
-
1439
- if ( $feed_options['headertext'] != '' ) {
1440
- $ctf_header_html .= esc_html( $feed_options['headertext'] );
1441
- } else {
1442
- $ctf_header_html .= esc_html( $tweet_set[0]['user']['name'] );
1443
- }
1444
-
1445
- $ctf_header_html .= '</span>';
1446
-
1447
- if ( $tweet_set[0]['user']['verified'] == 1 ) {
1448
- $ctf_header_html .= '<span class="ctf-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1449
- }
1450
-
1451
- $ctf_header_html .= '<span class="ctf-header-follow">' . ctf_get_fa_el( 'fa-twitter' ) . 'Follow</span>';
1452
- $ctf_header_html .= '</p>';
1453
-
1454
- if ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) {
1455
- $ctf_header_html .= '<p class="ctf-header-bio" style="' . $feed_options['headertextcolor'] . '">' . $tweet_set[0]['user']['description'] . '</p>';
1456
- }
1457
-
1458
- $ctf_header_html .= '</div>';
1459
- $ctf_header_html .= '<div class="ctf-header-img">';
1460
- $ctf_header_html .= '<div class="ctf-header-img-hover">' . ctf_get_fa_el( 'fa-twitter' ) . '</div>';
1461
- $ctf_header_html .= '<img src="' . $tweet_set[0]['user']['profile_image_url_https'] . '" alt="' . $tweet_set[0]['user']['name'] . '" width="48" height="48">';
1462
- $ctf_header_html .= '</div>';
1463
- $ctf_header_html .= '</a>';
1464
- $ctf_header_html .= '</div>';
1465
- } else {
1466
-
1467
- if ( $feed_options['type'] === 'search' ) {
1468
- $default_header_text = $feed_options['headertext'] != '' ? esc_html($feed_options['headertext']) : $feed_options['feed_term'];
1469
- $url_part = 'hashtag/' . str_replace("#", "", $feed_options['feed_term']);
1470
- } else {
1471
- $default_header_text = 'Twitter';
1472
- $url_part = $feed_options['screenname']; //Need to get screenname here
1473
- }
1474
-
1475
- $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1476
- $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" class="ctf-header-link">';
1477
- $ctf_header_html .= '<div class="ctf-header-text">';
1478
- $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1479
- $ctf_header_html .= '</div>';
1480
- $ctf_header_html .= '<div class="ctf-header-img">';
1481
- $ctf_header_html .= '<div class="ctf-header-generic-icon">';
1482
- $ctf_header_html .= ctf_get_fa_el( 'fa-twitter' );
1483
- $ctf_header_html .= '</div>';
1484
- $ctf_header_html .= '</div>';
1485
- $ctf_header_html .= '</a>';
1486
- $ctf_header_html .= '</div>';
1487
- }
1488
-
1489
- return $ctf_header_html;
1490
- }
1491
-
1492
- /**
1493
- * outputs the html for a set of tweets to be used in the feed
1494
- *
1495
- * @param int $is_pagination 1 or 0, used to differentiate between the first set and subsequent tweet sets
1496
- *
1497
- * @return string $tweet_html
1498
- */
1499
- public function getTweetSetHtml( $is_pagination = 0 )
1500
- {
1501
- $tweet_set = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
1502
- $len = min( $this->feed_options['num'] + $is_pagination, count( $tweet_set ) );
1503
- $i = $is_pagination; // starts at index "1" to offset duplicate tweet
1504
- $feed_options = $this->feed_options;
1505
- $tweet_html = $this->feed_html;
1506
-
1507
- if ( $is_pagination && ( ! isset ( $tweet_set[1]['id_str'] ) ) ) {
1508
- $tweet_html .= $this->getOutOfTweetsHtml( $this->feed_options );
1509
- } else {
1510
- while ( $i < $len ) {
1511
-
1512
- // run a check to accommodate the "search" endpoint as well
1513
- $post = $tweet_set[$i];
1514
-
1515
- // temporary workaround for cached http images
1516
- $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1517
-
1518
- // save the original tweet data in case it's a retweet
1519
- $post_id = $post['id_str'];
1520
- $author = strtolower( $post['user']['screen_name'] );
1521
-
1522
- // creates a string of classes applied to each tweet
1523
- $tweet_classes = 'ctf-item ctf-author-' . $author .' ctf-new';
1524
- if ( !ctf_show( 'avatar', $feed_options ) ) $tweet_classes .= ' ctf-hide-avatar';
1525
- $tweet_classes = apply_filters( 'ctf_tweet_classes', $tweet_classes ); // add_filter( 'ctf_tweet_classes', function( $tweet_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1526
-
1527
- // check for retweet
1528
- $retweet_data_att = '';
1529
- if ( isset( $post['retweeted_status'] ) ) {
1530
- $retweeter = array(
1531
- 'name' => $post['user']['name'],
1532
- 'screen_name' => $post['user']['screen_name']
1533
- );
1534
- $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
1535
- if ( isset( $post['retweeted_status'] ))
1536
- $post = $post['retweeted_status'];
1537
-
1538
- // temporary workaround for cached http images
1539
- $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1540
- $tweet_classes .= ' ctf-retweet';
1541
- } else {
1542
- unset( $retweeter );
1543
- }
1544
-
1545
- // check for quoted
1546
- if ( isset( $post['quoted_status'] ) ) {
1547
- $tweet_classes .= ' ctf-quoted';
1548
- $quoted = $post['quoted_status'];
1549
- $quoted_media_text = '';
1550
- if ( ( isset( $quoted['extended_entities']['media'][0] ) || isset( $quoted['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1551
- $quoted_media = isset( $quoted['extended_entities']['media'] ) ? $quoted['extended_entities']['media'] : $quoted['entities']['media'];
1552
- $quoted_media_count = count( $quoted_media );
1553
- switch ( $quoted_media[0]['type'] ) {
1554
- case 'video':
1555
- case 'animated_gif':
1556
- $quoted_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1557
- break;
1558
- default:
1559
- if ( $quoted_media_count > 1 ) {
1560
- $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap ctf-multi-media-icon">' . $quoted_media_count . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1561
- } else {
1562
- $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap">' . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1563
- }
1564
- break;
1565
- }
1566
- } else {
1567
- unset( $quoted_media );
1568
- }
1569
- } else {
1570
- unset( $quoted );
1571
- unset( $quoted_media_text );
1572
- }
1573
-
1574
- // check for media [0]['type']
1575
- $post_media_text = '';
1576
- $post_media_count = 0;
1577
- if ( ( isset( $post['extended_entities']['media'][0] ) || isset( $post['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1578
- $post_media = isset( $post['extended_entities']['media'] ) ? $post['extended_entities']['media'] : $post['entities']['media'];
1579
- $post_media_count = count( $post_media );
1580
- switch ( $post_media[0]['type'] ) {
1581
- case 'video':
1582
- case 'animated_gif':
1583
- $post_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1584
- break;
1585
- default:
1586
- if ( $post_media_count > 1 ) {
1587
- $post_media_text .= $post_media_count . ctf_get_fa_el( 'fa-picture-o' );
1588
- } else {
1589
- $post_media_text .= ctf_get_fa_el( 'fa-picture-o' );
1590
- }
1591
- break;
1592
- }
1593
- } else {
1594
- unset( $post_media );
1595
- }
1596
-
1597
- // include tweet view
1598
- $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1599
-
1600
- if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1601
- $tweet_html .= '<div class="ctf-context">';
1602
- $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1603
- $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . $feed_options['retweetedtext'] . '</a>';
1604
- $tweet_html .= '</div>';
1605
- }
1606
-
1607
- if ( ctf_show( 'avatar', $feed_options ) || ctf_show( 'logo', $feed_options ) || ctf_show( 'author', $feed_options ) || ctf_show( 'date', $feed_options ) ) {
1608
-
1609
- $tweet_html .= '<div class="ctf-author-box">';
1610
- $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1611
- if ( ctf_show( 'avatar', $feed_options ) ) {
1612
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1613
- $tweet_html .= '<img src="' . $post['user']['profile_image_url_https'] . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1614
- $tweet_html .= '</a>';
1615
- }
1616
-
1617
- if ( ctf_show( 'author', $feed_options ) ) {
1618
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1619
- if ( $post['user']['verified'] == 1 ) {
1620
- $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1621
- }
1622
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1623
- $tweet_html .= '<span class="ctf-screename-sep">&middot;</span>';
1624
- }
1625
-
1626
- if ( ctf_show( 'date', $feed_options ) ) {
1627
- $tweet_html .= '<div class="ctf-tweet-meta">';
1628
- //https://twitter.com/EnterLaw/status/869452491041243137
1629
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1630
- $tweet_html .= '</div>';
1631
- } // show date
1632
- $tweet_html .= '</div>';
1633
- if ( ctf_show( 'logo', $feed_options ) ) {
1634
- $tweet_html .= '<div class="ctf-corner-logo" style="' . $feed_options['logosize'] . $feed_options['logocolor'] . '">';
1635
- $tweet_html .= ctf_get_fa_el( 'fa-twitter' );
1636
- $tweet_html .= '</div>';
1637
- }
1638
- $tweet_html .= '</div>';
1639
- }
1640
-
1641
- if ( ctf_show( 'text', $feed_options ) ) {
1642
- $post_text = apply_filters( 'ctf_tweet_text', $post['text'], $feed_options, $post );
1643
-
1644
- $tweet_html .= '<div class="ctf-tweet-content">';
1645
-
1646
- if ( $feed_options['linktexttotwitter'] ) {
1647
- $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank">';
1648
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1649
- $tweet_html .= '</a>';
1650
- } else {
1651
- $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text );
1652
-
1653
- if( $post_media_count > 0 ){
1654
- $multi_class = '';
1655
- if ( $post_media_count > 1 ) {
1656
- $multi_class = ' ctf-multi-media-icon';
1657
- }
1658
- if ( $feed_options['disablelinks'] ) {
1659
- $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1660
- } else {
1661
- $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1662
- }
1663
- }
1664
- } // link text to twitter option is selected
1665
-
1666
- $tweet_html .= '</div>';
1667
- } // show tweet text
1668
-
1669
- if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1670
- $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank">';
1671
- $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1672
-
1673
- if ($quoted['user']['verified'] == 1) {
1674
- $tweet_html .= '<span class="ctf-quoted-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1675
- } // user is verified
1676
- $quoted_text = apply_filters( 'ctf_quoted_tweet_text', $quoted['text'], $feed_options, $quoted );
1677
-
1678
- $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1679
- $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $quoted_text ) . $quoted_media_text . '</p>';
1680
- //$tweet_html .= ;
1681
- $tweet_html .= '</a>';
1682
- }// show link box
1683
-
1684
- $tweet_html .= '<div class="ctf-tweet-actions">';
1685
- if ( ctf_show( 'actions', $feed_options ) ) {
1686
- $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1687
- $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1688
- if ( $post['retweet_count'] > 0 ) {
1689
- $tweet_html .= $post['retweet_count'];
1690
- }
1691
- $tweet_html .= '</span></a>';
1692
- $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1693
- if ( $post['favorite_count'] > 0 ) {
1694
- $tweet_html .= $post['favorite_count'];
1695
- }
1696
- $tweet_html .= '</span></a>';
1697
- }
1698
- if ( ctf_show( 'twitterlink', $feed_options ) ) {
1699
- $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1700
- } // show twitter link or actions
1701
- $tweet_html .= '</div>';
1702
- $tweet_html .= '</div>';
1703
-
1704
- $i++;
1705
- }
1706
- }
1707
- return $tweet_html;
1708
- }
1709
-
1710
- /**
1711
- * displays a message if there is an error in the feed
1712
- *
1713
- * @return string error html
1714
- */
1715
- public function getErrorHtml()
1716
- {
1717
- $error_html = '';
1718
- $error_html .= '<div id="ctf" class="ctf" data-ctfshortcode="' . $this->getShortCodeJSON() . '">';
1719
- $error_html .= '<div class="ctf-error">';
1720
- $error_html .= '<div class="ctf-error-user">';
1721
-
1722
- $error_html .= '</div>';
1723
-
1724
- if ( current_user_can( 'manage_options' ) ) {
1725
- $error_html .= '<div class="ctf-error-admin">';
1726
-
1727
- if ( ! empty( $this->api_obj->api_error_no ) ) {
1728
-
1729
- $error_html .= '<p>Unable to load Tweets</p>';
1730
- $error_html .= '<a class="twitter-share-button"';
1731
- $error_html .= 'href="https://twitter.com/share"';
1732
- $error_html .= 'data-size="large"';
1733
- $error_html .= 'data-url="' . get_the_permalink() . '"';
1734
- $error_html .= 'data-text="Check out this website">';
1735
- $error_html .= '</a>';
1736
-
1737
- if ( !empty( $this->feed_options['screenname'] ) ) {
1738
- $error_html .= '<a class="twitter-follow-button"';
1739
- $error_html .= 'href="https://twitter.com/' . $this->feed_options['screenname'] . '"';
1740
- $error_html .= 'data-show-count="false"';
1741
- $error_html .= 'data-size="large"';
1742
- $error_html .= 'data-dnt="true">Follow</a>';
1743
- }
1744
-
1745
- $error_html .= '<p><b>This message is only visible to admins:</b><br />';
1746
- $error_html .= 'An error has occurred with your feed.<br />';
1747
- if ( $this->missing_credentials ) {
1748
- $error_html .= 'There is a problem with your access token, access token secret, consumer token, or consumer secret<br />';
1749
- }
1750
- if ( isset( $this->errors['error_message'] ) ) {
1751
- $error_html .= $this->errors['error_message'] . '<br />';
1752
- }
1753
- $error_html .= 'The error response from the Twitter API is the following:<br />';
1754
- $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1755
- $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1756
- $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/" target="_blank">Click here to troubleshoot</a></p>';
1757
-
1758
-
1759
- }
1760
-
1761
- $error_html .= '</div>';
1762
- }
1763
- $error_html .= '</div>'; // end .ctf-error
1764
- $error_html .= '</div>'; // end #ctf
1765
-
1766
- return $error_html;
1767
- }
1768
  }
1
+ <?php
2
+ /**
3
+ * Class CtfFeed
4
+ *
5
+ * Creates the settings for the feed and outputs the html
6
+ */
7
+
8
+ // Don't load directly
9
+ if ( ! defined( 'ABSPATH' ) ) {
10
+ die( '-1' );
11
+ }
12
+
13
+ class CtfFeed
14
+ {
15
+ /**
16
+ * @var array
17
+ */
18
+ public $errors = array();
19
+
20
+ /**
21
+ * @var array
22
+ */
23
+ protected $atts;
24
+
25
+ /**
26
+ * @var string
27
+ */
28
+ protected $last_id_data;
29
+
30
+ private $num_needed_input;
31
+
32
+ /**
33
+ * @var mixed|void
34
+ */
35
+ protected $db_options;
36
+
37
+ /**
38
+ * @var array
39
+ */
40
+ public $feed_options = array();
41
+
42
+ /**
43
+ * @var mixed|void
44
+ */
45
+ public $missing_credentials;
46
+
47
+ /**
48
+ * @var string
49
+ */
50
+ public $transient_name;
51
+
52
+ /**
53
+ * @var bool
54
+ */
55
+ protected $transient_data = false;
56
+
57
+ /**
58
+ * @var int
59
+ */
60
+ private $num_tweets_needed;
61
+
62
+ private $check_for_duplicates = false;
63
+
64
+ /**
65
+ * @var array
66
+ */
67
+ public $tweet_set;
68
+
69
+ /**
70
+ * @var object
71
+ */
72
+ public $api_obj;
73
+
74
+ /**
75
+ * @var string
76
+ */
77
+ public $feed_html;
78
+
79
+ private $persistent_index;
80
+
81
+
82
+ /**
83
+ * retrieves and sets options that apply to the feed
84
+ *
85
+ * @param array $atts data from the shortcode
86
+ * @param string $last_id_data the last visible tweet on the feed, empty string if first set
87
+ * @param int $num_needed_input this number represents the number left to retrieve after the first set
88
+ */
89
+ public function __construct( $atts, $last_id_data, $num_needed_input )
90
+ {
91
+ $this->atts = $atts;
92
+ $this->last_id_data = $last_id_data;
93
+ $this->num_needed_input = $num_needed_input;
94
+ $this->db_options = get_option( 'ctf_options', array() );
95
+ }
96
+
97
+ /**
98
+ * creates and returns all of the data needed to generate the output for the feed
99
+ *
100
+ * @param array $atts data from the shortcode
101
+ * @param string $last_id_data the last visible tweet on the feed, empty string if first set
102
+ * @param int $num_needed_input this number represents the number left to retrieve after the first set
103
+ * @return CtfFeed the complete object for the feed
104
+ */
105
+ public static function init( $atts, $last_id_data = '', $num_needed_input = 0, $ids_to_remove = array(), $persistent_index = 1 )
106
+ {
107
+ $feed = new CtfFeed( $atts, $last_id_data, $num_needed_input );
108
+ $feed->setFeedOptions();
109
+
110
+ $feed->setCacheTypeOption();
111
+ if ( $feed->feed_options['persistentcache'] ) {
112
+ $feed->persistent_index = $persistent_index;
113
+ }
114
+
115
+ $feed->setTweetSet();
116
+ return $feed;
117
+ }
118
+
119
+ /**
120
+ * creates all of the feed options with shortcode settings having the highest priority
121
+ */
122
+ protected function setFeedOptions()
123
+ {
124
+ $this->setFeedTypeAndTermOptions();
125
+
126
+ $bool_false = array (
127
+ 'have_own_tokens',
128
+ 'includereplies',
129
+ 'ajax_theme',
130
+ 'width_mobile_no_fixed',
131
+ 'disablelinks',
132
+ 'linktexttotwitter',
133
+ 'creditctf',
134
+ 'selfreplies',
135
+ 'disableintents',
136
+ 'shorturls'
137
+ );
138
+ $this->setStandardBoolOptions( $bool_false, false );
139
+
140
+ $this->setAccessTokenAndSecretOptions();
141
+ $this->setConsumerKeyAndSecretOptions();
142
+
143
+ $db_only = array(
144
+ 'request_method'
145
+ );
146
+ $this->setDatabaseOnlyOptions( $db_only );
147
+
148
+ $this->setStandardTextOptions( 'num', 5 );
149
+
150
+ $standard_text = array(
151
+ 'class',
152
+ 'headertext',
153
+ 'dateformat',
154
+ 'datecustom',
155
+ 'mtime',
156
+ 'htime',
157
+ 'nowtime'
158
+ );
159
+ $this->setStandardTextOptions( $standard_text, '' );
160
+
161
+ $this->setStandardTextOptions( 'retweetedtext', __( 'Retweeted', 'custom-twitter-feeds' ) );
162
+ $this->setStandardTextOptions( 'font_method', 'svg' );
163
+ $this->setStandardTextOptions( 'multiplier', 1.25 );
164
+ $this->setStandardTextOptions( 'twitterlinktext', 'Twitter' );
165
+
166
+ $this->setStandardTextOptions( 'buttontext', __( 'Load More...', 'custom-twitter-feeds' ) );
167
+ $this->setStandardTextOptions( 'textlength', 280 );
168
+ $text_size = array(
169
+ 'authortextsize',
170
+ 'tweettextsize',
171
+ 'datetextsize',
172
+ 'quotedauthorsize',
173
+ 'iconsize',
174
+ 'logosize'
175
+ );
176
+ $this->setTextSizeOptions( $text_size );
177
+
178
+ $text_weight = array(
179
+ 'authortextweight',
180
+ 'tweettextweight',
181
+ 'datetextweight',
182
+ 'quotedauthorweight'
183
+ );
184
+ $this->setStandardStyleProperty( $text_weight, 'font-weight' );
185
+
186
+ $text_color = array(
187
+ 'headertextcolor',
188
+ 'textcolor',
189
+ 'linktextcolor',
190
+ 'iconcolor',
191
+ 'logocolor',
192
+ 'buttontextcolor'
193
+ );
194
+ $this->setStandardStyleProperty( $text_color, 'color' );
195
+
196
+ $bg_color = array(
197
+ 'bgcolor',
198
+ 'tweetbgcolor',
199
+ 'headerbgcolor',
200
+ 'buttoncolor'
201
+ );
202
+ $this->setStandardStyleProperty( $bg_color, 'background-color' );
203
+
204
+ $bool_true = array(
205
+ 'persistentcache',
206
+ 'showbutton',
207
+ 'showbio',
208
+ 'showheader'
209
+ );
210
+ $this->setStandardBoolOptions( $bool_true, true );
211
+
212
+ $this->setDimensionOptions();
213
+ $this->setCacheTimeOptions();
214
+ $this->setIncludeExcludeOptions();
215
+ }
216
+
217
+ /**
218
+ * uses the feed options to set the the tweets in the feed by using
219
+ * an existing set in a cache or by retrieving them from Twitter
220
+ */
221
+ protected function setTweetSet()
222
+ {
223
+ $this->setTransientName();
224
+ $success = $this->maybeSetTweetsFromCache();
225
+
226
+ if ( ! $success ) {
227
+ $this->maybeSetTweetsFromTwitter();
228
+ }
229
+
230
+ $this->num_tweets_needed = $this->numTweetsNeeded();
231
+ }
232
+
233
+ /**
234
+ * the access token and secret must be set in order for the feed to work
235
+ * this function processes the user input and sets a flag if none are entered
236
+ */
237
+ private function setAccessTokenAndSecretOptions()
238
+ {
239
+ $this->feed_options['access_token'] = isset( $this->db_options['access_token'] ) && strlen( $this->db_options['access_token'] ) > 30 ? $this->db_options['access_token'] : 'missing';
240
+ $this->feed_options['access_token_secret'] = isset( $this->db_options['access_token_secret'] ) && strlen( $this->db_options['access_token_secret'] ) > 30 ? $this->db_options['access_token_secret'] : 'missing';
241
+
242
+ // verify that access token and secret have been entered
243
+ $this->setMissingCredentials();
244
+ }
245
+
246
+ /**
247
+ * generates the flag if there are missing access tokens
248
+ */
249
+ private function setMissingCredentials() {
250
+ if ( $this->feed_options['access_token'] == 'missing' || $this->feed_options['access_token_secret'] == 'missing' ) {
251
+ $this->missing_credentials = true;
252
+ } else {
253
+ $this->missing_credentials = false;
254
+ }
255
+ }
256
+
257
+ /**
258
+ * processes the consumer key and secret options
259
+ */
260
+ protected function setConsumerKeyAndSecretOptions()
261
+ {
262
+ if ( $this->feed_options['have_own_tokens'] ) {
263
+ $this->feed_options['consumer_key'] = isset( $this->db_options['consumer_key'] ) && strlen( $this->db_options['consumer_key'] ) > 15 ? $this->db_options['consumer_key'] : 'FPYSYWIdyUIQ76Yz5hdYo5r7y';
264
+ $this->feed_options['consumer_secret'] = isset( $this->db_options['consumer_secret'] ) && strlen( $this->db_options['consumer_secret'] ) > 30 ? $this->db_options['consumer_secret'] : 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
265
+ } else {
266
+ $this->feed_options['consumer_key'] ='FPYSYWIdyUIQ76Yz5hdYo5r7y';
267
+ $this->feed_options['consumer_secret'] = 'GqPj9BPgJXjRKIGXCULJljocGPC62wN2eeMSnmZpVelWreFk9z';
268
+ }
269
+ }
270
+
271
+ /**
272
+ * determines what value to use and saves it for the appropriate key in the feed_options array
273
+ *
274
+ * @param $options mixed the key or array of keys to be set
275
+ * @param $options_page string options page this setting is set on
276
+ * @param string $default default value to use if there is no user input
277
+ */
278
+ public function setDatabaseOnlyOptions( $options, $default = '' )
279
+ {
280
+ if ( is_array( $options ) ) {
281
+ foreach ( $options as $option ) {
282
+ $this->feed_options[$option] = isset( $this->db_options[$option] ) && ! empty( $this->db_options[$option] ) ? $this->db_options[$option] : $default;
283
+ }
284
+ } else {
285
+ $this->feed_options[$options] = isset( $this->db_options[$options] ) && ! empty( $this->db_options[$options] ) ? $this->db_options[$options] : $default;
286
+ }
287
+ }
288
+
289
+ /**
290
+ * determines what value to use and saves it for the appropriate key in the feed_options array
291
+ *
292
+ * @param $options mixed the key or array of keys to be set
293
+ * @param $options_page string options page this setting is set on
294
+ * @param string $default default value to use if there is no user input
295
+ */
296
+ public function setStandardTextOptions( $options, $default = '' )
297
+ {
298
+ if ( is_array( $options ) ) {
299
+ foreach ( $options as $option ) {
300
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? esc_attr( __( $this->atts[$option], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$option] ) ? esc_attr( $this->db_options[$option] ) : $default );
301
+ }
302
+ } else {
303
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( __( $this->atts[$options], 'custom-twitter-feeds' ) ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
304
+ }
305
+ }
306
+
307
+ /**
308
+ * creates the appropriate style attribute string for the text size setting
309
+ *
310
+ * @param $value mixed pixel size or other that the user has selected
311
+ * @return string string for the style attribute
312
+ */
313
+ public static function processTextSizeStyle( $value )
314
+ {
315
+ if ( $value == '' ) {
316
+ return '';
317
+ }
318
+ $processed_value = $value == 'inherit' ? '' : 'font-size: ' . $value . 'px;';
319
+
320
+ return $processed_value;
321
+ }
322
+
323
+ /**
324
+ * determines what value to use and saves it for the appropriate key in the feed_options array
325
+ *
326
+ * @param $options mixed the key or array of keys to be set
327
+ * @param string $default default value to use if there is no user input
328
+ */
329
+ public function setTextSizeOptions( $options, $default = '' )
330
+ {
331
+ if ( is_array( $options ) ) {
332
+ foreach ( $options as $option ) {
333
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$option] ) ) : ( isset( $this->db_options[$option] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$option] ) ) : $default );
334
+ }
335
+ } else {
336
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->atts[$options] ) ) : ( isset( $this->db_options[$options] ) ? $this->processTextSizeStyle( esc_attr( $this->db_options[$options] ) ) : $default );
337
+ }
338
+ }
339
+
340
+ /**
341
+ * determines what value to use and saves it for the appropriate key in the feed_options array
342
+ *
343
+ * @param $options mixed the key or array of keys to be set
344
+ * @param $property string name of the property to be set
345
+ * @param string $default default value to use if there is no user input
346
+ */
347
+ public function setStandardStyleProperty( $options, $property, $default = '' )
348
+ {
349
+ if ( is_array( $options ) ) {
350
+ foreach ( $options as $option ) {
351
+ $this->feed_options[$option] = isset( $this->atts[$option] ) && $this->atts[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$option] ) . ';' : ( isset( $this->db_options[$option] ) && $this->db_options[$option] != '#' && $this->db_options[$option] != '' && $this->db_options[$option] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$option] ) . ';' : $default );
352
+ }
353
+ } else {
354
+ $this->feed_options[$options] = isset( $this->atts[$options] ) && $this->atts[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->atts[$options] ) . ';' : ( isset( $this->db_options[$options] ) && $this->db_options[$options] != '#' && $this->db_options[$options] != '' && $this->db_options[$options] != 'inherit' ? $property . ': ' . esc_attr( $this->db_options[$options] ) . ';' : $default );
355
+ }
356
+ }
357
+
358
+ /**
359
+ * determines what value to use and saves it for the appropriate key in the feed_options array
360
+ *
361
+ * @param $options mixed the key or array of keys to be set
362
+ * @param bool|true $default default value to use if there is no user input
363
+ */
364
+ public function setStandardBoolOptions( $options, $default = true )
365
+ {
366
+ if ( is_array( $options ) ) {
367
+ foreach ( $options as $option ) {
368
+ $this->feed_options[$option] = isset( $this->atts[$option] ) ? ( $this->atts[$option] === 'true' ) : ( isset( $this->db_options[$option] ) ? (bool) $this->db_options[$option] : (bool) $default );
369
+ }
370
+ } else {
371
+ $this->feed_options[$options] = isset( $this->atts[$options] ) ? esc_attr( $this->atts[$options] ) : ( isset( $this->db_options[$options] ) ? esc_attr( $this->db_options[$options] ) : $default );
372
+ }
373
+ }
374
+
375
+ /**
376
+ * sets the width and height of the feed based on user input
377
+ */
378
+ public function setDimensionOptions()
379
+ {
380
+ $this->feed_options['width'] = isset( $this->atts['width'] ) ? 'width: '. esc_attr( $this->atts['width'] ) .';' : ( ( isset( $this->db_options['width'] ) && $this->db_options['width'] != '' ) ? 'width: '. esc_attr( $this->db_options['width'] ) . ( isset( $this->db_options['width_unit'] ) ? esc_attr( $this->db_options['width_unit'] ) : '%' ) . ';' : '' );
381
+ $this->feed_options['height'] = isset( $this->atts['height'] ) ? 'height: '. esc_attr( $this->atts['height'] ) .';' : ( ( isset( $this->db_options['height'] ) && $this->db_options['height'] != '' ) ? 'height: '. esc_attr( $this->db_options['height'] ) . ( isset( $this->db_options['height_unit'] ) ? esc_attr( $this->db_options['height_unit'] ) : 'px' ) . ';' : '' );
382
+ }
383
+
384
+ /**
385
+ * sets the cache time based on user input
386
+ */
387
+ public function setCacheTimeOptions()
388
+ {
389
+ $user_cache = isset( $this->db_options['cache_time'] ) ? ( $this->db_options['cache_time'] * $this->db_options['cache_time_unit'] ) : HOUR_IN_SECONDS;
390
+
391
+ if ( $this->feed_options['have_own_tokens'] ) {
392
+ $this->feed_options['cache_time'] = max( $user_cache, 60 );
393
+ } else {
394
+ $this->feed_options['cache_time'] = max( $user_cache, 3600 );
395
+ }
396
+ }
397
+
398
+
399
+ /**
400
+ * sets the number of tweets to retrieve
401
+ */
402
+ public function setTweetsToRetrieve()
403
+ {
404
+ $min_tweets_to_retrieve = 10;
405
+
406
+ if ( $this->num_needed_input < 1 ) {
407
+ if ( $this->feed_options['includereplies'] ) {
408
+ $this->feed_options['count'] = $this->feed_options['num'];
409
+ } else {
410
+ if ( $this->feed_options['num'] < 10 ) {
411
+ $this->feed_options['count'] = max( round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.6 ), $min_tweets_to_retrieve );
412
+ } elseif ( $this->feed_options['num'] < 30 ) {
413
+ $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] * 1.2 );
414
+ } else {
415
+ $this->feed_options['count'] = round( $this->feed_options['num'] * $this->feed_options['multiplier'] );
416
+ }
417
+ }
418
+ } else {
419
+ $this->feed_options['count'] = max( $this->num_needed_input, 50 );
420
+ $this->feed_options['num'] = $this->num_needed_input;
421
+ }
422
+
423
+ }
424
+
425
+ /**
426
+ * sets the feed type and associated parameter
427
+ */
428
+ public function setFeedTypeAndTermOptions()
429
+ {
430
+ $this->feed_options['type'] = '';
431
+ $this->feed_options['feed_term'] = '';
432
+ $this->feed_options['screenname'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
433
+
434
+ if ( isset( $this->atts['home'] ) && $this->atts['home'] == 'true' ) {
435
+ $this->feed_options['type'] = 'hometimeline';
436
+ }
437
+ if ( isset( $this->atts['screenname'] ) ) {
438
+ $this->feed_options['type'] = 'usertimeline';
439
+ $this->feed_options['feed_term'] = isset( $this->atts['screenname'] ) ? ctf_validate_usertimeline_text( $this->atts['screenname'] ) : ( ( isset( $this->db_options['usertimeline_text'] ) ) ? $this->db_options['usertimeline_text'] : '' );
440
+ $this->feed_options['screenname'] = $this->feed_options['feed_term'];
441
+ }
442
+ if ( isset( $this->atts['search'] ) || isset( $this->atts['hashtag'] ) ) {
443
+ $this->feed_options['type'] = 'search';
444
+ $this->working_term = isset( $this->atts['hashtag'] ) ? $this->atts['hashtag'] : ( isset( $this->atts['search'] ) ? $this->atts['search'] : '' );
445
+ $this->feed_options['feed_term'] = isset( $this->working_term ) ? ctf_validate_search_text( $this->working_term ) : ( ( isset( $this->db_options['search_text'] ) ) ? $this->db_options['search_text'] : '' );
446
+ $this->check_for_duplicates = true;
447
+ }
448
+
449
+ if ( $this->feed_options['type'] == '' ) {
450
+ $this->feed_options['type'] = isset( $this->db_options['type'] ) ? $this->db_options['type'] : 'usertimeline';
451
+ switch ( $this->feed_options['type'] ) {
452
+ case 'usertimeline':
453
+ $this->feed_options['feed_term'] = isset( $this->db_options['usertimeline_text'] ) ? $this->db_options['usertimeline_text'] : '';
454
+ break;
455
+ case 'hometimeline':
456
+ $this->feed_options['type'] = 'hometimeline';
457
+ break;
458
+ case 'search':
459
+ $this->feed_options['feed_term'] = isset( $this->db_options['search_text'] ) ? $this->db_options['search_text'] : '';
460
+ $this->check_for_duplicates = true;
461
+ break;
462
+ }
463
+ }
464
+ }
465
+
466
+ /**
467
+ * sets the visible parts of each tweet for the feed
468
+ */
469
+ public function setIncludeExcludeOptions()
470
+ {
471
+ $this->feed_options['tweet_excludes'] = array();
472
+ $this->feed_options['tweet_includes'] = isset( $this->atts['include'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['include'] ) ) ) : array();
473
+
474
+ if ( empty( $this->feed_options['tweet_includes'][0] ) ) {
475
+ $this->feed_options['tweet_excludes'] = isset( $this->atts['exclude'] ) ? explode( ',', str_replace( ', ', ',', esc_attr( $this->atts['exclude'] ) ) ) : array();
476
+ }
477
+ if ( empty( $this->feed_options['tweet_excludes'][0] ) && empty( $this->feed_options['tweet_includes'][0] ) ) {
478
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_retweeter'] ) && $this->db_options['include_retweeter'] == false ? null : 'retweeter';
479
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_avatar'] ) && $this->db_options['include_avatar'] == false ? null : 'avatar';
480
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_author'] ) && $this->db_options['include_author'] == false ? null : 'author';
481
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_text'] ) && $this->db_options['include_text'] == false ? null : 'text';
482
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_media_placeholder'] ) && $this->db_options['include_media_placeholder'] == false ? null : 'placeholder';
483
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_date'] ) && $this->db_options['include_date'] == false ? null : 'date';
484
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_actions'] ) && $this->db_options['include_actions'] == false ? null : 'actions';
485
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_twitterlink'] ) && $this->db_options['include_twitterlink'] == false ? null : 'twitterlink';
486
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_linkbox'] ) && $this->db_options['include_linkbox'] == false ? null : 'linkbox';
487
+ $this->feed_options['tweet_includes'][] = isset( $this->db_options['include_logo'] ) && $this->db_options['include_logo'] == false ? null : 'logo';
488
+ }
489
+
490
+ }
491
+
492
+ /**
493
+ * sets the transient name for the caching system
494
+ */
495
+ public function setTransientName()
496
+ {
497
+ $last_id_data = $this->last_id_data;
498
+ $num = isset( $this->feed_options['num'] ) ? $this->feed_options['num'] : '';
499
+
500
+ switch ( $this->feed_options['type'] ) {
501
+ case 'hometimeline' :
502
+ $this->transient_name = 'ctf_' . $last_id_data . 'hometimeline'. $num;
503
+ break;
504
+ case 'usertimeline' :
505
+ $screenname = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
506
+ $this->transient_name = substr( 'ctf__' . $last_id_data . $screenname . $num, 0, 45 );
507
+ break;
508
+ case 'search' :
509
+ $hashtag = isset( $this->feed_options['feed_term'] ) ? $this->feed_options['feed_term'] : '';
510
+ $this->transient_name = substr( 'ctf_' . $last_id_data . $hashtag . $num, 0, 45 );
511
+ break;
512
+ }
513
+ }
514
+
515
+ public function setCacheTypeOption() {
516
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
517
+ $this->feed_options['persistentcache'] = true;
518
+ } else {
519
+ $this->feed_options['persistentcache'] = false;
520
+ }
521
+ }
522
+
523
+ /**
524
+ * checks the data available in the cache to make sure it seems to be valid
525
+ *
526
+ * @return bool|string false if the cache is valid, error otherwise
527
+ */
528
+ private function validateCache()
529
+ {
530
+ if ( isset( $this->transient_data[0] ) ) {
531
+ return false;
532
+ } else {
533
+ return 'invalid cache';
534
+ }
535
+ }
536
+
537
+ /**
538
+ * will use the cached data in the feed if data seems to be valid and user
539
+ * wants to use caching
540
+ *
541
+ * @return bool|mixed false if none is set, tweet set otherwise
542
+ */
543
+ public function maybeSetTweetsFromCache()
544
+ {
545
+ if ( $this->feed_options['persistentcache'] && ( $this->feed_options['type'] == 'search' || $this->feed_options['type'] == 'hashtag' ) ) {
546
+ $persistent_cache_tweets = $this->persistentCacheTweets();
547
+ if ( is_array( $persistent_cache_tweets ) ) {
548
+ $this->transient_data = array_slice( $persistent_cache_tweets, ( $this->persistent_index - $this->feed_options['num'] - 1 ) , $this->persistent_index );
549
+ } else {
550
+ $this->transient_data = $persistent_cache_tweets;
551
+ }
552
+ } else {
553
+ $this->transient_data = get_transient( $this->transient_name );
554
+ if ( ! is_array( $this->transient_data ) ) {
555
+ $this->transient_data = json_decode( $this->transient_data, $assoc = true );
556
+ }
557
+
558
+ if ( $this->feed_options['cache_time'] <= 0 ) {
559
+ return $this->tweet_set = false;
560
+ }
561
+ }
562
+ // validate the transient data
563
+ if ( $this->transient_data ) {
564
+ $this->errors['cache_status'] = $this->validateCache();
565
+ if ( $this->errors['cache_status'] === false ) {
566
+ return $this->tweet_set = $this->transient_data;
567
+ } else {
568
+ return $this->tweet_set = false;
569
+ }
570
+ } else {
571
+ $this->errors['cache_status'] = 'none found';
572
+ return $this->tweet_set = false;
573
+ }
574
+ }
575
+
576
+ private function persistentCacheTweets()
577
+ {
578
+ // if cache exists get cached data
579
+ $includewords = ! empty( $this->feed_options['includewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['includewords'] ), 0, 10 ) : '';
580
+ $excludewords = ! empty( $this->feed_options['excludewords'] ) ? substr( str_replace( array( ',', ' ' ), '', $this->feed_options['excludewords'] ), 0, 5 ) : '';
581
+ $cache_name = substr( 'ctf_!_' . $this->feed_options['feed_term'] . $includewords . $excludewords, 0, 45 );
582
+ $cache_time_limit_reached = get_transient( $cache_name ) ? false : true;
583
+
584
+ $existing_cache = get_option( $cache_name, false );
585
+ if ( $existing_cache && ! is_array( $existing_cache ) ) {
586
+ $existing_cache = json_decode( $existing_cache, $assoc = true );
587
+ }
588
+
589
+ $this->persistent_index = $this->persistent_index + $this->feed_options['num'];
590
+
591
+ $this->feed_options['count'] = 200;
592
+
593
+ if ( ! empty( $this->last_id_data ) || ( ! $cache_time_limit_reached && $existing_cache ) ) {
594
+ return $existing_cache;
595
+ } elseif ( $existing_cache ) {
596
+ // use "since-id" to look for more in an api request
597
+ $since_id = $existing_cache[0]['id_str'];
598
+ $api_obj = $this->getTweetsSinceID( $since_id, 'search', $this->feed_options['feed_term'], $this->feed_options['count'] );
599
+ // add any new tweets to the cache
600
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
601
+
602
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : array();
603
+
604
+ // add a transient to delay another api retrieval
605
+ set_transient( $cache_name, true, $this->feed_options['cache_time'] );
606
+
607
+ if ( empty( $tweets ) ) {
608
+ if ( ! is_array( $existing_cache ) ) {
609
+ return false;
610
+ } else {
611
+ return $existing_cache;
612
+ }
613
+ } else {
614
+ $tweet_set = $this->reduceTweetSetData( $tweets, false );
615
+ }
616
+ $tweet_set = $this->appendPersistentCacheTweets( $existing_cache, $tweet_set );
617
+ $cache_set = json_encode( $tweet_set );
618
+
619
+ update_option( $cache_name, $cache_set );
620
+
621
+ return $tweet_set;
622
+ // else if cached data doesn't exist
623
+ } else {
624
+ // make a request for last 200 tweets
625
+ $api_obj = $this->apiConnectionResponse( 'search', $this->feed_options['feed_term'] );
626
+ // cache them in a regular option
627
+ $this->tweet_set = json_decode( $api_obj->json , $assoc = true );
628
+
629
+ // check for errors/tweets present
630
+ if ( isset( $this->tweet_set['errors'][0] ) ) {
631
+ if ( empty( $this->api_obj ) ) {
632
+ $this->api_obj = new stdClass();
633
+ }
634
+ $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
635
+ $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
636
+
637
+ $this->tweet_set = false;
638
+ }
639
+
640
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
641
+
642
+ if ( empty( $tweets ) ) {
643
+ $this->errors['error_message'] = 'No Tweets returned';
644
+ $this->tweet_set = false;
645
+ } else {
646
+ $this->tweet_set = $this->reduceTweetSetData( $tweets, false );
647
+ }
648
+
649
+ // create a new persistent cache
650
+ if ( $this->tweet_set && isset( $this->tweet_set[0] ) ) {
651
+ $tweet_set = json_encode( $this->tweet_set );
652
+
653
+ update_option( $cache_name, $tweet_set );
654
+
655
+ // update list of persistent cache
656
+ $cache_list = get_option( 'ctf_cache_list', array() );
657
+
658
+ $cache_list[] = $cache_name;
659
+
660
+ update_option( 'ctf_cache_list', $cache_list );
661
+ }
662
+
663
+ return $this->tweet_set;
664
+ }
665
+
666
+ // add the search parameter to another option that contains a list of all persistent caches available
667
+ }
668
+
669
+ private function reduceTweetSetData( $tweet_set, $limit = true ) {
670
+ if ( $this->check_for_duplicates ) {
671
+ $this->tweet_set = $this->removeDuplicates( $tweet_set, $limit );
672
+ }
673
+
674
+ if ( $this->feed_options['selfreplies'] ) {
675
+ $this->tweet_set = $this->filterTweetSet( $tweet_set, $limit );
676
+ }
677
+
678
+ $this->tweet_set = $tweet_set;
679
+ $this->trimTweetData( false );
680
+ return $this->tweet_set;
681
+ }
682
+
683
+ /**
684
+ * this takes the current set of tweets and processes them until there are
685
+ * enough filtered tweets to create the feed from
686
+ */
687
+ private function filterTweetSet( $tweet_set, $limit = true )
688
+ {
689
+ $working_tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
690
+ $usable_tweets = 0;
691
+ if ( $limit ) {
692
+ $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
693
+ } else {
694
+ $tweets_needed = 200;
695
+ }
696
+ $i = 0; // index of working_tweet_set
697
+ $still_setting_filtered_tweets = true;
698
+
699
+ while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
700
+ if ( isset ( $working_tweet_set[$i] ) ) { // if there is another tweet available
701
+ if ( !$this->feed_options['selfreplies'] && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] ) ) {
702
+ unset( $working_tweet_set[$i] );
703
+ } elseif ( $this->feed_options['selfreplies']
704
+ && isset( $working_tweet_set[$i]['in_reply_to_screen_name'] )
705
+ && $working_tweet_set[$i]['in_reply_to_screen_name'] !== $working_tweet_set[$i]['user']['screen_name']) {
706
+ unset( $working_tweet_set[$i] );
707
+ } else {
708
+ $usable_tweets++;
709
+ }
710
+ } else {
711
+ $still_setting_filtered_tweets = false;
712
+ }
713
+
714
+ // if there are no more tweets needed
715
+ if ( $usable_tweets >= $tweets_needed ) {
716
+ $still_setting_filtered_tweets = false;
717
+ } else {
718
+ $i++;
719
+ }
720
+
721
+ }
722
+
723
+ if ( is_array( $working_tweet_set ) ) {
724
+ return array_values( $working_tweet_set );
725
+ } else {
726
+ return false;
727
+ }
728
+ }
729
+
730
+ private function appendPersistentCacheTweets( $existing_cache )
731
+ {
732
+ if ( is_array( $this->tweet_set ) ) {
733
+ $tweet_set = array_merge( $this->tweet_set, $existing_cache );
734
+ } else {
735
+ $tweet_set = $existing_cache;
736
+ }
737
+
738
+ $tweet_set = array_slice( $tweet_set, 0, 150 );
739
+
740
+ return $tweet_set;
741
+ }
742
+
743
+
744
+ private function removeDuplicates( $tweet_set, $limit = true )
745
+ {
746
+ $tweet_set = isset( $tweet_set['statuses'] ) ? $tweet_set['statuses'] : $tweet_set;
747
+ $usable_tweets = 0;
748
+ if ( $limit ) {
749
+ $tweets_needed = $this->feed_options['count'] + 1; // magic number here should be ADT
750
+ } else {
751
+ $tweets_needed = 200;
752
+ }
753
+ $ids_of_tweets_to_remove = array();
754
+
755
+ $i = 0; // index of tweet_set
756
+ $still_setting_filtered_tweets = true;
757
+ while ( $still_setting_filtered_tweets ) { // stays true until the number to display is reached or out of tweets
758
+ if ( isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ) {
759
+ unset( $tweet_set[$i] );
760
+ } elseif ( isset( $tweet_set[$i] ) ) {
761
+ $id = isset( $tweet_set[$i]['retweeted_status']['id_str'] ) ? $tweet_set[$i]['retweeted_status']['id_str'] : $tweet_set[$i]['id_str'];
762
+ if ( in_array( $id, $ids_of_tweets_to_remove ) ) {
763
+ unset( $tweet_set[$i] );
764
+ } else {
765
+ $usable_tweets++;
766
+ $ids_of_tweets_to_remove[] = $id;
767
+ }
768
+ } else {
769
+ $still_setting_filtered_tweets = false;
770
+ }
771
+
772
+ // if there are no more tweets needed
773
+ if ( $usable_tweets >= $tweets_needed ) {
774
+ $still_setting_filtered_tweets = false;
775
+ } else {
776
+ $i++;
777
+ }
778
+
779
+ }
780
+
781
+ if ( is_array( $tweet_set ) ) {
782
+ return array_values( $tweet_set );
783
+ } else {
784
+ return false;
785
+ }
786
+ }
787
+
788
+ /**
789
+ * will attempt to connect to the api to retrieve current tweets
790
+ */
791
+ public function maybeSetTweetsFromTwitter()
792
+ {
793
+ $this->setTweetsToRetrieve();
794
+ $this->api_obj = $this->apiConnectionResponse( $this->feed_options['type'], $this->feed_options['feed_term'] );
795
+ $this->tweet_set = json_decode( $this->api_obj->json , $assoc = true );
796
+
797
+ // check for errors/tweets present
798
+ if ( isset( $this->tweet_set['errors'][0] ) ) {
799
+ $this->api_obj->api_error_no = $this->tweet_set['errors'][0]['code'];
800
+ $this->api_obj->api_error_message = $this->tweet_set['errors'][0]['message'];
801
+ $this->tweet_set = false;
802
+ }
803
+
804
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
805
+
806
+ if ( empty( $tweets ) ) {
807
+ $this->errors['error_message'] = 'No Tweets returned';
808
+ $this->tweet_set = false;
809
+ }
810
+
811
+ if ( $this->check_for_duplicates ) {
812
+ $this->tweet_set = $this->removeDuplicates( $this->tweet_set );
813
+ }
814
+ }
815
+
816
+
817
+ /**
818
+ * calculates how many tweets short the feed is so more can be retrieved via ajax
819
+ *
820
+ * @return int number of tweets needed
821
+ */
822
+ protected function numTweetsNeeded() {
823
+ $tweet_count = 0;
824
+ if ( isset( $this->tweet_set['statuses'] ) && is_array( $this->tweet_set['statuses'] ) ) {
825
+ $tweet_count = count( $this->tweet_set['statuses'] );
826
+ } elseif ( isset( $this->tweet_set ) && is_array( $this->tweet_set ) ) {
827
+ $tweet_count = count( $this->tweet_set );
828
+ }
829
+
830
+ return $this->feed_options['num'] - $tweet_count;
831
+ }
832
+
833
+ /**
834
+ * trims the unused data retrieved for more efficient caching
835
+ */
836
+ protected function trimTweetData( $limit = true )
837
+ {
838
+ $is_pagination = !empty( $this->last_id_data ) ? 1 : 0;
839
+ $tweets = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
840
+ if ( $limit ) {
841
+ $len = min( $this->feed_options['num'] + $is_pagination, count( $tweets ) );
842
+ } else {
843
+ $len = count( $tweets );
844
+ }
845
+ $trimmed_tweets = array();
846
+
847
+ // for header
848
+ if ( $this->last_id_data == '' && isset( $tweets[0] ) ) { // if this is the first set of tweets
849
+ $trimmed_tweets[0]['user']['name']= $tweets[0]['user']['name'];
850
+ $trimmed_tweets[0]['user']['description']= $tweets[0]['user']['description'];
851
+ $trimmed_tweets[0]['user']['statuses_count']= $tweets[0]['user']['statuses_count'];
852
+ $trimmed_tweets[0]['user']['followers_count']= $tweets[0]['user']['followers_count'];
853
+ }
854
+
855
+ for ( $i = 0; $i < $len; $i++ ) {
856
+ $trimmed_tweets[$i]['user']['name'] = $tweets[$i]['user']['name'];
857
+ $trimmed_tweets[$i]['user']['screen_name'] = $tweets[$i]['user']['screen_name'];
858
+ $trimmed_tweets[$i]['user']['verified'] = $tweets[$i]['user']['verified'];
859
+ $trimmed_tweets[$i]['user']['profile_image_url_https'] = $tweets[$i]['user']['profile_image_url_https'];
860
+ $trimmed_tweets[$i]['user']['utc_offset']= $tweets[$i]['user']['utc_offset'];
861
+ $trimmed_tweets[$i]['text'] = isset( $tweets[$i]['text'] ) ? $tweets[$i]['text'] : $tweets[$i]['full_text'];
862
+ $trimmed_tweets[$i]['id_str']= $tweets[$i]['id_str'];
863
+ $trimmed_tweets[$i]['created_at']= $tweets[$i]['created_at'];
864
+ $trimmed_tweets[$i]['retweet_count']= $tweets[$i]['retweet_count'];
865
+ $trimmed_tweets[$i]['favorite_count']= $tweets[$i]['favorite_count'];
866
+
867
+ if ( isset( $tweets[$i]['entities']['urls'][0] ) ) {
868
+ foreach ( $tweets[$i]['entities']['urls'] as $url ) {
869
+ $trimmed_tweets[$i]['entities']['urls'][] = array(
870
+ 'url' => $url['url'],
871
+ 'expanded_url' => $url['expanded_url'],
872
+ 'display_url' => $url['display_url'],
873
+
874
+ );
875
+ }
876
+ }
877
+
878
+ if ( isset( $tweets[$i]['retweeted_status'] ) ) {
879
+ $trimmed_tweets[$i]['retweeted_status']['user']['name'] = $tweets[$i]['retweeted_status']['user']['name'];
880
+ $trimmed_tweets[$i]['retweeted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['user']['screen_name'];
881
+ $trimmed_tweets[$i]['retweeted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['user']['verified'];
882
+ $trimmed_tweets[$i]['retweeted_status']['user']['profile_image_url_https'] = $tweets[$i]['retweeted_status']['user']['profile_image_url_https'];
883
+ $trimmed_tweets[$i]['retweeted_status']['user']['utc_offset']= $tweets[$i]['retweeted_status']['user']['utc_offset'];
884
+ $trimmed_tweets[$i]['retweeted_status']['text'] = isset( $tweets[$i]['retweeted_status']['text'] ) ? $tweets[$i]['retweeted_status']['text'] : $tweets[$i]['retweeted_status']['full_text'];
885
+ $trimmed_tweets[$i]['retweeted_status']['id_str'] = $tweets[$i]['retweeted_status']['id_str'];
886
+ $trimmed_tweets[$i]['retweeted_status']['created_at']= $tweets[$i]['retweeted_status']['created_at'];
887
+ $trimmed_tweets[$i]['retweeted_status']['retweet_count']= $tweets[$i]['retweeted_status']['retweet_count'];
888
+ $trimmed_tweets[$i]['retweeted_status']['favorite_count']= $tweets[$i]['retweeted_status']['favorite_count'];
889
+ if ( isset( $tweets[$i]['retweeted_status']['entities']['urls'][0] ) ) {
890
+ foreach ( $tweets[$i]['retweeted_status']['entities']['urls'] as $url ) {
891
+ $trimmed_tweets[$i]['retweeted_status']['entities']['urls'][] = array(
892
+ 'url' => $url['url'],
893
+ 'expanded_url' => $url['expanded_url'],
894
+ 'display_url' => $url['display_url'],
895
+
896
+ );
897
+ }
898
+ }
899
+ }
900
+
901
+ if ( isset( $tweets[$i]['retweeted_status']['quoted_status'] ) ) {
902
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['name'];
903
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['screen_name'];
904
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['user']['verified'] = $tweets[$i]['retweeted_status']['quoted_status']['user']['verified'];
905
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['text'] = isset( $tweets[$i]['retweeted_status']['quoted_status']['text'] ) ? $tweets[$i]['retweeted_status']['quoted_status']['text'] : $tweets[$i]['retweeted_status']['quoted_status']['full_text'];
906
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['id_str'] = $tweets[$i]['retweeted_status']['quoted_status']['id_str'];
907
+ if ( isset( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][0] ) ) {
908
+ foreach ( $tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'] as $url ) {
909
+ $trimmed_tweets[$i]['retweeted_status']['quoted_status']['entities']['urls'][] = array(
910
+ 'url' => $url['url'],
911
+ 'expanded_url' => $url['expanded_url'],
912
+ 'display_url' => $url['display_url'],
913
+ );
914
+ }
915
+ }
916
+ }
917
+
918
+ if ( isset( $tweets[$i]['quoted_status'] ) ) {
919
+ $trimmed_tweets[$i]['quoted_status']['user']['name'] = $tweets[$i]['quoted_status']['user']['name'];
920
+ $trimmed_tweets[$i]['quoted_status']['user']['screen_name'] = $tweets[$i]['quoted_status']['user']['screen_name'];
921
+ $trimmed_tweets[$i]['quoted_status']['user']['verified'] = $tweets[$i]['quoted_status']['user']['verified'];
922
+ $trimmed_tweets[$i]['quoted_status']['text'] = isset( $tweets[$i]['quoted_status']['text'] ) ? $tweets[$i]['quoted_status']['text'] : $tweets[$i]['quoted_status']['full_text'];
923
+ $trimmed_tweets[$i]['quoted_status']['id_str'] = $tweets[$i]['quoted_status']['id_str'];
924
+ if ( isset( $tweets[$i]['quoted_status']['entities']['urls'][0] ) ) {
925
+ foreach ( $tweets[$i]['quoted_status']['entities']['urls'] as $url ) {
926
+ $trimmed_tweets[$i]['quoted_status']['entities']['urls'][] = array(
927
+ 'url' => $url['url'],
928
+ 'expanded_url' => $url['expanded_url'],
929
+ 'display_url' => $url['display_url'],
930
+ );
931
+ }
932
+ }
933
+ }
934
+
935
+ $trimmed_tweets[$i] = $this->filterTrimmedTweets( $trimmed_tweets[$i], $tweets[$i] );
936
+ }
937
+
938
+ $this->tweet_set = $trimmed_tweets;
939
+ }
940
+
941
+ protected function removeStringFromText( $string, $text) {
942
+ return str_replace( $string, '', $text );
943
+ }
944
+
945
+ /**
946
+ * captures additional data for "Pro" features
947
+ *
948
+ * @param $trimmed array current set of trimmed tweets
949
+ * @param $tweet array raw tweet data from api
950
+ * @return array
951
+ */
952
+ protected function filterTrimmedTweets( $trimmed, $tweet )
953
+ {
954
+ if ( isset( $tweet['in_reply_to_screen_name'] ) ) {
955
+ $trimmed['in_reply_to_screen_name'] = $tweet['in_reply_to_screen_name'];
956
+ $trimmed['entities']['user_mentions'][0]['name'] = isset( $tweet['entities']['user_mentions'][0]['name'] ) ? $tweet['entities']['user_mentions'][0]['name'] : '';
957
+ $trimmed['in_reply_to_status_id_str'] = $tweet['in_reply_to_status_id_str'];
958
+ }
959
+
960
+ if ( isset( $tweet['extended_entities']['media'] ) ) {
961
+ // if there is media, we need to remove the media url from the tweet text
962
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
963
+ if ( isset( $tweet['extended_entities']['media'][0]['url'] ) ) {
964
+ $trimmed['text'] = $this->removeStringFromText( $tweet['extended_entities']['media'][0]['url'], $text );
965
+ }
966
+ $num_media = count( $tweet['extended_entities']['media'] );
967
+ for ( $i = 0; $i < $num_media; $i++ ) {
968
+ $trimmed['extended_entities']['media'][$i]['media_url_https'] = $tweet['extended_entities']['media'][$i]['media_url_https'];
969
+ $trimmed['extended_entities']['media'][$i]['type'] = $tweet['extended_entities']['media'][$i]['type'];
970
+ if ( isset( $tweet['extended_entities']['media'][$i]['sizes'] ) ) {
971
+ $trimmed['extended_entities']['media'][$i]['sizes'] = $tweet['extended_entities']['media'][$i]['sizes'];
972
+ }
973
+ if ( $tweet['extended_entities']['media'][$i]['type'] == 'video' || $tweet['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
974
+ foreach ( $tweet['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
975
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
976
+ $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
977
+ }
978
+ }
979
+ if ( ! isset( $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
980
+ $trimmed['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
981
+ }
982
+ }
983
+ }
984
+
985
+ } elseif ( isset( $tweet['entities']['media'] ) ) {
986
+ // if there is media, we need to remove the media url from the tweet text
987
+ $text = isset( $tweet['full_text'] ) ? $tweet['full_text'] : $tweet['text'];
988
+ if ( isset( $tweet['entities']['media'][0]['url'] ) ) {
989
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['media'][0]['url'], $text );
990
+ }
991
+
992
+ $num_media = count( $tweet['entities']['media'] );
993
+ for ( $i = 0; $i < $num_media; $i++ ) {
994
+ $trimmed['entities']['media'][$i]['media_url_https'] = $tweet['entities']['media'][$i]['media_url_https'];
995
+ $trimmed['entities']['media'][$i]['type'] = $tweet['entities']['media'][$i]['type'];
996
+ if ( isset( $tweet['entities']['media'][$i]['sizes'] ) ) {
997
+ $trimmed['entities']['media'][$i]['sizes'] = $tweet['entities']['media'][$i]['sizes'];
998
+ }
999
+ if ( $tweet['entities']['media'][$i]['type'] == 'video' || $tweet['entities']['media'][$i]['type'] == 'animated_gif' ) {
1000
+ foreach ( $tweet['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1001
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1002
+ $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1003
+ }
1004
+ }
1005
+ if ( ! isset( $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1006
+ $trimmed['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['entities']['media'][$i]['video_info']['variants'][0]['url'];
1007
+ }
1008
+ }
1009
+ }
1010
+
1011
+ }
1012
+
1013
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'] ) ) {
1014
+ // if there is media, we need to remove the media url from the tweet text
1015
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1016
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'][0]['url'] ) ) {
1017
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1018
+ }
1019
+
1020
+ $num_media = count( $tweet['retweeted_status']['extended_entities']['media'] );
1021
+ for ( $i = 0; $i < $num_media; $i++ ) {
1022
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['media_url_https'];
1023
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['type'];
1024
+ if ( isset( $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1025
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['sizes'];
1026
+ }
1027
+ if ( $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1028
+ foreach ( $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1029
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1030
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1031
+ }
1032
+ }
1033
+ if ( ! isset( $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1034
+ $trimmed['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ } elseif ( isset( $tweet['retweeted_status']['entities']['media'] ) ) {
1040
+ // if there is media, we need to remove the media url from the tweet text
1041
+ $retweeted_text = isset( $tweet['retweeted_status']['full_text'] ) ? $tweet['retweeted_status']['full_text'] : $tweet['retweeted_status']['text'];
1042
+ if ( isset( $tweet['retweeted_status']['entities']['media'][0]['url'] ) ) {
1043
+ $trimmed['retweeted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['entities']['media'][0]['url'], $retweeted_text );
1044
+ }
1045
+
1046
+ $num_media = count( $tweet['retweeted_status']['entities']['media'] );
1047
+ for( $i = 0; $i < $num_media; $i++ ) {
1048
+ $trimmed['retweeted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['entities']['media'][$i]['media_url_https'];
1049
+ $trimmed['retweeted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['entities']['media'][$i]['type'];
1050
+ if ( isset( $tweet['retweeted_status']['entities']['media'][$i]['sizes'] ) ) {
1051
+ $trimmed['retweeted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['entities']['media'][$i]['sizes'];
1052
+ }
1053
+ if ( $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1054
+ foreach ( $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1055
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1056
+ $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1057
+ }
1058
+ }
1059
+ if ( ! isset( $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1060
+ $trimmed['retweeted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1061
+ }
1062
+ }
1063
+ }
1064
+
1065
+ } elseif ( isset( $tweet['quoted_status']['extended_entities']['media'] ) ) {
1066
+ // if there is media, we need to remove the media url from the tweet text
1067
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1068
+ if ( isset( $tweet['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1069
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['extended_entities']['media'][0]['url'], $quoted_text );
1070
+ }
1071
+
1072
+ $num_media = count( $tweet['quoted_status']['extended_entities']['media'] );
1073
+ for( $i = 0; $i < $num_media; $i++ ) {
1074
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1075
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['quoted_status']['extended_entities']['media'][$i]['type'];
1076
+ if ( $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1077
+ foreach ( $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1078
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1079
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1080
+ }
1081
+ }
1082
+ if ( ! isset( $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1083
+ $trimmed['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1084
+ }
1085
+ }
1086
+ }
1087
+
1088
+ } elseif ( isset( $tweet['quoted_status']['entities']['media'] ) ) {
1089
+ // if there is media, we need to remove the media url from the tweet text
1090
+ $quoted_text = isset( $tweet['quoted_status']['full_text'] ) ? $tweet['quoted_status']['full_text'] : $tweet['quoted_status']['text'];
1091
+ if ( isset( $tweet['quoted_status']['entities']['media'][0]['url'] ) ) {
1092
+ $trimmed['quoted_status']['text'] = $this->removeStringFromText( $tweet['quoted_status']['entities']['media'][0]['url'], $quoted_text );
1093
+ }
1094
+
1095
+ $num_media = count( $tweet['quoted_status']['entities']['media'] );
1096
+ for( $i = 0; $i < $num_media; $i++ ) {
1097
+ $trimmed['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['quoted_status']['entities']['media'][$i]['media_url_https'];
1098
+ $trimmed['quoted_status']['entities']['media'][$i]['type'] = $tweet['quoted_status']['entities']['media'][$i]['type'];
1099
+ if ( $tweet['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1100
+ foreach ( $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1101
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1102
+ $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1103
+ }
1104
+ }
1105
+ if ( ! isset( $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1106
+ $trimmed['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1107
+ }
1108
+ }
1109
+ }
1110
+
1111
+ }
1112
+
1113
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] ) ) {
1114
+ // if there is media, we need to remove the media url from the tweet text
1115
+ $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1116
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'] ) ) {
1117
+ $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][0]['url'], $retweeted_text );
1118
+ }
1119
+ $num_media = count( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'] );
1120
+ for ( $i = 0; $i < $num_media; $i++ ) {
1121
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['media_url_https'];
1122
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'];
1123
+ if ( isset( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] ) ) {
1124
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['sizes'];
1125
+ }
1126
+ if ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['type'] == 'animated_gif' ) {
1127
+ foreach ( $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'] as $variant ) {
1128
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1129
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1130
+ }
1131
+ }
1132
+ if ( ! isset( $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1133
+ $trimmed['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['extended_entities']['media'][$i]['video_info']['variants'][0]['url'];
1134
+ }
1135
+ }
1136
+ }
1137
+ } elseif ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'] ) ) {
1138
+ // if there is media, we need to remove the media url from the tweet text
1139
+ $retweeted_text = isset( $tweet['retweeted_status']['quoted_status']['full_text'] ) ? $tweet['retweeted_status']['quoted_status']['full_text'] : $tweet['retweeted_status']['quoted_status']['text'];
1140
+ if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'] ) ) {
1141
+ $trimmed['retweeted_status']['quoted_status']['text'] = $this->removeStringFromText( $tweet['retweeted_status']['quoted_status']['entities']['media'][0]['url'], $retweeted_text );
1142
+ }
1143
+ $num_media = count( $tweet['retweeted_status']['quoted_status']['entities']['media'] );
1144
+ for( $i = 0; $i < $num_media; $i++ ) {
1145
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['media_url_https'];
1146
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'];
1147
+ if ( isset( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] ) ) {
1148
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['sizes'];
1149
+ }
1150
+ if ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'video' || $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['type'] == 'animated_gif' ) {
1151
+ foreach ( $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'] as $variant ) {
1152
+ if ( isset( $variant['content_type'] ) && $variant['content_type'] == 'video/mp4' ) {
1153
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $variant['url'];
1154
+ }
1155
+ }
1156
+ if ( ! isset( $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] ) ) {
1157
+ $trimmed['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][$i]['url'] = $tweet['retweeted_status']['quoted_status']['entities']['media'][$i]['video_info']['variants'][0]['url'];
1158
+ }
1159
+ }
1160
+ }
1161
+ }
1162
+
1163
+ //remove the url from the text if it links to a quoted tweet that is already linked to
1164
+ if ( isset( $tweet['quoted_status'] ) ) {
1165
+ $maybe_remove_index = count( $tweet['entities']['urls'] ) - 1;
1166
+ if ( isset( $tweet['entities']['urls'][$maybe_remove_index]['url'] ) ) {
1167
+ $text = isset( $trimmed['full_text'] ) ? $trimmed['full_text'] : $trimmed['text'];
1168
+ $trimmed['text'] = $this->removeStringFromText( $tweet['entities']['urls'][$maybe_remove_index]['url'], $text );
1169
+ }
1170
+ }
1171
+
1172
+
1173
+ // used to generate twitter cards
1174
+ if ( isset( $tweet['entities']['urls'][0]['expanded_url'] ) ) {
1175
+ $trimmed['entities']['urls'][0]['expanded_url'] = $tweet['entities']['urls'][0]['expanded_url'];
1176
+ }
1177
+
1178
+ if ( isset( $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'] ) ) {
1179
+ $trimmed['retweeted_status']['entities']['urls'][0]['expanded_url'] = $tweet['retweeted_status']['entities']['urls'][0]['expanded_url'];
1180
+ }
1181
+
1182
+ return $trimmed;
1183
+ }
1184
+
1185
+ /**
1186
+ * will create a transient with the tweet cache if one doesn't exist, the data seems valid, and caching is active
1187
+ */
1188
+ public function maybeCacheTweets()
1189
+ {
1190
+ if ( ( ! $this->transient_data || $this->errors['cache_status'] ) && $this->feed_options['cache_time'] > 0 ) {
1191
+ $this->trimTweetData();
1192
+ $cache = json_encode( $this->tweet_set );
1193
+ set_transient( $this->transient_name, $cache, $this->feed_options['cache_time'] );
1194
+ }
1195
+ }
1196
+
1197
+ /**
1198
+ * returns a JSON string to be used in the data attribute that contains the shortcode data
1199
+ */
1200
+ public function getShortCodeJSON()
1201
+ {
1202
+ $json_data = '{';
1203
+ $i = 0;
1204
+ $len = is_array( $this->atts ) ? count( $this->atts ) : 0;
1205
+
1206
+ if ( ! empty( $this->atts ) ) {
1207
+ foreach ( $this->atts as $key => $value) {
1208
+ if ( $i == $len - 1 ) {
1209
+ $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;';
1210
+ } else {
1211
+ $json_data .= '&quot;' . $key . '&quot;: &quot;' . $value . '&quot;, ';
1212
+ }
1213
+ $i++;
1214
+ }
1215
+ }
1216
+
1217
+ $json_data .= '}';
1218
+
1219
+ return $json_data;
1220
+ }
1221
+
1222
+ /**
1223
+ * uses the endpoint to determing what get fields need to be set
1224
+ *
1225
+ * @param $end_point api endpoint needed
1226
+ * @param $feed_term term associated with the endpoint, user name or search term
1227
+ * @return array the get fields for the request
1228
+ */
1229
+ protected function setGetFieldsArray( $end_point, $feed_term )
1230
+ {
1231
+ $get_fields = array();
1232
+
1233
+ $get_fields['tweet_mode'] = 'extended';
1234
+
1235
+ if ( $end_point === 'usertimeline' ) {
1236
+ if ( ! empty ( $feed_term ) ) {
1237
+ $get_fields['screen_name'] = $feed_term;
1238
+ }
1239
+ if ( !$this->feed_options['selfreplies'] ) {
1240
+ $get_fields['exclude_replies'] = 'true';
1241
+ }
1242
+ }
1243
+ if ( $end_point === 'hometimeline' ) {
1244
+ $get_fields['exclude_replies'] = 'true';
1245
+ if ( !$this->feed_options['selfreplies'] ) {
1246
+ $get_fields['exclude_replies'] = 'true';
1247
+ }
1248
+ }
1249
+ if ( $end_point === 'search' ) {
1250
+ $get_fields['q'] = $feed_term;
1251
+ }
1252
+
1253
+ return $get_fields;
1254
+ }
1255
+
1256
+ /**
1257
+ * attempts to connect and retrieve tweets from the Twitter api
1258
+ *
1259
+ * @return mixed|string object containing the response
1260
+ */
1261
+ public function apiConnectionResponse( $end_point, $feed_term )
1262
+ {
1263
+ // Only can be set in the options page
1264
+ $request_settings = array(
1265
+ 'consumer_key' => $this->feed_options['consumer_key'],
1266
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
1267
+ 'access_token' => $this->feed_options['access_token'],
1268
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
1269
+ );
1270
+
1271
+ // For pagination, an extra post needs to be retrieved since the last post is
1272
+ // included in the next set
1273
+ $count = $this->feed_options['count'];
1274
+
1275
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1276
+
1277
+ if ( ! empty( $this->last_id_data ) ) {
1278
+ $count++;
1279
+ $max_id = $this->last_id_data;
1280
+ }
1281
+ $get_fields['count'] = $count;
1282
+
1283
+ // max_id parameter should only be included for the second set of posts
1284
+ if ( isset( $max_id ) ) {
1285
+ $get_fields['max_id'] = $max_id;
1286
+ }
1287
+
1288
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1289
+
1290
+ // actual connection
1291
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1292
+ $twitter_connect->setUrlBase();
1293
+ $twitter_connect->setGetFields( $get_fields );
1294
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1295
+
1296
+ return $twitter_connect->performRequest();
1297
+ }
1298
+
1299
+ private function getTweetsSinceID( $since_id, $end_point = 'search', $feed_term, $count )
1300
+ {
1301
+ // Only can be set in the options page
1302
+ $request_settings = array(
1303
+ 'consumer_key' => $this->feed_options['consumer_key'],
1304
+ 'consumer_secret' => $this->feed_options['consumer_secret'],
1305
+ 'access_token' => $this->feed_options['access_token'],
1306
+ 'access_token_secret' => $this->feed_options['access_token_secret'],
1307
+ );
1308
+
1309
+ $get_fields = $this->setGetFieldsArray( $end_point, $feed_term );
1310
+
1311
+ $get_fields['since_id'] = $since_id;
1312
+
1313
+ $get_fields['count'] = $count;
1314
+
1315
+ include_once( CTF_URL . '/inc/CtfOauthConnect.php' );
1316
+
1317
+ // actual connection
1318
+ $twitter_connect = new CtfOauthConnect( $request_settings, $end_point );
1319
+ $twitter_connect->setUrlBase();
1320
+ $twitter_connect->setGetFields( $get_fields );
1321
+ $twitter_connect->setRequestMethod( $this->feed_options['request_method'] );
1322
+
1323
+ return $twitter_connect->performRequest();
1324
+ }
1325
+
1326
+ /**
1327
+ * If the feed runs out of tweets to display for some reason,
1328
+ * this function creates a graceful failure message
1329
+ *
1330
+ * @param $feed_options
1331
+ * @return string html for "out of tweets" message
1332
+ */
1333
+ protected function getOutOfTweetsHtml( $feed_options )
1334
+ {
1335
+ $html = '';
1336
+
1337
+ $html .= '<div class="ctf-out-of-tweets">';
1338
+ $html .= '<p>' . __( "That's all! No more Tweets to load", 'custom-twitter-feeds' ) . '</p>';
1339
+ $html .= '<p>';
1340
+ $html .= '<a class="twitter-share-button" href="https://twitter.com/share" target="_blank" data-size="large" data-url="'.get_home_url().'">Share</a>';
1341
+ if ( !empty( $feed_options['screenname'] ) ) {
1342
+ $html .= '<a class="twitter-follow-button" href="https://twitter.com/' . $feed_options['screenname'] . '" target="_blank" data-show-count="false" data-size="large" data-dnt="true">Follow</a>';
1343
+ }
1344
+ $html .= '</p>';
1345
+ if ( !$feed_options['disableintents'] ) {
1346
+ $html .= "<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');</script>";
1347
+ }
1348
+ $html .= '</div>';
1349
+
1350
+ return $html;
1351
+ }
1352
+
1353
+ /**
1354
+ * creates opening html for the feed
1355
+ *
1356
+ * @return string opening html that creates the feed
1357
+ */
1358
+ public function getFeedOpeningHtml()
1359
+ {
1360
+ $feed_options = $this->feed_options;
1361
+ $ctf_data_disablelinks = ($feed_options['disablelinks'] == 'true') ? ' data-ctfdisablelinks="true"' : '';
1362
+ $ctf_data_linktextcolor = $feed_options['linktextcolor'] != '' ? ' data-ctflinktextcolor="'.$feed_options['linktextcolor'].'"' : '';
1363
+ $ctf_enable_intents = $feed_options['disableintents'] === false && ctf_show( 'actions', $feed_options ) ? ' data-ctfintents="1"' : '';
1364
+ $ctf_data_needed = $this->num_tweets_needed;
1365
+ $ctf_feed_type = ! empty ( $feed_options['type'] ) ? esc_attr( $feed_options['type'] ) : 'multiple';
1366
+ $ctf_feed_classes = 'ctf ctf-type-' . $ctf_feed_type;
1367
+ $ctf_feed_classes .= ' ' . $feed_options['class'] . ' ctf-styles';
1368
+ $ctf_feed_classes .= $feed_options['width_mobile_no_fixed'] ? ' ctf-width-resp' : '';
1369
+ if ( $this->check_for_duplicates ) { $ctf_feed_classes .= ' ctf-no-duplicates'; }
1370
+ $ctf_feed_classes = apply_filters( 'ctf_feed_classes', $ctf_feed_classes ); //add_filter( 'ctf_feed_classes', function( $ctf_feed_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1371
+ $ctf_feed_html = '';
1372
+
1373
+ $ctf_feed_html .= '<!-- Custom Twitter Feeds by Smash Balloon -->';
1374
+ $ctf_feed_html .= '<div id="ctf" class="' . $ctf_feed_classes . '" style="' . $feed_options['width'] . $feed_options['height'] . $feed_options['bgcolor'] . '" data-ctfshortcode="' . $this->getShortCodeJSON() . '"' .$ctf_data_disablelinks . $ctf_data_linktextcolor . $ctf_enable_intents . ' data-ctfneeded="'. $ctf_data_needed .'">';
1375
+ $tweet_set = $this->tweet_set;
1376
+
1377
+ // dynamically include header
1378
+ if ( $feed_options['showheader'] ) {
1379
+ $ctf_feed_html .= $this->getFeedHeaderHtml( $tweet_set, $this->feed_options );
1380
+ }
1381
+
1382
+ $ctf_feed_html .= '<div class="ctf-tweets">';
1383
+
1384
+ return $ctf_feed_html;
1385
+ }
1386
+
1387
+ /**
1388
+ * creates opening html for the feed
1389
+ *
1390
+ * @return string opening html that creates the feed
1391
+ */
1392
+ public function getFeedClosingHtml()
1393
+ {
1394
+ $feed_options = $this->feed_options;
1395
+ $ctf_feed_html = '';
1396
+
1397
+ $ctf_feed_html .= '</div>'; // closing div for ctf-tweets
1398
+
1399
+ if ( $feed_options['showbutton'] ) {
1400
+ $ctf_feed_html .= '<a href="javascript:void(0);" id="ctf-more" class="ctf-more" style="' . $feed_options['buttoncolor'] . $feed_options['buttontextcolor'] . '"><span>' . $feed_options['buttontext'] . '</span></a>';
1401
+ }
1402
+
1403
+ if ( $feed_options['creditctf'] ) {
1404
+ $ctf_feed_html .= '<div class="ctf-credit-link"><a href="https://smashballoon.com/custom-twitter-feeds" target="_blank">' . ctf_get_fa_el( 'fa-twitter' ) . 'Custom Twitter Feeds Plugin</a></div>';
1405
+ }
1406
+
1407
+ $ctf_feed_html .= '</div>'; // closing div tag for #ctf
1408
+
1409
+ if ( $feed_options['ajax_theme'] ) {
1410
+ $ctf_feed_html .= '<script type="text/javascript" src="' . CTF_JS_URL . '"></script>';
1411
+ }
1412
+
1413
+ return $ctf_feed_html;
1414
+ }
1415
+
1416
+ /**
1417
+ * creates html for header of the feed
1418
+ *
1419
+ * @param $tweet_set string trimmed tweets to be added to the feed
1420
+ * @param $feed_options options for the feed
1421
+ * @return string html that creates the header of the feed
1422
+ */
1423
+ protected function getFeedHeaderHtml( $tweet_set, $feed_options )
1424
+ {
1425
+ $ctf_header_html = '';
1426
+ $ctf_no_bio = ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) ? '' : ' ctf-no-bio';
1427
+
1428
+ // temporary workaround for cached http images
1429
+ $tweet_set[0]['user']['profile_image_url_https'] = isset( $tweet_set[0]['user']['profile_image_url_https'] ) ? $tweet_set[0]['user']['profile_image_url_https'] : $tweet_set[0]['user']['profile_image_url'];
1430
+
1431
+
1432
+ if ( $feed_options['type'] === 'usertimeline' ) {
1433
+ $ctf_header_html .= '<div class="ctf-header' . $ctf_no_bio . '" style="' . $feed_options['headerbgcolor'] . '">';
1434
+ $ctf_header_html .= '<a href="https://twitter.com/' . $tweet_set[0]['user']['screen_name'] . '" target="_blank" title="@' . $tweet_set[0]['user']['screen_name'] . '" class="ctf-header-link">';
1435
+ $ctf_header_html .= '<div class="ctf-header-text">';
1436
+ $ctf_header_html .= '<p class="ctf-header-user" style="' . $feed_options['headertextcolor'] . '">';
1437
+ $ctf_header_html .= '<span class="ctf-header-name">';
1438
+
1439
+ if ( $feed_options['headertext'] != '' ) {
1440
+ $ctf_header_html .= esc_html( $feed_options['headertext'] );
1441
+ } else {
1442
+ $ctf_header_html .= esc_html( $tweet_set[0]['user']['name'] );
1443
+ }
1444
+
1445
+ $ctf_header_html .= '</span>';
1446
+
1447
+ if ( $tweet_set[0]['user']['verified'] == 1 ) {
1448
+ $ctf_header_html .= '<span class="ctf-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1449
+ }
1450
+
1451
+ $ctf_header_html .= '<span class="ctf-header-follow">' . ctf_get_fa_el( 'fa-twitter' ) . 'Follow</span>';
1452
+ $ctf_header_html .= '</p>';
1453
+
1454
+ if ( $feed_options['showbio'] && !empty($tweet_set[0]['user']['description']) ) {
1455
+ $ctf_header_html .= '<p class="ctf-header-bio" style="' . $feed_options['headertextcolor'] . '">' . $tweet_set[0]['user']['description'] . '</p>';
1456
+ }
1457
+
1458
+ $ctf_header_html .= '</div>';
1459
+ $ctf_header_html .= '<div class="ctf-header-img">';
1460
+ $ctf_header_html .= '<div class="ctf-header-img-hover">' . ctf_get_fa_el( 'fa-twitter' ) . '</div>';
1461
+ $ctf_header_html .= '<img src="' . $tweet_set[0]['user']['profile_image_url_https'] . '" alt="' . $tweet_set[0]['user']['name'] . '" width="48" height="48">';
1462
+ $ctf_header_html .= '</div>';
1463
+ $ctf_header_html .= '</a>';
1464
+ $ctf_header_html .= '</div>';
1465
+ } else {
1466
+
1467
+ if ( $feed_options['type'] === 'search' ) {
1468
+ $default_header_text = $feed_options['headertext'] != '' ? esc_html($feed_options['headertext']) : $feed_options['feed_term'];
1469
+ $url_part = 'hashtag/' . str_replace("#", "", $feed_options['feed_term']);
1470
+ } else {
1471
+ $default_header_text = 'Twitter';
1472
+ $url_part = $feed_options['screenname']; //Need to get screenname here
1473
+ }
1474
+
1475
+ $ctf_header_html .= '<div class="ctf-header ctf-header-type-generic" style="' . $feed_options['headerbgcolor'] . '">';
1476
+ $ctf_header_html .= '<a href="https://twitter.com/' . $url_part . '" target="_blank" class="ctf-header-link">';
1477
+ $ctf_header_html .= '<div class="ctf-header-text">';
1478
+ $ctf_header_html .= '<p class="ctf-header-no-bio" style="' . $feed_options['headertextcolor'] . '">' . $default_header_text . '</p>';
1479
+ $ctf_header_html .= '</div>';
1480
+ $ctf_header_html .= '<div class="ctf-header-img">';
1481
+ $ctf_header_html .= '<div class="ctf-header-generic-icon">';
1482
+ $ctf_header_html .= ctf_get_fa_el( 'fa-twitter' );
1483
+ $ctf_header_html .= '</div>';
1484
+ $ctf_header_html .= '</div>';
1485
+ $ctf_header_html .= '</a>';
1486
+ $ctf_header_html .= '</div>';
1487
+ }
1488
+
1489
+ return $ctf_header_html;
1490
+ }
1491
+
1492
+ /**
1493
+ * outputs the html for a set of tweets to be used in the feed
1494
+ *
1495
+ * @param int $is_pagination 1 or 0, used to differentiate between the first set and subsequent tweet sets
1496
+ *
1497
+ * @return string $tweet_html
1498
+ */
1499
+ public function getTweetSetHtml( $is_pagination = 0 )
1500
+ {
1501
+ $tweet_set = isset( $this->tweet_set['statuses'] ) ? $this->tweet_set['statuses'] : $this->tweet_set;
1502
+ $len = min( $this->feed_options['num'] + $is_pagination, count( $tweet_set ) );
1503
+ $i = $is_pagination; // starts at index "1" to offset duplicate tweet
1504
+ $feed_options = $this->feed_options;
1505
+ $tweet_html = $this->feed_html;
1506
+
1507
+ if ( $is_pagination && ( ! isset ( $tweet_set[1]['id_str'] ) ) ) {
1508
+ $tweet_html .= $this->getOutOfTweetsHtml( $this->feed_options );
1509
+ } else {
1510
+ while ( $i < $len ) {
1511
+
1512
+ // run a check to accommodate the "search" endpoint as well
1513
+ $post = $tweet_set[$i];
1514
+
1515
+ // temporary workaround for cached http images
1516
+ $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1517
+
1518
+ // save the original tweet data in case it's a retweet
1519
+ $post_id = $post['id_str'];
1520
+ $author = strtolower( $post['user']['screen_name'] );
1521
+
1522
+ // creates a string of classes applied to each tweet
1523
+ $tweet_classes = 'ctf-item ctf-author-' . $author .' ctf-new';
1524
+ if ( !ctf_show( 'avatar', $feed_options ) ) $tweet_classes .= ' ctf-hide-avatar';
1525
+ $tweet_classes = apply_filters( 'ctf_tweet_classes', $tweet_classes ); // add_filter( 'ctf_tweet_classes', function( $tweet_classes ) { return $ctf_feed_classes . ' new-class'; }, 10, 1 );
1526
+
1527
+ // check for retweet
1528
+ $retweet_data_att = '';
1529
+ if ( isset( $post['retweeted_status'] ) ) {
1530
+ $retweeter = array(
1531
+ 'name' => $post['user']['name'],
1532
+ 'screen_name' => $post['user']['screen_name']
1533
+ );
1534
+ $retweet_data_att = ( $this->check_for_duplicates ) ? ' data-ctfretweetid="'.$post['retweeted_status']['id_str'].'"' : '';
1535
+ if ( isset( $post['retweeted_status'] ))
1536
+ $post = $post['retweeted_status'];
1537
+
1538
+ // temporary workaround for cached http images
1539
+ $post['user']['profile_image_url_https'] = isset( $post['user']['profile_image_url_https'] ) ? $post['user']['profile_image_url_https'] : $post['user']['profile_image_url'];
1540
+ $tweet_classes .= ' ctf-retweet';
1541
+ } else {
1542
+ unset( $retweeter );
1543
+ }
1544
+
1545
+ // check for quoted
1546
+ if ( isset( $post['quoted_status'] ) ) {
1547
+ $tweet_classes .= ' ctf-quoted';
1548
+ $quoted = $post['quoted_status'];
1549
+ $quoted_media_text = '';
1550
+ if ( ( isset( $quoted['extended_entities']['media'][0] ) || isset( $quoted['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1551
+ $quoted_media = isset( $quoted['extended_entities']['media'] ) ? $quoted['extended_entities']['media'] : $quoted['entities']['media'];
1552
+ $quoted_media_count = count( $quoted_media );
1553
+ switch ( $quoted_media[0]['type'] ) {
1554
+ case 'video':
1555
+ case 'animated_gif':
1556
+ $quoted_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1557
+ break;
1558
+ default:
1559
+ if ( $quoted_media_count > 1 ) {
1560
+ $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap ctf-multi-media-icon">' . $quoted_media_count . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1561
+ } else {
1562
+ $quoted_media_text .= '<span class="ctf-quoted-tweet-text-media-wrap">' . ctf_get_fa_el( 'fa-picture-o' ) . '</span>';
1563
+ }
1564
+ break;
1565
+ }
1566
+ } else {
1567
+ unset( $quoted_media );
1568
+ }
1569
+ } else {
1570
+ unset( $quoted );
1571
+ unset( $quoted_media_text );
1572
+ }
1573
+
1574
+ // check for media [0]['type']
1575
+ $post_media_text = '';
1576
+ $post_media_count = 0;
1577
+ if ( ( isset( $post['extended_entities']['media'][0] ) || isset( $post['entities']['media'][0] ) ) && ctf_show( 'placeholder', $feed_options ) ) {
1578
+ $post_media = isset( $post['extended_entities']['media'] ) ? $post['extended_entities']['media'] : $post['entities']['media'];
1579
+ $post_media_count = count( $post_media );
1580
+ switch ( $post_media[0]['type'] ) {
1581
+ case 'video':
1582
+ case 'animated_gif':
1583
+ $post_media_text .= ctf_get_fa_el( 'fa-file-video-o' );
1584
+ break;
1585
+ default:
1586
+ if ( $post_media_count > 1 ) {
1587
+ $post_media_text .= $post_media_count . ctf_get_fa_el( 'fa-picture-o' );
1588
+ } else {
1589
+ $post_media_text .= ctf_get_fa_el( 'fa-picture-o' );
1590
+ }
1591
+ break;
1592
+ }
1593
+ } else {
1594
+ unset( $post_media );
1595
+ }
1596
+
1597
+ // include tweet view
1598
+ $tweet_html .= '<div class="'. $tweet_classes . '" id="' . $post_id . '" style="' . $feed_options['tweetbgcolor'] . '"' . $retweet_data_att . '>';
1599
+
1600
+ if ( isset( $retweeter ) && ctf_show( 'retweeter', $feed_options ) ) {
1601
+ $tweet_html .= '<div class="ctf-context">';
1602
+ $tweet_html .= '<a href="https://twitter.com/intent/user?screen_name=' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-icon">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">'.__( 'Retweet on Twitter', 'custom-twitter-feeds' ).'</span></a>';
1603
+ $tweet_html .= '<a href="https://twitter.com/' . $retweeter['screen_name'] . '" target="_blank" class="ctf-retweet-text" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $retweeter['name'] . ' ' . $feed_options['retweetedtext'] . '</a>';
1604
+ $tweet_html .= '</div>';
1605
+ }
1606
+
1607
+ if ( ctf_show( 'avatar', $feed_options ) || ctf_show( 'logo', $feed_options ) || ctf_show( 'author', $feed_options ) || ctf_show( 'date', $feed_options ) ) {
1608
+
1609
+ $tweet_html .= '<div class="ctf-author-box">';
1610
+ $tweet_html .= '<div class="ctf-author-box-link" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1611
+ if ( ctf_show( 'avatar', $feed_options ) ) {
1612
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-avatar" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">';
1613
+ $tweet_html .= '<img src="' . $post['user']['profile_image_url_https'] . '" alt="' . $post['user']['screen_name'] . '" width="48" height="48">';
1614
+ $tweet_html .= '</a>';
1615
+ }
1616
+
1617
+ if ( ctf_show( 'author', $feed_options ) ) {
1618
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" target="_blank" class="ctf-author-name" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">' . $post['user']['name'] . '</a>';
1619
+ if ( $post['user']['verified'] == 1 ) {
1620
+ $tweet_html .= '<span class="ctf-verified" >' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1621
+ }
1622
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '" class="ctf-author-screenname" target="_blank" style="' . $feed_options['authortextsize'] . $feed_options['authortextweight'] . $feed_options['textcolor'] . '">@' . $post['user']['screen_name'] . '</a>';
1623
+ $tweet_html .= '<span class="ctf-screename-sep">&middot;</span>';
1624
+ }
1625
+
1626
+ if ( ctf_show( 'date', $feed_options ) ) {
1627
+ $tweet_html .= '<div class="ctf-tweet-meta">';
1628
+ //https://twitter.com/EnterLaw/status/869452491041243137
1629
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-tweet-date" target="_blank" style="' . $feed_options['datetextsize'] . $feed_options['datetextweight'] . $feed_options['textcolor'] . '">' . ctf_get_formatted_date( $post['created_at'], $feed_options, $post['user']['utc_offset'] ) . '</a>';
1630
+ $tweet_html .= '</div>';
1631
+ } // show date
1632
+ $tweet_html .= '</div>';
1633
+ if ( ctf_show( 'logo', $feed_options ) ) {
1634
+ $tweet_html .= '<div class="ctf-corner-logo" style="' . $feed_options['logosize'] . $feed_options['logocolor'] . '">';
1635
+ $tweet_html .= ctf_get_fa_el( 'fa-twitter' );
1636
+ $tweet_html .= '</div>';
1637
+ }
1638
+ $tweet_html .= '</div>';
1639
+ }
1640
+
1641
+ if ( ctf_show( 'text', $feed_options ) ) {
1642
+ $post_text = apply_filters( 'ctf_tweet_text', $post['text'], $feed_options, $post );
1643
+
1644
+ $tweet_html .= '<div class="ctf-tweet-content">';
1645
+
1646
+ if ( $feed_options['linktexttotwitter'] ) {
1647
+ $tweet_html .= '<a class="ctf-tweet-text-link" href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank">';
1648
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text ) . $post_media_text .'</p>';
1649
+ $tweet_html .= '</a>';
1650
+ } else {
1651
+ $tweet_html .= '<p class="ctf-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $post_text );
1652
+
1653
+ if( $post_media_count > 0 ){
1654
+ $multi_class = '';
1655
+ if ( $post_media_count > 1 ) {
1656
+ $multi_class = ' ctf-multi-media-icon';
1657
+ }
1658
+ if ( $feed_options['disablelinks'] ) {
1659
+ $tweet_html .= '<span class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</span>' . '</p>';
1660
+ } else {
1661
+ $tweet_html .= '</p><a href="https://twitter.com/' .$post['user']['screen_name'] . '/status/' . $post['id_str'] . '" target="_blank" class="ctf-tweet-text-media-wrap' . $multi_class . '">' . $post_media_text . '</a>';
1662
+ }
1663
+ }
1664
+ } // link text to twitter option is selected
1665
+
1666
+ $tweet_html .= '</div>';
1667
+ } // show tweet text
1668
+
1669
+ if ( ctf_show( 'linkbox', $feed_options ) && isset( $quoted ) ) {
1670
+ $tweet_html .= '<a href="https://twitter.com/' . $quoted['user']['screen_name'] . '/status/' . $quoted['id_str'] . '" class="ctf-quoted-tweet" style="' . $feed_options['quotedauthorsize'] . $feed_options['quotedauthorweight'] . $feed_options['textcolor'] . '" target="_blank">';
1671
+ $tweet_html .= '<span class="ctf-quoted-author-name">' . $quoted['user']['name'] . '</span>';
1672
+
1673
+ if ($quoted['user']['verified'] == 1) {
1674
+ $tweet_html .= '<span class="ctf-quoted-verified">' . ctf_get_fa_el( 'fa-check-circle' ) . '</span>';
1675
+ } // user is verified
1676
+ $quoted_text = apply_filters( 'ctf_quoted_tweet_text', $quoted['text'], $feed_options, $quoted );
1677
+
1678
+ $tweet_html .= '<span class="ctf-quoted-author-screenname">@' . $quoted['user']['screen_name'] . '</span>';
1679
+ $tweet_html .= '<p class="ctf-quoted-tweet-text" style="' . $feed_options['tweettextsize'] . $feed_options['tweettextweight'] . $feed_options['textcolor'] . '">' . nl2br( $quoted_text ) . $quoted_media_text . '</p>';
1680
+ //$tweet_html .= ;
1681
+ $tweet_html .= '</a>';
1682
+ }// show link box
1683
+
1684
+ $tweet_html .= '<div class="ctf-tweet-actions">';
1685
+ if ( ctf_show( 'actions', $feed_options ) ) {
1686
+ $tweet_html .= '<a href="https://twitter.com/intent/tweet?in_reply_to=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-reply" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-reply' ) . '<span class="ctf-screenreader">Reply on Twitter ' . $post['id_str'] . '</span></a>';
1687
+ $tweet_html .= '<a href="https://twitter.com/intent/retweet?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-retweet" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-retweet' ) . '<span class="ctf-screenreader">Retweet on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-retweet-count">';
1688
+ if ( $post['retweet_count'] > 0 ) {
1689
+ $tweet_html .= $post['retweet_count'];
1690
+ }
1691
+ $tweet_html .= '</span></a>';
1692
+ $tweet_html .= '<a href="https://twitter.com/intent/like?tweet_id=' . $post['id_str'] . '&related=' . $post['user']['screen_name'] . '" class="ctf-like" target="_blank" style="' . $feed_options['iconsize'] . $feed_options['iconcolor'] . '">' . ctf_get_fa_el( 'fa-heart' ) . '<span class="ctf-screenreader">Like on Twitter ' . $post['id_str'] . '</span><span class="ctf-action-count ctf-favorite-count">';
1693
+ if ( $post['favorite_count'] > 0 ) {
1694
+ $tweet_html .= $post['favorite_count'];
1695
+ }
1696
+ $tweet_html .= '</span></a>';
1697
+ }
1698
+ if ( ctf_show( 'twitterlink', $feed_options ) ) {
1699
+ $tweet_html .= '<a href="https://twitter.com/' . $post['user']['screen_name'] . '/status/' . $post['id_str'] . '" class="ctf-twitterlink" style="' . $feed_options['textcolor'] . '" target="_blank">' . esc_html( $feed_options['twitterlinktext'] ) . ' <span class="ctf-screenreader">' . $post['id_str'] . '</span></a>';
1700
+ } // show twitter link or actions
1701
+ $tweet_html .= '</div>';
1702
+ $tweet_html .= '</div>';
1703
+
1704
+ $i++;
1705
+ }
1706
+ }
1707
+ return $tweet_html;
1708
+ }
1709
+
1710
+ /**
1711
+ * displays a message if there is an error in the feed
1712
+ *
1713
+ * @return string error html
1714
+ */
1715
+ public function getErrorHtml()
1716
+ {
1717
+ $error_html = '';
1718
+ $error_html .= '<div id="ctf" class="ctf" data-ctfshortcode="' . $this->getShortCodeJSON() . '">';
1719
+ $error_html .= '<div class="ctf-error">';
1720
+ $error_html .= '<div class="ctf-error-user">';
1721
+
1722
+ $error_html .= '</div>';
1723
+
1724
+ if ( current_user_can( 'manage_options' ) ) {
1725
+ $error_html .= '<div class="ctf-error-admin">';
1726
+
1727
+ if ( ! empty( $this->api_obj->api_error_no ) ) {
1728
+
1729
+ $error_html .= '<p>Unable to load Tweets</p>';
1730
+ $error_html .= '<a class="twitter-share-button"';
1731
+ $error_html .= 'href="https://twitter.com/share"';
1732
+ $error_html .= 'data-size="large"';
1733
+ $error_html .= 'data-url="' . get_the_permalink() . '"';
1734
+ $error_html .= 'data-text="Check out this website">';
1735
+ $error_html .= '</a>';
1736
+
1737
+ if ( !empty( $this->feed_options['screenname'] ) ) {
1738
+ $error_html .= '<a class="twitter-follow-button"';
1739
+ $error_html .= 'href="https://twitter.com/' . $this->feed_options['screenname'] . '"';
1740
+ $error_html .= 'data-show-count="false"';
1741
+ $error_html .= 'data-size="large"';
1742
+ $error_html .= 'data-dnt="true">Follow</a>';
1743
+ }
1744
+
1745
+ $error_html .= '<p><b>This message is only visible to admins:</b><br />';
1746
+ $error_html .= 'An error has occurred with your feed.<br />';
1747
+ if ( $this->missing_credentials ) {
1748
+ $error_html .= 'There is a problem with your access token, access token secret, consumer token, or consumer secret<br />';
1749
+ }
1750
+ if ( isset( $this->errors['error_message'] ) ) {
1751
+ $error_html .= $this->errors['error_message'] . '<br />';
1752
+ }
1753
+ $error_html .= 'The error response from the Twitter API is the following:<br />';
1754
+ $error_html .= '<code>Error number: ' . $this->api_obj->api_error_no . '<br />';
1755
+ $error_html .= 'Message: ' . $this->api_obj->api_error_message . '</code>';
1756
+ $error_html .= '<a href="https://smashballoon.com/custom-twitter-feeds/docs/errors/" target="_blank">Click here to troubleshoot</a></p>';
1757
+
1758
+
1759
+ }
1760
+
1761
+ $error_html .= '</div>';
1762
+ }
1763
+ $error_html .= '</div>'; // end .ctf-error
1764
+ $error_html .= '</div>'; // end #ctf
1765
+
1766
+ return $error_html;
1767
+ }
1768
  }
inc/CtfOauthConnect.php CHANGED
@@ -1,305 +1,305 @@
1
- <?php
2
- /**
3
- * Class OauthConnect
4
- *
5
- * Simple, lightweight class to make a connection to the Twitter API
6
- * Supports home timeline, user timeline, and search endpoints
7
- */
8
-
9
- // Don't load directly
10
- if ( ! defined( 'ABSPATH' ) ) {
11
- die( '-1' );
12
- }
13
-
14
- class CtfOauthConnect
15
- {
16
- /**
17
- * @var string
18
- */
19
- protected $base_url;
20
-
21
- /**
22
- * @var string
23
- */
24
- private $get_fields;
25
-
26
- /**
27
- * @var string
28
- */
29
- private $request_method;
30
-
31
- /**
32
- * @var array
33
- */
34
- private $oauth;
35
-
36
- /**
37
- * @var string
38
- */
39
- private $header;
40
-
41
- /**
42
- * @var bool
43
- */
44
- public $api_error_no = false;
45
-
46
- /**
47
- * @var bool
48
- */
49
- public $api_error_message = false;
50
-
51
- /**
52
- * @var string
53
- */
54
- public $json;
55
-
56
- /**
57
- * @param array $request_settings all necessary tokens for OAuth connection
58
- * @param $feed_type string type of Twitter feed
59
- */
60
- public function __construct( array $request_settings, $feed_type )
61
- {
62
- $this->consumer_key = $request_settings['consumer_key'];
63
- $this->consumer_secret = $request_settings['consumer_secret'];
64
- $this->access_token = $request_settings['access_token'];
65
- $this->access_token_secret = $request_settings['access_token_secret'];
66
- $this->feed_type = $feed_type;
67
- }
68
-
69
- /**
70
- * Sets the complete url for our API endpoint. GET fields will be added later
71
- */
72
- public function setUrlBase()
73
- {
74
- switch ( $this->feed_type ) {
75
- case "hometimeline":
76
- $this->base_url = 'https://api.twitter.com/1.1/statuses/home_timeline.json';
77
- break;
78
- case "search":
79
- $this->base_url = 'https://api.twitter.com/1.1/search/tweets.json';
80
- break;
81
- default:
82
- $this->base_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
83
- }
84
- }
85
-
86
- /**
87
- * Encodes an array of GET field data into html characters for including in a URL
88
- *
89
- * @param array $get_fields array of GET fields that are compatible with the Twitter API
90
- */
91
- public function setGetFields( array $get_fields )
92
- {
93
- $url_string = '?';
94
- $length = count( $get_fields );
95
- $j = 1;
96
- foreach ( $get_fields as $key => $value ) {
97
- $url_string .= rawurlencode( $key ) . '=' . rawurlencode( $value );
98
- if ( $j != $length ) {
99
- $url_string .= '&';
100
- }
101
- $j++;
102
- }
103
-
104
- $this->get_fields = $url_string;
105
- }
106
-
107
- /**
108
- * Users can manually set the request method if there is an uncatchable error in
109
- * the other methods
110
- *
111
- * @param string $request_method
112
- */
113
- public function setRequestMethod( $request_method = 'auto' )
114
- {
115
- $this->request_method = $request_method;
116
- }
117
-
118
- /**
119
- * Uses the OAuth data to build the base string needed to create the
120
- * OAuth signature to be used in the header of the request
121
- *
122
- * @param $oauth array oauth data without the signature
123
- * @return string the base string for needed to construct the oauth signature
124
- */
125
- private function buildBaseString( $oauth )
126
- {
127
- $base_string = array();
128
- ksort( $oauth );
129
-
130
- // start forming the header string by creating a numeric index array with
131
- // each part of the header string it's own element in the array
132
- foreach ( $oauth as $key => $value ) {
133
- $base_string[] = rawurlencode( $key ) . '=' . rawurlencode( $value );
134
- }
135
-
136
- // convert the array of values into a single encoded string and return
137
- return 'GET&' . rawurlencode( $this->base_url ) . '&' . rawurlencode( implode( '&', $base_string ) );
138
- }
139
-
140
- /**
141
- * Builds the OAuth data array that is used to authenticate the connection
142
- * to the Twitter API
143
- */
144
- public function buildOauth()
145
- {
146
- $oauth = array(
147
- 'oauth_consumer_key' => $this->consumer_key,
148
- 'oauth_nonce' => time(),
149
- 'oauth_signature_method' => 'HMAC-SHA1',
150
- 'oauth_token' => $this->access_token,
151
- 'oauth_timestamp' => time(),
152
- 'oauth_version' => '1.0'
153
- );
154
-
155
- $getfields = str_replace( '?', '', explode( '&', $this->get_fields ) );
156
-
157
- // add the get fields to the oauth associative array to be
158
- // formed into the header string eventually
159
- foreach ( $getfields as $getfield ) {
160
- $split = explode( '=', $getfield );
161
-
162
- if ( isset( $split[1] ) ) {
163
- $oauth[$split[0]] = urldecode( $split[1] );
164
- }
165
- }
166
-
167
- // the OAuth signature for Twitter is a hashed, encoded version of the base url, 4 different keys
168
- $base_string = $this->buildBaseString( $oauth );
169
- $composite_key = rawurlencode( $this->consumer_secret ) . '&' . rawurlencode( $this->access_token_secret );
170
- $oauth_signature = base64_encode( hash_hmac( 'sha1', $base_string, $composite_key, true ) );
171
- $oauth['oauth_signature'] = $oauth_signature;
172
-
173
- $this->oauth = $oauth;
174
- }
175
-
176
- /**
177
- * Since the OAuth data is passed in a url, special characters need to be encoded
178
- */
179
- private function encodeHeader()
180
- {
181
- $header = 'Authorization: OAuth ';
182
- $values = array();
183
-
184
- // each element of the header needs to have it's special characters encoded for
185
- // passing through a url
186
- foreach ( $this->oauth as $key => $value ) {
187
- if ( in_array( $key, array( 'oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
188
- 'oauth_signature_method', 'oauth_timestamp', 'oauth_token', 'oauth_version' ) ) ){
189
- $values[] = "$key=\"" . rawurlencode( $value ) . "\"";
190
- }
191
- }
192
-
193
- $header .= implode( ', ', $values );
194
- $this->header = $header;
195
- }
196
-
197
- /**
198
- * Attempts to connect to the Twitter api using curl
199
- *
200
- * @param $url string the complete api endpoint url
201
- * @return mixed json string retrieved in the request
202
- */
203
- private function curlRequest( $url )
204
- {
205
- $br = curl_init( $url );
206
-
207
- curl_setopt( $br, CURLOPT_HTTPHEADER, array( $this->header ) ); // must pass in array
208
- curl_setopt( $br, CURLOPT_URL, $url );
209
- curl_setopt( $br, CURLOPT_RETURNTRANSFER, true );
210
- curl_setopt( $br, CURLOPT_TIMEOUT, 10 );
211
- curl_setopt( $br, CURLOPT_SSL_VERIFYPEER, false ); // must be false to connect without signed certificate
212
- curl_setopt( $br, CURLOPT_ENCODING, '' );
213
-
214
- $json = curl_exec( $br );
215
-
216
- if ( curl_errno( $br ) ){
217
- $this->api_error_no = curl_errno( $br );
218
- $this->api_error_message = curl_error( $br );
219
- }
220
-
221
- curl_close( $br );
222
-
223
- return $json;
224
- }
225
-
226
- /**
227
- * Attempts to connect to the Twitter api using file get contents
228
- *
229
- * @param $url string the complete api endpoint url
230
- * @return mixed json string retrieved in the request
231
- */
232
- public function fileGetContentsRequest( $url )
233
- {
234
- $opts = array(
235
- 'http' => array(
236
- 'method' => 'GET',
237
- 'header' => $this->header
238
- )
239
- );
240
-
241
- $context = stream_context_create( $opts );
242
-
243
- return file_get_contents( $url, false, $context );
244
- }
245
-
246
- /**
247
- * Attempts to connect to the Twitter api using WP_HTTP class
248
- *
249
- * @param $url string the complete api endpoint url
250
- * @return mixed json string retrieved in the request
251
- */
252
- private function wpHttpRequest( $url )
253
- {
254
- $args = array(
255
- 'headers' => $this->header,
256
- 'timeout' => 60,
257
- 'sslverify' => false
258
- );
259
- $result = wp_remote_get( $url, $args );
260
-
261
- return $result['body']; // just need the body to keep everything simple
262
- }
263
-
264
- /**
265
- * Uses the data created and gathered up to this point to make the actual connection
266
- * to the Twitter API. It first tests whether or not a curl connection is possible,
267
- * followed by file_get_contents connection, then defaults to the WordPress WP_HTTP object
268
- *
269
- * @return mixed|string raw json data retrieved from the API request
270
- */
271
- public function performRequest()
272
- {
273
- $url = $this->base_url . $this->get_fields;
274
- $this->buildOauth();
275
- $this->encodeHeader();
276
-
277
- switch ( $this->request_method ) {
278
- case 'curl':
279
- $this->json = $this->curlRequest( $url );
280
- break;
281
- case 'file_get_contents':
282
- $this->json = $this->fileGetContentsRequest( $url );
283
- break;
284
- case 'wp_http':
285
- $this->json = $this->wpHttpRequest( $url );
286
- break;
287
- default:
288
- if ( is_callable( 'curl_init' ) ) {
289
- $this->json = $this->curlRequest( $url );
290
-
291
- if ( $this->api_error_no ){
292
- $this->json = $this->fileGetContentsRequest( $url );
293
- }
294
- } elseif ( ( ini_get( 'allow_url_fopen' ) == 1 ||
295
- ini_get( 'allow_url_fopen' ) === TRUE ) &&
296
- in_array( 'https', stream_get_wrappers() ) ) {
297
- $this->json = $this->fileGetContentsRequest( $url );
298
- } else {
299
- $this->json = $this->wpHttpRequest( $url );
300
- }
301
- }
302
-
303
- return $this;
304
- }
305
  }
1
+ <?php
2
+ /**
3
+ * Class OauthConnect
4
+ *
5
+ * Simple, lightweight class to make a connection to the Twitter API
6
+ * Supports home timeline, user timeline, and search endpoints
7
+ */
8
+
9
+ // Don't load directly
10
+ if ( ! defined( 'ABSPATH' ) ) {
11
+ die( '-1' );
12
+ }
13
+
14
+ class CtfOauthConnect
15
+ {
16
+ /**
17
+ * @var string
18
+ */
19
+ protected $base_url;
20
+
21
+ /**
22
+ * @var string
23
+ */
24
+ private $get_fields;
25
+
26
+ /**
27
+ * @var string
28
+ */
29
+ private $request_method;
30
+
31
+ /**
32
+ * @var array
33
+ */
34
+ private $oauth;
35
+
36
+ /**
37
+ * @var string
38
+ */
39
+ private $header;
40
+
41
+ /**
42
+ * @var bool
43
+ */
44
+ public $api_error_no = false;
45
+
46
+ /**
47
+ * @var bool
48
+ */
49
+ public $api_error_message = false;
50
+
51
+ /**
52
+ * @var string
53
+ */
54
+ public $json;
55
+
56
+ /**
57
+ * @param array $request_settings all necessary tokens for OAuth connection
58
+ * @param $feed_type string type of Twitter feed
59
+ */
60
+ public function __construct( array $request_settings, $feed_type )
61
+ {
62
+ $this->consumer_key = $request_settings['consumer_key'];
63
+ $this->consumer_secret = $request_settings['consumer_secret'];
64
+ $this->access_token = $request_settings['access_token'];
65
+ $this->access_token_secret = $request_settings['access_token_secret'];
66
+ $this->feed_type = $feed_type;
67
+ }
68
+
69
+ /**
70
+ * Sets the complete url for our API endpoint. GET fields will be added later
71
+ */
72
+ public function setUrlBase()
73
+ {
74
+ switch ( $this->feed_type ) {
75
+ case "hometimeline":
76
+ $this->base_url = 'https://api.twitter.com/1.1/statuses/home_timeline.json';
77
+ break;
78
+ case "search":
79
+ $this->base_url = 'https://api.twitter.com/1.1/search/tweets.json';
80
+ break;
81
+ default:
82
+ $this->base_url = 'https://api.twitter.com/1.1/statuses/user_timeline.json';
83
+ }
84
+ }
85
+
86
+ /**
87
+ * Encodes an array of GET field data into html characters for including in a URL
88
+ *
89
+ * @param array $get_fields array of GET fields that are compatible with the Twitter API
90
+ */
91
+ public function setGetFields( array $get_fields )
92
+ {
93
+ $url_string = '?';
94
+ $length = count( $get_fields );
95
+ $j = 1;
96
+ foreach ( $get_fields as $key => $value ) {
97
+ $url_string .= rawurlencode( $key ) . '=' . rawurlencode( $value );
98
+ if ( $j != $length ) {
99
+ $url_string .= '&';
100
+ }
101
+ $j++;
102
+ }
103
+
104
+ $this->get_fields = $url_string;
105
+ }
106
+
107
+ /**
108
+ * Users can manually set the request method if there is an uncatchable error in
109
+ * the other methods
110
+ *
111
+ * @param string $request_method
112
+ */
113
+ public function setRequestMethod( $request_method = 'auto' )
114
+ {
115
+ $this->request_method = $request_method;
116
+ }
117
+
118
+ /**
119
+ * Uses the OAuth data to build the base string needed to create the
120
+ * OAuth signature to be used in the header of the request
121
+ *
122
+ * @param $oauth array oauth data without the signature
123
+ * @return string the base string for needed to construct the oauth signature
124
+ */
125
+ private function buildBaseString( $oauth )
126
+ {
127
+ $base_string = array();
128
+ ksort( $oauth );
129
+
130
+ // start forming the header string by creating a numeric index array with
131
+ // each part of the header string it's own element in the array
132
+ foreach ( $oauth as $key => $value ) {
133
+ $base_string[] = rawurlencode( $key ) . '=' . rawurlencode( $value );
134
+ }
135
+
136
+ // convert the array of values into a single encoded string and return
137
+ return 'GET&' . rawurlencode( $this->base_url ) . '&' . rawurlencode( implode( '&', $base_string ) );
138
+ }
139
+
140
+ /**
141
+ * Builds the OAuth data array that is used to authenticate the connection
142
+ * to the Twitter API
143
+ */
144
+ public function buildOauth()
145
+ {
146
+ $oauth = array(
147
+ 'oauth_consumer_key' => $this->consumer_key,
148
+ 'oauth_nonce' => time(),
149
+ 'oauth_signature_method' => 'HMAC-SHA1',
150
+ 'oauth_token' => $this->access_token,
151
+ 'oauth_timestamp' => time(),
152
+ 'oauth_version' => '1.0'
153
+ );
154
+
155
+ $getfields = str_replace( '?', '', explode( '&', $this->get_fields ) );
156
+
157
+ // add the get fields to the oauth associative array to be
158
+ // formed into the header string eventually
159
+ foreach ( $getfields as $getfield ) {
160
+ $split = explode( '=', $getfield );
161
+
162
+ if ( isset( $split[1] ) ) {
163
+ $oauth[$split[0]] = urldecode( $split[1] );
164
+ }
165
+ }
166
+
167
+ // the OAuth signature for Twitter is a hashed, encoded version of the base url, 4 different keys
168
+ $base_string = $this->buildBaseString( $oauth );
169
+ $composite_key = rawurlencode( $this->consumer_secret ) . '&' . rawurlencode( $this->access_token_secret );
170
+ $oauth_signature = base64_encode( hash_hmac( 'sha1', $base_string, $composite_key, true ) );
171
+ $oauth['oauth_signature'] = $oauth_signature;
172
+
173
+ $this->oauth = $oauth;
174
+ }
175
+
176
+ /**
177
+ * Since the OAuth data is passed in a url, special characters need to be encoded
178
+ */
179
+ private function encodeHeader()
180
+ {
181
+ $header = 'Authorization: OAuth ';
182
+ $values = array();
183
+
184
+ // each element of the header needs to have it's special characters encoded for
185
+ // passing through a url
186
+ foreach ( $this->oauth as $key => $value ) {
187
+ if ( in_array( $key, array( 'oauth_consumer_key', 'oauth_nonce', 'oauth_signature',
188
+ 'oauth_signature_method', 'oauth_timestamp', 'oauth_token', 'oauth_version' ) ) ){
189
+ $values[] = "$key=\"" . rawurlencode( $value ) . "\"";
190
+ }
191
+ }
192
+
193
+ $header .= implode( ', ', $values );
194
+ $this->header = $header;
195
+ }
196
+
197
+ /**
198
+ * Attempts to connect to the Twitter api using curl
199
+ *
200
+ * @param $url string the complete api endpoint url
201
+ * @return mixed json string retrieved in the request
202
+ */
203
+ private function curlRequest( $url )
204
+ {
205
+ $br = curl_init( $url );
206
+
207
+ curl_setopt( $br, CURLOPT_HTTPHEADER, array( $this->header ) ); // must pass in array
208
+ curl_setopt( $br, CURLOPT_URL, $url );
209
+ curl_setopt( $br, CURLOPT_RETURNTRANSFER, true );
210
+ curl_setopt( $br, CURLOPT_TIMEOUT, 10 );
211
+ curl_setopt( $br, CURLOPT_SSL_VERIFYPEER, false ); // must be false to connect without signed certificate
212
+ curl_setopt( $br, CURLOPT_ENCODING, '' );
213
+
214
+ $json = curl_exec( $br );
215
+
216
+ if ( curl_errno( $br ) ){
217
+ $this->api_error_no = curl_errno( $br );
218
+ $this->api_error_message = curl_error( $br );
219
+ }
220
+
221
+ curl_close( $br );
222
+
223
+ return $json;
224
+ }
225
+
226
+ /**
227
+ * Attempts to connect to the Twitter api using file get contents
228
+ *
229
+ * @param $url string the complete api endpoint url
230
+ * @return mixed json string retrieved in the request
231
+ */
232
+ public function fileGetContentsRequest( $url )
233
+ {
234
+ $opts = array(
235
+ 'http' => array(
236
+ 'method' => 'GET',
237
+ 'header' => $this->header
238
+ )
239
+ );
240
+
241
+ $context = stream_context_create( $opts );
242
+
243
+ return file_get_contents( $url, false, $context );
244
+ }
245
+
246
+ /**
247
+ * Attempts to connect to the Twitter api using WP_HTTP class
248
+ *
249
+ * @param $url string the complete api endpoint url
250
+ * @return mixed json string retrieved in the request
251
+ */
252
+ private function wpHttpRequest( $url )
253
+ {
254
+ $args = array(
255
+ 'headers' => $this->header,
256
+ 'timeout' => 60,
257
+ 'sslverify' => false
258
+ );
259
+ $result = wp_remote_get( $url, $args );
260
+
261
+ return $result['body']; // just need the body to keep everything simple
262
+ }
263
+
264
+ /**
265
+ * Uses the data created and gathered up to this point to make the actual connection
266
+ * to the Twitter API. It first tests whether or not a curl connection is possible,
267
+ * followed by file_get_contents connection, then defaults to the WordPress WP_HTTP object
268
+ *
269
+ * @return mixed|string raw json data retrieved from the API request
270
+ */
271
+ public function performRequest()
272
+ {
273
+ $url = $this->base_url . $this->get_fields;
274
+ $this->buildOauth();
275
+ $this->encodeHeader();
276
+
277
+ switch ( $this->request_method ) {
278
+ case 'curl':
279
+ $this->json = $this->curlRequest( $url );
280
+ break;
281
+ case 'file_get_contents':
282
+ $this->json = $this->fileGetContentsRequest( $url );
283
+ break;
284
+ case 'wp_http':
285
+ $this->json = $this->wpHttpRequest( $url );
286
+ break;
287
+ default:
288
+ if ( is_callable( 'curl_init' ) ) {
289
+ $this->json = $this->curlRequest( $url );
290
+
291
+ if ( $this->api_error_no ){
292
+ $this->json = $this->fileGetContentsRequest( $url );
293
+ }
294
+ } elseif ( ( ini_get( 'allow_url_fopen' ) == 1 ||
295
+ ini_get( 'allow_url_fopen' ) === TRUE ) &&
296
+ in_array( 'https', stream_get_wrappers() ) ) {
297
+ $this->json = $this->fileGetContentsRequest( $url );
298
+ } else {
299
+ $this->json = $this->wpHttpRequest( $url );
300
+ }
301
+ }
302
+
303
+ return $this;
304
+ }
305
  }
inc/admin-hooks.php CHANGED
@@ -1,356 +1,406 @@
1
- <?php
2
- add_filter( 'ctf_admin_search_label', 'ctf_return_string_hashtag' );
3
- function ctf_return_string_hashtag( $val ) {
4
- return 'Hashtag:';
5
- }
6
-
7
- add_filter( 'ctf_admin_search_whatis', 'ctf_return_string_instructions' );
8
- function ctf_return_string_instructions( $val ) {
9
- return 'Select this option and enter any single hashtag for a hashtag feed. Only tweets made within the last 7 days are available initially. Once a tweet has been retrieved the plugin will keep it in a persistent cache indefinitely';
10
- }
11
-
12
- add_filter( 'ctf_admin_validate_search_text', 'ctf_validate_search_text', 10, 1 );
13
- function ctf_validate_search_text( $val ) {
14
- preg_match( "/^[\p{L}0-9_]+|^#+[\p{L}0-9_]+/u", trim( $val ), $hashtags );
15
-
16
- $hashtags = preg_replace( "/#{2,}/", '', $hashtags );
17
-
18
- $new_val = ! empty( $hashtags ) ? $new_val = $hashtags[0] : '';
19
-
20
- if ( substr( $new_val, 0, 1 ) != '#' && $new_val != '' ) {
21
- $new_val = '#' . $new_val;
22
- }
23
-
24
- return $new_val;
25
- }
26
-
27
- add_filter( 'ctf_admin_validate_usertimeline_text', 'ctf_validate_usertimeline_text', 10, 1 );
28
- function ctf_validate_usertimeline_text( $val ) {
29
- preg_match( "/^[\p{L}0-9_]{1,16}/u" , str_replace( '@', '', trim( $val ) ), $screenname );
30
-
31
- $new_val = isset( $screenname[0] ) ? $screenname[0] : '';
32
-
33
- return $new_val;
34
- }
35
-
36
- add_filter( 'ctf_admin_validate_include_replies', 'ctf_validate_include_replies', 10, 1 );
37
- function ctf_validate_include_replies( $val ) {
38
- return false;
39
- }
40
-
41
- add_filter( 'ctf_admin_set_include_replies', 'ctf_set_include_replies', 10, 1 );
42
- function ctf_set_include_replies( $new_input ) {
43
- return false;
44
- }
45
-
46
- add_filter( 'ctf_admin_feed_type_list', 'ctf_return_feed_types' );
47
- function ctf_return_feed_types( $val ) {
48
- return array( 'hometimelineinclude_replies', 'usertimelineinclude_replies' );
49
- }
50
-
51
- add_action( 'ctf_admin_upgrade_note', 'ctf_update_note' );
52
- function ctf_update_note() {
53
- ?>
54
- <span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Available in Pro version</a></span>
55
- <?php
56
- }
57
-
58
- add_action( 'ctf_admin_feed_settings_radio_extra', 'ctf_usertimeline_error_message' );
59
- function ctf_usertimeline_error_message( $args )
60
- { //sbi_notice sbi_user_id_error
61
- if ( $args['name'] == 'usertimeline') : ?>
62
- <div class="ctf_notice ctf_usertimeline_error">
63
- <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf' target='_blank'>Pro version</a>.</p>" ); ?>
64
- </div>
65
- <?php endif;
66
- }
67
-
68
- add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
- function ctf_hashtag_error_message() {
70
- ?>
71
- <div class="ctf_notice ctf_search_error">
72
- <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf' target='_blank'>Pro version</a>.</p>" ); ?>
73
- </div>
74
- <?php
75
- }
76
-
77
- add_filter( 'ctf_admin_customize_quick_links', 'ctf_return_customize_quick_links' );
78
- function ctf_return_customize_quick_links() {
79
- return array(
80
- 0 => array( 'general', 'General' ),
81
- 1 => array( 'showhide', 'Show/Hide' ),
82
- 2 => array( 'misc', 'Misc' ),
83
- 3 => array( 'advanced', 'Advanced' )
84
- );
85
- }
86
-
87
- add_filter( 'ctf_admin_style_quick_links', 'ctf_return_style_quick_links' );
88
- function ctf_return_style_quick_links() {
89
- return array(
90
- 0 => array( 'general', 'General' ),
91
- 1 => array( 'header', 'Header' ),
92
- 2 => array( 'date', 'Date' ),
93
- 3 => array( 'author', 'Author' ),
94
- 4 => array( 'text', 'Tweet Text' ),
95
- 5 => array( 'links', 'Links' ),
96
- 6 => array( 'quoted', 'Retweet Boxes' ),
97
- 7 => array( 'actions', 'Tweet Actions' ),
98
- 8 => array( 'load', 'Load More' )
99
- );
100
- }
101
-
102
- /*
103
- * Pro Options ----------------------------------------
104
- */
105
-
106
- add_action( 'ctf_admin_endpoints', 'ctf_add_mentionstimeline_options', 10, 1 );
107
- function ctf_add_mentionstimeline_options( $admin ) {
108
- $admin->create_settings_field( array(
109
- 'name' => 'search_pro',
110
- 'title' => '<label></label>', // label for the input field
111
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
112
- 'page' => 'ctf_options_feed_settings', // matches the section name
113
- 'section' => 'ctf_options_feed_settings', // matches the section name
114
- 'option' => 'ctf_options', // matches the options name
115
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
116
- 'whatis' => 'You can create search feeds which contain a large variety of different terms and operators, such as a combination of #hashtags, @mentions, words, or "phrases"', // what is this? text
117
- 'label' => "Search",
118
- 'has_input' => false,
119
- 'has_replies' => false
120
- ));
121
- $admin->create_settings_field( array(
122
- 'name' => 'mentionstimeline',
123
- 'title' => '<label></label>', // label for the input field
124
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
125
- 'page' => 'ctf_options_feed_settings', // matches the section name
126
- 'section' => 'ctf_options_feed_settings', // matches the section name
127
- 'option' => 'ctf_options', // matches the options name
128
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
129
- 'whatis' => 'Select this option to display tweets that @mention your twitter handle', // what is this? text
130
- 'label' => "Mentions",
131
- 'has_input' => false,
132
- 'has_replies' => false
133
- ));
134
- $admin->create_settings_field( array(
135
- 'name' => 'lists',
136
- 'title' => '<label></label>', // label for the input field
137
- 'callback' => 'feed_settings_radio', // name of the function that outputs the html
138
- 'page' => 'ctf_options_feed_settings', // matches the section name
139
- 'section' => 'ctf_options_feed_settings', // matches the section name
140
- 'option' => 'ctf_options', // matches the options name
141
- 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
142
- 'whatis' => 'Enter the list ID of the list(s) you want to display. Use this FAQ to create a list on Twitter. Use the helper to find IDs', // what is this? text
143
- 'label' => "Lists",
144
- 'has_input' => false,
145
- 'has_replies' => false
146
- ));
147
- }
148
-
149
- add_filter( 'ctf_admin_show_hide_list', 'ctf_show_hide_list', 10, 1 );
150
- function ctf_show_hide_list( $show_hide_list ) {
151
- $show_hide_list[] = array( 'include_replied_to', 'In reply to text' );
152
- $show_hide_list[] = array( 'include_media', 'Media (images, videos, gifs)' );
153
- $show_hide_list[] = array( 'include_twittercards', 'Twitter Cards' );
154
- return $show_hide_list;
155
- }
156
-
157
- function ctf_pro_autoscroll_section() {
158
- ?>
159
- <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Upgrade to Pro to enable Autoscroll loading</a></p>
160
- <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
161
-
162
- <div class="ctf-pro-options">
163
- <table class="form-table"><tbody><tr><th scope="row"><label for="ctf_autoscroll" title="Click for shortcode option">Set Load More on Scroll as Default</label><code class="ctf_shortcode">autoscroll
164
- Eg: autoscroll=true</code></th><td> <input name="ctf_options[autoscroll]" id="ctf_autoscroll" type="checkbox" disabled>
165
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
166
- <p class="ctf-tooltip ctf-more-info">This will make every Twitter feed load more Tweets as the user gets to the bottom of the feed.</p>
167
- </td></tr><tr class="default-text"><th scope="row"><label for="ctf_autoscrolldistance">Auto Scroll Trigger Distance</label><code class="ctf_shortcode">autoscrolldistance
168
- Eg: autoscrolldistance=2</code></th><td> <input name="ctf_options[autoscrolldistance]" id="ctf_autoscrolldistance" class="default-text" type="text" value="200" disabled>
169
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
170
- <p class="ctf-tooltip ctf-more-info">This is the distance in pixels from the bottom of the page the user must scroll to to trigger the loading of more tweets.</p>
171
- </td></tr></tbody></table>
172
- </div>
173
- <div style="height: 18px;"></div>
174
- <?php
175
- }
176
-
177
- function ctf_pro_moderation_section() {
178
- ?>
179
- <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Upgrade to Pro to enable Tweet moderation</a></p>
180
- <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
181
-
182
- <div class="ctf-pro-options">
183
- <table class="form-table"><tbody><tr class="large-text"><th scope="row"><label for="ctf_includewords" title="Click for shortcode option">Show Tweets containing these words or hashtags</label><code class="ctf_shortcode">includewords
184
- Eg: includewords="#puppy,#cute"</code></th><td> <input name="ctf_options[includewords]" id="ctf_includewords" class="large-text" type="text" value="" disabled>
185
- <span>"includewords" separate words by comma</span>
186
- </td></tr><tr class="large-text"><th scope="row"><label for="ctf_excludewords">Remove Tweets containing these words or hashtags</label><code class="ctf_shortcode">excludewords
187
- Eg: excludewords="#ugly,#bad"</code></th><td> <input name="ctf_options[excludewords]" id="ctf_excludewords" class="large-text" type="text" value="" disabled>
188
- <span>"excludewords" separate words by comma</span>
189
- </td></tr><tr><th scope="row"></th><td> <p>Show Tweets that contain
190
- <select name="ctf_options[includeanyall]" id="ctf_includeanyall" disabled>
191
- <option value="any" selected="selected">any</option>
192
- <option value="all">all</option>
193
- </select>
194
- of the "includewords"
195
- <select name="ctf_options[filterandor]" id="ctf_filterandor" disabled>
196
- <option value="and" selected="selected">and</option>
197
- <option value="or">or</option>
198
- </select>
199
- do not contain
200
- <select name="ctf_options[excludeanyall]" id="ctf_excludeanyall" disabled>
201
- <option value="any" selected="selected">any</option>
202
- <option value="all">all</option>
203
- </select>
204
- of the "excludewords"
205
- </p>
206
- </td></tr><tr><th scope="row"><label for="ctf_remove_by_id">Hide Specific Tweets</label></th><td> <textarea name="ctf_options[remove_by_id]" id="ctf_remove_by_id" style="width: 70%;" rows="3" disabled></textarea>
207
- <p>separate IDs by comma <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
208
- <span class="ctf-tooltip ctf-more-info">These are the specific ID numbers associated with a tweet. You can find the ID of a Tweet by viewing the Tweet on Twitter and copy/pasting the ID number from the end of the URL.</span>
209
- </p> </td></tr></tbody></table>
210
- </div>
211
- <div style="height: 18px;"></div>
212
- <?php
213
- }
214
-
215
- add_action( 'ctf_admin_style_option', 'ctf_add_masonry_autoscroll_options', 5, 1 );
216
- function ctf_add_masonry_autoscroll_options( $admin ) {
217
- // custom in reply to text
218
- $admin->create_settings_field( array(
219
- 'name' => 'inreplytotext',
220
- 'title' => '<label for="ctf_inreplytotext">Translation for "In reply to"</label><code class="ctf_shortcode">inreplytotext
221
- Eg: inreplytotext="Als Antwort an"</code>', // label for the input field
222
- 'callback' => 'default_text', // name of the function that outputs the html
223
- 'page' => 'ctf_options_text', // matches the section name
224
- 'section' => 'ctf_options_text', // matches the section name
225
- 'option' => 'ctf_options', // matches the options name
226
- 'class' => 'default-text ctf_pro', // class for the wrapper and input field
227
- 'whatis' => 'This will replace the default text displayed for "In reply to"',
228
- 'default' => 'In reply to'// "what is this?" text
229
- ) );
230
-
231
- add_settings_section(
232
- 'ctf_options_autoscroll', // matches the section name
233
- '<span class="ctf_pro_header">Autoscroll Loading</span>',
234
- 'ctf_pro_autoscroll_section', // callback function to explain the section
235
- 'ctf_options_autoscroll' // matches the section name
236
- );
237
-
238
- add_settings_section(
239
- 'ctf_options_filter', // matches the section name
240
- '<span class="ctf_pro_header">Moderation</span>',
241
- 'ctf_pro_moderation_section', // callback function to explain the section
242
- 'ctf_options_filter' // matches the section name
243
- );
244
- }
245
-
246
- add_action( 'ctf_admin_customize_option', 'ctf_add_customize_general_options', 20, 1 );
247
- function ctf_add_customize_general_options( $admin ) {
248
-
249
- // Disable the lightbox
250
- $admin->create_settings_field( array(
251
- 'name' => 'disablelightbox',
252
- 'title' => '<label for="ctf_disablelightbox">Disable the lightbox</label><code class="ctf_shortcode">disablelightbox
253
- Eg: disablelightbox=true</code>', // label for the input field
254
- 'callback' => 'default_checkbox', // name of the function that outputs the html
255
- 'page' => 'ctf_options_general', // matches the section name
256
- 'section' => 'ctf_options_general', // matches the section name
257
- 'option' => 'ctf_options', // matches the options name
258
- 'class' => 'default-text ctf_pro', // class for the wrapper and input field
259
- 'whatis' => 'Disable the popup lightbox for media in the feed'
260
- ) );
261
- }
262
-
263
-
264
- add_action( 'ctf_admin_customize_option', 'ctf_add_filter_options', 10, 1 );
265
- function ctf_add_filter_options( $admin ) {
266
-
267
- add_settings_field(
268
- 'clear_tc_cache_button',
269
- '<label for="ctf_clear_tc_cache_button">Clear Twitter Card Cache</label>',
270
- 'ctf_clear_tc_cache_button',
271
- 'ctf_options_advanced',
272
- 'ctf_options_advanced',
273
- array( 'class' => 'ctf_pro')
274
- );
275
- }
276
-
277
- function ctf_remove_by_id( $args ) {
278
- $options = get_option( $args['option'] );
279
- $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
280
- ?>
281
- <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="3"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
282
- <?php if ( isset( $args['extra'] ) ) : ?><p><?php _e( $args['extra'], 'custom-twitter-feeds' ); ?>
283
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
284
- <span class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</span>
285
- </p> <?php endif; ?>
286
- <?php
287
- }
288
-
289
- function ctf_clear_tc_cache_button() {
290
- ?>
291
- <input id="ctf-clear-tc-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cards' ); ?>" />
292
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
293
- <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your links that have Twitter Cards', 'custom-twitter-feeds' ); ?>.</p>
294
- <?php
295
- }
296
-
297
- function ctf_filter_operator( $args ) {
298
- $options = get_option( $args['option'] );
299
- $include_any_all = ( isset( $options['includeanyall'] ) ) ? esc_attr( $options['includeanyall'] ) : 'any';
300
- $filter_and_or = ( isset( $options['filterandor'] ) ) ? esc_attr( $options['filterandor'] ) : 'and';
301
- $exclude_any_all = ( isset( $options['excludeanyall'] ) ) ? esc_attr( $options['excludeanyall'] ) : 'any';
302
-
303
- ?>
304
- <p>Show Tweets that contain
305
- <select name="<?php echo $args['option'].'[includeanyall]'; ?>" id="ctf_includeanyall">
306
- <option value="any" <?php if ( $include_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
307
- <option value="all" <?php if ( $include_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
308
- </select>
309
- of the "includewords"
310
- <select name="<?php echo $args['option'].'[filterandor]'; ?>" id="ctf_filterandor">
311
- <option value="and" <?php if ( $filter_and_or == "and" ) echo 'selected="selected"'; ?> ><?php _e('and'); ?></option>
312
- <option value="or" <?php if ( $filter_and_or == "or" ) echo 'selected="selected"'; ?> ><?php _e('or'); ?></option>
313
- </select>
314
- do not contain
315
- <select name="<?php echo $args['option'].'[excludeanyall]'; ?>" id="ctf_excludeanyall">
316
- <option value="any" <?php if ( $exclude_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
317
- <option value="all" <?php if ( $exclude_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
318
- </select>
319
- of the "excludewords"
320
- </p>
321
- <?php if ( isset( $args['whatis'] ) ) : ?>
322
- <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
323
- <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
324
- <?php endif; ?>
325
- <?php
326
- }
327
-
328
- add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_masonry_autoload_section_to_customize' );
329
- function ctf_add_masonry_autoload_section_to_customize() {
330
- ?>
331
- <a id="autoscroll"></a>
332
- <?php do_settings_sections( 'ctf_options_autoscroll' ); ?>
333
- <!-- <p class="submit"><input class="button-primary" type="submit" name="save" value="<?php esc_attr_e( 'Save Changes' ); ?>" /></p> -->
334
- <hr>
335
- <?php
336
- }
337
-
338
- add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_filter_section_to_customize' );
339
- function ctf_add_filter_section_to_customize() {
340
- echo '<a id="moderation"></a>';
341
- do_settings_sections( 'ctf_options_filter' ); // matches the section name
342
- echo '<hr>';
343
- }
344
-
345
- function ctf_lite_dismiss() {
346
- $nonce = isset( $_POST['ctf_nonce'] ) ? sanitize_text_field( $_POST['ctf_nonce'] ) : '';
347
-
348
- if ( ! wp_verify_nonce( $nonce, 'ctf-smash-balloon' ) ) {
349
- die ( 'You did not do this the right way!' );
350
- }
351
-
352
- set_transient( 'twitter_feed_dismiss_lite', 'dismiss', 1 * WEEK_IN_SECONDS );
353
-
354
- die();
355
- }
356
- add_action( 'wp_ajax_ctf_lite_dismiss', 'ctf_lite_dismiss' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ add_filter( 'ctf_admin_search_label', 'ctf_return_string_hashtag' );
3
+ function ctf_return_string_hashtag( $val ) {
4
+ return 'Hashtag:';
5
+ }
6
+
7
+ add_filter( 'ctf_admin_search_whatis', 'ctf_return_string_instructions' );
8
+ function ctf_return_string_instructions( $val ) {
9
+ return 'Select this option and enter any single hashtag for a hashtag feed. Only tweets made within the last 7 days are available initially. Once a tweet has been retrieved the plugin will keep it in a persistent cache indefinitely';
10
+ }
11
+
12
+ add_filter( 'ctf_admin_validate_search_text', 'ctf_validate_search_text', 10, 1 );
13
+ function ctf_validate_search_text( $val ) {
14
+ preg_match( "/^[\p{L}0-9_]+|^#+[\p{L}0-9_]+/u", trim( $val ), $hashtags );
15
+
16
+ $hashtags = preg_replace( "/#{2,}/", '', $hashtags );
17
+
18
+ $new_val = ! empty( $hashtags ) ? $new_val = $hashtags[0] : '';
19
+
20
+ if ( substr( $new_val, 0, 1 ) != '#' && $new_val != '' ) {
21
+ $new_val = '#' . $new_val;
22
+ }
23
+
24
+ return $new_val;
25
+ }
26
+
27
+ add_filter( 'ctf_admin_validate_usertimeline_text', 'ctf_validate_usertimeline_text', 10, 1 );
28
+ function ctf_validate_usertimeline_text( $val ) {
29
+ preg_match( "/^[\p{L}0-9_]{1,16}/u" , str_replace( '@', '', trim( $val ) ), $screenname );
30
+
31
+ $new_val = isset( $screenname[0] ) ? $screenname[0] : '';
32
+
33
+ return $new_val;
34
+ }
35
+
36
+ add_filter( 'ctf_admin_validate_include_replies', 'ctf_validate_include_replies', 10, 1 );
37
+ function ctf_validate_include_replies( $val ) {
38
+ return false;
39
+ }
40
+
41
+ add_filter( 'ctf_admin_set_include_replies', 'ctf_set_include_replies', 10, 1 );
42
+ function ctf_set_include_replies( $new_input ) {
43
+ return false;
44
+ }
45
+
46
+ add_filter( 'ctf_admin_feed_type_list', 'ctf_return_feed_types' );
47
+ function ctf_return_feed_types( $val ) {
48
+ return array( 'hometimelineinclude_replies', 'usertimelineinclude_replies' );
49
+ }
50
+
51
+ add_action( 'ctf_admin_upgrade_note', 'ctf_update_note' );
52
+ function ctf_update_note() {
53
+ ?>
54
+ <span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Available in Pro version</a></span>
55
+ <?php
56
+ }
57
+
58
+ add_action( 'ctf_admin_feed_settings_radio_extra', 'ctf_usertimeline_error_message' );
59
+ function ctf_usertimeline_error_message( $args )
60
+ { //sbi_notice sbi_user_id_error
61
+ if ( $args['name'] == 'usertimeline') : ?>
62
+ <div class="ctf_notice ctf_usertimeline_error">
63
+ <?php _e( "<p>Please use a single screenname or Twitter handle of numbers and letters. If you would like to use more than one screen name for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf' target='_blank'>Pro version</a>.</p>" ); ?>
64
+ </div>
65
+ <?php endif;
66
+ }
67
+
68
+ add_action( 'ctf_admin_feed_settings_search_extra', 'ctf_hashtag_error_message' );
69
+ function ctf_hashtag_error_message() {
70
+ ?>
71
+ <div class="ctf_notice ctf_search_error">
72
+ <?php _e( "<p>Please use a single hashtag of numbers and letters. If you would like to use more than one hashtag or use search terms for your feed, please upgrade to our <a href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf' target='_blank'>Pro version</a>.</p>" ); ?>
73
+ </div>
74
+ <?php
75
+ }
76
+
77
+ add_filter( 'ctf_admin_customize_quick_links', 'ctf_return_customize_quick_links' );
78
+ function ctf_return_customize_quick_links() {
79
+ return array(
80
+ 0 => array( 'general', 'General' ),
81
+ 1 => array( 'showhide', 'Show/Hide' ),
82
+ 2 => array( 'misc', 'Misc' ),
83
+ 3 => array( 'advanced', 'Advanced' )
84
+ );
85
+ }
86
+
87
+ add_filter( 'ctf_admin_style_quick_links', 'ctf_return_style_quick_links' );
88
+ function ctf_return_style_quick_links() {
89
+ return array(
90
+ 0 => array( 'general', 'General' ),
91
+ 1 => array( 'header', 'Header' ),
92
+ 2 => array( 'date', 'Date' ),
93
+ 3 => array( 'author', 'Author' ),
94
+ 4 => array( 'text', 'Tweet Text' ),
95
+ 5 => array( 'links', 'Links' ),
96
+ 6 => array( 'quoted', 'Retweet Boxes' ),
97
+ 7 => array( 'actions', 'Tweet Actions' ),
98
+ 8 => array( 'load', 'Load More' )
99
+ );
100
+ }
101
+
102
+ /*
103
+ * Pro Options ----------------------------------------
104
+ */
105
+
106
+ add_action( 'ctf_admin_endpoints', 'ctf_add_mentionstimeline_options', 10, 1 );
107
+ function ctf_add_mentionstimeline_options( $admin ) {
108
+ $admin->create_settings_field( array(
109
+ 'name' => 'search_pro',
110
+ 'title' => '<label></label>', // label for the input field
111
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
112
+ 'page' => 'ctf_options_feed_settings', // matches the section name
113
+ 'section' => 'ctf_options_feed_settings', // matches the section name
114
+ 'option' => 'ctf_options', // matches the options name
115
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
116
+ 'whatis' => 'You can create search feeds which contain a large variety of different terms and operators, such as a combination of #hashtags, @mentions, words, or "phrases"', // what is this? text
117
+ 'label' => "Search",
118
+ 'has_input' => false,
119
+ 'has_replies' => false
120
+ ));
121
+ $admin->create_settings_field( array(
122
+ 'name' => 'mentionstimeline',
123
+ 'title' => '<label></label>', // label for the input field
124
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
125
+ 'page' => 'ctf_options_feed_settings', // matches the section name
126
+ 'section' => 'ctf_options_feed_settings', // matches the section name
127
+ 'option' => 'ctf_options', // matches the options name
128
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
129
+ 'whatis' => 'Select this option to display tweets that @mention your twitter handle', // what is this? text
130
+ 'label' => "Mentions",
131
+ 'has_input' => false,
132
+ 'has_replies' => false
133
+ ));
134
+ $admin->create_settings_field( array(
135
+ 'name' => 'lists',
136
+ 'title' => '<label></label>', // label for the input field
137
+ 'callback' => 'feed_settings_radio', // name of the function that outputs the html
138
+ 'page' => 'ctf_options_feed_settings', // matches the section name
139
+ 'section' => 'ctf_options_feed_settings', // matches the section name
140
+ 'option' => 'ctf_options', // matches the options name
141
+ 'class' => 'ctf-radio ctf_pro', // class for the wrapper and input field
142
+ 'whatis' => 'Enter the list ID of the list(s) you want to display. Use this FAQ to create a list on Twitter. Use the helper to find IDs', // what is this? text
143
+ 'label' => "Lists",
144
+ 'has_input' => false,
145
+ 'has_replies' => false
146
+ ));
147
+ }
148
+
149
+ add_filter( 'ctf_admin_show_hide_list', 'ctf_show_hide_list', 10, 1 );
150
+ function ctf_show_hide_list( $show_hide_list ) {
151
+ $show_hide_list[] = array( 'include_replied_to', 'In reply to text' );
152
+ $show_hide_list[] = array( 'include_media', 'Media (images, videos, gifs)' );
153
+ $show_hide_list[] = array( 'include_twittercards', 'Twitter Cards' );
154
+ return $show_hide_list;
155
+ }
156
+
157
+ function ctf_pro_autoscroll_section() {
158
+ ?>
159
+ <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Upgrade to Pro to enable Autoscroll loading</a></p>
160
+ <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
161
+
162
+ <div class="ctf-pro-options">
163
+ <table class="form-table"><tbody><tr><th scope="row"><label for="ctf_autoscroll" title="Click for shortcode option">Set Load More on Scroll as Default</label><code class="ctf_shortcode">autoscroll
164
+ Eg: autoscroll=true</code></th><td> <input name="ctf_options[autoscroll]" id="ctf_autoscroll" type="checkbox" disabled>
165
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
166
+ <p class="ctf-tooltip ctf-more-info">This will make every Twitter feed load more Tweets as the user gets to the bottom of the feed.</p>
167
+ </td></tr><tr class="default-text"><th scope="row"><label for="ctf_autoscrolldistance">Auto Scroll Trigger Distance</label><code class="ctf_shortcode">autoscrolldistance
168
+ Eg: autoscrolldistance=2</code></th><td> <input name="ctf_options[autoscrolldistance]" id="ctf_autoscrolldistance" class="default-text" type="text" value="200" disabled>
169
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
170
+ <p class="ctf-tooltip ctf-more-info">This is the distance in pixels from the bottom of the page the user must scroll to to trigger the loading of more tweets.</p>
171
+ </td></tr></tbody></table>
172
+ </div>
173
+ <div style="height: 18px;"></div>
174
+ <?php
175
+ }
176
+
177
+ function ctf_pro_moderation_section() {
178
+ ?>
179
+ <p class="ctf_pro_section_note"><a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Upgrade to Pro to enable Tweet moderation</a></p>
180
+ <span><a href="javascript:void(0);" class="button button-secondary ctf-show-pro"><b>+</b> Show Pro Options</a></span>
181
+
182
+ <div class="ctf-pro-options">
183
+ <table class="form-table"><tbody><tr class="large-text"><th scope="row"><label for="ctf_includewords" title="Click for shortcode option">Show Tweets containing these words or hashtags</label><code class="ctf_shortcode">includewords
184
+ Eg: includewords="#puppy,#cute"</code></th><td> <input name="ctf_options[includewords]" id="ctf_includewords" class="large-text" type="text" value="" disabled>
185
+ <span>"includewords" separate words by comma</span>
186
+ </td></tr><tr class="large-text"><th scope="row"><label for="ctf_excludewords">Remove Tweets containing these words or hashtags</label><code class="ctf_shortcode">excludewords
187
+ Eg: excludewords="#ugly,#bad"</code></th><td> <input name="ctf_options[excludewords]" id="ctf_excludewords" class="large-text" type="text" value="" disabled>
188
+ <span>"excludewords" separate words by comma</span>
189
+ </td></tr><tr><th scope="row"></th><td> <p>Show Tweets that contain
190
+ <select name="ctf_options[includeanyall]" id="ctf_includeanyall" disabled>
191
+ <option value="any" selected="selected">any</option>
192
+ <option value="all">all</option>
193
+ </select>
194
+ of the "includewords"
195
+ <select name="ctf_options[filterandor]" id="ctf_filterandor" disabled>
196
+ <option value="and" selected="selected">and</option>
197
+ <option value="or">or</option>
198
+ </select>
199
+ do not contain
200
+ <select name="ctf_options[excludeanyall]" id="ctf_excludeanyall" disabled>
201
+ <option value="any" selected="selected">any</option>
202
+ <option value="all">all</option>
203
+ </select>
204
+ of the "excludewords"
205
+ </p>
206
+ </td></tr><tr><th scope="row"><label for="ctf_remove_by_id">Hide Specific Tweets</label></th><td> <textarea name="ctf_options[remove_by_id]" id="ctf_remove_by_id" style="width: 70%;" rows="3" disabled></textarea>
207
+ <p>separate IDs by comma <a class="ctf-tooltip-link" href="JavaScript:void(0);"><i class="fa fa-question-circle" aria-hidden="true"></i></a>
208
+ <span class="ctf-tooltip ctf-more-info">These are the specific ID numbers associated with a tweet. You can find the ID of a Tweet by viewing the Tweet on Twitter and copy/pasting the ID number from the end of the URL.</span>
209
+ </p> </td></tr></tbody></table>
210
+ </div>
211
+ <div style="height: 18px;"></div>
212
+ <?php
213
+ }
214
+
215
+ add_action( 'ctf_admin_style_option', 'ctf_add_masonry_autoscroll_options', 5, 1 );
216
+ function ctf_add_masonry_autoscroll_options( $admin ) {
217
+ // custom in reply to text
218
+ $admin->create_settings_field( array(
219
+ 'name' => 'inreplytotext',
220
+ 'title' => '<label for="ctf_inreplytotext">Translation for "In reply to"</label><code class="ctf_shortcode">inreplytotext
221
+ Eg: inreplytotext="Als Antwort an"</code>', // label for the input field
222
+ 'callback' => 'default_text', // name of the function that outputs the html
223
+ 'page' => 'ctf_options_text', // matches the section name
224
+ 'section' => 'ctf_options_text', // matches the section name
225
+ 'option' => 'ctf_options', // matches the options name
226
+ 'class' => 'default-text ctf_pro', // class for the wrapper and input field
227
+ 'whatis' => 'This will replace the default text displayed for "In reply to"',
228
+ 'default' => 'In reply to'// "what is this?" text
229
+ ) );
230
+
231
+ add_settings_section(
232
+ 'ctf_options_autoscroll', // matches the section name
233
+ '<span class="ctf_pro_header">Autoscroll Loading</span>',
234
+ 'ctf_pro_autoscroll_section', // callback function to explain the section
235
+ 'ctf_options_autoscroll' // matches the section name
236
+ );
237
+
238
+ add_settings_section(
239
+ 'ctf_options_filter', // matches the section name
240
+ '<span class="ctf_pro_header">Moderation</span>',
241
+ 'ctf_pro_moderation_section', // callback function to explain the section
242
+ 'ctf_options_filter' // matches the section name
243
+ );
244
+ }
245
+
246
+ add_action( 'ctf_admin_customize_option', 'ctf_add_customize_general_options', 20, 1 );
247
+ function ctf_add_customize_general_options( $admin ) {
248
+
249
+ // Disable the lightbox
250
+ $admin->create_settings_field( array(
251
+ 'name' => 'disablelightbox',
252
+ 'title' => '<label for="ctf_disablelightbox">Disable the lightbox</label><code class="ctf_shortcode">disablelightbox
253
+ Eg: disablelightbox=true</code>', // label for the input field
254
+ 'callback' => 'default_checkbox', // name of the function that outputs the html
255
+ 'page' => 'ctf_options_general', // matches the section name
256
+ 'section' => 'ctf_options_general', // matches the section name
257
+ 'option' => 'ctf_options', // matches the options name
258
+ 'class' => 'default-text ctf_pro', // class for the wrapper and input field
259
+ 'whatis' => 'Disable the popup lightbox for media in the feed'
260
+ ) );
261
+ }
262
+
263
+
264
+ add_action( 'ctf_admin_customize_option', 'ctf_add_filter_options', 10, 1 );
265
+ function ctf_add_filter_options( $admin ) {
266
+
267
+ add_settings_field(
268
+ 'clear_tc_cache_button',
269
+ '<label for="ctf_clear_tc_cache_button">Clear Twitter Card Cache</label>',
270
+ 'ctf_clear_tc_cache_button',
271
+ 'ctf_options_advanced',
272
+ 'ctf_options_advanced',
273
+ array( 'class' => 'ctf_pro')
274
+ );
275
+ }
276
+
277
+ function ctf_remove_by_id( $args ) {
278
+ $options = get_option( $args['option'] );
279
+ $option_string = ( isset( $options[ $args['name'] ] ) ) ? esc_attr( $options[ $args['name'] ] ) : '';
280
+ ?>
281
+ <textarea name="<?php echo $args['option'].'['.$args['name'].']'; ?>" id="ctf_<?php echo $args['name']; ?>" style="width: 70%;" rows="3"><?php esc_attr_e( stripslashes( $option_string ) ); ?></textarea>
282
+ <?php if ( isset( $args['extra'] ) ) : ?><p><?php _e( $args['extra'], 'custom-twitter-feeds' ); ?>
283
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
284
+ <span class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</span>
285
+ </p> <?php endif; ?>
286
+ <?php
287
+ }
288
+
289
+ function ctf_clear_tc_cache_button() {
290
+ ?>
291
+ <input id="ctf-clear-tc-cache" class="button-secondary" style="margin-top: 1px;" type="submit" value="<?php esc_attr_e( 'Clear Twitter Cards' ); ?>" />
292
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
293
+ <p class="ctf-tooltip ctf-more-info"><?php _e( 'Clicking this button will clear all cached data for your links that have Twitter Cards', 'custom-twitter-feeds' ); ?>.</p>
294
+ <?php
295
+ }
296
+
297
+ function ctf_filter_operator( $args ) {
298
+ $options = get_option( $args['option'] );
299
+ $include_any_all = ( isset( $options['includeanyall'] ) ) ? esc_attr( $options['includeanyall'] ) : 'any';
300
+ $filter_and_or = ( isset( $options['filterandor'] ) ) ? esc_attr( $options['filterandor'] ) : 'and';
301
+ $exclude_any_all = ( isset( $options['excludeanyall'] ) ) ? esc_attr( $options['excludeanyall'] ) : 'any';
302
+
303
+ ?>
304
+ <p>Show Tweets that contain
305
+ <select name="<?php echo $args['option'].'[includeanyall]'; ?>" id="ctf_includeanyall">
306
+ <option value="any" <?php if ( $include_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
307
+ <option value="all" <?php if ( $include_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
308
+ </select>
309
+ of the "includewords"
310
+ <select name="<?php echo $args['option'].'[filterandor]'; ?>" id="ctf_filterandor">
311
+ <option value="and" <?php if ( $filter_and_or == "and" ) echo 'selected="selected"'; ?> ><?php _e('and'); ?></option>
312
+ <option value="or" <?php if ( $filter_and_or == "or" ) echo 'selected="selected"'; ?> ><?php _e('or'); ?></option>
313
+ </select>
314
+ do not contain
315
+ <select name="<?php echo $args['option'].'[excludeanyall]'; ?>" id="ctf_excludeanyall">
316
+ <option value="any" <?php if ( $exclude_any_all == "any" ) echo 'selected="selected"'; ?> ><?php _e('any'); ?></option>
317
+ <option value="all" <?php if ( $exclude_any_all == "all" ) echo 'selected="selected"'; ?> ><?php _e('all'); ?></option>
318
+ </select>
319
+ of the "excludewords"
320
+ </p>
321
+ <?php if ( isset( $args['whatis'] ) ) : ?>
322
+ <a class="ctf-tooltip-link" href="JavaScript:void(0);"><span class="fa fa-question-circle" aria-hidden="true"></span></a>
323
+ <p class="ctf-tooltip ctf-more-info"><?php _e( $args['whatis'], 'custom-twitter-feeds' ); ?>.</p>
324
+ <?php endif; ?>
325
+ <?php
326
+ }
327
+
328
+ add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_masonry_autoload_section_to_customize' );
329
+ function ctf_add_masonry_autoload_section_to_customize() {
330
+ ?>
331
+ <a id="autoscroll"></a>
332
+ <?php do_settings_sections( 'ctf_options_autoscroll' ); ?>
333
+ <!-- <p class="submit"><input class="button-primary" type="submit" name="save" value="<?php esc_attr_e( 'Save Changes' ); ?>" /></p> -->
334
+ <hr>
335
+ <?php
336
+ }
337
+
338
+ add_action( 'ctf_admin_add_settings_sections_to_customize', 'ctf_add_filter_section_to_customize' );
339
+ function ctf_add_filter_section_to_customize() {
340
+ echo '<a id="moderation"></a>';
341
+ do_settings_sections( 'ctf_options_filter' ); // matches the section name
342
+ echo '<hr>';
343
+ }
344
+
345
+ function ctf_lite_dismiss() {
346
+ $nonce = isset( $_POST['ctf_nonce'] ) ? sanitize_text_field( $_POST['ctf_nonce'] ) : '';
347
+
348
+ if ( ! wp_verify_nonce( $nonce, 'ctf-smash-balloon' ) ) {
349
+ die ( 'You did not do this the right way!' );
350
+ }
351
+
352
+ set_transient( 'twitter_feed_dismiss_lite', 'dismiss', 1 * WEEK_IN_SECONDS );
353
+
354
+ die();
355
+ }
356
+ add_action( 'wp_ajax_ctf_lite_dismiss', 'ctf_lite_dismiss' );
357
+
358
+ function ctf_admin_hide_unrelated_notices() {
359
+
360
+ // Bail if we're not on a ctf screen or page.
361
+ if ( ! isset( $_GET['page'] ) || $_GET['page'] !== 'custom-twitter-feeds' ) {
362
+ return;
363
+ }
364
+
365
+ // Extra banned classes and callbacks from third-party plugins.
366
+ $blacklist = array(
367
+ 'classes' => array(),
368
+ 'callbacks' => array(
369
+ 'ctfdb_admin_notice', // 'Database for ctf' plugin.
370
+ ),
371
+ );
372
+
373
+ global $wp_filter;
374
+
375
+ foreach ( array( 'user_admin_notices', 'admin_notices', 'all_admin_notices' ) as $notices_type ) {
376
+ if ( empty( $wp_filter[ $notices_type ]->callbacks ) || ! is_array( $wp_filter[ $notices_type ]->callbacks ) ) {
377
+ continue;
378
+ }
379
+ foreach ( $wp_filter[ $notices_type ]->callbacks as $priority => $hooks ) {
380
+ foreach ( $hooks as $name => $arr ) {
381
+ if ( is_object( $arr['function'] ) && $arr['function'] instanceof Closure ) {
382
+ unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
383
+ continue;
384
+ }
385
+ $class = ! empty( $arr['function'][0] ) && is_object( $arr['function'][0] ) ? strtolower( get_class( $arr['function'][0] ) ) : '';
386
+ if (
387
+ ! empty( $class ) &&
388
+ strpos( $class, 'ctf' ) !== false &&
389
+ ! in_array( $class, $blacklist['classes'], true )
390
+ ) {
391
+ continue;
392
+ }
393
+ if (
394
+ ! empty( $name ) && (
395
+ strpos( $name, 'ctf' ) === false ||
396
+ in_array( $class, $blacklist['classes'], true ) ||
397
+ in_array( $name, $blacklist['callbacks'], true )
398
+ )
399
+ ) {
400
+ unset( $wp_filter[ $notices_type ]->callbacks[ $priority ][ $name ] );
401
+ }
402
+ }
403
+ }
404
+ }
405
+ }
406
+ add_action( 'admin_print_scripts', 'ctf_admin_hide_unrelated_notices' );
inc/blocks/class-ctf-blocks.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Instagram Feed block with live preview.
4
+ *
5
+ * @since 1.7.1
6
+ */
7
+ class CTF_Blocks {
8
+
9
+ /**
10
+ * Indicates if current integration is allowed to load.
11
+ *
12
+ * @since 1.8
13
+ *
14
+ * @return bool
15
+ */
16
+ public function allow_load() {
17
+ return function_exists( 'register_block_type' );
18
+ }
19
+
20
+ /**
21
+ * Loads an integration.
22
+ *
23
+ * @since 1.7.1
24
+ */
25
+ public function load() {
26
+ $this->hooks();
27
+ }
28
+
29
+ /**
30
+ * Integration hooks.
31
+ *
32
+ * @since 1.7.1
33
+ */
34
+ protected function hooks() {
35
+ add_action( 'init', array( $this, 'register_block' ) );
36
+ add_action( 'enqueue_block_editor_assets', array( $this, 'enqueue_block_editor_assets' ) );
37
+ }
38
+
39
+ /**
40
+ * Register Instagram Feed Gutenberg block on the backend.
41
+ *
42
+ * @since 1.7.1
43
+ */
44
+ public function register_block() {
45
+
46
+ wp_register_style(
47
+ 'ctf-blocks-styles',
48
+ trailingslashit( CTF_PLUGIN_URL ) . 'css/ctf-blocks.css',
49
+ array( 'wp-edit-blocks' ),
50
+ CTF_VERSION
51
+ );
52
+
53
+ $attributes = array(
54
+ 'shortcodeSettings' => array(
55
+ 'type' => 'string',
56
+ ),
57
+ 'noNewChanges' => array(
58
+ 'type' => 'boolean',
59
+ ),
60
+ 'executed' => array(
61
+ 'type' => 'boolean',
62
+ )
63
+ );
64
+
65
+ register_block_type(
66
+ 'ctf/ctf-feed-block',
67
+ array(
68
+ 'attributes' => $attributes,
69
+ 'render_callback' => array( $this, 'get_feed_html' ),
70
+ )
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Load Instagram Feed Gutenberg block scripts.
76
+ *
77
+ * @since 1.7.1
78
+ */
79
+ public function enqueue_block_editor_assets() {
80
+ ctf_scripts_and_styles( true );
81
+
82
+ wp_enqueue_style( 'ctf-blocks-styles' );
83
+ wp_enqueue_script(
84
+ 'ctf-feed-block',
85
+ trailingslashit( CTF_PLUGIN_URL ) . 'js/ctf-blocks.js',
86
+ array( 'wp-blocks', 'wp-i18n', 'wp-element' ),
87
+ CTF_VERSION,
88
+ true
89
+ );
90
+
91
+ $shortcodeSettings = '';
92
+
93
+ $i18n = array(
94
+ 'addSettings' => esc_html__( 'Add Settings', 'custom-twitter-feeds' ),
95
+ 'shortcodeSettings' => esc_html__( 'Shortcode Settings', 'custom-twitter-feeds' ),
96
+ 'example' => esc_html__( 'Example', 'custom-twitter-feeds' ),
97
+ 'preview' => esc_html__( 'Apply Changes', 'custom-twitter-feeds' ),
98
+
99
+ );
100
+
101
+ wp_localize_script(
102
+ 'ctf-feed-block',
103
+ 'ctf_block_editor',
104
+ array(
105
+ 'wpnonce' => wp_create_nonce( 'ctf-blocks' ),
106
+ 'canShowFeed' => true,
107
+ 'configureLink' => get_admin_url() . '?page=custom-twitter-feeds',
108
+ 'shortcodeSettings' => $shortcodeSettings,
109
+ 'i18n' => $i18n,
110
+ )
111
+ );
112
+ }
113
+
114
+ /**
115
+ * Get form HTML to display in a Instagram Feed Gutenberg block.
116
+ *
117
+ * @param array $attr Attributes passed by Instagram Feed Gutenberg block.
118
+ *
119
+ * @since 1.7.1
120
+ *
121
+ * @return string
122
+ */
123
+ public function get_feed_html( $attr ) {
124
+
125
+ $return = '';
126
+
127
+ $shortcode_settings = isset( $attr['shortcodeSettings'] ) ? $attr['shortcodeSettings'] : '';
128
+
129
+ $shortcode_settings = str_replace(array( '[custom-twitter-feeds', ']' ), '', $shortcode_settings );
130
+
131
+ $return .= do_shortcode( '[custom-twitter-feeds '.$shortcode_settings.']' );
132
+
133
+ return $return;
134
+
135
+ }
136
+
137
+ /**
138
+ * Checking if is Gutenberg REST API call.
139
+ *
140
+ * @since 1.7.1
141
+ *
142
+ * @return bool True if is Gutenberg REST API call.
143
+ */
144
+ public static function is_gb_editor() {
145
+
146
+ // TODO: Find a better way to check if is GB editor API call.
147
+ return defined( 'REST_REQUEST' ) && REST_REQUEST && ! empty( $_REQUEST['context'] ) && 'edit' === $_REQUEST['context']; // phpcs:ignore
148
+ }
149
+
150
+ }
inc/notices.php CHANGED
@@ -1,275 +1,275 @@
1
- <?php
2
- function ctf_get_current_time() {
3
- $current_time = time();
4
-
5
- // where to do tests
6
- //$current_time = strtotime( 'November 25, 2020' ) + 1;
7
-
8
- return $current_time;
9
- }
10
-
11
- // generates the html for the admin notices
12
- function ctf_notices_html() {
13
-
14
- if ( function_exists( 'sbi_notices_html' ) || function_exists( 'cff_notices_html' ) ) {
15
- return;
16
- }
17
-
18
- $current_screen = get_current_screen();
19
- $is_plugins_page = isset( $current_screen->id ) && $current_screen->id === 'plugins';
20
- $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
21
- //Only show to admins
22
- if ( ! current_user_can( 'manage_options' ) ) {
23
- return;
24
- }
25
-
26
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
27
- $current_time = ctf_get_current_time();
28
- $ctf_bfcm_discount_code = 'happysmashgiving' . date('Y', $current_time );
29
-
30
- // reset everything for testing
31
- if ( false ) {
32
- global $current_user;
33
- $user_id = $current_user->ID;
34
- delete_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
35
- //delete_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
36
- //$ctf_statuses_option = array( 'first_install' => strtotime( 'December 8, 2019' ) );
37
- //$ctf_statuses_option = array( 'first_install' => time() );
38
-
39
- //update_option( 'ctf_statuses', $ctf_statuses_option, false );
40
- //delete_option( 'ctf_rating_notice');
41
- //delete_transient( 'instagram_feed_rating_notice_waiting' );
42
-
43
- //set_transient( 'instagram_feed_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
44
- //update_option( 'ctf_rating_notice', 'pending', false );
45
- }
46
-
47
- //$ctf_statuses_option['rating_notice_dismissed'] = time();
48
- //update_option( 'ctf_statuses', $ctf_statuses_option, false );
49
- // rating notice logic
50
- $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
51
- $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
52
- $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
53
-
54
- // black friday cyber monday logic
55
- $thanksgiving_this_year = ctf_get_future_date( 11, date('Y', $current_time ), 4, 4, 1 );
56
- $one_week_before_black_friday_this_year = $thanksgiving_this_year - 7*24*60*60;
57
- $one_day_after_cyber_monday_this_year = $thanksgiving_this_year + 5*24*60*60;
58
- $has_been_two_days_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 2*24*60*60) < $current_time : true;
59
-
60
- $could_show_bfcm_discount = ($current_time > $one_week_before_black_friday_this_year && $current_time < $one_day_after_cyber_monday_this_year);
61
- $should_show_bfcm_discount = false;
62
- if ( $could_show_bfcm_discount && $has_been_two_days_since_rating_dismissal ) {
63
- global $current_user;
64
- $user_id = $current_user->ID;
65
-
66
- $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
67
- $ignore_bfcm_sale_notice_meta = isset( $ignore_bfcm_sale_notice_meta[0] ) ? $ignore_bfcm_sale_notice_meta[0] : '';
68
-
69
- /* Check that the user hasn't already clicked to ignore the message */
70
- $should_show_bfcm_discount = ($ignore_bfcm_sale_notice_meta !== 'always' && $ignore_bfcm_sale_notice_meta !== date( 'Y', $current_time ));
71
- }
72
-
73
- // new user discount logic
74
- $in_new_user_month_range = true;
75
- $should_show_new_user_discount = false;
76
- $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 30*24*60*60) < $current_time + 1: true;
77
-
78
- if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
79
- global $current_user;
80
- $user_id = $current_user->ID;
81
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
82
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
83
-
84
- if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
85
- $should_show_new_user_discount = true;
86
- }
87
- } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal ) {
88
- global $current_user;
89
- $user_id = $current_user->ID;
90
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
91
- $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
92
-
93
- if ( $ignore_new_user_sale_notice_meta !== 'always'
94
- && isset( $ctf_statuses_option['first_install'] )
95
- && $current_time > (int)$ctf_statuses_option['first_install'] + 60*60*24*30 ) {
96
- $should_show_new_user_discount = true;
97
- }
98
- }
99
-
100
- // for debugging
101
- if ( false ) {
102
- global $current_user;
103
- $user_id = $current_user->ID;
104
- $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
105
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
106
-
107
- var_dump( 'new user rating option', $ctf_rating_notice_option );
108
- var_dump( 'new user rating transient', $ctf_rating_notice_waiting );
109
-
110
- var_dump( 'should show new user rating notice?', $should_show_rating_notice );
111
-
112
- var_dump( 'new user discount month range?', $in_new_user_month_range );
113
- var_dump( 'should show new user discount?', $should_show_new_user_discount );
114
-
115
- var_dump( 'Thanksgiving this year?', date('m/d/Y', $thanksgiving_this_year ) );
116
-
117
- var_dump( 'could show bfcm discount?', $could_show_bfcm_discount );
118
- var_dump( 'rating was dismissed?', date('m/d/Y', $ctf_statuses_option['rating_notice_dismissed'] ) );
119
-
120
- var_dump( 'should show bfcm discount?', $should_show_bfcm_discount );
121
-
122
- var_dump( 'ignore_bfcm_sale_notice_meta', $ignore_bfcm_sale_notice_meta );
123
- var_dump( 'ignore_new_user_sale_notice_meta', $ignore_new_user_sale_notice_meta );
124
-
125
- var_dump( $ctf_statuses_option );
126
- }
127
-
128
-
129
- if ( $should_show_rating_notice ) {
130
- $other_notice_html = '';
131
- $dismiss_url = add_query_arg( 'ctf_ignore_rating_notice_nag', '1' );
132
- $later_url = add_query_arg( 'ctf_ignore_rating_notice_nag', 'later' );
133
- if ( $should_show_bfcm_discount ) {
134
- $other_notice_html = '<p class="ctf_other_notice">' . __( 'PS. We currently have a <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount='.$ctf_bfcm_discount_code.'" target="_blank"><b style="font-weight: 700;">Black Friday deal</b></a> for 20% off the Pro version!', 'custom-twitter-feed' ) . '</p>';
135
-
136
- $dismiss_url = add_query_arg( array(
137
- 'ctf_ignore_rating_notice_nag' => '1',
138
- 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
139
- )
140
- );
141
- $later_url = add_query_arg( array(
142
- 'ctf_ignore_rating_notice_nag' => 'later',
143
- 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
144
- )
145
- );
146
- }
147
-
148
- echo "
149
- <div class='ctf_notice ctf_review_notice'>
150
- <img src='". CTF_PLUGIN_URL . 'img/ctf-icon.jpg' ."' alt='" . __( 'Custom Twitter Feed', 'custom-twitter-feed' ) . "'>
151
- <div class='ctf-notice-text'>
152
- <p style='padding-top: 4px;'>" . __( "It's great to see that you've been using the <strong style='font-weight: 700;'>Custom Twitter Feeds</strong> plugin for a while now. Hopefully you're happy with it!&nbsp; If so, would you consider leaving a positive review? It really helps to support the plugin and helps others to discover it too!", 'custom-twitter-feed' ) . "</p>
153
- <p class='links'";
154
- if( $should_show_bfcm_discount ) echo " style='margin-top: 0 !important;'";
155
- echo ">
156
- <a class='ctf_notice_dismiss' href='https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/' target='_blank'>" . __( 'Sure, I\'d love to!', 'custom-twitter-feed' ) . "</a>
157
- &middot;
158
- <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'No thanks', 'custom-twitter-feed' ) . "</a>
159
- &middot;
160
- <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'I\'ve already given a review', 'custom-twitter-feed' ) . "</a>
161
- &middot;
162
- <a class='ctf_notice_dismiss' href='" .esc_url( $later_url ). "'>" . __( 'Ask Me Later', 'custom-twitter-feed' ) . "</a>
163
- </p>"
164
- . $other_notice_html .
165
- "</div>
166
- <a class='ctf_notice_close' href='" .esc_url( $dismiss_url ). "'><i class='fa fa-close'></i></a>
167
- </div>";
168
-
169
- } elseif ( $should_show_new_user_discount ) {
170
- global $current_user;
171
- $user_id = $current_user->ID;
172
- $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
173
- if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
174
-
175
- echo "
176
- <div class='ctf_notice ctf_review_notice ctf_new_user_sale_notice'>
177
- <img src='" . CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' . "' alt='Custom Twitter Feed'>
178
- <div class='ctf-notice-text'>
179
- <p>" . __( '<b style="font-weight: 700;">Exclusive offer!</b> We don\'t run promotions very often, but for a limited time we\'re offering <b style="font-weight: 700;">20% off</b> our Pro version to all users of our free Custom Twitter Feeds plugin.', 'custom-twitter-feed' ) . "</p>
180
- <p class='ctf-links'>
181
- <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount=twitterthankyou' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
182
- <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
183
-
184
- </p>
185
- </div>
186
- <a class='ctf_new_user_sale_notice_close' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'><i class='fa fa-close'></i></a>
187
- </div>
188
- ";
189
- }
190
-
191
- } elseif ( $should_show_bfcm_discount ) {
192
-
193
- echo "
194
- <div class='ctf_notice ctf_review_notice ctf_bfcm_sale_notice'>
195
- <img src='". CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' ."' alt='Custom Twitter Feed'>
196
- <div class='ctf-notice-text'>
197
- <p>" . __( '<b style="font-weight: 700;">Black Friday/Cyber Monday Deal!</b> Thank you for using our free Custom Twitter Feeds plugin. For a limited time, we\'re offering <b style="font-weight: 700;">20% off</b> the Pro version for all of our users.', 'custom-twitter-feed' ) . "</p>
198
- <p class='ctf-links'>
199
- <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount=".$ctf_bfcm_discount_code."' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
200
- <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
201
- </p>
202
- </div>
203
- <a class='ctf_bfcm_sale_notice_close' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'><i class='fa fa-close'></i></a>
204
- </div>
205
- ";
206
-
207
- }
208
-
209
- }
210
- add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
211
-
212
- function ctf_process_nags() {
213
-
214
- global $current_user;
215
- $user_id = $current_user->ID;
216
- $ctf_statuses_option = get_option( 'ctf_statuses', array() );
217
-
218
- if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
219
- if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
220
- update_option( 'ctf_rating_notice', 'dismissed', false );
221
- $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
222
- update_option( 'ctf_statuses', $ctf_statuses_option, false );
223
-
224
- } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
225
- set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
226
- update_option( 'ctf_rating_notice', 'pending', false );
227
- }
228
- }
229
-
230
- if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
231
- $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
232
- if ( $response === 'always' ) {
233
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
234
-
235
- $current_month_number = (int)date('n', ctf_get_current_time() );
236
- $not_early_in_the_year = ($current_month_number > 5);
237
-
238
- if ( $not_early_in_the_year ) {
239
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
240
- }
241
-
242
- }
243
- }
244
-
245
- if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
246
- $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
247
- if ( $response === 'always' ) {
248
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
249
- } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
250
- update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
251
- }
252
- update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
253
- }
254
-
255
- }
256
- add_action( 'admin_init', 'ctf_process_nags' );
257
-
258
- function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
259
- if ( $direction > 0 ) {
260
- $startday = 1;
261
- } else {
262
- $startday = date( 't', mktime(0, 0, 0, $month, 1, $year ) );
263
- }
264
-
265
- $start = mktime( 0, 0, 0, $month, $startday, $year );
266
- $weekday = date( 'N', $start );
267
-
268
- $offset = 0;
269
- if ( $direction * $day >= $direction * $weekday ) {
270
- $offset = -$direction * 7;
271
- }
272
-
273
- $offset += $direction * ($week * 7) + ($day - $weekday);
274
- return mktime( 0, 0, 0, $month, $startday + $offset, $year );
275
  }
1
+ <?php
2
+ function ctf_get_current_time() {
3
+ $current_time = time();
4
+
5
+ // where to do tests
6
+ //$current_time = strtotime( 'November 25, 2020' ) + 1;
7
+
8
+ return $current_time;
9
+ }
10
+
11
+ // generates the html for the admin notices
12
+ function ctf_notices_html() {
13
+
14
+ if ( function_exists( 'sbi_notices_html' ) || function_exists( 'cff_notices_html' ) ) {
15
+ return;
16
+ }
17
+
18
+ $current_screen = get_current_screen();
19
+ $is_plugins_page = isset( $current_screen->id ) && $current_screen->id === 'plugins';
20
+ $page = isset( $_GET['page'] ) ? sanitize_text_field( $_GET['page'] ) : '';
21
+ //Only show to admins
22
+ if ( ! current_user_can( 'manage_options' ) ) {
23
+ return;
24
+ }
25
+
26
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
27
+ $current_time = ctf_get_current_time();
28
+ $ctf_bfcm_discount_code = 'happysmashgiving' . date('Y', $current_time );
29
+
30
+ // reset everything for testing
31
+ if ( false ) {
32
+ global $current_user;
33
+ $user_id = $current_user->ID;
34
+ delete_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
35
+ //delete_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
36
+ //$ctf_statuses_option = array( 'first_install' => strtotime( 'December 8, 2019' ) );
37
+ //$ctf_statuses_option = array( 'first_install' => time() );
38
+
39
+ //update_option( 'ctf_statuses', $ctf_statuses_option, false );
40
+ //delete_option( 'ctf_rating_notice');
41
+ //delete_transient( 'instagram_feed_rating_notice_waiting' );
42
+
43
+ //set_transient( 'instagram_feed_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
44
+ //update_option( 'ctf_rating_notice', 'pending', false );
45
+ }
46
+
47
+ //$ctf_statuses_option['rating_notice_dismissed'] = time();
48
+ //update_option( 'ctf_statuses', $ctf_statuses_option, false );
49
+ // rating notice logic
50
+ $ctf_rating_notice_option = get_option( 'ctf_rating_notice', false );
51
+ $ctf_rating_notice_waiting = get_transient( 'custom_twitter_feeds_rating_notice_waiting' );
52
+ $should_show_rating_notice = ($ctf_rating_notice_waiting !== 'waiting' && $ctf_rating_notice_option !== 'dismissed');
53
+
54
+ // black friday cyber monday logic
55
+ $thanksgiving_this_year = ctf_get_future_date( 11, date('Y', $current_time ), 4, 4, 1 );
56
+ $one_week_before_black_friday_this_year = $thanksgiving_this_year - 7*24*60*60;
57
+ $one_day_after_cyber_monday_this_year = $thanksgiving_this_year + 5*24*60*60;
58
+ $has_been_two_days_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 2*24*60*60) < $current_time : true;
59
+
60
+ $could_show_bfcm_discount = ($current_time > $one_week_before_black_friday_this_year && $current_time < $one_day_after_cyber_monday_this_year);
61
+ $should_show_bfcm_discount = false;
62
+ if ( $could_show_bfcm_discount && $has_been_two_days_since_rating_dismissal ) {
63
+ global $current_user;
64
+ $user_id = $current_user->ID;
65
+
66
+ $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
67
+ $ignore_bfcm_sale_notice_meta = isset( $ignore_bfcm_sale_notice_meta[0] ) ? $ignore_bfcm_sale_notice_meta[0] : '';
68
+
69
+ /* Check that the user hasn't already clicked to ignore the message */
70
+ $should_show_bfcm_discount = ($ignore_bfcm_sale_notice_meta !== 'always' && $ignore_bfcm_sale_notice_meta !== date( 'Y', $current_time ));
71
+ }
72
+
73
+ // new user discount logic
74
+ $in_new_user_month_range = true;
75
+ $should_show_new_user_discount = false;
76
+ $has_been_one_month_since_rating_dismissal = isset( $ctf_statuses_option['rating_notice_dismissed'] ) ? ((int)$ctf_statuses_option['rating_notice_dismissed'] + 30*24*60*60) < $current_time + 1: true;
77
+
78
+ if ( isset( $ctf_statuses_option['first_install'] ) && $ctf_statuses_option['first_install'] === 'from_update' ) {
79
+ global $current_user;
80
+ $user_id = $current_user->ID;
81
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
82
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
83
+
84
+ if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
85
+ $should_show_new_user_discount = true;
86
+ }
87
+ } elseif ( $in_new_user_month_range && $has_been_one_month_since_rating_dismissal ) {
88
+ global $current_user;
89
+ $user_id = $current_user->ID;
90
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
91
+ $ignore_new_user_sale_notice_meta = isset( $ignore_new_user_sale_notice_meta[0] ) ? $ignore_new_user_sale_notice_meta[0] : '';
92
+
93
+ if ( $ignore_new_user_sale_notice_meta !== 'always'
94
+ && isset( $ctf_statuses_option['first_install'] )
95
+ && $current_time > (int)$ctf_statuses_option['first_install'] + 60*60*24*30 ) {
96
+ $should_show_new_user_discount = true;
97
+ }
98
+ }
99
+
100
+ // for debugging
101
+ if ( false ) {
102
+ global $current_user;
103
+ $user_id = $current_user->ID;
104
+ $ignore_bfcm_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice' );
105
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
106
+
107
+ var_dump( 'new user rating option', $ctf_rating_notice_option );
108
+ var_dump( 'new user rating transient', $ctf_rating_notice_waiting );
109
+
110
+ var_dump( 'should show new user rating notice?', $should_show_rating_notice );
111
+
112
+ var_dump( 'new user discount month range?', $in_new_user_month_range );
113
+ var_dump( 'should show new user discount?', $should_show_new_user_discount );
114
+
115
+ var_dump( 'Thanksgiving this year?', date('m/d/Y', $thanksgiving_this_year ) );
116
+
117
+ var_dump( 'could show bfcm discount?', $could_show_bfcm_discount );
118
+ var_dump( 'rating was dismissed?', date('m/d/Y', $ctf_statuses_option['rating_notice_dismissed'] ) );
119
+
120
+ var_dump( 'should show bfcm discount?', $should_show_bfcm_discount );
121
+
122
+ var_dump( 'ignore_bfcm_sale_notice_meta', $ignore_bfcm_sale_notice_meta );
123
+ var_dump( 'ignore_new_user_sale_notice_meta', $ignore_new_user_sale_notice_meta );
124
+
125
+ var_dump( $ctf_statuses_option );
126
+ }
127
+
128
+
129
+ if ( $should_show_rating_notice ) {
130
+ $other_notice_html = '';
131
+ $dismiss_url = add_query_arg( 'ctf_ignore_rating_notice_nag', '1' );
132
+ $later_url = add_query_arg( 'ctf_ignore_rating_notice_nag', 'later' );
133
+ if ( $should_show_bfcm_discount ) {
134
+ $other_notice_html = '<p class="ctf_other_notice">' . __( 'PS. We currently have a <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount='.$ctf_bfcm_discount_code.'" target="_blank"><b style="font-weight: 700;">Black Friday deal</b></a> for 20% off the Pro version!', 'custom-twitter-feed' ) . '</p>';
135
+
136
+ $dismiss_url = add_query_arg( array(
137
+ 'ctf_ignore_rating_notice_nag' => '1',
138
+ 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
139
+ )
140
+ );
141
+ $later_url = add_query_arg( array(
142
+ 'ctf_ignore_rating_notice_nag' => 'later',
143
+ 'ctf_ignore_bfcm_sale_notice' => date( 'Y', $current_time )
144
+ )
145
+ );
146
+ }
147
+
148
+ echo "
149
+ <div class='ctf_notice ctf_review_notice'>
150
+ <img src='". CTF_PLUGIN_URL . 'img/ctf-icon.jpg' ."' alt='" . __( 'Custom Twitter Feed', 'custom-twitter-feed' ) . "'>
151
+ <div class='ctf-notice-text'>
152
+ <p style='padding-top: 4px;'>" . __( "It's great to see that you've been using the <strong style='font-weight: 700;'>Custom Twitter Feeds</strong> plugin for a while now. Hopefully you're happy with it!&nbsp; If so, would you consider leaving a positive review? It really helps to support the plugin and helps others to discover it too!", 'custom-twitter-feed' ) . "</p>
153
+ <p class='links'";
154
+ if( $should_show_bfcm_discount ) echo " style='margin-top: 0 !important;'";
155
+ echo ">
156
+ <a class='ctf_notice_dismiss' href='https://wordpress.org/support/plugin/custom-twitter-feeds/reviews/' target='_blank'>" . __( 'Sure, I\'d love to!', 'custom-twitter-feed' ) . "</a>
157
+ &middot;
158
+ <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'No thanks', 'custom-twitter-feed' ) . "</a>
159
+ &middot;
160
+ <a class='ctf_notice_dismiss' href='" .esc_url( $dismiss_url ). "'>" . __( 'I\'ve already given a review', 'custom-twitter-feed' ) . "</a>
161
+ &middot;
162
+ <a class='ctf_notice_dismiss' href='" .esc_url( $later_url ). "'>" . __( 'Ask Me Later', 'custom-twitter-feed' ) . "</a>
163
+ </p>"
164
+ . $other_notice_html .
165
+ "</div>
166
+ <a class='ctf_notice_close' href='" .esc_url( $dismiss_url ). "'><i class='fa fa-close'></i></a>
167
+ </div>";
168
+
169
+ } elseif ( $should_show_new_user_discount ) {
170
+ global $current_user;
171
+ $user_id = $current_user->ID;
172
+ $ignore_new_user_sale_notice_meta = get_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice' );
173
+ if ( $ignore_new_user_sale_notice_meta !== 'always' ) {
174
+
175
+ echo "
176
+ <div class='ctf_notice ctf_review_notice ctf_new_user_sale_notice'>
177
+ <img src='" . CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' . "' alt='Custom Twitter Feed'>
178
+ <div class='ctf-notice-text'>
179
+ <p>" . __( '<b style="font-weight: 700;">Exclusive offer!</b> We don\'t run promotions very often, but for a limited time we\'re offering <b style="font-weight: 700;">20% off</b> our Pro version to all users of our free Custom Twitter Feeds plugin.', 'custom-twitter-feed' ) . "</p>
180
+ <p class='ctf-links'>
181
+ <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount=twitterthankyou' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
182
+ <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
183
+
184
+ </p>
185
+ </div>
186
+ <a class='ctf_new_user_sale_notice_close' href='" . esc_url( add_query_arg( 'ctf_ignore_new_user_sale_notice', 'always' ) ) . "'><i class='fa fa-close'></i></a>
187
+ </div>
188
+ ";
189
+ }
190
+
191
+ } elseif ( $should_show_bfcm_discount ) {
192
+
193
+ echo "
194
+ <div class='ctf_notice ctf_review_notice ctf_bfcm_sale_notice'>
195
+ <img src='". CTF_PLUGIN_URL . 'img/ctf-icon-offer.jpg' ."' alt='Custom Twitter Feed'>
196
+ <div class='ctf-notice-text'>
197
+ <p>" . __( '<b style="font-weight: 700;">Black Friday/Cyber Monday Deal!</b> Thank you for using our free Custom Twitter Feeds plugin. For a limited time, we\'re offering <b style="font-weight: 700;">20% off</b> the Pro version for all of our users.', 'custom-twitter-feed' ) . "</p>
198
+ <p class='ctf-links'>
199
+ <a class='ctf_notice_dismiss ctf_offer_btn' href='https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf&discount=".$ctf_bfcm_discount_code."' target='_blank'><b>" . __( 'Get this offer', 'custom-twitter-feed' ) . "</b></a>
200
+ <a class='ctf_notice_dismiss' style='margin-left: 5px;' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'>" . __( 'I\'m not interested', 'custom-twitter-feed' ) . "</a>
201
+ </p>
202
+ </div>
203
+ <a class='ctf_bfcm_sale_notice_close' href='" .esc_url( add_query_arg( 'ctf_ignore_bfcm_sale_notice', date( 'Y', $current_time ) ) ). "'><i class='fa fa-close'></i></a>
204
+ </div>
205
+ ";
206
+
207
+ }
208
+
209
+ }
210
+ add_action( 'admin_notices', 'ctf_notices_html', 12 ); // priority 8 for Instagram, priority 10 for Facebook
211
+
212
+ function ctf_process_nags() {
213
+
214
+ global $current_user;
215
+ $user_id = $current_user->ID;
216
+ $ctf_statuses_option = get_option( 'ctf_statuses', array() );
217
+
218
+ if ( isset( $_GET['ctf_ignore_rating_notice_nag'] ) ) {
219
+ if ( (int)$_GET['ctf_ignore_rating_notice_nag'] === 1 ) {
220
+ update_option( 'ctf_rating_notice', 'dismissed', false );
221
+ $ctf_statuses_option['rating_notice_dismissed'] = ctf_get_current_time();
222
+ update_option( 'ctf_statuses', $ctf_statuses_option, false );
223
+
224
+ } elseif ( $_GET['ctf_ignore_rating_notice_nag'] === 'later' ) {
225
+ set_transient( 'custom_twitter_feeds_rating_notice_waiting', 'waiting', 2 * WEEK_IN_SECONDS );
226
+ update_option( 'ctf_rating_notice', 'pending', false );
227
+ }
228
+ }
229
+
230
+ if ( isset( $_GET['ctf_ignore_new_user_sale_notice'] ) ) {
231
+ $response = sanitize_text_field( $_GET['ctf_ignore_new_user_sale_notice'] );
232
+ if ( $response === 'always' ) {
233
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
234
+
235
+ $current_month_number = (int)date('n', ctf_get_current_time() );
236
+ $not_early_in_the_year = ($current_month_number > 5);
237
+
238
+ if ( $not_early_in_the_year ) {
239
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
240
+ }
241
+
242
+ }
243
+ }
244
+
245
+ if ( isset( $_GET['ctf_ignore_bfcm_sale_notice'] ) ) {
246
+ $response = sanitize_text_field( $_GET['ctf_ignore_bfcm_sale_notice'] );
247
+ if ( $response === 'always' ) {
248
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', 'always' );
249
+ } elseif ( $response === date( 'Y', ctf_get_current_time() ) ) {
250
+ update_user_meta( $user_id, 'ctf_ignore_bfcm_sale_notice', date( 'Y', ctf_get_current_time() ) );
251
+ }
252
+ update_user_meta( $user_id, 'ctf_ignore_new_user_sale_notice', 'always' );
253
+ }
254
+
255
+ }
256
+ add_action( 'admin_init', 'ctf_process_nags' );
257
+
258
+ function ctf_get_future_date( $month, $year, $week, $day, $direction ) {
259
+ if ( $direction > 0 ) {
260
+ $startday = 1;
261
+ } else {
262
+ $startday = date( 't', mktime(0, 0, 0, $month, 1, $year ) );
263
+ }
264
+
265
+ $start = mktime( 0, 0, 0, $month, $startday, $year );
266
+ $weekday = date( 'N', $start );
267
+
268
+ $offset = 0;
269
+ if ( $direction * $day >= $direction * $weekday ) {
270
+ $offset = -$direction * 7;
271
+ }
272
+
273
+ $offset += $direction * ($week * 7) + ($day - $weekday);
274
+ return mktime( 0, 0, 0, $month, $startday + $offset, $year );
275
  }
inc/widget.php CHANGED
@@ -1,63 +1,63 @@
1
- <?php
2
- /**
3
- * Class CtfWidget
4
- *
5
- * Creates a text widget with the custom-twitter-feeds shortcode inside
6
- */
7
-
8
- class CtfWidget extends WP_Widget
9
- {
10
- function __construct() {
11
- parent::__construct(
12
- 'custom-twitter-feeds-widget',
13
- __( 'Custom Twitter Feeds', 'custom-twitter-feeds' ),
14
- array( 'description' => __( 'Display your Twitter feed', 'custom-twitter-feeds' ), )
15
- );
16
- }
17
-
18
- public function widget( $args, $instance ) {
19
-
20
- $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : '';
21
- $content = isset( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
22
-
23
- echo $args['before_widget'];
24
-
25
- if ( ! empty( $title ) ) {
26
- echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
27
- }
28
-
29
- echo do_shortcode( $content );
30
-
31
- echo $args['after_widget'];
32
- }
33
-
34
- public function form( $instance ) {
35
-
36
- $title = isset( $instance['title'] ) ? $instance['title'] : '';
37
- $content = isset ( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
38
- ?>
39
- <p>
40
- <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title:' ); ?></label>
41
- <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
42
- </p>
43
- <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>" rows="16"><?php echo strip_tags( $content ); ?></textarea>
44
- <?php
45
- }
46
-
47
- public function update( $new_instance, $old_instance ) {
48
- $instance = array();
49
- $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
50
- $instance['content'] = ( ! empty( $new_instance['content'] ) ) ? strip_tags( $new_instance['content'] ) : '';
51
-
52
- return $instance;
53
- }
54
- }
55
-
56
- // register and load the widget
57
- function ctf_load_widget() {
58
- register_widget( 'CtfWidget' );
59
- }
60
- add_action( 'widgets_init', 'ctf_load_widget' );
61
-
62
- // allow shortcode in widgets
63
- add_filter( 'widget_text', 'do_shortcode' );
1
+ <?php
2
+ /**
3
+ * Class CtfWidget
4
+ *
5
+ * Creates a text widget with the custom-twitter-feeds shortcode inside
6
+ */
7
+
8
+ class CtfWidget extends WP_Widget
9
+ {
10
+ function __construct() {
11
+ parent::__construct(
12
+ 'custom-twitter-feeds-widget',
13
+ __( 'Custom Twitter Feeds', 'custom-twitter-feeds' ),
14
+ array( 'description' => __( 'Display your Twitter feed', 'custom-twitter-feeds' ), )
15
+ );
16
+ }
17
+
18
+ public function widget( $args, $instance ) {
19
+
20
+ $title = isset( $instance['title'] ) ? apply_filters( 'widget_title', $instance['title'], $instance, $this->id_base ) : '';
21
+ $content = isset( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
22
+
23
+ echo $args['before_widget'];
24
+
25
+ if ( ! empty( $title ) ) {
26
+ echo $args['before_title'] . esc_html( $title ) . $args['after_title'];
27
+ }
28
+
29
+ echo do_shortcode( $content );
30
+
31
+ echo $args['after_widget'];
32
+ }
33
+
34
+ public function form( $instance ) {
35
+
36
+ $title = isset( $instance['title'] ) ? $instance['title'] : '';
37
+ $content = isset ( $instance['content'] ) ? strip_tags( $instance['content'] ) : '[custom-twitter-feeds]';
38
+ ?>
39
+ <p>
40
+ <label for="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>"><?php _e( 'Title:' ); ?></label>
41
+ <input class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'title' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'title' ) ); ?>" type="text" value="<?php echo esc_attr( $title ); ?>" />
42
+ </p>
43
+ <textarea class="widefat" id="<?php echo esc_attr( $this->get_field_id( 'content' ) ); ?>" name="<?php echo esc_attr( $this->get_field_name( 'content' ) ); ?>" rows="16"><?php echo strip_tags( $content ); ?></textarea>
44
+ <?php
45
+ }
46
+
47
+ public function update( $new_instance, $old_instance ) {
48
+ $instance = array();
49
+ $instance['title'] = ( ! empty( $new_instance['title'] ) ) ? strip_tags( $new_instance['title'] ) : '';
50
+ $instance['content'] = ( ! empty( $new_instance['content'] ) ) ? strip_tags( $new_instance['content'] ) : '';
51
+
52
+ return $instance;
53
+ }
54
+ }
55
+
56
+ // register and load the widget
57
+ function ctf_load_widget() {
58
+ register_widget( 'CtfWidget' );
59
+ }
60
+ add_action( 'widgets_init', 'ctf_load_widget' );
61
+
62
+ // allow shortcode in widgets
63
+ add_filter( 'widget_text', 'do_shortcode' );
js/ctf-admin-scripts.js CHANGED
@@ -1,319 +1,319 @@
1
- jQuery(document).ready(function($){
2
-
3
- // access token retrieving
4
- var $ctfRetrievedAccessToken = $('#ctf-retrieved-access-token'),
5
- $ctfRetrievedAccessTokenSecret = $('#ctf-retrieved-access-token-secret'),
6
- $ctfRetrievedDefaultScreenName = $('#ctf-retrieved-default-screen-name'),
7
-
8
- // toggle token input fields
9
- $ctfConsumerFields = $('.ctf-toggle-consumer'),
10
- $ctfAccessFields = $('.ctf-toggle-access'),
11
- $ctfHaveOwnTokens = $('#ctf_have_own_tokens');
12
-
13
- if ( $ctfRetrievedAccessToken.length ) {
14
- $('#ctf_access_token').val($ctfRetrievedAccessToken.val());
15
- $('#ctf_access_token_secret').val($ctfRetrievedAccessTokenSecret.val());
16
- if($('#ctf_usertimeline_text').val() == '') {
17
- $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
- }
19
-
20
- if (!$ctfHaveOwnTokens.is(':checked')) {
21
- $.ajax({
22
- url: ctf.ajax_url,
23
- type: 'post',
24
- data: {
25
- action: 'ctf_auto_save_tokens',
26
- security: ctf.sb_nonce,
27
- access_token: $ctfRetrievedAccessToken.val(),
28
- access_token_secret: $ctfRetrievedAccessTokenSecret.val(),
29
- just_tokens: true
30
- },
31
- success: function (data) {
32
- $('#ctf_access_token').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
33
- $('#ctf_access_token_secret').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
34
- }
35
- });
36
- }
37
- }
38
-
39
- function toggleAccessInputs() {
40
- if($ctfHaveOwnTokens.is(':checked')) {
41
- $ctfAccessFields.show();
42
- $ctfConsumerFields.show();
43
- } else {
44
- $ctfConsumerFields.hide();
45
- if($ctfAccessFields.find('#ctf_access_token').val() == '' && $ctfAccessFields.find('#ctf_access_token_secret').val() == '') {
46
- $ctfAccessFields.hide();
47
- $ctfConsumerFields.hide();
48
- }
49
- }
50
- }
51
- toggleAccessInputs();
52
-
53
- $ctfHaveOwnTokens.on('change', function() {
54
- toggleAccessInputs();
55
- });
56
-
57
- // variables for time triggered validator
58
- var typingTimer,
59
- doneTypingInterval = 1000,
60
- $ctfSearchText = $('#ctf-admin #ctf_search_text'),
61
- $ctfUserText = $('#ctf-admin #ctf_usertimeline_text'),
62
- $ctfSearchError = $('#ctf-admin .ctf_search_error'),
63
- $ctfUserError= $('#ctf-admin .ctf_usertimeline_error');
64
-
65
- // hide elements when page loads
66
- $ctfSearchError.hide();
67
- $ctfUserError.hide();
68
-
69
- // on search text keyup, start timer to trigger validator
70
- $ctfSearchText.keyup(function(){
71
- clearTimeout(typingTimer);
72
- if($ctfSearchText.val){
73
- typingTimer = setTimeout(searchValidator, doneTypingInterval);
74
- }
75
- });
76
-
77
- // on usertimeline text keyup, start timer to trigger validator
78
- $ctfUserText.keyup(function(){
79
- clearTimeout(typingTimer);
80
- if($ctfUserText.val){
81
- typingTimer = setTimeout(userValidator, doneTypingInterval);
82
- }
83
- });
84
-
85
- // validate search input when user is done typing
86
- var internationalHashtagRegexString = "[A-z\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC",
87
- hashtagRegex = new RegExp("^"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$|^#+"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$");
88
-
89
- function searchValidator() {
90
- var ctfSearch = $ctfSearchText.val();
91
-
92
- if ( ctfSearch.indexOf(',') > -1 || ctfSearch.indexOf(' ') > -1 ){
93
- $ctfSearchError.slideDown();
94
- } else {
95
- $ctfSearchError.slideUp();
96
- }
97
- }
98
-
99
- // validate screen name input when user is done typing
100
- function userValidator(){
101
- var ctfUser = $ctfUserText.val();
102
-
103
- if(ctfUser.match(/^@[A-Za-z0-9_]{1,15}$/) || ctfUser.match(/^[A-Za-z0-9_]{1,15}$/)){
104
- $ctfUserError.slideUp();
105
- } else {
106
- $ctfUserError.slideDown();
107
- }
108
- }
109
-
110
- // search term guide toggle
111
- var $ctfToggleSearchGuide = $('#ctf-admin .ctf-toggle-search-guide');
112
-
113
- // hide initially
114
- $ctfToggleSearchGuide.closest('h4').next('div').hide();
115
-
116
- // show on click
117
- $ctfToggleSearchGuide.click(function(){
118
- $(this).closest('h4').next('div').slideToggle();
119
- });
120
-
121
- // tooltips
122
- $('#ctf-admin .ctf-tooltip-link').click(function(){
123
- $(this).closest('tr, h3, .ctf-tooltip-wrap').find('.ctf-tooltip').slideToggle();
124
- });
125
-
126
- // include replies
127
- $('.ctf_include_replies_toggle').hide();
128
- $('.ctf_include_replies_toggle input').prop('disabled', true);
129
-
130
- function toggleIncludeReplies() {
131
- $('.ctf_include_replies_toggle').each(function() {
132
- if($(this).closest('td').find('.ctf-feed-settings-radio').is(':checked')) {
133
- $(this).slideDown();
134
- } else {
135
- $(this).slideUp();
136
- }
137
- });
138
- }
139
- toggleIncludeReplies();
140
-
141
- $('.ctf-feed-settings-radio').on('change', function() {
142
- toggleIncludeReplies();
143
-
144
- if( $('#ctf-admin #ctf_usertimeline_radio').is(':checked') ) {
145
- userValidator();
146
- // $ctfSearchError.slideUp();
147
- } else if( $('#ctf-admin #ctf_search_radio').is(':checked') ) {
148
- searchValidator();
149
- // $ctfUserError.slideUp();
150
- }
151
- });
152
-
153
- // color picker
154
- var $ctfColorpicker = $('.ctf-colorpicker');
155
-
156
- if($ctfColorpicker.length > 0){
157
- $ctfColorpicker.wpColorPicker();
158
- }
159
-
160
- // shortcode tooltips
161
- var $ctfAdminLabel = $('#ctf-admin label');
162
-
163
- $ctfAdminLabel.click(function(){
164
- var $sbi_shortcode = $(this).siblings('.ctf_shortcode');
165
- if($sbi_shortcode.is(':visible')){
166
- $(this).siblings('.ctf_shortcode').css('display','none');
167
- } else {
168
- $(this).siblings('.ctf_shortcode').css('display','block');
169
- }
170
- });
171
- $ctfAdminLabel.hover(function(){
172
- if($(this).siblings('.ctf_shortcode').length > 0 ){
173
- $(this).attr('title', 'Click for shortcode option').append('<code class="ctf_shortcode_symbol">[]</code>');
174
- }
175
- }, function(){
176
- $(this).find('.ctf_shortcode_symbol').remove();
177
- });
178
-
179
- //Scroll to hash for quick links
180
- $('#ctf-admin a').click(function() {
181
- if(location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
182
- var target = $(this.hash);
183
- target = target.length ? target : this.hash.slice(1);
184
- if(target.length) {
185
- $('html,body').animate({
186
- scrollTop: target.offset().top
187
- }, 500);
188
- return false;
189
- }
190
- }
191
- });
192
-
193
- //Mobile width
194
- var ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
195
- ctfWidth = $('#ctf-admin #ctf_width').val(),
196
- $ctfWidthOptions = $('#ctf-admin #ctf_width_options');
197
-
198
- if (typeof ctfWidth !== 'undefined') {
199
- //Show initially if a width is set
200
- if(ctfWidth.length > 1 && !(ctfWidth == '100' && ctfWidthUnit == '%')) $ctfWidthOptions.show();
201
-
202
- $('#ctf_width, #ctf_width_unit').change(function(){
203
- ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
204
- ctfWidth = $('#ctf-admin #ctf_width').val();
205
-
206
- if(ctfWidth.length < 2 || (ctfWidth == '100' && ctfWidthUnit == '%')) {
207
- $ctfWidthOptions.slideUp();
208
- } else {
209
- $ctfWidthOptions.slideDown();
210
- }
211
- });
212
- }
213
-
214
- // clear cache
215
- var $ctfClearCacheButton = $('#ctf-admin #ctf-clear-cache');
216
-
217
- $ctfClearCacheButton.click(function(event) {
218
- event.preventDefault();
219
-
220
- $('#ctf-clear-cache-success').remove();
221
- $(this).prop("disabled",true);
222
-
223
- $.ajax({
224
- url : ctf.ajax_url,
225
- type : 'post',
226
- data : {
227
- action : 'ctf_clear_cache_admin'
228
- },
229
- success : function(data) {
230
- $ctfClearCacheButton.prop('disabled',false);
231
- if(!data===false) {
232
- $ctfClearCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
233
- } else {
234
- $ctfClearCacheButton.after('<span>error</span>');
235
- }
236
- }
237
- }); // ajax call
238
- }); // clear-cache click
239
-
240
- // clear persistent cache
241
- var $ctfClearPersistentCacheButton = $('#ctf-admin #ctf-clear-persistent-cache');
242
-
243
- $ctfClearPersistentCacheButton.click(function(event) {
244
- event.preventDefault();
245
-
246
- $('#ctf-clear-cache-success').remove();
247
- $(this).prop("disabled",true);
248
-
249
- $.ajax({
250
- url : ctf.ajax_url,
251
- type : 'post',
252
- data : {
253
- action : 'ctf_clear_persistent_cache'
254
- },
255
- success : function(data) {
256
- $ctfClearPersistentCacheButton.prop('disabled',false);
257
- if(!data===false) {
258
- $ctfClearPersistentCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
259
- } else {
260
- $ctfClearPersistentCacheButton.after('<span>error</span>');
261
- }
262
- }
263
- }); // ajax call
264
- }); // clear-persistent-cache click
265
-
266
- //Pro version notices
267
- var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Available in Pro version</a></span>';
268
- $('.ctf_pro').each(function(){
269
- var $pro = $(this);
270
- if (!$pro.find('.ctf_layout_options_wrap').length) {
271
- $pro.find('td').last().append(ctfUpgradeNote);
272
- $pro.find('input, select, textarea').attr('disabled', 'true');
273
- }
274
- });
275
- $('#ctf_include_twittercards, #ctf_include_media, #ctf_include_replied_to').attr('disabled', 'true').removeAttr('checked').next('label').css('color', '#999').after(ctfUpgradeNote);
276
-
277
- $('#ctf-admin .ctf-show-pro').closest('span').next('.ctf-pro-options').hide();
278
- $('#ctf-admin .ctf-show-pro').click(function() {
279
- if ($(this).closest('span').next('.ctf-pro-options').is(':visible')) {
280
- $(this).closest('span').next('.ctf-pro-options').hide();
281
- } else {
282
- $(this).closest('span').next('.ctf-pro-options').show();
283
- }
284
- });
285
-
286
- function ctfUpdateLayoutTypeOptionsDisplay() {
287
- setTimeout(function(){
288
- jQuery('.ctf_layout_settings').hide();
289
- jQuery('.ctf_layout_settings.ctf_layout_type_'+jQuery('.ctf_layout_type:checked').val()).show();
290
- }, 1);
291
- }
292
- ctfUpdateLayoutTypeOptionsDisplay();
293
- jQuery('.ctf_layout_type').change(ctfUpdateLayoutTypeOptionsDisplay);
294
-
295
- // notices
296
-
297
- if (jQuery('#ctf-notice-bar').length) {
298
- jQuery('#wpadminbar').after(jQuery('#ctf-notice-bar'));
299
- jQuery('#wpcontent').css('padding-left', 0);
300
- jQuery('#wpbody').css('padding-left', '20px');
301
- jQuery('#ctf-notice-bar').show();
302
- }
303
-
304
- jQuery('#ctf-notice-bar .dismiss').click(function(e) {
305
- e.preventDefault();
306
- jQuery('#ctf-notice-bar').remove();
307
- jQuery.ajax({
308
- url: ctf.ajax_url,
309
- type: 'post',
310
- data: {
311
- action : 'ctf_lite_dismiss',
312
- ctf_nonce: ctf.sb_nonce
313
- },
314
- success: function (data) {
315
- }
316
- });
317
- });
318
-
319
  });
1
+ jQuery(document).ready(function($){
2
+
3
+ // access token retrieving
4
+ var $ctfRetrievedAccessToken = $('#ctf-retrieved-access-token'),
5
+ $ctfRetrievedAccessTokenSecret = $('#ctf-retrieved-access-token-secret'),
6
+ $ctfRetrievedDefaultScreenName = $('#ctf-retrieved-default-screen-name'),
7
+
8
+ // toggle token input fields
9
+ $ctfConsumerFields = $('.ctf-toggle-consumer'),
10
+ $ctfAccessFields = $('.ctf-toggle-access'),
11
+ $ctfHaveOwnTokens = $('#ctf_have_own_tokens');
12
+
13
+ if ( $ctfRetrievedAccessToken.length ) {
14
+ $('#ctf_access_token').val($ctfRetrievedAccessToken.val());
15
+ $('#ctf_access_token_secret').val($ctfRetrievedAccessTokenSecret.val());
16
+ if($('#ctf_usertimeline_text').val() == '') {
17
+ $('#ctf_usertimeline_text').val($ctfRetrievedDefaultScreenName.val());
18
+ }
19
+
20
+ if (!$ctfHaveOwnTokens.is(':checked')) {
21
+ $.ajax({
22
+ url: ctf.ajax_url,
23
+ type: 'post',
24
+ data: {
25
+ action: 'ctf_auto_save_tokens',
26
+ security: ctf.sb_nonce,
27
+ access_token: $ctfRetrievedAccessToken.val(),
28
+ access_token_secret: $ctfRetrievedAccessTokenSecret.val(),
29
+ just_tokens: true
30
+ },
31
+ success: function (data) {
32
+ $('#ctf_access_token').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
33
+ $('#ctf_access_token_secret').after('<span class="ctf-success"><span class="fa fa-check-circle"></span> saved</span>');
34
+ }
35
+ });
36
+ }
37
+ }
38
+
39
+ function toggleAccessInputs() {
40
+ if($ctfHaveOwnTokens.is(':checked')) {
41
+ $ctfAccessFields.show();
42
+ $ctfConsumerFields.show();
43
+ } else {
44
+ $ctfConsumerFields.hide();
45
+ if($ctfAccessFields.find('#ctf_access_token').val() == '' && $ctfAccessFields.find('#ctf_access_token_secret').val() == '') {
46
+ $ctfAccessFields.hide();
47
+ $ctfConsumerFields.hide();
48
+ }
49
+ }
50
+ }
51
+ toggleAccessInputs();
52
+
53
+ $ctfHaveOwnTokens.on('change', function() {
54
+ toggleAccessInputs();
55
+ });
56
+
57
+ // variables for time triggered validator
58
+ var typingTimer,
59
+ doneTypingInterval = 1000,
60
+ $ctfSearchText = $('#ctf-admin #ctf_search_text'),
61
+ $ctfUserText = $('#ctf-admin #ctf_usertimeline_text'),
62
+ $ctfSearchError = $('#ctf-admin .ctf_search_error'),
63
+ $ctfUserError= $('#ctf-admin .ctf_usertimeline_error');
64
+
65
+ // hide elements when page loads
66
+ $ctfSearchError.hide();
67
+ $ctfUserError.hide();
68
+
69
+ // on search text keyup, start timer to trigger validator
70
+ $ctfSearchText.keyup(function(){
71
+ clearTimeout(typingTimer);
72
+ if($ctfSearchText.val){
73
+ typingTimer = setTimeout(searchValidator, doneTypingInterval);
74
+ }
75
+ });
76
+
77
+ // on usertimeline text keyup, start timer to trigger validator
78
+ $ctfUserText.keyup(function(){
79
+ clearTimeout(typingTimer);
80
+ if($ctfUserText.val){
81
+ typingTimer = setTimeout(userValidator, doneTypingInterval);
82
+ }
83
+ });
84
+
85
+ // validate search input when user is done typing
86
+ var internationalHashtagRegexString = "[A-z\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC",
87
+ hashtagRegex = new RegExp("^"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$|^#+"+internationalHashtagRegexString+"]"+internationalHashtagRegexString+"0-9_]+$");
88
+
89
+ function searchValidator() {
90
+ var ctfSearch = $ctfSearchText.val();
91
+
92
+ if ( ctfSearch.indexOf(',') > -1 || ctfSearch.indexOf(' ') > -1 ){
93
+ $ctfSearchError.slideDown();
94
+ } else {
95
+ $ctfSearchError.slideUp();
96
+ }
97
+ }
98
+
99
+ // validate screen name input when user is done typing
100
+ function userValidator(){
101
+ var ctfUser = $ctfUserText.val();
102
+
103
+ if(ctfUser.match(/^@[A-Za-z0-9_]{1,15}$/) || ctfUser.match(/^[A-Za-z0-9_]{1,15}$/)){
104
+ $ctfUserError.slideUp();
105
+ } else {
106
+ $ctfUserError.slideDown();
107
+ }
108
+ }
109
+
110
+ // search term guide toggle
111
+ var $ctfToggleSearchGuide = $('#ctf-admin .ctf-toggle-search-guide');
112
+
113
+ // hide initially
114
+ $ctfToggleSearchGuide.closest('h4').next('div').hide();
115
+
116
+ // show on click
117
+ $ctfToggleSearchGuide.click(function(){
118
+ $(this).closest('h4').next('div').slideToggle();
119
+ });
120
+
121
+ // tooltips
122
+ $('#ctf-admin .ctf-tooltip-link').click(function(){
123
+ $(this).closest('tr, h3, .ctf-tooltip-wrap').find('.ctf-tooltip').slideToggle();
124
+ });
125
+
126
+ // include replies
127
+ $('.ctf_include_replies_toggle').hide();
128
+ $('.ctf_include_replies_toggle input').prop('disabled', true);
129
+
130
+ function toggleIncludeReplies() {
131
+ $('.ctf_include_replies_toggle').each(function() {
132
+ if($(this).closest('td').find('.ctf-feed-settings-radio').is(':checked')) {
133
+ $(this).slideDown();
134
+ } else {
135
+ $(this).slideUp();
136
+ }
137
+ });
138
+ }
139
+ toggleIncludeReplies();
140
+
141
+ $('.ctf-feed-settings-radio').on('change', function() {
142
+ toggleIncludeReplies();
143
+
144
+ if( $('#ctf-admin #ctf_usertimeline_radio').is(':checked') ) {
145
+ userValidator();
146
+ // $ctfSearchError.slideUp();
147
+ } else if( $('#ctf-admin #ctf_search_radio').is(':checked') ) {
148
+ searchValidator();
149
+ // $ctfUserError.slideUp();
150
+ }
151
+ });
152
+
153
+ // color picker
154
+ var $ctfColorpicker = $('.ctf-colorpicker');
155
+
156
+ if($ctfColorpicker.length > 0){
157
+ $ctfColorpicker.wpColorPicker();
158
+ }
159
+
160
+ // shortcode tooltips
161
+ var $ctfAdminLabel = $('#ctf-admin label');
162
+
163
+ $ctfAdminLabel.click(function(){
164
+ var $sbi_shortcode = $(this).siblings('.ctf_shortcode');
165
+ if($sbi_shortcode.is(':visible')){
166
+ $(this).siblings('.ctf_shortcode').css('display','none');
167
+ } else {
168
+ $(this).siblings('.ctf_shortcode').css('display','block');
169
+ }
170
+ });
171
+ $ctfAdminLabel.hover(function(){
172
+ if($(this).siblings('.ctf_shortcode').length > 0 ){
173
+ $(this).attr('title', 'Click for shortcode option').append('<code class="ctf_shortcode_symbol">[]</code>');
174
+ }
175
+ }, function(){
176
+ $(this).find('.ctf_shortcode_symbol').remove();
177
+ });
178
+
179
+ //Scroll to hash for quick links
180
+ $('#ctf-admin a').click(function() {
181
+ if(location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
182
+ var target = $(this.hash);
183
+ target = target.length ? target : this.hash.slice(1);
184
+ if(target.length) {
185
+ $('html,body').animate({
186
+ scrollTop: target.offset().top
187
+ }, 500);
188
+ return false;
189
+ }
190
+ }
191
+ });
192
+
193
+ //Mobile width
194
+ var ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
195
+ ctfWidth = $('#ctf-admin #ctf_width').val(),
196
+ $ctfWidthOptions = $('#ctf-admin #ctf_width_options');
197
+
198
+ if (typeof ctfWidth !== 'undefined') {
199
+ //Show initially if a width is set
200
+ if(ctfWidth.length > 1 && !(ctfWidth == '100' && ctfWidthUnit == '%')) $ctfWidthOptions.show();
201
+
202
+ $('#ctf_width, #ctf_width_unit').change(function(){
203
+ ctfWidthUnit = $('#ctf-admin #ctf_width_unit').val(),
204
+ ctfWidth = $('#ctf-admin #ctf_width').val();
205
+
206
+ if(ctfWidth.length < 2 || (ctfWidth == '100' && ctfWidthUnit == '%')) {
207
+ $ctfWidthOptions.slideUp();
208
+ } else {
209
+ $ctfWidthOptions.slideDown();
210
+ }
211
+ });
212
+ }
213
+
214
+ // clear cache
215
+ var $ctfClearCacheButton = $('#ctf-admin #ctf-clear-cache');
216
+
217
+ $ctfClearCacheButton.click(function(event) {
218
+ event.preventDefault();
219
+
220
+ $('#ctf-clear-cache-success').remove();
221
+ $(this).prop("disabled",true);
222
+
223
+ $.ajax({
224
+ url : ctf.ajax_url,
225
+ type : 'post',
226
+ data : {
227
+ action : 'ctf_clear_cache_admin'
228
+ },
229
+ success : function(data) {
230
+ $ctfClearCacheButton.prop('disabled',false);
231
+ if(!data===false) {
232
+ $ctfClearCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
233
+ } else {
234
+ $ctfClearCacheButton.after('<span>error</span>');
235
+ }
236
+ }
237
+ }); // ajax call
238
+ }); // clear-cache click
239
+
240
+ // clear persistent cache
241
+ var $ctfClearPersistentCacheButton = $('#ctf-admin #ctf-clear-persistent-cache');
242
+
243
+ $ctfClearPersistentCacheButton.click(function(event) {
244
+ event.preventDefault();
245
+
246
+ $('#ctf-clear-cache-success').remove();
247
+ $(this).prop("disabled",true);
248
+
249
+ $.ajax({
250
+ url : ctf.ajax_url,
251
+ type : 'post',
252
+ data : {
253
+ action : 'ctf_clear_persistent_cache'
254
+ },
255
+ success : function(data) {
256
+ $ctfClearPersistentCacheButton.prop('disabled',false);
257
+ if(!data===false) {
258
+ $ctfClearPersistentCacheButton.after('<span id="ctf-clear-cache-success" class="fa fa-check-circle ctf-success"></span>');
259
+ } else {
260
+ $ctfClearPersistentCacheButton.after('<span>error</span>');
261
+ }
262
+ }
263
+ }); // ajax call
264
+ }); // clear-persistent-cache click
265
+
266
+ //Pro version notices
267
+ var ctfUpgradeNote = '<span class="ctf_note"> - <a href="https://smashballoon.com/custom-twitter-feeds/?utm_source=plugin-free&utm_campaign=ctf" target="_blank">Available in Pro version</a></span>';
268
+ $('.ctf_pro').each(function(){
269
+ var $pro = $(this);
270
+ if (!$pro.find('.ctf_layout_options_wrap').length) {
271
+ $pro.find('td').last().append(ctfUpgradeNote);
272
+ $pro.find('input, select, textarea').attr('disabled', 'true');
273
+ }
274
+ });
275
+ $('#ctf_include_twittercards, #ctf_include_media, #ctf_include_replied_to').attr('disabled', 'true').removeAttr('checked').next('label').css('color', '#999').after(ctfUpgradeNote);
276
+
277
+ $('#ctf-admin .ctf-show-pro').closest('span').next('.ctf-pro-options').hide();
278
+ $('#ctf-admin .ctf-show-pro').click(function() {
279
+ if ($(this).closest('span').next('.ctf-pro-options').is(':visible')) {
280
+ $(this).closest('span').next('.ctf-pro-options').hide();
281
+ } else {
282
+ $(this).closest('span').next('.ctf-pro-options').show();
283
+ }
284
+ });
285
+
286
+ function ctfUpdateLayoutTypeOptionsDisplay() {
287
+ setTimeout(function(){
288
+ jQuery('.ctf_layout_settings').hide();
289
+ jQuery('.ctf_layout_settings.ctf_layout_type_'+jQuery('.ctf_layout_type:checked').val()).show();
290
+ }, 1);
291
+ }
292
+ ctfUpdateLayoutTypeOptionsDisplay();
293
+ jQuery('.ctf_layout_type').change(ctfUpdateLayoutTypeOptionsDisplay);
294
+
295
+ // notices
296
+
297
+ if (jQuery('#ctf-notice-bar').length) {
298
+ jQuery('#wpadminbar').after(jQuery('#ctf-notice-bar'));
299
+ jQuery('#wpcontent').css('padding-left', 0);
300
+ jQuery('#wpbody').css('padding-left', '20px');
301
+ jQuery('#ctf-notice-bar').show();
302
+ }
303
+
304
+ jQuery('#ctf-notice-bar .dismiss').click(function(e) {
305
+ e.preventDefault();
306
+ jQuery('#ctf-notice-bar').remove();
307
+ jQuery.ajax({
308
+ url: ctf.ajax_url,
309
+ type: 'post',
310
+ data: {
311
+ action : 'ctf_lite_dismiss',
312
+ ctf_nonce: ctf.sb_nonce
313
+ },
314
+ success: function (data) {
315
+ }
316
+ });
317
+ });
318
+
319
  });
js/ctf-blocks.js ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ "use strict";
2
+
3
+ (function () {
4
+ var _wp = wp,
5
+ _wp$serverSideRender = _wp.serverSideRender,
6
+ createElement = wp.element.createElement,
7
+ ServerSideRender = _wp$serverSideRender === void 0 ? wp.components.ServerSideRender : _wp$serverSideRender,
8
+ _ref = wp.blockEditor || wp.editor,
9
+ InspectorControls = _ref.InspectorControls,
10
+ _wp$components = wp.components,
11
+ TextareaControl = _wp$components.TextareaControl,
12
+ Button = _wp$components.Button,
13
+ PanelBody = _wp$components.PanelBody,
14
+ Placeholder = _wp$components.Placeholder,
15
+ registerBlockType = wp.blocks.registerBlockType;
16
+
17
+ var ctfIcon = createElement('svg', {
18
+ width: 20,
19
+ height: 20,
20
+ viewBox: '0 0 448 512',
21
+ className: 'dashicon'
22
+ }, createElement('path', {
23
+ fill: 'currentColor',
24
+ d: 'M459.37 151.716c.325 4.548.325 9.097.325 13.645 0 138.72-105.583 298.558-298.558 298.558-59.452 0-114.68-17.219-161.137-47.106 8.447.974 16.568 1.299 25.34 1.299 49.055 0 94.213-16.568 130.274-44.832-46.132-.975-84.792-31.188-98.112-72.772 6.498.974 12.995 1.624 19.818 1.624 9.421 0 18.843-1.3 27.614-3.573-48.081-9.747-84.143-51.98-84.143-102.985v-1.299c13.969 7.797 30.214 12.67 47.431 13.319-28.264-18.843-46.781-51.005-46.781-87.391 0-19.492 5.197-37.36 14.294-52.954 51.655 63.675 129.3 105.258 216.365 109.807-1.624-7.797-2.599-15.918-2.599-24.04 0-57.828 46.782-104.934 104.934-104.934 30.213 0 57.502 12.67 76.67 33.137 23.715-4.548 46.456-13.32 66.599-25.34-7.798 24.366-24.366 44.833-46.132 57.827 21.117-2.273 41.584-8.122 60.426-16.243-14.292 20.791-32.161 39.308-52.628 54.253z'
25
+ }));
26
+
27
+ registerBlockType('ctf/ctf-feed-block', {
28
+ title: 'Twitter Feed',
29
+ icon: ctfIcon,
30
+ category: 'widgets',
31
+ attributes: {
32
+ noNewChanges: {
33
+ type: 'boolean',
34
+ },
35
+ shortcodeSettings: {
36
+ type: 'string',
37
+ },
38
+ executed: {
39
+ type: 'boolean'
40
+ }
41
+ },
42
+ edit: function edit(props) {
43
+ var _props = props,
44
+ setAttributes = _props.setAttributes,
45
+ _props$attributes = _props.attributes,
46
+ _props$attributes$sho = _props$attributes.shortcodeSettings,
47
+ shortcodeSettings = _props$attributes$sho === void 0 ? ctf_block_editor.shortcodeSettings : _props$attributes$sho,
48
+ _props$attributes$cli = _props$attributes.noNewChanges,
49
+ noNewChanges = _props$attributes$cli === void 0 ? true : _props$attributes$cli,
50
+ _props$attributes$exe = _props$attributes.executed,
51
+ executed = _props$attributes$exe === void 0 ? false : _props$attributes$exe;
52
+
53
+ function setState(shortcodeSettingsContent) {
54
+ setAttributes({
55
+ noNewChanges: false,
56
+ shortcodeSettings: shortcodeSettingsContent
57
+ });
58
+ }
59
+
60
+ function previewClick(content) {
61
+ setAttributes({
62
+ noNewChanges: true,
63
+ executed: false,
64
+ });
65
+ }
66
+ function afterRender() {
67
+ // no way to run a script after AJAX call to get feed so we just try to execute it on a few intervals
68
+ if (! executed
69
+ || typeof window.ctfGB === 'undefined') {
70
+ window.ctfGB = true;
71
+ setTimeout(function() { if (typeof ctf_init !== 'undefined') {ctf_init();}},1000);
72
+ setTimeout(function() { if (typeof ctf_init !== 'undefined') {ctf_init();}},2000);
73
+ setTimeout(function() { if (typeof ctf_init !== 'undefined') {ctf_init();}},3000);
74
+ setTimeout(function() { if (typeof ctf_init !== 'undefined') {ctf_init();}},5000);
75
+ setTimeout(function() { if (typeof ctf_init !== 'undefined') {ctf_init();}},10000);
76
+ }
77
+ setAttributes({
78
+ executed: true,
79
+ });
80
+ }
81
+
82
+ var jsx = [React.createElement(InspectorControls, {
83
+ key: "ctf-gutenberg-setting-selector-inspector-controls"
84
+ }, React.createElement(PanelBody, {
85
+ title: ctf_block_editor.i18n.addSettings
86
+ }, React.createElement(TextareaControl, {
87
+ key: "ctf-gutenberg-settings",
88
+ className: "ctf-gutenberg-settings",
89
+ label: ctf_block_editor.i18n.shortcodeSettings,
90
+ help: ctf_block_editor.i18n.example + ": 'screenname=\"smashballoon\" showbutton=\"true\"'",
91
+ value: shortcodeSettings,
92
+ onChange: setState
93
+ }), React.createElement(Button, {
94
+ key: "ctf-gutenberg-preview",
95
+ className: "ctf-gutenberg-preview",
96
+ onClick: previewClick,
97
+ isDefault: true
98
+ }, ctf_block_editor.i18n.preview)))];
99
+
100
+ if (noNewChanges) {
101
+ afterRender();
102
+ jsx.push(React.createElement(ServerSideRender, {
103
+ key: "custom-twitter-feeds/custom-twitter-feeds",
104
+ block: "ctf/ctf-feed-block",
105
+ attributes: props.attributes,
106
+ }));
107
+ } else {
108
+ props.attributes.noNewChanges = false;
109
+ jsx.push(React.createElement(Placeholder, {
110
+ key: "ctf-gutenberg-setting-selector-select-wrap",
111
+ className: "ctf-gutenberg-setting-selector-select-wrap"
112
+ }, React.createElement(Button, {
113
+ key: "ctf-gutenberg-preview",
114
+ className: "ctf-gutenberg-preview",
115
+ onClick: previewClick,
116
+ isDefault: true
117
+ }, ctf_block_editor.i18n.preview)));
118
+ }
119
+
120
+ return jsx;
121
+ },
122
+ save: function save() {
123
+ return null;
124
+ }
125
+ });
126
+ })();
js/ctf-scripts.js CHANGED
@@ -1,212 +1,3888 @@
1
- var ctf_js_exists = (typeof ctf_js_exists !== 'undefined') ? true : false;
2
- if(!ctf_js_exists){
3
-
4
- (function ($) {
5
-
6
- var ctfIntentsIncluded = false;
7
- $('.ctf').each(function() {
8
- if (!ctfIntentsIncluded && typeof $(this).attr('data-ctfintents') !== 'undefined') {
9
- ctfIntentsIncluded = true;
10
- Function&&Function.prototype&&Function.prototype.bind&&(/MSIE ([6789]|10)/.test(navigator.userAgent)||(window.__twttr&&window.__twttr.widgets&&window.__twttr.widgets.loaded&&window.twttr.widgets.load&&window.twttr.widgets.load(),window.__twttr&&window.__twttr.widgets&&window.__twttr.widgets.init||function(t){function e(e){for(var n,i,o=e[0],s=e[1],a=0,c=[];a<o.length;a++)i=o[a],r[i]&&c.push(r[i][0]),r[i]=0;for(n in s)Object.prototype.hasOwnProperty.call(s,n)&&(t[n]=s[n]);for(u&&u(e);c.length;)c.shift()()}var n={},r={1:0};function i(e){if(n[e])return n[e].exports;var r=n[e]={i:e,l:!1,exports:{}};return t[e].call(r.exports,r,r.exports,i),r.l=!0,r.exports}i.e=function(t){var e=[],n=r[t];if(0!==n)if(n)e.push(n[2]);else{var o=new Promise(function(e,i){n=r[t]=[e,i]});e.push(n[2]=o);var s,a=document.getElementsByTagName("head")[0],u=document.createElement("script");u.charset="utf-8",u.timeout=120,i.nc&&u.setAttribute("nonce",i.nc),u.src=function(t){return i.p+"js/"+({0:"moment~timeline~tweet",2:"dm_button",3:"button",4:"moment",5:"periscope_on_air",6:"timeline",7:"tweet"}[t]||t)+"."+{0:"ec04a6cb5ba879d0e0db41f211639fdf",2:"6542a7407a2eccac51f5c5e0fac5bb80",3:"d941c9a422e2e3faf474b82a1f39e936",4:"0a3cc02317b85399478995c763a1296c",5:"d26526abe761c4d8d8d71cf0ec565649",6:"0a7b4db67eacd23e35c5ce02e6ea3470",7:"b2d749028be81f16d9cb4994d9692feb"}[t]+".js"}(t),s=function(e){u.onerror=u.onload=null,clearTimeout(c);var n=r[t];if(0!==n){if(n){var i=e&&("load"===e.type?"missing":e.type),o=e&&e.target&&e.target.src,s=new Error("Loading chunk "+t+" failed.\n("+i+": "+o+")");s.type=i,s.request=o,n[1](s)}r[t]=void 0}};var c=setTimeout(function(){s({type:"timeout",target:u})},12e4);u.onerror=u.onload=s,a.appendChild(u)}return Promise.all(e)},i.m=t,i.c=n,i.d=function(t,e,n){i.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:n})},i.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},i.t=function(t,e){if(1&e&&(t=i(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var n=Object.create(null);if(i.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var r in t)i.d(n,r,function(e){return t[e]}.bind(null,r));return n},i.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return i.d(e,"a",e),e},i.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},i.p="https://platform.twitter.com/",i.oe=function(t){throw console.error(t),t};var o=window.__twttrll=window.__twttrll||[],s=o.push.bind(o);o.push=e,o=o.slice();for(var a=0;a<o.length;a++)e(o[a]);var u=s;i(i.s=94)}([function(t,e,n){var r=n(1);function i(t,e){var n;for(n in t)t.hasOwnProperty&&!t.hasOwnProperty(n)||e(n,t[n]);return t}function o(t){return{}.toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase()}function s(t){return t===Object(t)}function a(t){var e;if(!s(t))return!1;if(Object.keys)return!Object.keys(t).length;for(e in t)if(t.hasOwnProperty(e))return!1;return!0}function u(t){return t?Array.prototype.slice.call(t):[]}t.exports={aug:function(t){return u(arguments).slice(1).forEach(function(e){i(e,function(e,n){t[e]=n})}),t},async:function(t,e){r.setTimeout(function(){t.call(e||null)},0)},compact:function t(e){return i(e,function(n,r){s(r)&&(t(r),a(r)&&delete e[n]),void 0!==r&&null!==r&&""!==r||delete e[n]}),e},contains:function(t,e){return!(!t||!t.indexOf)&&t.indexOf(e)>-1},forIn:i,isObject:s,isEmptyObject:a,toType:o,isType:function(t,e){return t==o(e)},toRealArray:u}},function(t,e){t.exports=window},function(t,e,n){var r=n(6);t.exports=function(){var t=this;this.promise=new r(function(e,n){t.resolve=e,t.reject=n})}},function(t,e,n){var r=n(11),i=/(?:^|(?:https?:)?\/\/(?:www\.)?twitter\.com(?::\d+)?(?:\/intent\/(?:follow|user)\/?\?screen_name=|(?:\/#!)?\/))@?([\w]+)(?:\?|&|$)/i,o=/(?:^|(?:https?:)?\/\/(?:www\.)?twitter\.com(?::\d+)?\/(?:#!\/)?[\w_]+\/status(?:es)?\/)(\d+)/i,s=/^http(s?):\/\/(\w+\.)*twitter\.com([:/]|$)/i,a=/^http(s?):\/\/pbs\.twimg\.com\//,u=/^#?([^.,<>!\s/#\-()'"]+)$/,c=/twitter\.com(?::\d{2,4})?\/intent\/(\w+)/,d=/^https?:\/\/(?:www\.)?twitter\.com\/\w+\/timelines\/(\d+)/i,l=/^https?:\/\/(?:www\.)?twitter\.com\/i\/moments\/(\d+)/i,f=/^https?:\/\/(?:www\.)?twitter\.com\/(\w+)\/(?:likes|favorites)/i,h=/^https?:\/\/(?:www\.)?twitter\.com\/(\w+)\/lists\/([\w-%]+)/i,p=/^https?:\/\/(?:www\.)?twitter\.com\/i\/live\/(\d+)/i,m=/^https?:\/\/syndication\.twitter\.com\/settings/i,v=/^https?:\/\/(localhost|platform)\.twitter\.com(?::\d+)?\/widgets\/widget_iframe\.(.+)/i,g=/^https?:\/\/(?:www\.)?twitter\.com\/search\?q=(\w+)/i;function w(t){return"string"==typeof t&&i.test(t)&&RegExp.$1.length<=20}function y(t){if(w(t))return RegExp.$1}function b(t,e){var n=r.decodeURL(t);if(e=e||!1,n.screen_name=y(t),n.screen_name)return r.url("https://twitter.com/intent/"+(e?"follow":"user"),n)}function _(t){return"string"==typeof t&&u.test(t)}function E(t){return"string"==typeof t&&o.test(t)}t.exports={isHashTag:_,hashTag:function(t,e){if(e=void 0===e||e,_(t))return(e?"#":"")+RegExp.$1},isScreenName:w,screenName:y,isStatus:E,status:function(t){return E(t)&&RegExp.$1},intentForProfileURL:b,intentForFollowURL:function(t){return b(t,!0)},isTwitterURL:function(t){return s.test(t)},isTwimgURL:function(t){return a.test(t)},isIntentURL:function(t){return c.test(t)},isSettingsURL:function(t){return m.test(t)},isWidgetIframeURL:function(t){return v.test(t)},isSearchUrl:function(t){return g.test(t)},regexen:{profile:i},momentId:function(t){return l.test(t)&&RegExp.$1},collectionId:function(t){return d.test(t)&&RegExp.$1},intentType:function(t){return c.test(t)&&RegExp.$1},likesScreenName:function(t){return f.test(t)&&RegExp.$1},listScreenNameAndSlug:function(t){var e,n,r;if(h.test(t)){e=RegExp.$1,n=RegExp.$2;try{r=decodeURIComponent(n)}catch(t){}return{ownerScreenName:e,slug:r||n}}return!1},eventId:function(t){return p.test(t)&&RegExp.$1}}},function(t,e){t.exports=document},function(t,e,n){var r=n(0),i=[!0,1,"1","on","ON","true","TRUE","yes","YES"],o=[!1,0,"0","off","OFF","false","FALSE","no","NO"];function s(t){return void 0!==t&&null!==t&&""!==t}function a(t){return c(t)&&t%1==0}function u(t){return c(t)&&!a(t)}function c(t){return s(t)&&!isNaN(t)}function d(t){return r.contains(o,t)}function l(t){return r.contains(i,t)}t.exports={hasValue:s,isInt:a,isFloat:u,isNumber:c,isString:function(t){return"string"===r.toType(t)},isArray:function(t){return s(t)&&"array"==r.toType(t)},isTruthValue:l,isFalseValue:d,asInt:function(t){if(a(t))return parseInt(t,10)},asFloat:function(t){if(u(t))return t},asNumber:function(t){if(c(t))return t},asBoolean:function(t){return!(!s(t)||!l(t)&&(d(t)||!t))}}},function(t,e,n){var r=n(1),i=n(21),o=n(48);i.hasPromiseSupport()||(r.Promise=o),t.exports=r.Promise},function(t,e,n){var r=n(0);t.exports=function(t,e){var n=Array.prototype.slice.call(arguments,2);return function(){var i=r.toRealArray(arguments);return t.apply(e,n.concat(i))}}},function(t,e){t.exports=location},function(t,e,n){var r=n(50);t.exports=new r("__twttr")},function(t,e,n){var r=n(0),i=/\b([\w-_]+)\b/g;function o(t){return new RegExp("\\b"+t+"\\b","g")}function s(t,e){t.classList?t.classList.add(e):o(e).test(t.className)||(t.className+=" "+e)}function a(t,e){t.classList?t.classList.remove(e):t.className=t.className.replace(o(e)," ")}function u(t,e){return t.classList?t.classList.contains(e):r.contains(c(t),e)}function c(t){return r.toRealArray(t.classList?t.classList:t.className.match(i))}t.exports={add:s,remove:a,replace:function(t,e,n){if(t.classList&&u(t,e))return a(t,e),void s(t,n);t.className=t.className.replace(o(e),n)},toggle:function(t,e,n){return void 0===n&&t.classList&&t.classList.toggle?t.classList.toggle(e,n):(n?s(t,e):a(t,e),n)},present:u,list:c}},function(t,e,n){var r=n(5),i=n(0);function o(t){return encodeURIComponent(t).replace(/\+/g,"%2B").replace(/'/g,"%27")}function s(t){return decodeURIComponent(t)}function a(t){var e=[];return i.forIn(t,function(t,n){var s=o(t);i.isType("array",n)||(n=[n]),n.forEach(function(t){r.hasValue(t)&&e.push(s+"="+o(t))})}),e.sort().join("&")}function u(t){var e={};return t?(t.split("&").forEach(function(t){var n=t.split("="),r=s(n[0]),o=s(n[1]);if(2==n.length){if(!i.isType("array",e[r]))return r in e?(e[r]=[e[r]],void e[r].push(o)):void(e[r]=o);e[r].push(o)}}),e):{}}t.exports={url:function(t,e){return a(e).length>0?i.contains(t,"?")?t+"&"+a(e):t+"?"+a(e):t},decodeURL:function(t){var e=t&&t.split("?");return 2==e.length?u(e[1]):{}},decode:u,encode:a,encodePart:o,decodePart:s}},function(t,e,n){var r=n(8),i=n(1),o=n(0),s={},a=o.contains(r.href,"tw_debug=true");function u(){}function c(){}function d(){return i.performance&&+i.performance.now()||+new Date}function l(t,e){if(i.console&&i.console[t])switch(e.length){case 1:i.console[t](e[0]);break;case 2:i.console[t](e[0],e[1]);break;case 3:i.console[t](e[0],e[1],e[2]);break;case 4:i.console[t](e[0],e[1],e[2],e[3]);break;case 5:i.console[t](e[0],e[1],e[2],e[3],e[4]);break;default:0!==e.length&&i.console.warn&&i.console.warn("too many params passed to logger."+t)}}t.exports={devError:u,devInfo:c,devObject:function(t,e){},publicError:function(){l("error",o.toRealArray(arguments))},publicLog:function(){l("info",o.toRealArray(arguments))},time:function(t){a&&(s[t]=d())},timeEnd:function(t){a&&s[t]&&(d(),s[t])}}},function(t,e,n){var r=n(20),i=n(5),o=n(11),s=n(0),a=n(119);t.exports=function(t){var e=t.href&&t.href.split("?")[1],n=e?o.decode(e):{},u={lang:a(t),width:t.getAttribute("data-width")||t.getAttribute("width"),height:t.getAttribute("data-height")||t.getAttribute("height"),related:t.getAttribute("data-related"),partner:t.getAttribute("data-partner")};return i.asBoolean(t.getAttribute("data-dnt"))&&r.setOn(),s.forIn(u,function(t,e){var r=n[t];n[t]=i.hasValue(r)?r:e}),s.compact(n)}},function(t,e,n){var r=n(81),i=n(23);t.exports=function(){var t="data-twitter-extracted-"+i.generate();return function(e,n){return r(e,n).filter(function(e){return!e.hasAttribute(t)}).map(function(e){return e.setAttribute(t,"true"),e})}}},function(t,e){function n(t,e,n,r,i,o,s){this.factory=t,this.Sandbox=e,this.srcEl=o,this.targetEl=i,this.parameters=r,this.className=n,this.options=s}n.prototype.destroy=function(){this.srcEl=this.targetEl=null},t.exports=n},function(t,e){t.exports={DM_BUTTON:"twitter-dm-button",FOLLOW_BUTTON:"twitter-follow-button",HASHTAG_BUTTON:"twitter-hashtag-button",MENTION_BUTTON:"twitter-mention-button",MOMENT:"twitter-moment",PERISCOPE:"periscope-on-air",SHARE_BUTTON:"twitter-share-button",TIMELINE:"twitter-timeline",TWEET:"twitter-tweet"}},function(t,e,n){var r=n(6),i=n(20),o=n(53),s=n(52),a=n(35),u=n(5),c=n(0);t.exports=function(t,e,n){var d;return t=t||[],e=e||{},d="ƒ("+t.join(", ")+", target, [options]);",function(){var l,f,h,p,m=Array.prototype.slice.apply(arguments,[0,t.length]),v=Array.prototype.slice.apply(arguments,[t.length]);return v.forEach(function(t){t&&(t.nodeType!==Node.ELEMENT_NODE?c.isType("function",t)?l=t:c.isType("object",t)&&(f=t):h=t)}),m.length!==t.length||0===v.length?(l&&c.async(function(){l(!1)}),r.reject(new Error("Not enough parameters. Expected: "+d))):h?(f=c.aug({},f||{},e),t.forEach(function(t){f[t]=m.shift()}),u.asBoolean(f.dnt)&&i.setOn(),p=a.getExperiments().then(function(t){return o.addWidget(n(f,h,void 0,s.isHorizonTweetEnabled(t)))}),l&&p.then(l,function(){l(!1)}),p):(l&&c.async(function(){l(!1)}),r.reject(new Error("No target element specified. Expected: "+d)))}}},function(t,e,n){var r=n(102),i=n(2),o=n(0);function s(t,e){return function(){try{e.resolve(t.call(this))}catch(t){e.reject(t)}}}t.exports={sync:function(t,e){t.call(e)},read:function(t,e){var n=new i;return r.read(s(t,n),e),n.promise},write:function(t,e){var n=new i;return r.write(s(t,n),e),n.promise},defer:function(t,e,n){var a=new i;return o.isType("function",t)&&(n=e,e=t,t=1),r.defer(t,s(e,a),n),a.promise}}},function(t,e,n){var r=n(9),i=["https://syndication.twitter.com","https://cdn.syndication.twimg.com","https://localhost.twitter.com:8444"],o=["https://syndication.twitter.com","https://localhost.twitter.com:8445"],s=["https://platform.twitter.com/embed/index.html","https://localhost.twitter.com:8080",/https:\/\/ton\.smf1\.twitter\.com\/syndication-internal\/embed-iframe\/[0-9A-Za-z_-]+\/app\/index\.html/],a=function(t,e){return t.some(function(t){return t instanceof RegExp?t.test(e):t===e})},u=function(){var t=r.get("backendHost");return t&&a(i,t)?t:"https://cdn.syndication.twimg.com"},c=function(){var t=r.get("settingsSvcHost");return t&&a(o,t)?t:"https://syndication.twitter.com"},d=function(){var t=r.get("embedIframeURL");return t&&a(s,t)?t:"https://platform.twitter.com/embed/index.html"};function l(t,e){var n=[t];return e.forEach(function(t){n.push(function(t){var e=(t||"").toString(),n="/"===e.slice(0,1)?1:0,r=function(t){return"/"===t.slice(-1)}(e)?-1:void 0;return e.slice(n,r)}(t))}),n.join("/")}t.exports={cookieConsent:function(t){var e=t||[];return e.unshift("cookie/consent"),l(c(),e)},embedIframe:function(){return d()},eventVideo:function(t){var e=t||[];return e.unshift("video/event"),l(u(),e)},grid:function(t){var e=t||[];return e.unshift("grid/collection"),l(u(),e)},moment:function(t){var e=t||[];return e.unshift("moments"),l(u(),e)},settings:function(t){var e=t||[];return e.unshift("settings"),l(c(),e)},timeline:function(t){var e=t||[];return e.unshift("timeline"),l(u(),e)},tweetBatch:function(t){var e=t||[];return e.unshift("tweets.json"),l(u(),e)},video:function(t){var e=t||[];return e.unshift("widgets/video"),l(u(),e)}}},function(t,e,n){var r=n(4),i=n(8),o=n(38),s=n(79),a=n(5),u=n(33),c=!1,d=/https?:\/\/([^/]+).*/i;t.exports={setOn:function(){c=!0},enabled:function(t,e){return!!(c||a.asBoolean(u.val("dnt"))||s.isUrlSensitive(e||i.host)||o.isFramed()&&s.isUrlSensitive(o.rootDocumentLocation())||(t=d.test(t||r.referrer)&&RegExp.$1)&&s.isUrlSensitive(t))}}},function(t,e,n){var r=n(4),i=n(12),o=n(95),s=n(1),a=n(0),u=o.userAgent;function c(t){return/(Trident|MSIE|Edge[/ ]?\d)/.test(t=t||u)}t.exports={retina:function(t){return(t=t||s).devicePixelRatio?t.devicePixelRatio>=1.5:!!t.matchMedia&&t.matchMedia("only screen and (min-resolution: 144dpi)").matches},anyIE:c,ie9:function(t){return/MSIE 9/.test(t=t||u)},ie10:function(t){return/MSIE 10/.test(t=t||u)},ios:function(t){return/(iPad|iPhone|iPod)/.test(t=t||u)},android:function(t){return/^Mozilla\/5\.0 \(Linux; (U; )?Android/.test(t=t||u)},canPostMessage:function(t,e){return t=t||s,e=e||u,t.postMessage&&!(c(e)&&t.opener)},touch:function(t,e,n){return t=t||s,e=e||o,n=n||u,"ontouchstart"in t||/Opera Mini/.test(n)||e.msMaxTouchPoints>0},cssTransitions:function(){var t=r.body.style;return void 0!==t.transition||void 0!==t.webkitTransition||void 0!==t.mozTransition||void 0!==t.oTransition||void 0!==t.msTransition},hasPromiseSupport:function(){return!!(s.Promise&&s.Promise.resolve&&s.Promise.reject&&s.Promise.all&&s.Promise.race&&(new s.Promise(function(e){t=e}),a.isType("function",t)));var t},hasIntersectionObserverSupport:function(){return!!s.IntersectionObserver},hasPerformanceInformation:function(){return s.performance&&s.performance.getEntriesByType},hasLocalStorageSupport:function(){try{return s.localStorage.setItem("local_storage_support_test","true"),void 0!==s.localStorage}catch(t){return i.devError("window.localStorage is not supported:",t),!1}}}},function(t,e,n){var r=n(6),i=n(2);function o(t,e){return t.then(e,e)}function s(t){return t instanceof r}t.exports={always:o,allResolved:function(t){var e;return void 0===t?r.reject(new Error("undefined is not an object")):Array.isArray(t)?(e=t.length)?new r(function(n,r){var i=0,o=[];function a(){(i+=1)===e&&(0===o.length?r():n(o))}function u(t){o.push(t),a()}t.forEach(function(t){s(t)?t.then(u,a):u(t)})}):r.resolve([]):r.reject(new Error("Type error"))},some:function(t){var e;return e=(t=t||[]).length,t=t.filter(s),e?e!==t.length?r.reject("non-Promise passed to .some"):new r(function(e,n){var r=0;function i(){(r+=1)===t.length&&n()}t.forEach(function(t){t.then(e,i)})}):r.reject("no promises passed to .some")},isPromise:s,allSettled:function(t){function e(){}return r.all((t||[]).map(function(t){return o(t,e)}))},timeout:function(t,e){var n=new i;return setTimeout(function(){n.reject(new Error("Promise timed out"))},e),t.then(function(t){n.resolve(t)},function(t){n.reject(t)}),n.promise}}},function(t,e){var n="i",r=0,i=0;t.exports={generate:function(){return n+String(+new Date)+Math.floor(1e5*Math.random())+r++},deterministic:function(){return n+String(i++)}}},function(t,e,n){var r=n(49),i=n(51),o=n(0);t.exports=o.aug(r.get("events")||{},i.Emitter)},function(t,e,n){var r=n(26),i=n(110);t.exports=r.build([i])},function(t,e,n){var r=n(40),i=n(107),o=n(7);(r=Object.create(r)).build=o(r.build,null,i),t.exports=r},function(t,e,n){var r=n(40),i=n(41),o=n(7);(r=Object.create(r)).build=o(r.build,null,i),t.exports=r},function(t,e,n){var r=n(83),i=n(75),o=n(84),s=n(8),a=n(71),u=n(74),c=n(20),d=n(5),l=n(23),f=n(0);function h(t){if(!t||!t.headers)throw new Error("unexpected response schema");return{html:t.body,config:t.config,pollInterval:1e3*parseInt(t.headers.xPolling,10)||null,maxCursorPosition:t.headers.maxPosition,minCursorPosition:t.headers.minPosition}}function p(t){if(t&&t.headers)throw new Error(t.headers.status);throw t instanceof Error?t:new Error(t)}t.exports=function(t){t.params({instanceId:{required:!0,fallback:l.deterministic},lang:{required:!0,transform:a.matchLanguage,fallback:"en"},tweetLimit:{transform:d.asInt}}),t.defineProperty("endpoint",{get:function(){throw new Error("endpoint not specified")}}),t.defineProperty("pollEndpoint",{get:function(){return this.endpoint}}),t.define("cbId",function(t){var e=t?"_new":"_old";return"tl_"+this.params.instanceId+"_"+this.id+e}),t.define("queryParams",function(){return{lang:this.params.lang,tz:u.getTimezoneOffset(),t:r(),domain:s.host,tweet_limit:this.params.tweetLimit,dnt:c.enabled()}}),t.define("fetch",function(){return i.fetch(this.endpoint,this.queryParams(),o,this.cbId()).then(h,p)}),t.define("poll",function(t,e){var n,r;return n={since_id:(t=t||{}).sinceId,max_id:t.maxId,min_position:t.minPosition,max_position:t.maxPosition},r=f.aug(this.queryParams(),n),i.fetch(this.pollEndpoint,r,o,this.cbId(e)).then(h,p)})}},function(t,e,n){var r=n(51).makeEmitter();t.exports={emitter:r,START:"start",ALL_WIDGETS_RENDER_START:"all_widgets_render_start",ALL_WIDGETS_RENDER_END:"all_widgets_render_end",ALL_WIDGETS_AND_IMAGES_LOADED:"all_widgets_and_images_loaded"}},function(t,e,n){var r=n(4),i=n(0);t.exports=function(t,e,n){var o;if(n=n||r,t=t||{},e=e||{},t.name){try{o=n.createElement('<iframe name="'+t.name+'"></iframe>')}catch(e){(o=n.createElement("iframe")).name=t.name}delete t.name}else o=n.createElement("iframe");return t.id&&(o.id=t.id,delete t.id),o.allowtransparency="true",o.scrolling="no",o.setAttribute("frameBorder",0),o.setAttribute("allowTransparency",!0),i.forIn(t,function(t,e){o.setAttribute(t,e)}),i.forIn(e,function(t,e){o.style[t]=e}),o}},function(t,e,n){var r=n(1).JSON;t.exports={stringify:r.stringify||r.encode,parse:r.parse||r.decode}},function(t,e,n){var r=n(0),i=n(43);t.exports={closest:function t(e,n,o){var s;if(n)return o=o||n&&n.ownerDocument,s=r.isType("function",e)?e:function(t){return function(e){return!!e.tagName&&i(e,t)}}(e),n===o?s(n)?n:void 0:s(n)?n:t(s,n.parentNode,o)}}},function(t,e,n){var r,i=n(4);function o(t){var e,n,o,s=0;for(r={},e=(t=t||i).getElementsByTagName("meta");e[s];s++){if(n=e[s],/^twitter:/.test(n.getAttribute("name")))o=n.getAttribute("name").replace(/^twitter:/,"");else{if(!/^twitter:/.test(n.getAttribute("property")))continue;o=n.getAttribute("property").replace(/^twitter:/,"")}r[o]=n.getAttribute("content")||n.getAttribute("value")}}o(),t.exports={init:o,val:function(t){return r[t]}}},function(t,e,n){var r=n(4),i=n(31),o=n(20),s=n(0),a=n(44),u=n(9),c=n(3),d=n(32),l=a.version,f=u.get("clientEventEndpoint")||"https://syndication.twitter.com/i/jot",h=1;function p(t){return s.aug({client:"tfw"},t||{})}function m(t,e,n){return e=e||{},s.aug({},e,{_category_:t,triggered_on:e.triggered_on||+new Date,dnt:o.enabled(n)})}t.exports={extractTermsFromDOM:function t(e,n){var r;return n=n||{},e&&e.nodeType===Node.ELEMENT_NODE?((r=e.getAttribute("data-scribe"))&&r.split(" ").forEach(function(t){var e=t.trim().split(":"),r=e[0],i=e[1];r&&i&&!n[r]&&(n[r]=i)}),t(e.parentNode,n)):n},clickEventElement:function(t){var e=d.closest("[data-expanded-url]",t),n=e&&e.getAttribute("data-expanded-url");return n&&c.isTwitterURL(n)?"twitter_url":"url"},flattenClientEventPayload:function(t,e){return s.aug({},e,{event_namespace:t})},formatGenericEventData:m,formatClientEventData:function(t,e,n){var i=t&&t.widget_origin||r.referrer;return(t=m("tfw_client_event",t,i)).client_version=l,t.format_version=void 0!==n?n:1,e||(t.widget_origin=i),t},formatClientEventNamespace:p,formatTweetAssociation:function(t,e){var n={};return(e=e||{}).association_namespace=p(t),n[h]=e,n},noticeSeen:function(t){return"notice"===t.element&&"seen"===t.action},splitLogEntry:function(t){var e,n,r,i,o;return t.item_ids&&t.item_ids.length>1?(e=Math.floor(t.item_ids.length/2),n=t.item_ids.slice(0,e),r={},i=t.item_ids.slice(e),o={},n.forEach(function(e){r[e]=t.item_details[e]}),i.forEach(function(e){o[e]=t.item_details[e]}),[s.aug({},t,{item_ids:n,item_details:r}),s.aug({},t,{item_ids:i,item_details:o})]):[t]},stringify:function(t){var e,n=Array.prototype.toJSON;return delete Array.prototype.toJSON,e=i.stringify(t),n&&(Array.prototype.toJSON=n),e},AUDIENCE_ENDPOINT:"https://syndication.twitter.com/i/jot/syndication",CLIENT_EVENT_ENDPOINT:f,RUFOUS_REDIRECT:"https://platform.twitter.com/jot.html"}},function(t,e,n){var r=n(113),i=n(116);function o(t){return r.settingsLoaded().then(function(e){return e[t]})}function s(){return o("experiments")}t.exports={shouldObtainCookieConsent:function(){return o("shouldObtainCookieConsent")},getExperiments:s,getExperiment:function(t){return s().then(function(e){if(!e[t])throw new Error("Experiment not found");return e[t]})},getActiveExperimentDataString:function(){return s().then(function(t){var e=Object.keys(t).reduce(function(e,n){var r;return t[n].version&&(r=n.split("_").slice(-1)[0],e.push(r+";"+t[n].bucket)),e},[]);return i(e.join(","))})},getExperimentKeys:function(){return s().then(function(t){return Object.keys(t)})},load:function(){r.load()}}},function(t,e,n){var r=n(10),i={},o=-1,s={};function a(t){var e=t.getAttribute("data-twitter-event-id");return e||(t.setAttribute("data-twitter-event-id",++o),o)}function u(t,e,n){var r=0,i=t&&t.length||0;for(r=0;r<i;r++)if(t[r].call(e,n,e),n.ceaseImmediately)return!1}function c(t,e,n){for(var i=n||t.target||t.srcElement,o=r.list(i).map(function(t){return"."+t}).concat(i.tagName),s=0,a=o.length;s<a;s++)if(!1===u(e[o[s]],i,t))return;t.cease||i!==this&&c.call(this,t,e,i.parentElement||i.parentNode)}function d(t,e,n,r){function i(r){c.call(t,r,n[e])}!function(t,e,n,r){t.id&&(s[t.id]=s[t.id]||[],s[t.id].push({el:t,listener:e,type:n,rootId:r}))}(t,i,e,r),t.addEventListener(e,i,!1)}function l(t){t&&t.preventDefault?t.preventDefault():t.returnValue=!1}function f(t){t&&(t.cease=!0)&&t.stopPropagation?t.stopPropagation():t.cancelBubble=!0}t.exports={stop:function(t){return f(t),l(t),!1},stopPropagation:f,stopImmediatePropagation:function(t){t&&(t.ceaseImmediately=!0,f(t),t.stopImmediatePropagation())},preventDefault:l,delegate:function(t,e,n,r){var o=a(t);i[o]=i[o]||{},i[o][e]||(i[o][e]={},d(t,e,i[o],o)),i[o][e][n]=i[o][e][n]||[],i[o][e][n].push(r)},simulate:function(t,e,n){var r=a(e),o=i[r]&&i[r];c.call(e,{target:n},o[t])},removeDelegatesForWidget:function(t){var e=s[t];e&&(e.forEach(function(t){t.el.removeEventListener(t.type,t.listener,!1),delete i[t.rootId]}),delete s[t])}}},function(t,e,n){var r=n(26),i=n(125);t.exports=r.build([i])},function(t,e,n){var r=n(8),i=n(78),o=n(0),s=i.getCanonicalURL()||r.href,a=s;t.exports={isFramed:function(){return s!==a},rootDocumentLocation:function(t){return t&&o.isType("string",t)&&(s=t),s},currentDocumentLocation:function(){return a}}},function(t,e,n){var r=n(77),i=n(104),o=n(80),s=n(34),a=new(n(112))(function(t){var e,n,a;if(0!==t.length){if(u(t))return c(t);e=r(t,function(t){return s.noticeSeen(t.input.namespace)}),n=e.true,a=e.false,n&&n.length>0&&(n=n.slice(0,1),o.canFlushOneItem(n[0])||(n[0].input.data.message=""),c(n)),a&&(u(a)?c:function(t){i.init(),t.forEach(function(t){var e=t.input.namespace,n=t.input.data,r=t.input.offsite,o=t.input.version;i.clientEvent(e,n,r,o)}),i.flush().then(function(){t.forEach(function(t){t.taskDoneDeferred.resolve()})},function(){t.forEach(function(t){t.taskDoneDeferred.reject()})})})(a)}});function u(t){return 1===t.length&&o.canFlushOneItem(t[0])}function c(t){t.forEach(function(t){var e=t.input.namespace,n=t.input.data,r=t.input.offsite,i=t.input.version;o.clientEvent(e,n,r,i),t.taskDoneDeferred.resolve()})}t.exports={scribe:function(t,e,n,r){return a.add({namespace:t,data:e,offsite:n,version:r})},pause:function(){a.pause()},resume:function(){a.resume()}}},function(t,e,n){var r=n(105),i=n(106),o=n(0);t.exports={couple:function(){return o.toRealArray(arguments)},build:function(t,e,n){var o=new t;return(e=i(r(e||[]))).forEach(function(t){t.call(null,o)}),o.build(n)}}},function(t,e,n){var r=n(108),i=n(0),o=n(42);function s(){this.Component=this.factory(),this._adviceArgs=[],this._lastArgs=[]}i.aug(s.prototype,{factory:o,build:function(t){var e=this;return this.Component,i.aug(this.Component.prototype.boundParams,t),this._adviceArgs.concat(this._lastArgs).forEach(function(t){(function(t,e,n){var r=this[e];if(!r)throw new Error(e+" does not exist");this[e]=t(r,n)}).apply(e.Component.prototype,t)}),delete this._lastArgs,delete this._adviceArgs,this.Component},params:function(t){var e=this.Component.prototype.paramConfigs;t=t||{},this.Component.prototype.paramConfigs=i.aug({},t,e)},define:function(t,e){if(t in this.Component.prototype)throw new Error(t+" has previously been defined");this.override(t,e)},defineStatic:function(t,e){this.Component[t]=e},override:function(t,e){this.Component.prototype[t]=e},defineProperty:function(t,e){if(t in this.Component.prototype)throw new Error(t+" has previously been defined");this.overrideProperty(t,e)},overrideProperty:function(t,e){var n=i.aug({configurable:!0},e);Object.defineProperty(this.Component.prototype,t,n)},before:function(t,e){this._adviceArgs.push([r.before,t,e])},after:function(t,e){this._adviceArgs.push([r.after,t,e])},around:function(t,e){this._adviceArgs.push([r.around,t,e])},last:function(t,e){this._lastArgs.push([r.after,t,e])}}),t.exports=s},function(t,e,n){var r=n(0);function i(){return!0}function o(t){return t}t.exports=function(){function t(t){var e=this;t=t||{},this.params=Object.keys(this.paramConfigs).reduce(function(n,s){var a=[],u=e.boundParams,c=e.paramConfigs[s],d=c.validate||i,l=c.transform||o;if(s in u&&a.push(u[s]),s in t&&a.push(t[s]),a="fallback"in c?a.concat(c.fallback):a,n[s]=function(t,e,n){var i=null;return t.some(function(t){if(t=r.isType("function",t)?t():t,e(t))return i=n(t),!0}),i}(a,d,l),c.required&&null==n[s])throw new Error(s+" is a required parameter");return n},{}),this.initialize()}return r.aug(t.prototype,{paramConfigs:{},boundParams:{},initialize:function(){}}),t}},function(t,e,n){var r=n(1).HTMLElement,i=r.prototype.matches||r.prototype.matchesSelector||r.prototype.webkitMatchesSelector||r.prototype.mozMatchesSelector||r.prototype.msMatchesSelector||r.prototype.oMatchesSelector;t.exports=function(t,e){if(i)return i.call(t,e)}},function(t){t.exports={version:"2a81c84:1568701398616"}},function(t,e,n){var r,i=n(10),o=n(4),s=n(1),a=n(33),u=n(54),c=n(5),d=n(23),l="csptest";t.exports={inlineStyle:function(){var t=l+d.generate(),e=o.createElement("div"),n=o.createElement("style"),f="."+t+" { visibility: hidden; }";return!!o.body&&(c.asBoolean(a.val("widgets:csp"))&&(r=!1),void 0!==r?r:(e.style.display="none",i.add(e,t),n.type="text/css",n.appendChild(o.createTextNode(f)),o.body.appendChild(n),o.body.appendChild(e),r="hidden"===s.getComputedStyle(e).visibility,u(e),u(n),r))}}},function(t,e,n){var r=n(1);t.exports=function(t,e,n){var i,o=0;return n=n||null,function s(){var a=n||this,u=arguments,c=+new Date;if(r.clearTimeout(i),c-o>e)return o=c,void t.apply(a,u);i=r.setTimeout(function(){s.apply(a,u)},e)}}},function(t,e){t.exports=function(t){var e=t.getBoundingClientRect();return{width:e.width,height:e.height}}},function(t,e,n){
11
- /*!
12
- * @overview es6-promise - a tiny implementation of Promises/A+.
13
- * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
14
- * @license Licensed under MIT license
15
- * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
16
- * @version v4.2.5+7f2b526d
17
- */var r;r=function(){"use strict";function t(t){return"function"==typeof t}var e=Array.isArray?Array.isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},n=0,r=void 0,i=void 0,o=function(t,e){f[n]=t,f[n+1]=e,2===(n+=2)&&(i?i(h):w())},s="undefined"!=typeof window?window:void 0,a=s||{},u=a.MutationObserver||a.WebKitMutationObserver,c="undefined"==typeof self&&"undefined"!=typeof process&&"[object process]"==={}.toString.call(process),d="undefined"!=typeof Uint8ClampedArray&&"undefined"!=typeof importScripts&&"undefined"!=typeof MessageChannel;function l(){var t=setTimeout;return function(){return t(h,1)}}var f=new Array(1e3);function h(){for(var t=0;t<n;t+=2)(0,f[t])(f[t+1]),f[t]=void 0,f[t+1]=void 0;n=0}var p,m,v,g,w=void 0;function y(t,e){var n=this,r=new this.constructor(E);void 0===r[_]&&k(r);var i=n._state;if(i){var s=arguments[i-1];o(function(){return D(i,r,s,n._result)})}else I(n,r,t,e);return r}function b(t){if(t&&"object"==typeof t&&t.constructor===this)return t;var e=new this(E);return C(e,t),e}c?w=function(){return process.nextTick(h)}:u?(m=0,v=new u(h),g=document.createTextNode(""),v.observe(g,{characterData:!0}),w=function(){g.data=m=++m%2}):d?((p=new MessageChannel).port1.onmessage=h,w=function(){return p.port2.postMessage(0)}):w=void 0===s?function(){try{var t=Function("return this")().require("vertx");return void 0!==(r=t.runOnLoop||t.runOnContext)?function(){r(h)}:l()}catch(t){return l()}}():l();var _=Math.random().toString(36).substring(2);function E(){}var x=void 0,T=1,A=2,S={error:null};function R(t){try{return t.then}catch(t){return S.error=t,S}}function N(e,n,r){n.constructor===e.constructor&&r===y&&n.constructor.resolve===b?function(t,e){e._state===T?L(t,e._result):e._state===A?j(t,e._result):I(e,void 0,function(e){return C(t,e)},function(e){return j(t,e)})}(e,n):r===S?(j(e,S.error),S.error=null):void 0===r?L(e,n):t(r)?function(t,e,n){o(function(t){var r=!1,i=function(t,e,n,r){try{t.call(e,n,r)}catch(t){return t}}(n,e,function(n){r||(r=!0,e!==n?C(t,n):L(t,n))},function(e){r||(r=!0,j(t,e))},t._label);!r&&i&&(r=!0,j(t,i))},t)}(e,n,r):L(e,n)}function C(t,e){var n,r;t===e?j(t,new TypeError("You cannot resolve a promise with itself")):(r=typeof(n=e),null===n||"object"!==r&&"function"!==r?L(t,e):N(t,e,R(e)))}function P(t){t._onerror&&t._onerror(t._result),O(t)}function L(t,e){t._state===x&&(t._result=e,t._state=T,0!==t._subscribers.length&&o(O,t))}function j(t,e){t._state===x&&(t._state=A,t._result=e,o(P,t))}function I(t,e,n,r){var i=t._subscribers,s=i.length;t._onerror=null,i[s]=e,i[s+T]=n,i[s+A]=r,0===s&&t._state&&o(O,t)}function O(t){var e=t._subscribers,n=t._state;if(0!==e.length){for(var r=void 0,i=void 0,o=t._result,s=0;s<e.length;s+=3)r=e[s],i=e[s+n],r?D(n,r,i,o):i(o);t._subscribers.length=0}}function D(e,n,r,i){var o=t(r),s=void 0,a=void 0,u=void 0,c=void 0;if(o){if((s=function(t,e){try{return t(e)}catch(t){return S.error=t,S}}(r,i))===S?(c=!0,a=s.error,s.error=null):u=!0,n===s)return void j(n,new TypeError("A promises callback cannot return that same promise."))}else s=i,u=!0;n._state!==x||(o&&u?C(n,s):c?j(n,a):e===T?L(n,s):e===A&&j(n,s))}var z=0;function k(t){t[_]=z++,t._state=void 0,t._result=void 0,t._subscribers=[]}var M=function(){function t(t,n){this._instanceConstructor=t,this.promise=new t(E),this.promise[_]||k(this.promise),e(n)?(this.length=n.length,this._remaining=n.length,this._result=new Array(this.length),0===this.length?L(this.promise,this._result):(this.length=this.length||0,this._enumerate(n),0===this._remaining&&L(this.promise,this._result))):j(this.promise,new Error("Array Methods must be provided an Array"))}return t.prototype._enumerate=function(t){for(var e=0;this._state===x&&e<t.length;e++)this._eachEntry(t[e],e)},t.prototype._eachEntry=function(t,e){var n=this._instanceConstructor,r=n.resolve;if(r===b){var i=R(t);if(i===y&&t._state!==x)this._settledAt(t._state,e,t._result);else if("function"!=typeof i)this._remaining--,this._result[e]=t;else if(n===U){var o=new n(E);N(o,t,i),this._willSettleAt(o,e)}else this._willSettleAt(new n(function(e){return e(t)}),e)}else this._willSettleAt(r(t),e)},t.prototype._settledAt=function(t,e,n){var r=this.promise;r._state===x&&(this._remaining--,t===A?j(r,n):this._result[e]=n),0===this._remaining&&L(r,this._result)},t.prototype._willSettleAt=function(t,e){var n=this;I(t,void 0,function(t){return n._settledAt(T,e,t)},function(t){return n._settledAt(A,e,t)})},t}(),U=function(){function e(t){this[_]=z++,this._result=this._state=void 0,this._subscribers=[],E!==t&&("function"!=typeof t&&function(){throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")}(),this instanceof e?function(t,e){try{e(function(e){C(t,e)},function(e){j(t,e)})}catch(e){j(t,e)}}(this,t):function(){throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")}())}return e.prototype.catch=function(t){return this.then(null,t)},e.prototype.finally=function(e){var n=this.constructor;return t(e)?this.then(function(t){return n.resolve(e()).then(function(){return t})},function(t){return n.resolve(e()).then(function(){throw t})}):this.then(e,e)},e}();return U.prototype.then=y,U.all=function(t){return new M(this,t).promise},U.race=function(t){var n=this;return e(t)?new n(function(e,r){for(var i=t.length,o=0;o<i;o++)n.resolve(t[o]).then(e,r)}):new n(function(t,e){return e(new TypeError("You must pass an array to race."))})},U.resolve=b,U.reject=function(t){var e=new this(E);return j(e,t),e},U._setScheduler=function(t){i=t},U._setAsap=function(t){o=t},U._asap=o,U.polyfill=function(){var t=void 0;if("undefined"!=typeof global)t=global;else if("undefined"!=typeof self)t=self;else try{t=Function("return this")()}catch(t){throw new Error("polyfill failed because global object is unavailable in this environment")}var e=t.Promise;if(e){var n=null;try{n=Object.prototype.toString.call(e.resolve())}catch(t){}if("[object Promise]"===n&&!e.cast)return}t.Promise=U},U.Promise=U,U},t.exports=r()},function(t,e,n){var r=n(50);t.exports=new r("twttr")},function(t,e,n){var r=n(1),i=n(0);function o(t){return i.isType("string",t)?t.split("."):i.isType("array",t)?t:[]}function s(t,e){(e=e||r)[t]=e[t]||{},Object.defineProperty(this,"base",{value:e[t]}),Object.defineProperty(this,"name",{value:t})}i.aug(s.prototype,{get:function(t){return o(t).reduce(function(t,e){if(i.isObject(t))return t[e]},this.base)},set:function(t,e,n){var r=o(t),s=function(t,e){var n=o(e).slice(0,-1);return n.reduce(function(t,e,r){if(t[e]=t[e]||{},!i.isObject(t[e]))throw new Error(n.slice(0,r+1).join(".")+" is already defined with a value.");return t[e]},t)}(this.base,t),a=r.slice(-1);return n&&a in s?s[a]:s[a]=e},init:function(t,e){return this.set(t,e,!0)},unset:function(t){var e=o(t),n=this.get(e.slice(0,-1));n&&delete n[e.slice(-1)]},aug:function(t){var e=this.get(t),n=i.toRealArray(arguments).slice(1);if(e=void 0!==e?e:{},n.unshift(e),!n.every(i.isObject))throw new Error("Cannot augment non-object.");return this.set(t,i.aug.apply(null,n))},call:function(t){var e=this.get(t),n=i.toRealArray(arguments).slice(1);if(!i.isType("function",e))throw new Error("Function "+t+"does not exist.");return e.apply(null,n)},fullPath:function(t){var e=o(t);return e.unshift(this.name),e.join(".")}}),t.exports=s},function(t,e,n){var r=n(0),i=n(7),o={bind:function(t,e){return this._handlers=this._handlers||{},this._handlers[t]=this._handlers[t]||[],this._handlers[t].push(e)},unbind:function(t,e){var n;this._handlers&&this._handlers[t]&&(e?(n=this._handlers[t].indexOf(e))>=0&&this._handlers[t].splice(n,1):this._handlers[t]=[])},trigger:function(t,e){var n=this._handlers&&this._handlers[t];(e=e||{}).type=t,n&&n.forEach(function(t){r.async(i(t,this,e))})}};t.exports={Emitter:o,makeEmitter:function(){return r.aug(function(){},o)}}},function(t,e){var n="tfw_horizon_tweet_embed_9555";t.exports={isHorizonTweetEnabled:function(t){return!(!t||!t[n]||"hte"!==t[n].bucket)}}},function(t,e,n){var r=n(101),i=n(76),o=n(6),s=n(22),a=n(7),u=n(0),c=new i(function(t){var e=function(t){return t.reduce(function(t,e){return t[e._className]=t[e._className]||[],t[e._className].push(e),t},{})}(t.map(r.fromRawTask));u.forIn(e,function(t,e){s.allSettled(e.map(function(t){return t.initialize()})).then(function(){e.forEach(function(t){o.all([t.hydrate(),t.insertIntoDom()]).then(a(t.render,t)).then(a(t.success,t),a(t.fail,t))})})})});t.exports={addWidget:function(t){return c.add(t)}}},function(t,e,n){var r=n(18);t.exports=function(t){return r.write(function(){t&&t.parentNode&&t.parentNode.removeChild(t)})}},function(t,e,n){n(12),t.exports={log:function(t,e){}}},function(t,e,n){var r=n(1);function i(t){return(t=t||r).getSelection&&t.getSelection()}t.exports={getSelection:i,getSelectedText:function(t){var e=i(t);return e?e.toString():""}}},function(t,e,n){var r=n(4),i=n(1),o=n(2),s=2e4;t.exports=function(t){var e=new o,n=r.createElement("img");return n.onload=n.onerror=function(){i.setTimeout(e.resolve,50)},n.src=t,i.setTimeout(e.reject,s),e.promise}},function(t,e,n){var r=n(111);t.exports=function(t){t.define("createElement",r),t.define("createFragment",r),t.define("htmlToElement",r),t.define("hasSelectedText",r),t.define("addRootClass",r),t.define("removeRootClass",r),t.define("hasRootClass",r),t.define("prependStyleSheet",r),t.define("appendStyleSheet",r),t.define("prependCss",r),t.define("appendCss",r),t.define("makeVisible",r),t.define("injectWidgetEl",r),t.define("matchHeightToContent",r),t.define("matchWidthToContent",r)}},function(t,e){t.exports=function(t){var e,n=!1;return function(){return n?e:(n=!0,e=t.apply(this,arguments))}}},function(t,e,n){var r=n(15),i=n(120),o=n(61),s=n(16);t.exports=function(t,e,n){return new r(i,o,s.DM_BUTTON,t,e,n)}},function(t,e,n){var r=n(62),i=n(25);t.exports=r.isSupported()?r:i},function(t,e,n){var r=n(26),i=n(121);t.exports=r.build([i])},function(t,e,n){var r=n(15),i=n(124),o=n(37),s=n(16);t.exports=function(t,e,n){return new r(i,o,s.FOLLOW_BUTTON,t,e,n)}},function(t,e,n){var r=n(15),i=n(132),o=n(25),s=n(16);t.exports=function(t,e,n){return new r(i,o,s.MOMENT,t,e,n)}},function(t,e,n){var r=n(15),i=n(134),o=n(25),s=n(16);t.exports=function(t,e,n){return new r(i,o,s.PERISCOPE,t,e,n)}},function(t,e,n){var r=n(82),i=n(136),o=n(140),s=n(142),a=n(144),u=n(146),c={collection:i,event:o,likes:s,list:a,profile:u,url:l},d=[u,s,i,a,o];function l(t){return r(d,function(e){try{return new e(t)}catch(t){}})}t.exports=function(t){return t?function(t){var e,n;return e=(t.sourceType+"").toLowerCase(),(n=c[e])?new n(t):null}(t)||l(t):null}},function(t,e,n){var r=n(15),i=n(148),o=n(25),s=n(16);t.exports=function(t,e,n){return new r(i,o,s.TIMELINE,t,e,n)}},function(t,e,n){var r=n(15),i=n(4),o=n(37),s=n(150),a=n(61),u=n(151),c=n(16);t.exports=function(t,e,n,d){var l;return d?(l=i.createElement("div"),new r(s,o,c.TWEET,t,e,n,{sandboxWrapperEl:l})):new r(u,a,c.TWEET,t,e,n)}},function(t,e,n){var r=n(15),i=n(153),o=n(37),s=n(16);t.exports=function(t,e,n){var a=t&&t.type||"share",u="hashtag"==a?s.HASHTAG_BUTTON:"mention"==a?s.MENTION_BUTTON:s.SHARE_BUTTON;return new r(i,o,u,t,e,n)}},function(t,e,n){var r=n(39),i=n(38),o=n(0);t.exports=function(t){var e={widget_origin:i.rootDocumentLocation(),widget_frame:i.isFramed()?i.currentDocumentLocation():null,duration_ms:t.duration,item_ids:t.widgetIds||[]},n=o.aug(t.namespace,{page:"page",component:"performance"});r.scribe(n,e)}},function(t,e,n){var r=n(0),i=n(137),o=["ar","fa","he","ur"];t.exports={isRtlLang:function(t){return t=String(t).toLowerCase(),r.contains(o,t)},matchLanguage:function(t){return t=(t=(t||"").toLowerCase()).replace("_","-"),i(t)?t:(t=t.replace(/-.*/,""),i(t)?t:"en")}}},function(t){t.exports={tweetButtonHtmlPath:"/widgets/tweet_button.d6364fae9340b0be5f13818370141fd0.{{lang}}.html",followButtonHtmlPath:"/widgets/follow_button.d6364fae9340b0be5f13818370141fd0.{{lang}}.html",hubHtmlPath:"/widgets/hub.html",widgetIframeHtmlPath:"/widgets/widget_iframe.d6364fae9340b0be5f13818370141fd0.html",resourceBaseUrl:"https://platform.twitter.com"}},function(t,e,n){var r=n(3),i=n(98),o=n(24),s=n(11),a={favorite:["favorite","like"],follow:["follow"],like:["favorite","like"],retweet:["retweet"],tweet:["tweet"]};function u(t){this.srcEl=[],this.element=t}u.open=function(t,e,n){var u=(r.intentType(t)||"").toLowerCase();r.isTwitterURL(t)&&(function(t,e){i.open(t,{},e)}(t,n),e&&o.trigger("click",{target:e,region:"intent",type:"click",data:{}}),e&&a[u]&&a[u].forEach(function(n){o.trigger(n,{target:e,region:"intent",type:n,data:function(t,e){var n=s.decodeURL(e);switch(t){case"favorite":case"like":return{tweet_id:n.tweet_id};case"follow":return{screen_name:n.screen_name,user_id:n.user_id};case"retweet":return{source_tweet_id:n.tweet_id};default:return{}}}(u,t)})}))},t.exports=u},function(t,e){t.exports={getTimezoneOffset:function(){var t=(new Date).toString().match(/(GMT[+-]?\d+)/);return t&&t[0]||"GMT"}}},function(t,e,n){var r=n(4),i=n(9),o=n(2),s=n(0),a=n(11),u="cb",c=0;t.exports={fetch:function(t,e,n,d){var l,f,h;return d=function(t){if(t)return t.replace(/[^\w$]/g,"_")}(d||u+c++),l=i.fullPath(["callbacks",d]),f=r.createElement("script"),h=new o,e=s.aug({},e,{callback:l,suppress_response_codes:!0}),i.set(["callbacks",d],function(t){var e;t=(e=n(t||!1)).resp,e.success?h.resolve(t):h.reject(t),f.onload=f.onreadystatechange=null,f.parentNode&&f.parentNode.removeChild(f),i.unset(["callbacks",d])}),f.onerror=function(){h.reject(new Error("failed to fetch "+f.src))},f.src=a.url(t,e),f.async="async",r.body.appendChild(f),h.promise}}},function(t,e,n){var r=n(2),i=n(103),o=n(7);function s(t){this._inputsQueue=[],this._task=t,this._hasFlushBeenScheduled=!1}s.prototype.add=function(t){var e=new r;return this._inputsQueue.push({input:t,taskDoneDeferred:e}),this._hasFlushBeenScheduled||(this._hasFlushBeenScheduled=!0,i(o(this._flush,this))),e.promise},s.prototype._flush=function(){try{this._task.call(null,this._inputsQueue)}catch(t){this._inputsQueue.forEach(function(e){e.taskDoneDeferred.reject(t)})}this._inputsQueue=[],this._hasFlushBeenScheduled=!1},t.exports=s},function(t,e){t.exports=function(t,e){return t.reduce(function(t,n){var r=e(n);return t[r]=t[r]||[],t[r].push(n),t},{})}},function(t,e,n){var r=n(4),i=n(8),o=n(3);function s(t,e){var n,r;return e=e||i,/^https?:\/\//.test(t)?t:/^\/\//.test(t)?e.protocol+t:(n=e.host+(e.port.length?":"+e.port:""),0!==t.indexOf("/")&&((r=e.pathname.split("/")).pop(),r.push(t),t="/"+r.join("/")),[e.protocol,"//",n,t].join(""))}t.exports={absolutize:s,getCanonicalURL:function(){for(var t,e=r.getElementsByTagName("link"),n=0;e[n];n++)if("canonical"==(t=e[n]).rel)return s(t.href)},getScreenNameFromPage:function(){for(var t,e,n,i=[r.getElementsByTagName("a"),r.getElementsByTagName("link")],s=0,a=0,u=/\bme\b/;t=i[s];s++)for(a=0;e=t[a];a++)if(u.test(e.rel)&&(n=o.screenName(e.href)))return n}}},function(t,e,n){var r=n(8),i=/^[^#?]*\.(gov|mil)(:\d+)?([#?].*)?$/i,o={};function s(t){return t in o?o[t]:o[t]=i.test(t)}t.exports={isUrlSensitive:s,isHostPageSensitive:function(){return s(r.host)}}},function(t,e,n){var r=n(20),i=n(55),o=n(11),s=n(34),a=n(0),u=n(9).get("scribeCallback"),c=2083,d=[],l=o.url(s.CLIENT_EVENT_ENDPOINT,{dnt:0,l:""}),f=encodeURIComponent(l).length;function h(t,e,n,r){var i=!a.isObject(t),o=!!e&&!a.isObject(e);i||o||(u&&u(arguments),p(s.formatClientEventNamespace(t),s.formatClientEventData(e,n,r),s.CLIENT_EVENT_ENDPOINT))}function p(t,e,n){var r,u;n&&a.isObject(t)&&a.isObject(e)&&(i.log(t,e),r=s.flattenClientEventPayload(t,e),u={l:s.stringify(r)},s.noticeSeen(t)&&(u.notice_seen=!0),r.dnt&&(u.dnt=1),w(o.url(n,u)))}function m(t,e,n,r){var i=!a.isObject(t),o=!!e&&!a.isObject(e);if(!i&&!o)return v(s.flattenClientEventPayload(s.formatClientEventNamespace(t),s.formatClientEventData(e,n,r)))}function v(t){return d.push(t),d}function g(t){return encodeURIComponent(t).length+3}function w(t){return(new Image).src=t}t.exports={canFlushOneItem:function(t){var e=g(s.stringify(t));return f+e<c},_enqueueRawObject:v,scribe:p,clientEvent:h,clientEvent2:function(t,e,n){return h(t,e,n,2)},enqueueClientEvent:m,flushClientEvents:function(){var t;return d.length>1&&m({page:"widgets_js",component:"scribe_pixel",action:"batch_log"},{}),t=d,d=[],t.reduce(function(e,n,r){var i=e.length,o=i&&e[i-1];return r+1==t.length&&n.event_namespace&&"batch_log"==n.event_namespace.action&&(n.message=["entries:"+r,"requests:"+i].join("/")),function t(e){return Array.isArray(e)||(e=[e]),e.reduce(function(e,n){var r,i=s.stringify(n),o=g(i);return f+o<c?e=e.concat(i):(r=s.splitLogEntry(n)).length>1&&(e=e.concat(t(r))),e},[])}(n).forEach(function(t){var n=g(t);(!o||o.urlLength+n>c)&&(o={urlLength:f,items:[]},e.push(o)),o.urlLength+=n,o.items.push(t)}),e},[]).map(function(t){var e={l:t.items};return r.enabled()&&(e.dnt=1),w(o.url(s.CLIENT_EVENT_ENDPOINT,e))})},interaction:function(t,e,n,r){var i=s.extractTermsFromDOM(t.target||t.srcElement);i.action=r||"click",h(i,e,n)}}},function(t,e,n){var r=n(0),i=n(43);t.exports=function(t,e){return i(t,e)?[t]:r.toRealArray(t.querySelectorAll(e))}},function(t,e){t.exports=function(t,e,n){for(var r,i=0;i<t.length;i++)if(r=e.call(n,t[i],i,t))return r}},function(t,e){t.exports=function(){return Math.floor(+new Date/9e5)}},function(t,e,n){var r=n(12);t.exports=function(t){var e,n;return e=t.headers&&t.headers.status,!(n=t&&!t.error&&200===e)&&t.headers&&t.headers.message&&r.publicError(t.headers.message),{success:n,resp:t}}},function(t,e,n){var r,i=n(29),o=0;function s(){r&&r.length===o&&(i.emitter.trigger(i.ALL_WIDGETS_AND_IMAGES_LOADED,r),r=null)}i.emitter.bind(i.ALL_WIDGETS_RENDER_END,function(t){r=t.widgets,s()}),t.exports={reportImagesLoadForAWidget:function(){o++,s()}}},,,,,,,,,function(t,e,n){var r,i=n(2),o=n(4),s=n(96),a=n(49),u=n(9),c=n(97),d=n(24),l=n(100),f=n(154),h=n(162),p=n(163),m=n(29),v=n(35);n(164),m.emitter.trigger(m.START),u.set("widgets.init",!0),a.set("init",!0),p(),r=new i,s.exposeReadyPromise(r.promise,a.base,"_e"),a.set("widgets",f),a.set("widgets.load",l.load),a.set("events",d),h(function(){v.load(),r.resolve(a.base),c.attachTo(o),l.loadPage()})},function(t,e){t.exports=navigator},function(t,e,n){var r=n(7);t.exports={exposeReadyPromise:function(t,e,n){e.ready=r(t.then,t),n&&Array.isArray(e[n])&&(e[n].forEach(r(t.then,t)),delete e[n])}}},function(t,e,n){var r=n(8),i=n(36),o=n(32),s=n(73),a=n(3);function u(t){var e,n,u;t.altKey||t.metaKey||t.shiftKey||(e=o.closest(function(t){return"A"===t.tagName||"AREA"===t.tagName},t.target))&&a.isIntentURL(e.href)&&(n=(n=(n=[u=e.href,"original_referer="+r.href].join(-1==u.indexOf("?")?"?":"&")).replace(/^http[:]/,"https:")).replace(/^\/\//,"https://"),s.open(n,e),i.preventDefault(t))}t.exports={attachTo:function(t){t.addEventListener("click",u,!1)}}},function(t,e,n){var r,i=n(1),o=n(99),s=n(36),a=n(32),u=n(21),c=n(3),d=n(23),l=n(0),f="intent_",h=i.screen.width,p=i.screen.height;function m(t,e){function n(t){return Math.round(t/2)}return t>e?{coordinate:0,size:e}:{coordinate:n(e)-n(t),size:t}}function v(t,e,n){var i,o;e=r.parse(e),n=n||{},i=m(e.width,n.width||h),e.left=i.coordinate,e.width=i.size,o=m(e.height,n.height||p),e.top=o.coordinate,e.height=o.size,this.win=t,this.features=function(t){var e=[];return l.forIn(t,function(t,n){e.push(t+"="+n)}),e.join(",")}(e)}r=(new o).defaults({width:550,height:520,personalbar:"0",toolbar:"0",location:"1",scrollbars:"1",resizable:"1"}),v.prototype.open=function(t,e){var n=e&&"click"==e.type&&a.closest("a",e.target),r=e&&(e.altKey||e.metaKey||e.shiftKey),i=n&&(u.ios()||u.android());if(c.isTwitterURL(t))return r||i?this:(this.name=f+d.generate(),this.popup=this.win.open(t,this.name,this.features),e&&s.preventDefault(e),this)},v.open=function(t,e,n){return new v(i,e).open(t,n)},t.exports=v},function(t,e,n){var r=n(5),i=n(0);function o(){this.assertions=[],this._defaults={}}o.prototype.assert=function(t,e){return this.assertions.push({fn:t,msg:e||"assertion failed"}),this},o.prototype.defaults=function(t){return this._defaults=t||this._defaults,this},o.prototype.require=function(t){var e=this;return(t=Array.isArray(t)?t:i.toRealArray(arguments)).forEach(function(t){e.assert(function(t){return function(e){return r.hasValue(e[t])}}(t),"required: "+t)}),this},o.prototype.parse=function(t){var e,n;if(e=i.aug({},this._defaults,t||{}),(n=this.assertions.reduce(function(t,n){return n.fn(e)||t.push(n.msg),t},[])).length>0)throw new Error(n.join("\n"));return e},t.exports=o},function(t,e,n){var r=n(4),i=n(52),o=n(6),s=n(22),a=n(53),u=n(33),c=n(9),d=n(39),l=n(24),f=n(5),h=n(0),p=n(35),m=n(117),v=n(29);function g(){var t=u.val("widgets:autoload")||!0;return!f.isFalseValue(t)&&(f.isTruthValue(t)?r.body:r.querySelectorAll(t))}function w(t,e){var n,i;return t=(t=t||r.body).length?h.toRealArray(t):[t],d.pause(),i=function(t,e){return t.reduce(function(t,n){return t.concat(m.reduce(function(t,r){return t.concat(r(n,e))},[]))},[])}(t,e),v.emitter.trigger(v.ALL_WIDGETS_RENDER_START,{widgets:i}),n=s.allResolved(i.map(function(t){return a.addWidget(t)})).then(function(t){l.trigger("loaded",{widgets:t}),t&&t.length&&v.emitter.trigger(v.ALL_WIDGETS_RENDER_END,{widgets:t})}),s.always(n,function(){d.resume()}),n}function y(t){return p.getExperiments().then(function(e){return w(t,i.isHorizonTweetEnabled(e))})}t.exports={load:y,loadPage:function(){var t=g();return!1===t?o.resolve():(c.set("widgets.loaded",!0),y(t))},_getPageLoadTarget:g}},function(t,e,n){var r=n(10),i=n(18),o=n(24),s=n(54),a=n(6),u=n(22);function c(t,e){this._widget=null,this._sandbox=null,this._hydrated=!1,this._insertedIntoDom=!1,this._Sandbox=t.Sandbox,this._factory=t.factory,this._widgetParams=t.parameters,this._resolve=e,this._className=t.className,this._renderedClassName=t.className+"-rendered",this._errorClassName=t.className+"-error",this._srcEl=t.srcEl,this._targetGlobal=function(t){return(t.srcEl||t.targetEl).ownerDocument.defaultView}(t),this._sandboxWrapperEl=t.options?t.options.sandboxWrapperEl:null,this._insertionStrategy=function(e){var n,r=t.srcEl,i=t.targetEl,o=t.options?t.options.sandboxWrapperEl:null;o?(o.appendChild(e),n=o):n=e,r?i.insertBefore(n,r):i.appendChild(n)}}c.fromRawTask=function(t){return new c(t.input,t.taskDoneDeferred.resolve)},c.prototype.initialize=function(){var t=this,e=new this._Sandbox(this._targetGlobal);return this._factory(this._widgetParams,e).then(function(n){return t._widget=n,t._sandbox=e,n._sandboxWrapperEl=t._sandboxWrapperEl,n})},c.prototype.insertIntoDom=function(){var t=this;return this._widget?this._sandbox.insert(this._widget.id,{class:[this._className,this._renderedClassName].join(" ")},null,this._insertionStrategy).then(function(){t._insertedIntoDom=!0}):a.reject(new Error("cannot insert widget into DOM before it is initialized"))},c.prototype.hydrate=function(){var t=this;return this._widget?this._widget.hydrate().then(function(){t._hydrated=!0}):a.reject(new Error("cannot hydrate widget before it is initialized"))},c.prototype.render=function(){var t=this;function e(e){return s(t._sandbox.sandboxEl).then(function(){return a.reject(e)})}return this._hydrated?this._insertedIntoDom?t._widget.render(t._sandbox).then(function(){return t._sandbox.onResize(function(){return t._widget.resize().then(function(){o.trigger("resize",{target:t._sandbox.sandboxEl})})}),t._widget.show()}).then(function(){return s(t._srcEl).then(function(){return t._sandbox.sandboxEl})},e):e(new Error("cannot render widget before DOM insertion")):e(new Error("cannot render widget before hydration"))},c.prototype.fail=function(){var t=this;return this._srcEl?u.always(i.write(function(){r.add(t._srcEl,t._errorClassName)}),function(){o.trigger("rendered",{target:t._srcEl}),t._resolve(t._srcEl)}):(t._resolve(),a.resolve())},c.prototype.success=function(){o.trigger("rendered",{target:this._sandbox.sandboxEl}),this._resolve(this._sandbox.sandboxEl)},t.exports=c},function(t,e,n){var r;!function(){"use strict";var i=window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.msRequestAnimationFrame||function(t){return window.setTimeout(t,1e3/60)};function o(){this.frames=[],this.lastId=0,this.raf=i,this.batch={hash:{},read:[],write:[],mode:null}}o.prototype.read=function(t,e){var n=this.add("read",t,e),r=n.id;return this.batch.read.push(n.id),"reading"===this.batch.mode||this.batch.scheduled?r:(this.scheduleBatch(),r)},o.prototype.write=function(t,e){var n=this.add("write",t,e),r=this.batch.mode,i=n.id;return this.batch.write.push(n.id),"writing"===r||"reading"===r||this.batch.scheduled?i:(this.scheduleBatch(),i)},o.prototype.defer=function(t,e,n){"function"==typeof t&&(n=e,e=t,t=1);var r=this,i=t-1;return this.schedule(i,function(){r.run({fn:e,ctx:n})})},o.prototype.clear=function(t){if("function"==typeof t)return this.clearFrame(t);t=Number(t);var e=this.batch.hash[t];if(e){var n=this.batch[e.type],r=n.indexOf(t);delete this.batch.hash[t],~r&&n.splice(r,1)}},o.prototype.clearFrame=function(t){var e=this.frames.indexOf(t);~e&&this.frames.splice(e,1)},o.prototype.scheduleBatch=function(){var t=this;this.schedule(0,function(){t.batch.scheduled=!1,t.runBatch()}),this.batch.scheduled=!0},o.prototype.uniqueId=function(){return++this.lastId},o.prototype.flush=function(t){for(var e;e=t.shift();)this.run(this.batch.hash[e])},o.prototype.runBatch=function(){try{this.batch.mode="reading",this.flush(this.batch.read),this.batch.mode="writing",this.flush(this.batch.write),this.batch.mode=null}catch(t){throw this.runBatch(),t}},o.prototype.add=function(t,e,n){var r=this.uniqueId();return this.batch.hash[r]={id:r,fn:e,ctx:n,type:t}},o.prototype.run=function(t){var e=t.ctx||this,n=t.fn;if(delete this.batch.hash[t.id],!this.onError)return n.call(e);try{n.call(e)}catch(t){this.onError(t)}},o.prototype.loop=function(){var t,e=this,n=this.raf,r=!1;function i(){var t=e.frames.shift();e.frames.length?n(i):e.looping=!1,t&&t()}this.looping||(t=setTimeout(function(){r=!0,i()},500),n(function(){r||(clearTimeout(t),i())}),this.looping=!0)},o.prototype.schedule=function(t,e){return this.frames[t]?this.schedule(t+1,e):(this.loop(),this.frames[t]=e)};var s=new o;void 0!==t&&t.exports?t.exports=s:void 0===(r=function(){return s}.call(e,n,e,t))||(t.exports=r)}()},function(t,e,n){var r=n(48).Promise;t.exports=r._asap},function(t,e,n){var r,i,o,s=n(4),a=n(1),u=n(30),c=n(20),d=n(2),l=n(6),f=n(55),h=n(34),p=n(0),m=n(25),v=n(9).get("scribeCallback"),g=Math.floor(1e3*Math.random())+"_",w="rufous-frame-"+g+"-",y="rufous-form-"+g+"-",b=0,_=!1,E=new d;function x(){var t=o.createElement("form"),e=o.createElement("input"),n=o.createElement("input");return b++,t.action=h.CLIENT_EVENT_ENDPOINT,t.method="POST",t.target=w+b,t.id=y+b,e.type="hidden",e.name="dnt",e.value=c.enabled(),n.type="hidden",n.name="tfw_redirect",n.value=h.RUFOUS_REDIRECT,t.appendChild(e),t.appendChild(n),t}function T(){var t=w+b;return u({id:t,name:t,width:0,height:0,border:0},{display:"none"},o.doc)}t.exports={clientEvent:function(t,e,n,i){(function(t,e){var n=!p.isObject(t),r=!!e&&!p.isObject(e),i=n||r;return i})(t,e)||(v&&v(arguments),E.promise.then(function(){!function(t,e){var n,i,s;p.isObject(t)&&p.isObject(e)&&(f.log(t,e),s=h.flattenClientEventPayload(t,e),(n=r.firstChild).value=+(+n.value||s.dnt||0),(i=o.createElement("input")).type="hidden",i.name="l",i.value=h.stringify(s),r.appendChild(i))}(h.formatClientEventNamespace(t),h.formatClientEventData(e,n,i))}))},flush:function(){return E.promise.then(function(){var t;return r.children.length<=2?l.reject():(t=l.all([o.doc.body.appendChild(r),o.doc.body.appendChild(i)]).then(function(t){var e=t[0],n=t[1];return n.addEventListener("load",function(){!function(t,e){return function(){var n=t.parentNode;n&&(n.removeChild(t),n.removeChild(e))}}(e,n)()}),e.submit(),t}),r=x(),i=T(),t)})},init:function(){return _?E.promise:((o=new m(a)).insert("rufous-sandbox",null,{display:"none"},function(t){s.body.appendChild(t)}).then(function(){o.setTitle("Twitter analytics iframe"),r=x(),i=T(),E.resolve([r,i])}),_=!0,E.promise)}}},function(t,e,n){var r=n(0);t.exports=function t(e){var n=[];return e.forEach(function(e){var i=r.isType("array",e)?t(e):[e];n=n.concat(i)}),n}},function(t,e){t.exports=function(t){return t.filter(function(e,n){return t.indexOf(e)===n})}},function(t,e,n){var r=n(41),i=n(0),o=n(109);function s(){r.apply(this,arguments)}s.prototype=Object.create(r.prototype),i.aug(s.prototype,{factory:o}),t.exports=s},function(t,e,n){var r=n(22),i=n(0),o=n(7);t.exports={before:function(t,e){return function(){var n,i=this,o=arguments;return n=e.apply(this,arguments),r.isPromise(n)?n.then(function(){return t.apply(i,o)}):t.apply(this,arguments)}},after:function(t,e){return function(){var n,i=this,o=arguments;function s(t,e){return r.isPromise(e)?e.then(function(){return t}):t}return n=t.apply(this,arguments),r.isPromise(n)?n.then(function(t){return s(t,e.apply(i,o))}):s(n,e.apply(this,arguments))}},around:function(t,e){return function(){var n=i.toRealArray(arguments);return n.unshift(o(t,this)),e.apply(this,n)}}}},function(t,e,n){var r=n(10),i=n(18),o=n(42),s=n(6),a=n(0);t.exports=function(){var t=o();function e(e){t.apply(this,arguments),Object.defineProperty(this,"targetGlobal",{value:e})}return e.prototype=Object.create(t.prototype),a.aug(e.prototype,{id:null,initialized:!1,width:0,height:0,sandboxEl:null,insert:function(){return s.reject()},onResize:function(){},addClass:function(t){var e=this.sandboxEl;return t=Array.isArray(t)?t:[t],i.write(function(){t.forEach(function(t){r.add(e,t)})})},removeClass:function(t){var e=this.sandboxEl;return t=Array.isArray(t)?t:[t],i.write(function(){t.forEach(function(t){r.remove(e,t)})})},styleSelf:function(t){var e=this;return i.write(function(){a.forIn(t,function(t,n){e.sandboxEl.style[t]=n})})}}),e}},function(t,e,n){var r=n(4),i=n(10),o=n(18),s=n(56),a=n(26),u=n(57),c=n(45),d=n(46),l=n(30),f=n(12),h=n(47),p=n(2),m=n(6),v=n(0),g=n(9),w=n(23),y=n(7),b={allowfullscreen:"true"},_={position:"absolute",visibility:"hidden",display:"block",width:"0px",height:"0px",padding:"0",border:"none"},E={position:"static",visibility:"visible"},x="SandboxRoot",T=".SandboxRoot { display: none; }",A=50;function S(t,e,n,r){return e=v.aug({id:t},b,e),n=v.aug({},_,n),l(e,n,r)}function R(t,e,n,i,s){var a=new p,u=w.generate(),c=S(t,e,n,s);return g.set(["sandbox",u],function(){var t=c.contentWindow.document;o.write(function(){t.write("<!DOCTYPE html><html><head></head><body></body></html>")}).then(function(){t.close(),a.resolve(c)})}),c.src=["javascript:",'document.write("");',"try { window.parent.document; }",'catch (e) { document.domain="'+r.domain+'"; }',"window.parent."+g.fullPath(["sandbox",u])+"();"].join(""),c.addEventListener("error",a.reject,!1),o.write(function(){i.parentNode.replaceChild(c,i)}),a.promise}t.exports=a.couple(n(58),function(t){t.overrideProperty("id",{get:function(){return this.sandboxEl&&this.sandboxEl.id}}),t.overrideProperty("initialized",{get:function(){return!!this.win}}),t.overrideProperty("width",{get:function(){return this._width}}),t.overrideProperty("height",{get:function(){return this._height}}),t.overrideProperty("sandboxEl",{get:function(){return this.iframeEl}}),t.defineProperty("iframeEl",{get:function(){return this._iframe}}),t.defineProperty("rootEl",{get:function(){return this.doc&&this.doc.documentElement}}),t.defineProperty("widgetEl",{get:function(){return this.doc&&this.doc.body.firstElementChild}}),t.defineProperty("win",{get:function(){return this.iframeEl&&this.iframeEl.contentWindow}}),t.defineProperty("doc",{get:function(){return this.win&&this.win.document}}),t.define("_updateCachedDimensions",function(){var t=this;return o.read(function(){var e,n=h(t.sandboxEl);"visible"==t.sandboxEl.style.visibility?t._width=n.width:(e=h(t.sandboxEl.parentElement).width,t._width=Math.min(n.width,e)),t._height=n.height})}),t.define("_setTargetToBlank",function(){var t=this.createElement("base");t.target="_blank",this.doc.head.appendChild(t)}),t.define("_didResize",function(){var t=this,e=this._resizeHandlers.slice(0);return this._updateCachedDimensions().then(function(){e.forEach(function(e){e(t)})})}),t.define("setTitle",function(t){this.iframeEl.title=t}),t.override("createElement",function(t){return this.doc.createElement(t)}),t.override("createFragment",function(){return this.doc.createDocumentFragment()}),t.override("htmlToElement",function(t){var e;return(e=this.createElement("div")).innerHTML=t,e.firstElementChild}),t.override("hasSelectedText",function(){return!!s.getSelectedText(this.win)}),t.override("addRootClass",function(t){var e=this.rootEl;return t=Array.isArray(t)?t:[t],this.initialized?o.write(function(){t.forEach(function(t){i.add(e,t)})}):m.reject(new Error("sandbox not initialized"))}),t.override("removeRootClass",function(t){var e=this.rootEl;return t=Array.isArray(t)?t:[t],this.initialized?o.write(function(){t.forEach(function(t){i.remove(e,t)})}):m.reject(new Error("sandbox not initialized"))}),t.override("hasRootClass",function(t){return i.present(this.rootEl,t)}),t.define("addStyleSheet",function(t,e){var n,r=new p;return this.initialized?((n=this.createElement("link")).type="text/css",n.rel="stylesheet",n.href=t,n.addEventListener("load",r.resolve,!1),n.addEventListener("error",r.reject,!1),o.write(y(e,null,n)).then(function(){return u(t).then(r.resolve,r.reject),r.promise})):m.reject(new Error("sandbox not initialized"))}),t.override("prependStyleSheet",function(t){var e=this.doc;return this.addStyleSheet(t,function(t){var n=e.head.firstElementChild;return n?e.head.insertBefore(t,n):e.head.appendChild(t)})}),t.override("appendStyleSheet",function(t){var e=this.doc;return this.addStyleSheet(t,function(t){return e.head.appendChild(t)})}),t.define("addCss",function(t,e){var n;return c.inlineStyle()?((n=this.createElement("style")).type="text/css",n.appendChild(this.doc.createTextNode(t)),o.write(y(e,null,n))):(f.devError("CSP enabled; cannot embed inline styles"),m.resolve())}),t.override("prependCss",function(t){var e=this.doc;return this.addCss(t,function(t){var n=e.head.firstElementChild;return n?e.head.insertBefore(t,n):e.head.appendChild(t)})}),t.override("appendCss",function(t){var e=this.doc;return this.addCss(t,function(t){return e.head.appendChild(t)})}),t.override("makeVisible",function(){var t=this;return this.styleSelf(E).then(function(){t._updateCachedDimensions()})}),t.override("injectWidgetEl",function(t){var e=this;return this.initialized?this.widgetEl?m.reject(new Error("widget already injected")):o.write(function(){e.doc.body.appendChild(t)}):m.reject(new Error("sandbox not initialized"))}),t.override("matchHeightToContent",function(){var t,e=this;return o.read(function(){t=e.widgetEl?h(e.widgetEl).height:0}),o.write(function(){e.sandboxEl.style.height=t+"px"}).then(function(){return e._updateCachedDimensions()})}),t.override("matchWidthToContent",function(){var t,e=this;return o.read(function(){t=e.widgetEl?h(e.widgetEl).width:0}),o.write(function(){e.sandboxEl.style.width=t+"px"}).then(function(){return e._updateCachedDimensions()})}),t.after("initialize",function(){this._iframe=null,this._width=this._height=0,this._resizeHandlers=[]}),t.override("insert",function(t,e,n,r){var i=this,s=new p,a=this.targetGlobal.document,u=S(t,e,n,a);return o.write(y(r,null,u)),u.addEventListener("load",function(){(function(t){try{t.contentWindow.document}catch(t){return m.reject(t)}return m.resolve(t)})(u).then(null,y(R,null,t,e,n,u,a)).then(s.resolve,s.reject)},!1),u.addEventListener("error",s.reject,!1),s.promise.then(function(t){var e=d(i._didResize,A,i);return i._iframe=t,i.win.addEventListener("resize",e,!1),m.all([i._setTargetToBlank(),i.addRootClass(x),i.prependCss(T)])})}),t.override("onResize",function(t){this._resizeHandlers.push(t)}),t.after("styleSelf",function(){return this._updateCachedDimensions()})})},function(t,e){t.exports=function(){throw new Error("unimplemented method")}},function(t,e,n){var r=n(2),i=n(7),o=100,s=3e3;function a(t,e){this._inputsQueue=[],this._task=t,this._isPaused=!1,this._flushDelay=e&&e.flushDelay||o,this._pauseLength=e&&e.pauseLength||s,this._flushTimeout=void 0}a.prototype.add=function(t){var e=new r;return this._inputsQueue.push({input:t,taskDoneDeferred:e}),this._scheduleFlush(),e.promise},a.prototype._scheduleFlush=function(){this._isPaused||(clearTimeout(this._flushTimeout),this._flushTimeout=setTimeout(i(this._flush,this),this._flushDelay))},a.prototype._flush=function(){try{this._task.call(null,this._inputsQueue)}catch(t){this._inputsQueue.forEach(function(e){e.taskDoneDeferred.reject(t)})}this._inputsQueue=[],this._flushTimeout=void 0},a.prototype.pause=function(t){clearTimeout(this._flushTimeout),this._isPaused=!0,!t&&this._pauseLength&&setTimeout(i(this.resume,this),this._pauseLength)},a.prototype.resume=function(){this._isPaused=!1,this._scheduleFlush()},t.exports=a},function(t,e,n){var r,i=n(72),o=n(30),s=n(2),a=n(4),u=n(19),c=n(21),d=n(31),l=n(8),f=n(12),h=n(114),p=n(59),m=n(9),v=n(11),g=n(115),w=n(3),y=n(0),b=n(1),_=p(function(){return new s});function E(t){var e=t||{should_obtain_cookie_consent:!0,experiments:{}};return new g(e.should_obtain_cookie_consent,e.experiments)}t.exports={load:function(){var t,e,n,s;if(c.ie9()||c.ie10()||"http:"!==l.protocol&&"https:"!==l.protocol)return f.devError("Using default settings due to unsupported browser or protocol."),r=E(),void _().resolve();t={origin:l.origin},u.settings().indexOf("localhost")>-1&&(t.localSettings=!0),e=v.url(i.resourceBaseUrl+i.widgetIframeHtmlPath,t),n=function(t){var n;if(e.substr(0,t.origin.length)===t.origin&&w.isTwitterURL(t.origin))try{(n="string"==typeof t.data?d.parse(t.data):t.data).namespace===h.settings&&(r=E(n.settings),_().resolve())}catch(t){f.devError(t)}},b.addEventListener("message",n),s=o({src:e,title:"Twitter settings iframe"},{display:"none"}),a.body.appendChild(s)},settingsLoaded:function(){var t,e,n;return t=new s,e=m.get("experimentOverride"),_().promise.then(function(){e&&e.name&&e.assignment&&((n={})[e.name]={bucket:e.assignment},r.experiments=y.aug(r.experiments,n)),t.resolve(r)}).catch(function(e){t.reject(e)}),t.promise}}},function(t,e){t.exports={settings:"twttr.settings"}},function(t,e){t.exports=function(t,e){this.shouldObtainCookieConsent=t,this.experiments=e||{}}},function(t,e){t.exports=function(t){return t.split("").map(function(t){return t.charCodeAt(0).toString(16)}).join("")}},function(t,e,n){t.exports=[n(118),n(123),n(131),n(133),n(135),n(149),n(152)]},function(t,e,n){var r=n(11),i=n(5),o=n(0),s=n(13),a=n(14)(),u=n(60),c="a.twitter-dm-button";t.exports=function(t){return a(t,c).map(function(t){return u(function(t){var e=t.getAttribute("data-show-screen-name"),n=s(t),a=t.getAttribute("href"),u=t.getAttribute("data-screen-name"),c=e?i.asBoolean(e):null,d=t.getAttribute("data-size"),l=r.decodeURL(a),f=l.recipient_id,h=t.getAttribute("data-text")||l.text,p=t.getAttribute("data-welcome-message-id")||l.welcomeMessageId;return o.aug(n,{screenName:u,showScreenName:c,size:d,text:h,userId:f,welcomeMessageId:p})}(t),t.parentNode,t)})}},function(t,e,n){var r=n(0);t.exports=function t(e){var n;if(e)return n=e.lang||e.getAttribute("data-lang"),r.isType("string",n)?n:t(e.parentElement)}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return n.e(2).then(function(r){var o;try{o=n(86),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(122),i=n(1),o=n(10),s=n(36),a=n(18),u=n(56),c=n(26),d=n(57),l=n(45),f=n(47),h=n(7),p=n(46),m=n(6),v=n(0),g=50,w={position:"absolute",visibility:"hidden",display:"block",transform:"rotate(0deg)"},y={position:"static",visibility:"visible"},b="twitter-widget",_="open",E="SandboxRoot",x=".SandboxRoot { display: none; max-height: 10000px; }";t.exports=c.couple(n(58),function(t){t.defineStatic("isSupported",function(){return!!i.HTMLElement.prototype.attachShadow&&l.inlineStyle()}),t.overrideProperty("id",{get:function(){return this.sandboxEl&&this.sandboxEl.id}}),t.overrideProperty("initialized",{get:function(){return!!this._shadowHost}}),t.overrideProperty("width",{get:function(){return this._width}}),t.overrideProperty("height",{get:function(){return this._height}}),t.overrideProperty("sandboxEl",{get:function(){return this._shadowHost}}),t.define("_updateCachedDimensions",function(){var t=this;return a.read(function(){var e,n=f(t.sandboxEl);"visible"==t.sandboxEl.style.visibility?t._width=n.width:(e=f(t.sandboxEl.parentElement).width,t._width=Math.min(n.width,e)),t._height=n.height})}),t.define("_didResize",function(){var t=this,e=this._resizeHandlers.slice(0);return this._updateCachedDimensions().then(function(){e.forEach(function(e){e(t)})})}),t.override("createElement",function(t){return this.targetGlobal.document.createElement(t)}),t.override("createFragment",function(){return this.targetGlobal.document.createDocumentFragment()}),t.override("htmlToElement",function(t){var e;return(e=this.createElement("div")).innerHTML=t,e.firstElementChild}),t.override("hasSelectedText",function(){return!!u.getSelectedText(this.targetGlobal)}),t.override("addRootClass",function(t){var e=this._shadowRootBody;return t=Array.isArray(t)?t:[t],this.initialized?a.write(function(){t.forEach(function(t){o.add(e,t)})}):m.reject(new Error("sandbox not initialized"))}),t.override("removeRootClass",function(t){var e=this._shadowRootBody;return t=Array.isArray(t)?t:[t],this.initialized?a.write(function(){t.forEach(function(t){o.remove(e,t)})}):m.reject(new Error("sandbox not initialized"))}),t.override("hasRootClass",function(t){return o.present(this._shadowRootBody,t)}),t.override("addStyleSheet",function(t,e){return this.addCss('@import url("'+t+'");',e).then(function(){return d(t)})}),t.override("prependStyleSheet",function(t){var e=this._shadowRoot;return this.addStyleSheet(t,function(t){var n=e.firstElementChild;return n?e.insertBefore(t,n):e.appendChild(t)})}),t.override("appendStyleSheet",function(t){var e=this._shadowRoot;return this.addStyleSheet(t,function(t){return e.appendChild(t)})}),t.override("addCss",function(t,e){var n;return this.initialized?l.inlineStyle()?((n=this.createElement("style")).type="text/css",n.appendChild(this.targetGlobal.document.createTextNode(t)),a.write(h(e,null,n))):m.resolve():m.reject(new Error("sandbox not initialized"))}),t.override("prependCss",function(t){var e=this._shadowRoot;return this.addCss(t,function(t){var n=e.firstElementChild;return n?e.insertBefore(t,n):e.appendChild(t)})}),t.override("appendCss",function(t){var e=this._shadowRoot;return this.addCss(t,function(t){return e.appendChild(t)})}),t.override("makeVisible",function(){return this.styleSelf(y)}),t.override("injectWidgetEl",function(t){var e=this;return this.initialized?this._shadowRootBody.firstElementChild?m.reject(new Error("widget already injected")):a.write(function(){e._shadowRootBody.appendChild(t)}).then(function(){return e._updateCachedDimensions()}).then(function(){var t=p(e._didResize,g,e);new r(e._shadowRootBody,t)}):m.reject(new Error("sandbox not initialized"))}),t.override("matchHeightToContent",function(){return m.resolve()}),t.override("matchWidthToContent",function(){return m.resolve()}),t.override("insert",function(t,e,n,r){var i=this.targetGlobal.document,o=this._shadowHost=i.createElement(b),u=this._shadowRoot=o.attachShadow({mode:_}),c=this._shadowRootBody=i.createElement("div");return v.forIn(e||{},function(t,e){o.setAttribute(t,e)}),o.id=t,u.appendChild(c),s.delegate(c,"click","A",function(t,e){e.hasAttribute("target")||e.setAttribute("target","_blank")}),m.all([this.styleSelf(w),this.addRootClass(E),this.prependCss(x),a.write(r.bind(null,o))])}),t.override("onResize",function(t){this._resizeHandlers.push(t)}),t.after("initialize",function(){this._shadowHost=this._shadowRoot=this._shadowRootBody=null,this._width=this._height=0,this._resizeHandlers=[]}),t.after("styleSelf",function(){return this._updateCachedDimensions()})})},function(t,e){var n;(n=function(t,e){function r(t,e){if(t.resizedAttached){if(t.resizedAttached)return void t.resizedAttached.add(e)}else t.resizedAttached=new function(){var t,e;this.q=[],this.add=function(t){this.q.push(t)},this.call=function(){for(t=0,e=this.q.length;t<e;t++)this.q[t].call()}},t.resizedAttached.add(e);t.resizeSensor=document.createElement("div"),t.resizeSensor.className="resize-sensor";var n="position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;",r="position: absolute; left: 0; top: 0; transition: 0s;";t.resizeSensor.style.cssText=n,t.resizeSensor.innerHTML='<div class="resize-sensor-expand" style="'+n+'"><div style="'+r+'"></div></div><div class="resize-sensor-shrink" style="'+n+'"><div style="'+r+' width: 200%; height: 200%"></div></div>',t.appendChild(t.resizeSensor),{fixed:1,absolute:1}[function(t,e){return t.currentStyle?t.currentStyle[e]:window.getComputedStyle?window.getComputedStyle(t,null).getPropertyValue(e):t.style[e]}(t,"position")]||(t.style.position="relative");var i,o,s=t.resizeSensor.childNodes[0],a=s.childNodes[0],u=t.resizeSensor.childNodes[1],c=(u.childNodes[0],function(){a.style.width=s.offsetWidth+10+"px",a.style.height=s.offsetHeight+10+"px",s.scrollLeft=s.scrollWidth,s.scrollTop=s.scrollHeight,u.scrollLeft=u.scrollWidth,u.scrollTop=u.scrollHeight,i=t.offsetWidth,o=t.offsetHeight});c();var d=function(t,e,n){t.attachEvent?t.attachEvent("on"+e,n):t.addEventListener(e,n)},l=function(){t.offsetWidth==i&&t.offsetHeight==o||t.resizedAttached&&t.resizedAttached.call(),c()};d(s,"scroll",l),d(u,"scroll",l)}var i=Object.prototype.toString.call(t),o="[object Array]"===i||"[object NodeList]"===i||"[object HTMLCollection]"===i||"undefined"!=typeof jQuery&&t instanceof jQuery||"undefined"!=typeof Elements&&t instanceof Elements;if(o)for(var s=0,a=t.length;s<a;s++)r(t[s],e);else r(t,e);this.detach=function(){if(o)for(var e=0,r=t.length;e<r;e++)n.detach(t[e]);else n.detach(t)}}).detach=function(t){t.resizeSensor&&(t.removeChild(t.resizeSensor),delete t.resizeSensor,delete t.resizedAttached)},void 0!==t&&void 0!==t.exports?t.exports=n:window.ResizeSensor=n},function(t,e,n){var r=n(3),i=n(0),o=n(13),s=n(14)(),a=n(63),u=n(5),c="a.twitter-follow-button";t.exports=function(t){return s(t,c).map(function(t){return a(function(t){var e=o(t),n={screenName:r.screenName(t.href),showScreenName:"false"!==t.getAttribute("data-show-screen-name"),showCount:"false"!==t.getAttribute("data-show-count"),size:t.getAttribute("data-size"),count:t.getAttribute("data-count"),preview:t.getAttribute("data-preview")};return i.forIn(n,function(t,n){var r=e[t];e[t]=u.hasValue(r)?r:n}),e.screenName=e.screenName||e.screen_name,e}(t),t.parentNode,t)})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return n.e(3).then(function(r){var o;try{o=n(87),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(18),i=n(126),o=n(59),s=n(30),a=n(2),u=n(6),c=n(7),d=n(0),l={position:"absolute",visibility:"hidden",width:"0px",height:"0px"},f={position:"static",visibility:"visible"},h={};i(function(t,e,n){var r=h[t];if(r)return e=e||1,n=n||1,r.styleSelf({width:e+"px",height:n+"px"}).then(function(){r.didResize()})}),t.exports=function(t){t.overrideProperty("id",{get:function(){return this.sandboxEl&&this.sandboxEl.id}}),t.overrideProperty("initialized",{get:function(){return!!this.iframeEl}}),t.overrideProperty("width",{get:function(){return this._width}}),t.overrideProperty("height",{get:function(){return this._height}}),t.overrideProperty("sandboxEl",{get:function(){return this.iframeEl}}),t.defineProperty("iframeEl",{get:function(){return this._iframe}}),t.define("updateCachedDimensions",function(){var t=this;return this.initialized?r.read(function(){t._width=t.sandboxEl.offsetWidth,t._height=t.sandboxEl.offsetHeight}):u.resolve()}),t.define("setTitle",function(t){this.iframeEl.title=t}),t.define("makeVisible",function(){return this.styleSelf(f)}),t.define("didResize",function(){var t=this,e=t._resizeHandlers.length>0;return this.updateCachedDimensions().then(function(){e&&t._resizeHandlers.forEach(function(e){e(t)})})}),t.define("loadDocument",function(t){var e=new a;return this.initialized?this.iframeEl.src?u.reject(new Error("widget already loaded")):(this.iframeEl.addEventListener("load",e.resolve,!1),this.iframeEl.addEventListener("error",e.reject,!1),this.iframeEl.src=t,e.promise):u.reject(new Error("sandbox not initialized"))}),t.after("initialize",function(){this._iframe=null,this._width=this._height=0,this._resizeHandlers=[]}),t.override("insert",function(t,e,n,i){var a=this;return e=d.aug({id:t},e),n=d.aug({},l,n),this._iframe=s(e,n),h[t]=this,this.onResize(o(function(){a.makeVisible()})),r.write(c(i,null,this._iframe))}),t.override("onResize",function(t){this._resizeHandlers.push(t)}),t.after("styleSelf",function(){return this.updateCachedDimensions()})}},function(t,e,n){var r=n(1),i=n(127),o=n(129),s=n(24),a=n(5),u=n(130);t.exports=function(t){function e(t,e){var n=u(this);s.trigger(t,{target:n,region:e,type:t,data:{}})}function n(e){var n=u(this),r=n&&n.id,i=a.asInt(e.width),o=a.asInt(e.height);r&&void 0!==i&&void 0!==o&&t(r,i,o)}(new i).attachReceiver(new o.Receiver(r,"twttr.button")).bind("twttr.private.trigger",e).bind("twttr.private.resizeButton",n),(new i).attachReceiver(new o.Receiver(r,"twttr.embed")).bind("twttr.private.trigger",e).bind("twttr.private.rendered",n).bind("twttr.private.resize",n)}},function(t,e,n){var r=n(31),i=n(128),o=n(0),s=n(6),a=n(22),u="2.0";function c(t){this.registry=t||{}}function d(t){var e,n;return e=o.isType("string",t),n=o.isType("number",t),e||n||null===t}function l(t,e){return{jsonrpc:u,id:d(t)?t:null,error:e}}c.prototype._invoke=function(t,e){var n,r,i;n=this.registry[t.method],r=t.params||[],r=o.isType("array",r)?r:[r];try{i=n.apply(e.source||null,r)}catch(t){i=s.reject(t.message)}return a.isPromise(i)?i:s.resolve(i)},c.prototype._processRequest=function(t,e){var n,r;return function(t){var e,n,r;return!!o.isObject(t)&&(e=t.jsonrpc===u,n=o.isType("string",t.method),r=!("id"in t)||d(t.id),e&&n&&r)}(t)?(n="params"in t&&(r=t.params,!o.isObject(r)||o.isType("function",r))?s.resolve(l(t.id,i.INVALID_PARAMS)):this.registry[t.method]?this._invoke(t,{source:e}).then(function(e){return n=t.id,{jsonrpc:u,id:n,result:e};var n},function(){return l(t.id,i.INTERNAL_ERROR)}):s.resolve(l(t.id,i.METHOD_NOT_FOUND)),null!=t.id?n:s.resolve()):s.resolve(l(t.id,i.INVALID_REQUEST))},c.prototype.attachReceiver=function(t){return t.attachTo(this),this},c.prototype.bind=function(t,e){return this.registry[t]=e,this},c.prototype.receive=function(t,e){var n,a,u,c=this;try{u=t,t=o.isType("string",u)?r.parse(u):u}catch(t){return s.resolve(l(null,i.PARSE_ERROR))}return e=e||null,a=((n=o.isType("array",t))?t:[t]).map(function(t){return c._processRequest(t,e)}),n?function(t){return s.all(t).then(function(t){return(t=t.filter(function(t){return void 0!==t})).length?t:void 0})}(a):a[0]},t.exports=c},function(t){t.exports={PARSE_ERROR:{code:-32700,message:"Parse error"},INVALID_REQUEST:{code:-32600,message:"Invalid Request"},INVALID_PARAMS:{code:-32602,message:"Invalid params"},METHOD_NOT_FOUND:{code:-32601,message:"Method not found"},INTERNAL_ERROR:{code:-32603,message:"Internal error"}}},function(t,e,n){var r=n(8),i=n(1),o=n(31),s=n(2),a=n(21),u=n(0),c=n(3),d=n(7),l=a.ie9();function f(t,e,n){var r;t&&t.postMessage&&(l?r=(n||"")+o.stringify(e):n?(r={})[n]=e:r=e,t.postMessage(r,"*"))}function h(t){return u.isType("string",t)?t:"JSONRPC"}function p(t,e){return e?u.isType("string",t)&&0===t.indexOf(e)?t.substring(e.length):t&&t[e]?t[e]:void 0:t}function m(t,e){var n=t.document;this.filter=h(e),this.server=null,this.isTwitterFrame=c.isTwitterURL(n.location.href),t.addEventListener("message",d(this._onMessage,this),!1)}function v(t,e){this.pending={},this.target=t,this.isTwitterHost=c.isTwitterURL(r.href),this.filter=h(e),i.addEventListener("message",d(this._onMessage,this),!1)}u.aug(m.prototype,{_onMessage:function(t){var e,n=this;this.server&&(this.isTwitterFrame&&!c.isTwitterURL(t.origin)||(e=p(t.data,this.filter))&&this.server.receive(e,t.source).then(function(e){e&&f(t.source,e,n.filter)}))},attachTo:function(t){this.server=t},detach:function(){this.server=null}}),u.aug(v.prototype,{_processResponse:function(t){var e=this.pending[t.id];e&&(e.resolve(t),delete this.pending[t.id])},_onMessage:function(t){var e;if((!this.isTwitterHost||c.isTwitterURL(t.origin))&&(e=p(t.data,this.filter))){if(u.isType("string",e))try{e=o.parse(e)}catch(t){return}(e=u.isType("array",e)?e:[e]).forEach(d(this._processResponse,this))}},send:function(t){var e=new s;return t.id?this.pending[t.id]=e:e.resolve(),f(this.target,t,this.filter),e.promise}}),t.exports={Receiver:m,Dispatcher:v,_stringifyPayload:function(t){return arguments.length>0&&(l=!!t),l}}},function(t,e,n){var r=n(4);t.exports=function(t){for(var e,n=r.getElementsByTagName("iframe"),i=0;n[i];i++)if((e=n[i]).contentWindow===t)return e}},function(t,e,n){var r=n(5),i=n(0),o=n(3),s=n(13),a=n(14)(),u=n(64),c="a.twitter-moment";t.exports=function(t){return a(t,c).map(function(t){return u(function(t){var e=s(t),n={momentId:o.momentId(t.href),chrome:t.getAttribute("data-chrome"),limit:t.getAttribute("data-limit")};return i.forIn(n,function(t,n){var i=e[t];e[t]=r.hasValue(i)?i:n}),e}(t),t.parentNode,t)})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return Promise.all([n.e(0),n.e(4)]).then(function(r){var o;try{o=n(88),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(0),i=n(13),o=n(14)(),s=n(65),a="a.periscope-on-air",u=/^https?:\/\/(?:www\.)?(?:periscope|pscp)\.tv\/@?([a-zA-Z0-9_]+)\/?$/i;t.exports=function(t){return o(t,a).map(function(t){return s(function(t){var e=i(t),n=t.getAttribute("href"),o=t.getAttribute("data-size"),s=u.exec(n)[1];return r.aug(e,{username:s,size:o})}(t),t.parentNode,t)})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return n.e(5).then(function(r){var o;try{o=n(89),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(5),i=n(0),o=n(66),s=n(13),a=n(14)(),u=n(67),c=n(3),d=n(12),l="a.twitter-timeline,div.twitter-timeline,a.twitter-grid",f="Embedded Search timelines have been deprecated. See https://twittercommunity.com/t/deprecating-widget-settings/102295.",h="You may have been affected by an update to settings in embedded timelines. See https://twittercommunity.com/t/deprecating-widget-settings/102295.",p="Embedded grids have been deprecated and will now render as timelines. Please update your embed code to use the twitter-timeline class. More info: https://twittercommunity.com/t/update-on-the-embedded-grid-display-type/119564.";t.exports=function(t){return a(t,l).map(function(t){return u(function(t){var e=s(t),n=t.getAttribute("data-show-replies"),a={isPreconfigured:!!t.getAttribute("data-widget-id"),chrome:t.getAttribute("data-chrome"),tweetLimit:t.getAttribute("data-tweet-limit")||t.getAttribute("data-limit"),ariaLive:t.getAttribute("data-aria-polite"),theme:t.getAttribute("data-theme"),linkColor:t.getAttribute("data-link-color"),borderColor:t.getAttribute("data-border-color"),showReplies:n?r.asBoolean(n):null,profileScreenName:t.getAttribute("data-screen-name"),profileUserId:t.getAttribute("data-user-id"),favoritesScreenName:t.getAttribute("data-favorites-screen-name"),favoritesUserId:t.getAttribute("data-favorites-user-id"),likesScreenName:t.getAttribute("data-likes-screen-name"),likesUserId:t.getAttribute("data-likes-user-id"),listOwnerScreenName:t.getAttribute("data-list-owner-screen-name"),listOwnerUserId:t.getAttribute("data-list-owner-id"),listId:t.getAttribute("data-list-id"),listSlug:t.getAttribute("data-list-slug"),customTimelineId:t.getAttribute("data-custom-timeline-id"),staticContent:t.getAttribute("data-static-content"),url:t.href};return a.isPreconfigured&&(c.isSearchUrl(a.url)?d.publicError(f,t):d.publicLog(h,t)),"twitter-grid"===t.className&&d.publicLog(p,t),(a=i.aug(a,e)).dataSource=o(a),a.id=a.dataSource&&a.dataSource.id,a}(t),t.parentNode,t)})}},function(t,e,n){var r=n(27);t.exports=r.build([n(28),n(139)])},function(t,e,n){var r=n(0),i=n(138);t.exports=function(t){return"en"===t||r.contains(i,t)}},function(t,e){t.exports=["hi","zh-cn","fr","zh-tw","msa","fil","fi","sv","pl","ja","ko","de","it","pt","es","ru","id","tr","da","no","nl","hu","fa","ar","ur","he","th","cs","uk","vi","ro","bn","el","en-gb","gu","kn","mr","ta","bg","ca","hr","sr","sk"]},function(t,e,n){var r=n(3),i=n(0),o=n(19),s="collection:";function a(t,e){return r.collectionId(t)||e}t.exports=function(t){t.params({id:{},url:{}}),t.overrideProperty("id",{get:function(){var t=a(this.params.url,this.params.id);return s+t}}),t.overrideProperty("endpoint",{get:function(){return o.timeline(["collection"])}}),t.around("queryParams",function(t){return i.aug(t(),{collection_id:a(this.params.url,this.params.id)})}),t.before("initialize",function(){if(!a(this.params.url,this.params.id))throw new Error("one of url or id is required")})}},function(t,e,n){var r=n(27);t.exports=r.build([n(28),n(141)])},function(t,e,n){var r=n(3),i=n(0),o=n(19),s="event:";function a(t,e){return r.eventId(t)||e}t.exports=function(t){t.params({id:{},url:{}}),t.overrideProperty("id",{get:function(){var t=a(this.params.url,this.params.id);return s+t}}),t.overrideProperty("endpoint",{get:function(){return o.timeline(["event"])}}),t.around("queryParams",function(t){return i.aug(t(),{event_id:a(this.params.url,this.params.id)})}),t.before("initialize",function(){if(!a(this.params.url,this.params.id))throw new Error("one of url or id is required")})}},function(t,e,n){var r=n(27);t.exports=r.build([n(28),n(143)])},function(t,e,n){var r=n(3),i=n(0),o=n(19),s="likes:";function a(t){return r.likesScreenName(t.url)||t.screenName}t.exports=function(t){t.params({screenName:{},userId:{},url:{}}),t.overrideProperty("id",{get:function(){var t=a(this.params)||this.params.userId;return s+t}}),t.overrideProperty("endpoint",{get:function(){return o.timeline(["likes"])}}),t.define("_getLikesQueryParam",function(){var t=a(this.params);return t?{screen_name:t}:{user_id:this.params.userId}}),t.around("queryParams",function(t){return i.aug(t(),this._getLikesQueryParam())}),t.before("initialize",function(){if(!a(this.params)&&!this.params.userId)throw new Error("screen name or user id is required")})}},function(t,e,n){var r=n(27);t.exports=r.build([n(28),n(145)])},function(t,e,n){var r=n(3),i=n(0),o=n(19),s="list:";function a(t){var e=r.listScreenNameAndSlug(t.url)||t;return i.compact({screen_name:e.ownerScreenName,user_id:e.ownerUserId,list_slug:e.slug})}t.exports=function(t){t.params({id:{},ownerScreenName:{},ownerUserId:{},slug:{},url:{}}),t.overrideProperty("id",{get:function(){var t,e,n;return this.params.id?s+this.params.id:(e=(t=a(this.params))&&t.list_slug.replace(/-/g,"_"),n=t&&(t.screen_name||t.user_id),s+(n+":")+e)}}),t.overrideProperty("endpoint",{get:function(){return o.timeline(["list"])}}),t.define("_getListQueryParam",function(){return this.params.id?{list_id:this.params.id}:a(this.params)}),t.around("queryParams",function(t){return i.aug(t(),this._getListQueryParam())}),t.before("initialize",function(){var t=a(this.params);if(i.isEmptyObject(t)&&!this.params.id)throw new Error("qualified slug or list id required")})}},function(t,e,n){var r=n(27);t.exports=r.build([n(28),n(147)])},function(t,e,n){var r=n(3),i=n(5),o=n(0),s=n(19),a="profile:";function u(t,e){return r.screenName(t)||e}t.exports=function(t){t.params({showReplies:{fallback:!1,transform:i.asBoolean},screenName:{},userId:{},url:{}}),t.overrideProperty("id",{get:function(){var t=u(this.params.url,this.params.screenName);return a+(t||this.params.userId)}}),t.overrideProperty("endpoint",{get:function(){return s.timeline(["profile"])}}),t.define("_getProfileQueryParam",function(){var t=u(this.params.url,this.params.screenName),e=t?{screen_name:t}:{user_id:this.params.userId};return o.aug(e,{with_replies:this.params.showReplies?"true":"false"})}),t.around("queryParams",function(t){return o.aug(t(),this._getProfileQueryParam())}),t.before("initialize",function(){if(!u(this.params.url,this.params.screenName)&&!this.params.userId)throw new Error("screen name or user id is required")})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return Promise.all([n.e(0),n.e(6)]).then(function(r){var o;try{o=n(90),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(10),i=n(3),o=n(0),s=n(13),a=n(14)(),u=n(68),c="blockquote.twitter-tweet, blockquote.twitter-video",d=/\btw-align-(left|right|center)\b/;t.exports=function(t,e){return a(t,c).map(function(t){return u(function(t){var e=s(t),n=t.getElementsByTagName("A"),a=n&&n[n.length-1],u=a&&i.status(a.href),c=t.getAttribute("data-conversation"),l="none"==c||"hidden"==c||r.present(t,"tw-hide-thread"),f=t.getAttribute("data-cards"),h="none"==f||"hidden"==f||r.present(t,"tw-hide-media"),p=t.getAttribute("data-align")||t.getAttribute("align"),m=t.getAttribute("data-link-color"),v=t.getAttribute("data-theme");return!p&&d.test(t.className)&&(p=RegExp.$1),o.aug(e,{tweetId:u,hideThread:l,hideCard:h,align:p,linkColor:m,theme:v,id:u})}(t),t.parentNode,t,e)})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return Promise.all([n.e(0),n.e(7)]).then(function(r){var o;try{o=n(91),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return Promise.all([n.e(0),n.e(7)]).then(function(r){var o;try{o=n(92),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(10),i=n(0),o=n(13),s=n(14)(),a=n(69),u=n(5),c="a.twitter-share-button, a.twitter-mention-button, a.twitter-hashtag-button",d="twitter-hashtag-button",l="twitter-mention-button";t.exports=function(t){return s(t,c).map(function(t){return a(function(t){var e=o(t),n={screenName:t.getAttribute("data-button-screen-name"),text:t.getAttribute("data-text"),type:t.getAttribute("data-type"),size:t.getAttribute("data-size"),url:t.getAttribute("data-url"),hashtags:t.getAttribute("data-hashtags"),via:t.getAttribute("data-via"),buttonHashtag:t.getAttribute("data-button-hashtag")};return i.forIn(n,function(t,n){var r=e[t];e[t]=u.hasValue(r)?r:n}),e.screenName=e.screenName||e.screen_name,e.buttonHashtag=e.buttonHashtag||e.button_hashtag||e.hashtag,r.present(t,d)&&(e.type="hashtag"),r.present(t,l)&&(e.type="mention"),e}(t),t.parentNode,t)})}},function(t,e,n){var r=n(2);t.exports=function(t,e){var i=new r;return n.e(3).then(function(r){var o;try{o=n(93),i.resolve(new o(t,e))}catch(t){i.reject(t)}}.bind(null,n)).catch(function(t){i.reject(t)}),i.promise}},function(t,e,n){var r=n(0);t.exports=r.aug({},n(155),n(156),n(157),n(158),n(159),n(160),n(161))},function(t,e,n){var r=n(60),i=n(17)(["userId"],{},r);t.exports={createDMButton:i}},function(t,e,n){var r=n(63),i=n(17)(["screenName"],{},r);t.exports={createFollowButton:i}},function(t,e,n){var r=n(64),i=n(17)(["momentId"],{},r);t.exports={createMoment:i}},function(t,e,n){var r=n(65),i=n(17)(["username"],{},r);t.exports={createPeriscopeOnAirButton:i}},function(t,e,n){var r=n(8),i=n(12),o=n(3),s=n(0),a=n(5),u=n(66),c=n(67),d=n(17)([],{},c),l=n(6),f="Embedded grids have been deprecated. Please use twttr.widgets.createTimeline instead. More info: https://twittercommunity.com/t/update-on-the-embedded-grid-display-type/119564.",h={createTimeline:p,createGridFromCollection:function(t){var e=s.toRealArray(arguments).slice(1),n={sourceType:"collection",id:t};return e.unshift(n),i.publicLog(f),p.apply(this,e)}};function p(t){var e,n=s.toRealArray(arguments).slice(1);return a.isString(t)||a.isNumber(t)?l.reject("Embedded timelines with widget settings have been deprecated. See https://twittercommunity.com/t/deprecating-widget-settings/102295."):s.isObject(t)?(t=t||{},n.forEach(function(t){s.isType("object",t)&&function(t){t.ariaLive=t.ariaPolite}(e=t)}),e||(e={},n.push(e)),t.lang=e.lang,t.tweetLimit=e.tweetLimit,t.showReplies=e.showReplies,e.dataSource=u(t),d.apply(this,n)):l.reject("data source must be an object.")}o.isTwitterURL(r.href)&&(h.createTimelinePreview=function(t,e,n){var r={previewParams:t,useLegacyDefaults:!0,isPreviewTimeline:!0};return r.dataSource=u(r),d(e,r,n)}),t.exports=h},function(t,e,n){var r,i=n(0),o=n(68),s=n(17),a=(r=s(["tweetId"],{},o),function(){return i.toRealArray(arguments).slice(1).forEach(function(t){i.isType("object",t)&&(t.hideCard="none"==t.cards||"hidden"==t.cards,t.hideThread="none"==t.conversation||"hidden"==t.conversation)}),r.apply(this,arguments)});t.exports={createTweet:a,createTweetEmbed:a,createVideo:a}},function(t,e,n){var r=n(0),i=n(69),o=n(17),s=o(["url"],{type:"share"},i),a=o(["buttonHashtag"],{type:"hashtag"},i),u=o(["screenName"],{type:"mention"},i);function c(t){return function(){return r.toRealArray(arguments).slice(1).forEach(function(t){r.isType("object",t)&&(t.screenName=t.screenName||t.screen_name,t.buttonHashtag=t.buttonHashtag||t.button_hashtag||t.hashtag)}),t.apply(this,arguments)}}t.exports={createShareButton:c(s),createHashtagButton:c(a),createMentionButton:c(u)}},function(t,e,n){var r,i,o,s=n(4),a=n(1),u=0,c=[],d=s.createElement("a");function l(){var t,e;for(u=1,t=0,e=c.length;t<e;t++)c[t]()}/^loade|c/.test(s.readyState)&&(u=1),s.addEventListener&&s.addEventListener("DOMContentLoaded",i=function(){s.removeEventListener("DOMContentLoaded",i,!1),l()},!1),d.doScroll&&s.attachEvent("onreadystatechange",r=function(){/^c/.test(s.readyState)&&(s.detachEvent("onreadystatechange",r),l())}),o=d.doScroll?function(t){a.self!=a.top?u?t():c.push(t):function(){try{d.doScroll("left")}catch(e){return setTimeout(function(){o(t)},50)}t()}()}:function(t){u?t():c.push(t)},t.exports=o},function(t,e,n){var r=n(44),i=n(9);t.exports=function(){i.set("buildVersion",r.version)}},function(t,e,n){n(165),n(85),n(168)},function(t,e,n){var r=n(166),i=n(29),o=n(70),s=new r,a=function(t){t.widgets&&1===t.widgets.length&&(s.start(),i.emitter.unbind(i.ALL_WIDGETS_RENDER_START,a))},u=function(t){var e;t.widgets&&1===t.widgets.length&&(e=t.widgets[0],s.end(),e.dataset&&e.dataset.tweetId&&o({duration:s.duration(),namespace:{element:"tweet",action:"render"},widgetIds:[e.dataset.tweetId]})),i.emitter.unbind(i.ALL_WIDGETS_RENDER_END,u)};i.emitter.bind(i.ALL_WIDGETS_RENDER_START,a),i.emitter.bind(i.ALL_WIDGETS_RENDER_END,u)},function(t,e,n){var r=n(167);function i(){}i.prototype.start=function(){this._startTime=r()},i.prototype.end=function(){this._duration=r()-this._startTime},i.prototype.duration=function(){return this._duration},t.exports=i},function(t,e,n){var r=n(1);t.exports=function(){return r.performance&&r.performance.now?r.performance.now():Date.now()}},function(t,e,n){var r=n(29),i=n(70),o=n(169),s=n(3),a=n(1),u=n(0),c=n(21),d=n(62);function l(t){return t.performance.getEntriesByType("resource").filter(function(t){return s.isTwimgURL(t.name)||s.isTwitterURL(t.name)}).reduce(function(t,e){return t[e.name]=e.duration,t},{})}r.emitter.bind(r.ALL_WIDGETS_AND_IMAGES_LOADED,function(t){var e,n,r=[];c.hasPerformanceInformation()&&(e=l(a),d.isSupported()||(r=function(t){return t.reduce(function(t,e){return u.aug(t,l(e.contentDocument.defaultView))},{})}(t)),n=u.aug({},e,r),Object.keys(o).forEach(function(t){!function(t,e,n){var r=Object.keys(t).reduce(function(e,r){return n(r)?e+t[r]:e},0);i({duration:r,namespace:{element:e,action:"resource"}})}(n,t,o[t])}))})},function(t,e,n){var r=n(3),i={all:function(){return!0},image:function(t){return r.isTwimgURL(t)},settings:function(t){return r.isSettingsURL(t)},widget_iframe:function(t){return r.isWidgetIframeURL(t)}};t.exports=i}])));
18
- }
19
- });
20
-
21
-
22
- //Runs every time new tweets are loaded
23
- function ctfScripts( $ctf ){
24
-
25
- //Loop through each newly loaded tweet
26
- $ctf.find('.ctf-item.ctf-new').each(function(){
27
-
28
- var $ctfItem = $(this),
29
- $ctfTextMedia = $ctfItem.find('.ctf-tweet-text-media-wrap'),
30
- $ctfText = $ctfItem.find('.ctf-tweet-text').remove('.ctf-tweet-text-media-wrap'),
31
- ctfTextStr = ' ' + $ctfText.html();
32
-
33
- if( $ctf.attr('data-ctfdisablelinks') != 'true' && typeof ctfTextStr !== 'undefined' && ! $ctf.find('.ctf-tweet-text-link').length ){
34
-
35
- var ctfLinkColor = $ctf.attr('data-ctflinktextcolor'),
36
- ctfLinkColorHex = '';
37
- if( ctfLinkColor ) ctfLinkColorHex = ctfLinkColor.replace(';','').split("#")[1];
38
-
39
- //Link URLs
40
- window.ctfLinkify=(function(){var k="[a-z\\d.-]+://",h="(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",c="(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",n="(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",f="(?:"+c+n+"|"+h+")",o="(?:[;/][^#?<>\\s]*)?",e="(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?",d="\\b"+k+"[^<>\\s]+",a="\\b"+f+o+e+"(?!\\w)",m="mailto:",j="(?:"+m+")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@"+f+e+"(?!\\w)",l=new RegExp("(?:"+d+"|"+a+"|"+j+")","ig"),g=new RegExp("^"+k,"i"),b={"'":"`",">":"<",")":"(","]":"[","}":"{","B;":"B+","b:":"b9"},i={callback:function(q,p){return p?'<a href="'+p+'" title="'+p+'" target="_blank">'+q+"</a>":q},punct_regexp:/(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/};return function(u,z){z=z||{};var w,v,A,p,x="",t=[],s,E,C,y,q,D,B,r;for(v in i){if(z[v]===undefined){z[v]=i[v]}}while(w=l.exec(u)){A=w[0];E=l.lastIndex;C=E-A.length;if(/[\/:]/.test(u.charAt(C-1))){continue}do{y=A;r=A.substr(-1);B=b[r];if(B){q=A.match(new RegExp("\\"+B+"(?!$)","g"));D=A.match(new RegExp("\\"+r,"g"));if((q?q.length:0)<(D?D.length:0)){A=A.substr(0,A.length-1);E--}}if(z.punct_regexp){A=A.replace(z.punct_regexp,function(F){E-=F.length;return""})}}while(A.length&&A!==y);p=A;if(!g.test(p)){p=(p.indexOf("@")!==-1?(!p.indexOf(m)?"":m):!p.indexOf("irc.")?"irc://":!p.indexOf("ftp.")?"ftp://":"http://")+p}if(s!=C){t.push([u.slice(s,C)]);s=E}t.push([A,p])}t.push([u.substr(s)]);for(v=0;v<t.length;v++){x+=z.callback.apply(window,t[v])}return x||u}})();
41
- ctfTextStr = ctfLinkify( ctfTextStr );
42
-
43
- //Link hashtags
44
- var ctfHashRegex = /(^|\s)#(\w*[\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+\w*)/gi;
45
-
46
- function ctfHashReplacer(hash){
47
- //Remove white space at beginning of hash
48
- var replacementString = jQuery.trim(hash);
49
- //If the hash is a hex code then don't replace it with a link as it's likely in the style attr, eg: "color: #ff0000"
50
- if ( /^#[0-9A-F]{6}$/i.test( replacementString ) ){
51
- return replacementString;
52
- } else {
53
- return ' <a href="https://twitter.com/hashtag/'+ replacementString.substring(1) +'" target="_blank" rel="nofollow">' + replacementString + '</a>';
54
- }
55
- }
56
-
57
- //Replace hashtags in text
58
- if(ctfTextStr.length > 0){
59
- //Add a space after all <br> tags so that #hashtags immediately after them are also converted to hashtag links. Without the space they aren't captured by the regex.
60
- ctfTextStr = ctfTextStr.replace(/<br>/g, "<br> ");
61
- ctfTextStr = ctfTextStr.replace( ctfHashRegex , ctfHashReplacer );
62
- }
63
-
64
- //Link @tags
65
- function ctfReplaceTags(tag){
66
- var replacementString = jQuery.trim(tag);
67
- return ' <a href="https://twitter.com/'+ replacementString.substring(1) +'" target="_blank" rel="nofollow">' + replacementString + '</a>';
68
- }
69
-
70
- var tagRegex = /[\s][@]+[A-Za-z0-9-_]+/g;
71
- ctfTextStr = ctfTextStr.replace( tagRegex , ctfReplaceTags );
72
-
73
-
74
- //Replace text with linked version
75
- $ctfText.html( ctfTextStr.trim() );
76
- $ctfText.append($ctfTextMedia);
77
-
78
- //Add link color
79
- $ctfText.find('a').css('color', '#' + ctfLinkColorHex);
80
- $ctfTextMedia.css('color', '#' + ctfLinkColorHex);
81
-
82
- } // End "ctfdata-disablelinks" check
83
-
84
- // shorten long urls in tweets
85
- $ctfItem.find('.ctf-tweet-text a').each(function() {
86
- if (jQuery(this).text().indexOf('http') > -1 && jQuery(this).text().length > 63) {
87
- jQuery(this).text(jQuery(this).text().substring(0,60)+'...');
88
- }
89
- });
90
-
91
- }); // End .ctfItem loop
92
-
93
- //Change color of retweet icon to match text
94
- // $ctf.find('.ctf-retweet-icon').css({'background' : $ctf.find('.ctf-tweet-text a').css('color')}); //This doesn't work well if the link color is set to white as the default color of the icon text is also white
95
-
96
- //Change colors of some items to match tweet text
97
- $ctf.find('.ctf-author-name, .ctf-tweet-date, .ctf-author-screenname, .ctf-twitterlink, .ctf-author-box-link, .ctf-retweet-text, .ctf-quoted-tweet').css('color', $ctf.find('.ctf-tweet-text').css('color') );
98
-
99
- //Set the line height of the twitter link to match the icons so that it's centered vertically
100
- var $ctfIconFirst = $ctf.find('.ctf-tweet-actions a').first();
101
- $ctf.find('.ctf-twitterlink').css('line-height', $ctfIconFirst.height() + 'px' );
102
-
103
- //Adjust icon number font size to be slightly smaller than the icon size
104
- if( $ctfIconFirst.length ){
105
- var ctfIconSize = parseInt( $ctfIconFirst.css('font-size').replace('px', '') );
106
- $ctf.find('.ctf-action-count').css({'display' : 'block', 'font-size' : (ctfIconSize-4) + 'px', 'line-height' : $ctfIconFirst.height() + 'px' });
107
- }
108
-
109
- //Header profile pic hover
110
- $ctf.find('.ctf-header .ctf-header-link').hover(function(){
111
- $ctf.find('.ctf-header .ctf-header-img-hover').fadeIn(200);
112
- }, function(){
113
- $ctf.find('.ctf-header .ctf-header-img-hover').stop().fadeOut(600);
114
- });
115
-
116
- $ctf.find('.ctf_more').unbind('click').bind('click', function(e){
117
- e.preventDefault();
118
- $(this).hide().next('.ctf_remaining').show();
119
- });
120
-
121
- // Call Custom JS if it exists
122
- if (typeof ctf_custom_js == 'function') ctf_custom_js($);
123
-
124
- $ctf.find('.ctf-author-box-link p:empty').remove();
125
- } // end ctfScripts()
126
-
127
- function ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded, persistentIndex ) {
128
-
129
- //Display loader
130
- $ctfMore.addClass('ctf-loading').append('<div class="ctf-loader"></div>');
131
- $ctfMore.find('.ctf-loader').css('background-color', $ctfMore.css('color'));
132
-
133
- jQuery.ajax({
134
- url : ctf.ajax_url,
135
- type : 'post',
136
- data : {
137
- action : 'ctf_get_more_posts',
138
- last_id_data : lastIDData,
139
- shortcode_data : shortcodeData,
140
- num_needed : numNeeded,
141
- persistent_index: persistentIndex
142
- },
143
- success : function(data) {
144
- if(lastIDData !== '') {
145
- // appends the html echoed out in ctf_get_new_posts() to the last post element
146
- if(data.indexOf('<meta charset') == -1) {
147
- $ctf.find('.ctf-item').removeClass('ctf-new').last().after(data);
148
- }
149
-
150
- if($ctf.find('.ctf-out-of-tweets').length) {
151
- $ctfMore.hide();
152
- //Fade in the no more tweets message
153
- $ctf.find('.ctf-out-of-tweets p').eq(0).fadeIn().end().eq(1).delay(500).fadeIn();
154
- }
155
- } else {
156
- $ctf.find('.ctf-tweets').append(data);
157
- }
158
-
159
-
160
- //Remove loader
161
- $ctfMore.removeClass('ctf-loading').find('.ctf-loader').remove();
162
-
163
- //Re-run JS code
164
- ctfScripts( $ctf );
165
-
166
- }
167
- }); // ajax call
168
- }
169
-
170
- $('.ctf').each(function(){
171
-
172
- var $ctf = $(this),
173
- numNeeded = parseInt($ctf.attr('data-ctfneeded'));
174
-
175
- //Adds a class if the feed is in a narrow column or on mobile so we can make styling adjustments
176
- if( $ctf.width() <= 480 ) $ctf.addClass('ctf-narrow');
177
- if( $ctf.width() <= 320 ) $ctf.addClass('ctf-super-narrow');
178
-
179
- ctfScripts( $ctf );
180
-
181
- // delay added to prevent strange issue with ajax themes returning the entire page
182
- setTimeout(function(){
183
- if(numNeeded > 0) {
184
- var $ctfMore = $ctf.find('.ctf-more'),
185
- lastIDData = $ctf.find('.ctf-item').last().attr('id'),
186
- persistentIndex = $ctf.find('.ctf-item').length,
187
- shortcodeData = $ctf.attr('data-ctfshortcode');
188
-
189
- ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, numNeeded, persistentIndex );
190
- }
191
- },500);
192
-
193
- // add the load more button and input to simulate a dynamic json file call
194
- $ctf.find('.ctf-more').on('click', function() {
195
- // read the json that is in the ctf-shortcode-data that contains all of the shortcode arguments
196
- var $ctfMore = $(this),
197
- lastIDData = $ctf.find('.ctf-item').last().attr('id'),
198
- persistentIndex = $ctf.find('.ctf-item').length,
199
- shortcodeData = $ctf.attr('data-ctfshortcode');
200
-
201
- ctfLoadTweets( lastIDData, shortcodeData , $ctf, $ctfMore, 0, persistentIndex );
202
-
203
- });
204
-
205
- $ctf.find('.ctf-author-box-link p:empty').remove();
206
- }); // end .cff each loop
207
-
208
-
209
- })(jQuery);
210
-
211
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ var ctf_js_exists = (typeof ctf_js_exists !== 'undefined') ? true : false;
2
+ if(!ctf_js_exists){
3
+
4
+ (function ($) {
5
+
6
+ window.ctf_init = function() {
7
+
8
+ if ($('.ctf').length <= $('.ctf_is_initialized').length) {
9
+ return;
10
+ }
11
+
12
+ var ctfIntentsIncluded = false;
13
+ $('.ctf').each(function () {
14
+ if (!$(this).hasClass('ctf_is_initialized') && !ctfIntentsIncluded && typeof $(this).attr('data-ctfintents') !== 'undefined') {
15
+ ctfIntentsIncluded = true;
16
+ Function && Function.prototype && Function.prototype.bind && (/MSIE ([6789]|10)/.test(navigator.userAgent) || (window.__twttr && window.__twttr.widgets && window.__twttr.widgets.loaded && window.twttr.widgets.load && window.twttr.widgets.load(), window.__twttr && window.__twttr.widgets && window.__twttr.widgets.init || function (t) {
17
+ function e(e) {
18
+ for (var n, i, o = e[0], s = e[1], a = 0, c = []; a < o.length; a++) i = o[a], r[i] && c.push(r[i][0]), r[i] = 0;
19
+ for (n in s) Object.prototype.hasOwnProperty.call(s, n) && (t[n] = s[n]);
20
+ for (u && u(e); c.length;) c.shift()()
21
+ }
22
+
23
+ var n = {}, r = {1: 0};
24
+
25
+ function i(e) {
26
+ if (n[e]) return n[e].exports;
27
+ var r = n[e] = {i: e, l: !1, exports: {}};
28
+ return t[e].call(r.exports, r, r.exports, i), r.l = !0, r.exports
29
+ }
30
+
31
+ i.e = function (t) {
32
+ var e = [], n = r[t];
33
+ if (0 !== n) if (n) e.push(n[2]); else {
34
+ var o = new Promise(function (e, i) {
35
+ n = r[t] = [e, i]
36
+ });
37
+ e.push(n[2] = o);
38
+ var s, a = document.getElementsByTagName("head")[0],
39
+ u = document.createElement("script");
40
+ u.charset = "utf-8", u.timeout = 120, i.nc && u.setAttribute("nonce", i.nc), u.src = function (t) {
41
+ return i.p + "js/" + ({
42
+ 0: "moment~timeline~tweet",
43
+ 2: "dm_button",
44
+ 3: "button",
45
+ 4: "moment",
46
+ 5: "periscope_on_air",
47
+ 6: "timeline",
48
+ 7: "tweet"
49
+ }[t] || t) + "." + {
50
+ 0: "ec04a6cb5ba879d0e0db41f211639fdf",
51
+ 2: "6542a7407a2eccac51f5c5e0fac5bb80",
52
+ 3: "d941c9a422e2e3faf474b82a1f39e936",
53
+ 4: "0a3cc02317b85399478995c763a1296c",
54
+ 5: "d26526abe761c4d8d8d71cf0ec565649",
55
+ 6: "0a7b4db67eacd23e35c5ce02e6ea3470",
56
+ 7: "b2d749028be81f16d9cb4994d9692feb"
57
+ }[t] + ".js"
58
+ }(t), s = function (e) {
59
+ u.onerror = u.onload = null, clearTimeout(c);
60
+ var n = r[t];
61
+ if (0 !== n) {
62
+ if (n) {
63
+ var i = e && ("load" === e.type ? "missing" : e.type),
64
+ o = e && e.target && e.target.src,
65
+ s = new Error("Loading chunk " + t + " failed.\n(" + i + ": " + o + ")");
66
+ s.type = i, s.request = o, n[1](s)
67
+ }
68
+ r[t] = void 0
69
+ }
70
+ };
71
+ var c = setTimeout(function () {
72
+ s({type: "timeout", target: u})
73
+ }, 12e4);
74
+ u.onerror = u.onload = s, a.appendChild(u)
75
+ }
76
+ return Promise.all(e)
77
+ }, i.m = t, i.c = n, i.d = function (t, e, n) {
78
+ i.o(t, e) || Object.defineProperty(t, e, {enumerable: !0, get: n})
79
+ }, i.r = function (t) {
80
+ "undefined" != typeof Symbol && Symbol.toStringTag && Object.defineProperty(t, Symbol.toStringTag, {value: "Module"}), Object.defineProperty(t, "__esModule", {value: !0})
81
+ }, i.t = function (t, e) {
82
+ if (1 & e && (t = i(t)), 8 & e) return t;
83
+ if (4 & e && "object" == typeof t && t && t.__esModule) return t;
84
+ var n = Object.create(null);
85
+ if (i.r(n), Object.defineProperty(n, "default", {
86
+ enumerable: !0,
87
+ value: t
88
+ }), 2 & e && "string" != typeof t) for (var r in t) i.d(n, r, function (e) {
89
+ return t[e]
90
+ }.bind(null, r));
91
+ return n
92
+ }, i.n = function (t) {
93
+ var e = t && t.__esModule ? function () {
94
+ return t.default
95
+ } : function () {
96
+ return t
97
+ };
98
+ return i.d(e, "a", e), e
99
+ }, i.o = function (t, e) {
100
+ return Object.prototype.hasOwnProperty.call(t, e)
101
+ }, i.p = "https://platform.twitter.com/", i.oe = function (t) {
102
+ throw console.error(t), t
103
+ };
104
+ var o = window.__twttrll = window.__twttrll || [], s = o.push.bind(o);
105
+ o.push = e, o = o.slice();
106
+ for (var a = 0; a < o.length; a++) e(o[a]);
107
+ var u = s;
108
+ i(i.s = 94)
109
+ }([function (t, e, n) {
110
+ var r = n(1);
111
+
112
+ function i(t, e) {
113
+ var n;
114
+ for (n in t) t.hasOwnProperty && !t.hasOwnProperty(n) || e(n, t[n]);
115
+ return t
116
+ }
117
+
118
+ function o(t) {
119
+ return {}.toString.call(t).match(/\s([a-zA-Z]+)/)[1].toLowerCase()
120
+ }
121
+
122
+ function s(t) {
123
+ return t === Object(t)
124
+ }
125
+
126
+ function a(t) {
127
+ var e;
128
+ if (!s(t)) return !1;
129
+ if (Object.keys) return !Object.keys(t).length;
130
+ for (e in t) if (t.hasOwnProperty(e)) return !1;
131
+ return !0
132
+ }
133
+
134
+ function u(t) {
135
+ return t ? Array.prototype.slice.call(t) : []
136
+ }
137
+
138
+ t.exports = {
139
+ aug: function (t) {
140
+ return u(arguments).slice(1).forEach(function (e) {
141
+ i(e, function (e, n) {
142
+ t[e] = n
143
+ })
144
+ }), t
145
+ }, async: function (t, e) {
146
+ r.setTimeout(function () {
147
+ t.call(e || null)
148
+ }, 0)
149
+ }, compact: function t(e) {
150
+ return i(e, function (n, r) {
151
+ s(r) && (t(r), a(r) && delete e[n]), void 0 !== r && null !== r && "" !== r || delete e[n]
152
+ }), e
153
+ }, contains: function (t, e) {
154
+ return !(!t || !t.indexOf) && t.indexOf(e) > -1
155
+ }, forIn: i, isObject: s, isEmptyObject: a, toType: o, isType: function (t, e) {
156
+ return t == o(e)
157
+ }, toRealArray: u
158
+ }
159
+ }, function (t, e) {
160
+ t.exports = window
161
+ }, function (t, e, n) {
162
+ var r = n(6);
163
+ t.exports = function () {
164
+ var t = this;
165
+ this.promise = new r(function (e, n) {
166
+ t.resolve = e, t.reject = n
167
+ })
168
+ }
169
+ }, function (t, e, n) {
170
+ var r = n(11),
171
+ i = /(?:^|(?:https?:)?\/\/(?:www\.)?twitter\.com(?::\d+)?(?:\/intent\/(?:follow|user)\/?\?screen_name=|(?:\/#!)?\/))@?([\w]+)(?:\?|&|$)/i,
172
+ o = /(?:^|(?:https?:)?\/\/(?:www\.)?twitter\.com(?::\d+)?\/(?:#!\/)?[\w_]+\/status(?:es)?\/)(\d+)/i,
173
+ s = /^http(s?):\/\/(\w+\.)*twitter\.com([:/]|$)/i, a = /^http(s?):\/\/pbs\.twimg\.com\//,
174
+ u = /^#?([^.,<>!\s/#\-()'"]+)$/, c = /twitter\.com(?::\d{2,4})?\/intent\/(\w+)/,
175
+ d = /^https?:\/\/(?:www\.)?twitter\.com\/\w+\/timelines\/(\d+)/i,
176
+ l = /^https?:\/\/(?:www\.)?twitter\.com\/i\/moments\/(\d+)/i,
177
+ f = /^https?:\/\/(?:www\.)?twitter\.com\/(\w+)\/(?:likes|favorites)/i,
178
+ h = /^https?:\/\/(?:www\.)?twitter\.com\/(\w+)\/lists\/([\w-%]+)/i,
179
+ p = /^https?:\/\/(?:www\.)?twitter\.com\/i\/live\/(\d+)/i,
180
+ m = /^https?:\/\/syndication\.twitter\.com\/settings/i,
181
+ v = /^https?:\/\/(localhost|platform)\.twitter\.com(?::\d+)?\/widgets\/widget_iframe\.(.+)/i,
182
+ g = /^https?:\/\/(?:www\.)?twitter\.com\/search\?q=(\w+)/i;
183
+
184
+ function w(t) {
185
+ return "string" == typeof t && i.test(t) && RegExp.$1.length <= 20
186
+ }
187
+
188
+ function y(t) {
189
+ if (w(t)) return RegExp.$1
190
+ }
191
+
192
+ function b(t, e) {
193
+ var n = r.decodeURL(t);
194
+ if (e = e || !1, n.screen_name = y(t), n.screen_name) return r.url("https://twitter.com/intent/" + (e ? "follow" : "user"), n)
195
+ }
196
+
197
+ function _(t) {
198
+ return "string" == typeof t && u.test(t)
199
+ }
200
+
201
+ function E(t) {
202
+ return "string" == typeof t && o.test(t)
203
+ }
204
+
205
+ t.exports = {
206
+ isHashTag: _, hashTag: function (t, e) {
207
+ if (e = void 0 === e || e, _(t)) return (e ? "#" : "") + RegExp.$1
208
+ }, isScreenName: w, screenName: y, isStatus: E, status: function (t) {
209
+ return E(t) && RegExp.$1
210
+ }, intentForProfileURL: b, intentForFollowURL: function (t) {
211
+ return b(t, !0)
212
+ }, isTwitterURL: function (t) {
213
+ return s.test(t)
214
+ }, isTwimgURL: function (t) {
215
+ return a.test(t)
216
+ }, isIntentURL: function (t) {
217
+ return c.test(t)
218
+ }, isSettingsURL: function (t) {
219
+ return m.test(t)
220
+ }, isWidgetIframeURL: function (t) {
221
+ return v.test(t)
222
+ }, isSearchUrl: function (t) {
223
+ return g.test(t)
224
+ }, regexen: {profile: i}, momentId: function (t) {
225
+ return l.test(t) && RegExp.$1
226
+ }, collectionId: function (t) {
227
+ return d.test(t) && RegExp.$1
228
+ }, intentType: function (t) {
229
+ return c.test(t) && RegExp.$1
230
+ }, likesScreenName: function (t) {
231
+ return f.test(t) && RegExp.$1
232
+ }, listScreenNameAndSlug: function (t) {
233
+ var e, n, r;
234
+ if (h.test(t)) {
235
+ e = RegExp.$1, n = RegExp.$2;
236
+ try {
237
+ r = decodeURIComponent(n)
238
+ } catch (t) {
239
+ }
240
+ return {ownerScreenName: e, slug: r || n}
241
+ }
242
+ return !1
243
+ }, eventId: function (t) {
244
+ return p.test(t) && RegExp.$1
245
+ }
246
+ }
247
+ }, function (t, e) {
248
+ t.exports = document
249
+ }, function (t, e, n) {
250
+ var r = n(0), i = [!0, 1, "1", "on", "ON", "true", "TRUE", "yes", "YES"],
251
+ o = [!1, 0, "0", "off", "OFF", "false", "FALSE", "no", "NO"];
252
+
253
+ function s(t) {
254
+ return void 0 !== t && null !== t && "" !== t
255
+ }
256
+
257
+ function a(t) {
258
+ return c(t) && t % 1 == 0
259
+ }
260
+
261
+ function u(t) {
262
+ return c(t) && !a(t)
263
+ }
264
+
265
+ function c(t) {
266
+ return s(t) && !isNaN(t)
267
+ }
268
+
269
+ function d(t) {
270
+ return r.contains(o, t)
271
+ }
272
+
273
+ function l(t) {
274
+ return r.contains(i, t)
275
+ }
276
+
277
+ t.exports = {
278
+ hasValue: s, isInt: a, isFloat: u, isNumber: c, isString: function (t) {
279
+ return "string" === r.toType(t)
280
+ }, isArray: function (t) {
281
+ return s(t) && "array" == r.toType(t)
282
+ }, isTruthValue: l, isFalseValue: d, asInt: function (t) {
283
+ if (a(t)) return parseInt(t, 10)
284
+ }, asFloat: function (t) {
285
+ if (u(t)) return t
286
+ }, asNumber: function (t) {
287
+ if (c(t)) return t
288
+ }, asBoolean: function (t) {
289
+ return !(!s(t) || !l(t) && (d(t) || !t))
290
+ }
291
+ }
292
+ }, function (t, e, n) {
293
+ var r = n(1), i = n(21), o = n(48);
294
+ i.hasPromiseSupport() || (r.Promise = o), t.exports = r.Promise
295
+ }, function (t, e, n) {
296
+ var r = n(0);
297
+ t.exports = function (t, e) {
298
+ var n = Array.prototype.slice.call(arguments, 2);
299
+ return function () {
300
+ var i = r.toRealArray(arguments);
301
+ return t.apply(e, n.concat(i))
302
+ }
303
+ }
304
+ }, function (t, e) {
305
+ t.exports = location
306
+ }, function (t, e, n) {
307
+ var r = n(50);
308
+ t.exports = new r("__twttr")
309
+ }, function (t, e, n) {
310
+ var r = n(0), i = /\b([\w-_]+)\b/g;
311
+
312
+ function o(t) {
313
+ return new RegExp("\\b" + t + "\\b", "g")
314
+ }
315
+
316
+ function s(t, e) {
317
+ t.classList ? t.classList.add(e) : o(e).test(t.className) || (t.className += " " + e)
318
+ }
319
+
320
+ function a(t, e) {
321
+ t.classList ? t.classList.remove(e) : t.className = t.className.replace(o(e), " ")
322
+ }
323
+
324
+ function u(t, e) {
325
+ return t.classList ? t.classList.contains(e) : r.contains(c(t), e)
326
+ }
327
+
328
+ function c(t) {
329
+ return r.toRealArray(t.classList ? t.classList : t.className.match(i))
330
+ }
331
+
332
+ t.exports = {
333
+ add: s, remove: a, replace: function (t, e, n) {
334
+ if (t.classList && u(t, e)) return a(t, e), void s(t, n);
335
+ t.className = t.className.replace(o(e), n)
336
+ }, toggle: function (t, e, n) {
337
+ return void 0 === n && t.classList && t.classList.toggle ? t.classList.toggle(e, n) : (n ? s(t, e) : a(t, e), n)
338
+ }, present: u, list: c
339
+ }
340
+ }, function (t, e, n) {
341
+ var r = n(5), i = n(0);
342
+
343
+ function o(t) {
344
+ return encodeURIComponent(t).replace(/\+/g, "%2B").replace(/'/g, "%27")
345
+ }
346
+
347
+ function s(t) {
348
+ return decodeURIComponent(t)
349
+ }
350
+
351
+ function a(t) {
352
+ var e = [];
353
+ return i.forIn(t, function (t, n) {
354
+ var s = o(t);
355
+ i.isType("array", n) || (n = [n]), n.forEach(function (t) {
356
+ r.hasValue(t) && e.push(s + "=" + o(t))
357
+ })
358
+ }), e.sort().join("&")
359
+ }
360
+
361
+ function u(t) {
362
+ var e = {};
363
+ return t ? (t.split("&").forEach(function (t) {
364
+ var n = t.split("="), r = s(n[0]), o = s(n[1]);
365
+ if (2 == n.length) {
366
+ if (!i.isType("array", e[r])) return r in e ? (e[r] = [e[r]], void e[r].push(o)) : void (e[r] = o);
367
+ e[r].push(o)
368
+ }
369
+ }), e) : {}
370
+ }
371
+
372
+ t.exports = {
373
+ url: function (t, e) {
374
+ return a(e).length > 0 ? i.contains(t, "?") ? t + "&" + a(e) : t + "?" + a(e) : t
375
+ }, decodeURL: function (t) {
376
+ var e = t && t.split("?");
377
+ return 2 == e.length ? u(e[1]) : {}
378
+ }, decode: u, encode: a, encodePart: o, decodePart: s
379
+ }
380
+ }, function (t, e, n) {
381
+ var r = n(8), i = n(1), o = n(0), s = {}, a = o.contains(r.href, "tw_debug=true");
382
+
383
+ function u() {
384
+ }
385
+
386
+ function c() {
387
+ }
388
+
389
+ function d() {
390
+ return i.performance && +i.performance.now() || +new Date
391
+ }
392
+
393
+ function l(t, e) {
394
+ if (i.console && i.console[t]) switch (e.length) {
395
+ case 1:
396
+ i.console[t](e[0]);
397
+ break;
398
+ case 2:
399
+ i.console[t](e[0], e[1]);
400
+ break;
401
+ case 3:
402
+ i.console[t](e[0], e[1], e[2]);
403
+ break;
404
+ case 4:
405
+ i.console[t](e[0], e[1], e[2], e[3]);
406
+ break;
407
+ case 5:
408
+ i.console[t](e[0], e[1], e[2], e[3], e[4]);
409
+ break;
410
+ default:
411
+ 0 !== e.length && i.console.warn && i.console.warn("too many params passed to logger." + t)
412
+ }
413
+ }
414
+
415
+ t.exports = {
416
+ devError: u, devInfo: c, devObject: function (t, e) {
417
+ }, publicError: function () {
418
+ l("error", o.toRealArray(arguments))
419
+ }, publicLog: function () {
420
+ l("info", o.toRealArray(arguments))
421
+ }, time: function (t) {
422
+ a && (s[t] = d())
423
+ }, timeEnd: function (t) {
424
+ a && s[t] && (d(), s[t])
425
+ }
426
+ }
427
+ }, function (t, e, n) {
428
+ var r = n(20), i = n(5), o = n(11), s = n(0), a = n(119);
429
+ t.exports = function (t) {
430
+ var e = t.href && t.href.split("?")[1], n = e ? o.decode(e) : {}, u = {
431
+ lang: a(t),
432
+ width: t.getAttribute("data-width") || t.getAttribute("width"),
433
+ height: t.getAttribute("data-height") || t.getAttribute("height"),
434
+ related: t.getAttribute("data-related"),
435
+ partner: t.getAttribute("data-partner")
436
+ };
437
+ return i.asBoolean(t.getAttribute("data-dnt")) && r.setOn(), s.forIn(u, function (t, e) {
438
+ var r = n[t];
439
+ n[t] = i.hasValue(r) ? r : e
440
+ }), s.compact(n)
441
+ }
442
+ }, function (t, e, n) {
443
+ var r = n(81), i = n(23);
444
+ t.exports = function () {
445
+ var t = "data-twitter-extracted-" + i.generate();
446
+ return function (e, n) {
447
+ return r(e, n).filter(function (e) {
448
+ return !e.hasAttribute(t)
449
+ }).map(function (e) {
450
+ return e.setAttribute(t, "true"), e
451
+ })
452
+ }
453
+ }
454
+ }, function (t, e) {
455
+ function n(t, e, n, r, i, o, s) {
456
+ this.factory = t, this.Sandbox = e, this.srcEl = o, this.targetEl = i, this.parameters = r, this.className = n, this.options = s
457
+ }
458
+
459
+ n.prototype.destroy = function () {
460
+ this.srcEl = this.targetEl = null
461
+ }, t.exports = n
462
+ }, function (t, e) {
463
+ t.exports = {
464
+ DM_BUTTON: "twitter-dm-button",
465
+ FOLLOW_BUTTON: "twitter-follow-button",
466
+ HASHTAG_BUTTON: "twitter-hashtag-button",
467
+ MENTION_BUTTON: "twitter-mention-button",
468
+ MOMENT: "twitter-moment",
469
+ PERISCOPE: "periscope-on-air",
470
+ SHARE_BUTTON: "twitter-share-button",
471
+ TIMELINE: "twitter-timeline",
472
+ TWEET: "twitter-tweet"
473
+ }
474
+ }, function (t, e, n) {
475
+ var r = n(6), i = n(20), o = n(53), s = n(52), a = n(35), u = n(5), c = n(0);
476
+ t.exports = function (t, e, n) {
477
+ var d;
478
+ return t = t || [], e = e || {}, d = "ƒ(" + t.join(", ") + ", target, [options]);", function () {
479
+ var l, f, h, p, m = Array.prototype.slice.apply(arguments, [0, t.length]),
480
+ v = Array.prototype.slice.apply(arguments, [t.length]);
481
+ return v.forEach(function (t) {
482
+ t && (t.nodeType !== Node.ELEMENT_NODE ? c.isType("function", t) ? l = t : c.isType("object", t) && (f = t) : h = t)
483
+ }), m.length !== t.length || 0 === v.length ? (l && c.async(function () {
484
+ l(!1)
485
+ }), r.reject(new Error("Not enough parameters. Expected: " + d))) : h ? (f = c.aug({}, f || {}, e), t.forEach(function (t) {
486
+ f[t] = m.shift()
487
+ }), u.asBoolean(f.dnt) && i.setOn(), p = a.getExperiments().then(function (t) {
488
+ return o.addWidget(n(f, h, void 0, s.isHorizonTweetEnabled(t)))
489
+ }), l && p.then(l, function () {
490
+ l(!1)
491
+ }), p) : (l && c.async(function () {
492
+ l(!1)
493
+ }), r.reject(new Error("No target element specified. Expected: " + d)))
494
+ }
495
+ }
496
+ }, function (t, e, n) {
497
+ var r = n(102), i = n(2), o = n(0);
498
+
499
+ function s(t, e) {
500
+ return function () {
501
+ try {
502
+ e.resolve(t.call(this))
503
+ } catch (t) {
504
+ e.reject(t)
505
+ }
506
+ }
507
+ }
508
+
509
+ t.exports = {
510
+ sync: function (t, e) {
511
+ t.call(e)
512
+ }, read: function (t, e) {
513
+ var n = new i;
514
+ return r.read(s(t, n), e), n.promise
515
+ }, write: function (t, e) {
516
+ var n = new i;
517
+ return r.write(s(t, n), e), n.promise
518
+ }, defer: function (t, e, n) {
519
+ var a = new i;
520
+ return o.isType("function", t) && (n = e, e = t, t = 1), r.defer(t, s(e, a), n), a.promise
521
+ }
522
+ }
523
+ }, function (t, e, n) {
524
+ var r = n(9),
525
+ i = ["https://syndication.twitter.com", "https://cdn.syndication.twimg.com", "https://localhost.twitter.com:8444"],
526
+ o = ["https://syndication.twitter.com", "https://localhost.twitter.com:8445"],
527
+ s = ["https://platform.twitter.com/embed/index.html", "https://localhost.twitter.com:8080", /https:\/\/ton\.smf1\.twitter\.com\/syndication-internal\/embed-iframe\/[0-9A-Za-z_-]+\/app\/index\.html/],
528
+ a = function (t, e) {
529
+ return t.some(function (t) {
530
+ return t instanceof RegExp ? t.test(e) : t === e
531
+ })
532
+ }, u = function () {
533
+ var t = r.get("backendHost");
534
+ return t && a(i, t) ? t : "https://cdn.syndication.twimg.com"
535
+ }, c = function () {
536
+ var t = r.get("settingsSvcHost");
537
+ return t && a(o, t) ? t : "https://syndication.twitter.com"
538
+ }, d = function () {
539
+ var t = r.get("embedIframeURL");
540
+ return t && a(s, t) ? t : "https://platform.twitter.com/embed/index.html"
541
+ };
542
+
543
+ function l(t, e) {
544
+ var n = [t];
545
+ return e.forEach(function (t) {
546
+ n.push(function (t) {
547
+ var e = (t || "").toString(), n = "/" === e.slice(0, 1) ? 1 : 0, r = function (t) {
548
+ return "/" === t.slice(-1)
549
+ }(e) ? -1 : void 0;
550
+ return e.slice(n, r)
551
+ }(t))
552
+ }), n.join("/")
553
+ }
554
+
555
+ t.exports = {
556
+ cookieConsent: function (t) {
557
+ var e = t || [];
558
+ return e.unshift("cookie/consent"), l(c(), e)
559
+ }, embedIframe: function () {
560
+ return d()
561
+ }, eventVideo: function (t) {
562
+ var e = t || [];
563
+ return e.unshift("video/event"), l(u(), e)
564
+ }, grid: function (t) {
565
+ var e = t || [];
566
+ return e.unshift("grid/collection"), l(u(), e)
567
+ }, moment: function (t) {
568
+ var e = t || [];
569
+ return e.unshift("moments"), l(u(), e)
570
+ }, settings: function (t) {
571
+ var e = t || [];
572
+ return e.unshift("settings"), l(c(), e)
573
+ }, timeline: function (t) {
574
+ var e = t || [];
575
+ return e.unshift("timeline"), l(u(), e)
576
+ }, tweetBatch: function (t) {
577
+ var e = t || [];
578
+ return e.unshift("tweets.json"), l(u(), e)
579
+ }, video: function (t) {
580
+ var e = t || [];
581
+ return e.unshift("widgets/video"), l(u(), e)
582
+ }
583
+ }
584
+ }, function (t, e, n) {
585
+ var r = n(4), i = n(8), o = n(38), s = n(79), a = n(5), u = n(33), c = !1,
586
+ d = /https?:\/\/([^/]+).*/i;
587
+ t.exports = {
588
+ setOn: function () {
589
+ c = !0
590
+ }, enabled: function (t, e) {
591
+ return !!(c || a.asBoolean(u.val("dnt")) || s.isUrlSensitive(e || i.host) || o.isFramed() && s.isUrlSensitive(o.rootDocumentLocation()) || (t = d.test(t || r.referrer) && RegExp.$1) && s.isUrlSensitive(t))
592
+ }
593
+ }
594
+ }, function (t, e, n) {
595
+ var r = n(4), i = n(12), o = n(95), s = n(1), a = n(0), u = o.userAgent;
596
+
597
+ function c(t) {
598
+ return /(Trident|MSIE|Edge[/ ]?\d)/.test(t = t || u)
599
+ }
600
+
601
+ t.exports = {
602
+ retina: function (t) {
603
+ return (t = t || s).devicePixelRatio ? t.devicePixelRatio >= 1.5 : !!t.matchMedia && t.matchMedia("only screen and (min-resolution: 144dpi)").matches
604
+ }, anyIE: c, ie9: function (t) {
605
+ return /MSIE 9/.test(t = t || u)
606
+ }, ie10: function (t) {
607
+ return /MSIE 10/.test(t = t || u)
608
+ }, ios: function (t) {
609
+ return /(iPad|iPhone|iPod)/.test(t = t || u)
610
+ }, android: function (t) {
611
+ return /^Mozilla\/5\.0 \(Linux; (U; )?Android/.test(t = t || u)
612
+ }, canPostMessage: function (t, e) {
613
+ return t = t || s, e = e || u, t.postMessage && !(c(e) && t.opener)
614
+ }, touch: function (t, e, n) {
615
+ return t = t || s, e = e || o, n = n || u, "ontouchstart" in t || /Opera Mini/.test(n) || e.msMaxTouchPoints > 0
616
+ }, cssTransitions: function () {
617
+ var t = r.body.style;
618
+ return void 0 !== t.transition || void 0 !== t.webkitTransition || void 0 !== t.mozTransition || void 0 !== t.oTransition || void 0 !== t.msTransition
619
+ }, hasPromiseSupport: function () {
620
+ return !!(s.Promise && s.Promise.resolve && s.Promise.reject && s.Promise.all && s.Promise.race && (new s.Promise(function (e) {
621
+ t = e
622
+ }), a.isType("function", t)));
623
+ var t
624
+ }, hasIntersectionObserverSupport: function () {
625
+ return !!s.IntersectionObserver
626
+ }, hasPerformanceInformation: function () {
627
+ return s.performance && s.performance.getEntriesByType
628
+ }, hasLocalStorageSupport: function () {
629
+ try {
630
+ return s.localStorage.setItem("local_storage_support_test", "true"), void 0 !== s.localStorage
631
+ } catch (t) {
632
+ return i.devError("window.localStorage is not supported:", t), !1
633
+ }
634
+ }
635
+ }
636
+ }, function (t, e, n) {
637
+ var r = n(6), i = n(2);
638
+
639
+ function o(t, e) {
640
+ return t.then(e, e)
641
+ }
642
+
643
+ function s(t) {
644
+ return t instanceof r
645
+ }
646
+
647
+ t.exports = {
648
+ always: o, allResolved: function (t) {
649
+ var e;
650
+ return void 0 === t ? r.reject(new Error("undefined is not an object")) : Array.isArray(t) ? (e = t.length) ? new r(function (n, r) {
651
+ var i = 0, o = [];
652
+
653
+ function a() {
654
+ (i += 1) === e && (0 === o.length ? r() : n(o))
655
+ }
656
+
657
+ function u(t) {
658
+ o.push(t), a()
659
+ }
660
+
661
+ t.forEach(function (t) {
662
+ s(t) ? t.then(u, a) : u(t)
663
+ })
664
+ }) : r.resolve([]) : r.reject(new Error("Type error"))
665
+ }, some: function (t) {
666
+ var e;
667
+ return e = (t = t || []).length, t = t.filter(s), e ? e !== t.length ? r.reject("non-Promise passed to .some") : new r(function (e, n) {
668
+ var r = 0;
669
+
670
+ function i() {
671
+ (r += 1) === t.length && n()
672
+ }
673
+
674
+ t.forEach(function (t) {
675
+ t.then(e, i)
676
+ })
677
+ }) : r.reject("no promises passed to .some")
678
+ }, isPromise: s, allSettled: function (t) {
679
+ function e() {
680
+ }
681
+
682
+ return r.all((t || []).map(function (t) {
683
+ return o(t, e)
684
+ }))
685
+ }, timeout: function (t, e) {
686
+ var n = new i;
687
+ return setTimeout(function () {
688
+ n.reject(new Error("Promise timed out"))
689
+ }, e), t.then(function (t) {
690
+ n.resolve(t)
691
+ }, function (t) {
692
+ n.reject(t)
693
+ }), n.promise
694
+ }
695
+ }
696
+ }, function (t, e) {
697
+ var n = "i", r = 0, i = 0;
698
+ t.exports = {
699
+ generate: function () {
700
+ return n + String(+new Date) + Math.floor(1e5 * Math.random()) + r++
701
+ }, deterministic: function () {
702
+ return n + String(i++)
703
+ }
704
+ }
705
+ }, function (t, e, n) {
706
+ var r = n(49), i = n(51), o = n(0);
707
+ t.exports = o.aug(r.get("events") || {}, i.Emitter)
708
+ }, function (t, e, n) {
709
+ var r = n(26), i = n(110);
710
+ t.exports = r.build([i])
711
+ }, function (t, e, n) {
712
+ var r = n(40), i = n(107), o = n(7);
713
+ (r = Object.create(r)).build = o(r.build, null, i), t.exports = r
714
+ }, function (t, e, n) {
715
+ var r = n(40), i = n(41), o = n(7);
716
+ (r = Object.create(r)).build = o(r.build, null, i), t.exports = r
717
+ }, function (t, e, n) {
718
+ var r = n(83), i = n(75), o = n(84), s = n(8), a = n(71), u = n(74), c = n(20), d = n(5),
719
+ l = n(23), f = n(0);
720
+
721
+ function h(t) {
722
+ if (!t || !t.headers) throw new Error("unexpected response schema");
723
+ return {
724
+ html: t.body,
725
+ config: t.config,
726
+ pollInterval: 1e3 * parseInt(t.headers.xPolling, 10) || null,
727
+ maxCursorPosition: t.headers.maxPosition,
728
+ minCursorPosition: t.headers.minPosition
729
+ }
730
+ }
731
+
732
+ function p(t) {
733
+ if (t && t.headers) throw new Error(t.headers.status);
734
+ throw t instanceof Error ? t : new Error(t)
735
+ }
736
+
737
+ t.exports = function (t) {
738
+ t.params({
739
+ instanceId: {required: !0, fallback: l.deterministic},
740
+ lang: {required: !0, transform: a.matchLanguage, fallback: "en"},
741
+ tweetLimit: {transform: d.asInt}
742
+ }), t.defineProperty("endpoint", {
743
+ get: function () {
744
+ throw new Error("endpoint not specified")
745
+ }
746
+ }), t.defineProperty("pollEndpoint", {
747
+ get: function () {
748
+ return this.endpoint
749
+ }
750
+ }), t.define("cbId", function (t) {
751
+ var e = t ? "_new" : "_old";
752
+ return "tl_" + this.params.instanceId + "_" + this.id + e
753
+ }), t.define("queryParams", function () {
754
+ return {
755
+ lang: this.params.lang,
756
+ tz: u.getTimezoneOffset(),
757
+ t: r(),
758
+ domain: s.host,
759
+ tweet_limit: this.params.tweetLimit,
760
+ dnt: c.enabled()
761
+ }
762
+ }), t.define("fetch", function () {
763
+ return i.fetch(this.endpoint, this.queryParams(), o, this.cbId()).then(h, p)
764
+ }), t.define("poll", function (t, e) {
765
+ var n, r;
766
+ return n = {
767
+ since_id: (t = t || {}).sinceId,
768
+ max_id: t.maxId,
769
+ min_position: t.minPosition,
770
+ max_position: t.maxPosition
771
+ }, r = f.aug(this.queryParams(), n), i.fetch(this.pollEndpoint, r, o, this.cbId(e)).then(h, p)
772
+ })
773
+ }
774
+ }, function (t, e, n) {
775
+ var r = n(51).makeEmitter();
776
+ t.exports = {
777
+ emitter: r,
778
+ START: "start",
779
+ ALL_WIDGETS_RENDER_START: "all_widgets_render_start",
780
+ ALL_WIDGETS_RENDER_END: "all_widgets_render_end",
781
+ ALL_WIDGETS_AND_IMAGES_LOADED: "all_widgets_and_images_loaded"
782
+ }
783
+ }, function (t, e, n) {
784
+ var r = n(4), i = n(0);
785
+ t.exports = function (t, e, n) {
786
+ var o;
787
+ if (n = n || r, t = t || {}, e = e || {}, t.name) {
788
+ try {
789
+ o = n.createElement('<iframe name="' + t.name + '"></iframe>')
790
+ } catch (e) {
791
+ (o = n.createElement("iframe")).name = t.name
792
+ }
793
+ delete t.name
794
+ } else o = n.createElement("iframe");
795
+ return t.id && (o.id = t.id, delete t.id), o.allowtransparency = "true", o.scrolling = "no", o.setAttribute("frameBorder", 0), o.setAttribute("allowTransparency", !0), i.forIn(t, function (t, e) {
796
+ o.setAttribute(t, e)
797
+ }), i.forIn(e, function (t, e) {
798
+ o.style[t] = e
799
+ }), o
800
+ }
801
+ }, function (t, e, n) {
802
+ var r = n(1).JSON;
803
+ t.exports = {stringify: r.stringify || r.encode, parse: r.parse || r.decode}
804
+ }, function (t, e, n) {
805
+ var r = n(0), i = n(43);
806
+ t.exports = {
807
+ closest: function t(e, n, o) {
808
+ var s;
809
+ if (n) return o = o || n && n.ownerDocument, s = r.isType("function", e) ? e : function (t) {
810
+ return function (e) {
811
+ return !!e.tagName && i(e, t)
812
+ }
813
+ }(e), n === o ? s(n) ? n : void 0 : s(n) ? n : t(s, n.parentNode, o)
814
+ }
815
+ }
816
+ }, function (t, e, n) {
817
+ var r, i = n(4);
818
+
819
+ function o(t) {
820
+ var e, n, o, s = 0;
821
+ for (r = {}, e = (t = t || i).getElementsByTagName("meta"); e[s]; s++) {
822
+ if (n = e[s], /^twitter:/.test(n.getAttribute("name"))) o = n.getAttribute("name").replace(/^twitter:/, ""); else {
823
+ if (!/^twitter:/.test(n.getAttribute("property"))) continue;
824
+ o = n.getAttribute("property").replace(/^twitter:/, "")
825
+ }
826
+ r[o] = n.getAttribute("content") || n.getAttribute("value")
827
+ }
828
+ }
829
+
830
+ o(), t.exports = {
831
+ init: o, val: function (t) {
832
+ return r[t]
833
+ }
834
+ }
835
+ }, function (t, e, n) {
836
+ var r = n(4), i = n(31), o = n(20), s = n(0), a = n(44), u = n(9), c = n(3), d = n(32),
837
+ l = a.version, f = u.get("clientEventEndpoint") || "https://syndication.twitter.com/i/jot",
838
+ h = 1;
839
+
840
+ function p(t) {
841
+ return s.aug({client: "tfw"}, t || {})
842
+ }
843
+
844
+ function m(t, e, n) {
845
+ return e = e || {}, s.aug({}, e, {
846
+ _category_: t,
847
+ triggered_on: e.triggered_on || +new Date,
848
+ dnt: o.enabled(n)
849
+ })
850
+ }
851
+
852
+ t.exports = {
853
+ extractTermsFromDOM: function t(e, n) {
854
+ var r;
855
+ return n = n || {}, e && e.nodeType === Node.ELEMENT_NODE ? ((r = e.getAttribute("data-scribe")) && r.split(" ").forEach(function (t) {
856
+ var e = t.trim().split(":"), r = e[0], i = e[1];
857
+ r && i && !n[r] && (n[r] = i)
858
+ }), t(e.parentNode, n)) : n
859
+ },
860
+ clickEventElement: function (t) {
861
+ var e = d.closest("[data-expanded-url]", t),
862
+ n = e && e.getAttribute("data-expanded-url");
863
+ return n && c.isTwitterURL(n) ? "twitter_url" : "url"
864
+ },
865
+ flattenClientEventPayload: function (t, e) {
866
+ return s.aug({}, e, {event_namespace: t})
867
+ },
868
+ formatGenericEventData: m,
869
+ formatClientEventData: function (t, e, n) {
870
+ var i = t && t.widget_origin || r.referrer;
871
+ return (t = m("tfw_client_event", t, i)).client_version = l, t.format_version = void 0 !== n ? n : 1, e || (t.widget_origin = i), t
872
+ },
873
+ formatClientEventNamespace: p,
874
+ formatTweetAssociation: function (t, e) {
875
+ var n = {};
876
+ return (e = e || {}).association_namespace = p(t), n[h] = e, n
877
+ },
878
+ noticeSeen: function (t) {
879
+ return "notice" === t.element && "seen" === t.action
880
+ },
881
+ splitLogEntry: function (t) {
882
+ var e, n, r, i, o;
883
+ return t.item_ids && t.item_ids.length > 1 ? (e = Math.floor(t.item_ids.length / 2), n = t.item_ids.slice(0, e), r = {}, i = t.item_ids.slice(e), o = {}, n.forEach(function (e) {
884
+ r[e] = t.item_details[e]
885
+ }), i.forEach(function (e) {
886
+ o[e] = t.item_details[e]
887
+ }), [s.aug({}, t, {item_ids: n, item_details: r}), s.aug({}, t, {
888
+ item_ids: i,
889
+ item_details: o
890
+ })]) : [t]
891
+ },
892
+ stringify: function (t) {
893
+ var e, n = Array.prototype.toJSON;
894
+ return delete Array.prototype.toJSON, e = i.stringify(t), n && (Array.prototype.toJSON = n), e
895
+ },
896
+ AUDIENCE_ENDPOINT: "https://syndication.twitter.com/i/jot/syndication",
897
+ CLIENT_EVENT_ENDPOINT: f,
898
+ RUFOUS_REDIRECT: "https://platform.twitter.com/jot.html"
899
+ }
900
+ }, function (t, e, n) {
901
+ var r = n(113), i = n(116);
902
+
903
+ function o(t) {
904
+ return r.settingsLoaded().then(function (e) {
905
+ return e[t]
906
+ })
907
+ }
908
+
909
+ function s() {
910
+ return o("experiments")
911
+ }
912
+
913
+ t.exports = {
914
+ shouldObtainCookieConsent: function () {
915
+ return o("shouldObtainCookieConsent")
916
+ }, getExperiments: s, getExperiment: function (t) {
917
+ return s().then(function (e) {
918
+ if (!e[t]) throw new Error("Experiment not found");
919
+ return e[t]
920
+ })
921
+ }, getActiveExperimentDataString: function () {
922
+ return s().then(function (t) {
923
+ var e = Object.keys(t).reduce(function (e, n) {
924
+ var r;
925
+ return t[n].version && (r = n.split("_").slice(-1)[0], e.push(r + ";" + t[n].bucket)), e
926
+ }, []);
927
+ return i(e.join(","))
928
+ })
929
+ }, getExperimentKeys: function () {
930
+ return s().then(function (t) {
931
+ return Object.keys(t)
932
+ })
933
+ }, load: function () {
934
+ r.load()
935
+ }
936
+ }
937
+ }, function (t, e, n) {
938
+ var r = n(10), i = {}, o = -1, s = {};
939
+
940
+ function a(t) {
941
+ var e = t.getAttribute("data-twitter-event-id");
942
+ return e || (t.setAttribute("data-twitter-event-id", ++o), o)
943
+ }
944
+
945
+ function u(t, e, n) {
946
+ var r = 0, i = t && t.length || 0;
947
+ for (r = 0; r < i; r++) if (t[r].call(e, n, e), n.ceaseImmediately) return !1
948
+ }
949
+
950
+ function c(t, e, n) {
951
+ for (var i = n || t.target || t.srcElement, o = r.list(i).map(function (t) {
952
+ return "." + t
953
+ }).concat(i.tagName), s = 0, a = o.length; s < a; s++) if (!1 === u(e[o[s]], i, t)) return;
954
+ t.cease || i !== this && c.call(this, t, e, i.parentElement || i.parentNode)
955
+ }
956
+
957
+ function d(t, e, n, r) {
958
+ function i(r) {
959
+ c.call(t, r, n[e])
960
+ }
961
+
962
+ !function (t, e, n, r) {
963
+ t.id && (s[t.id] = s[t.id] || [], s[t.id].push({
964
+ el: t,
965
+ listener: e,
966
+ type: n,
967
+ rootId: r
968
+ }))
969
+ }(t, i, e, r), t.addEventListener(e, i, !1)
970
+ }
971
+
972
+ function l(t) {
973
+ t && t.preventDefault ? t.preventDefault() : t.returnValue = !1
974
+ }
975
+
976
+ function f(t) {
977
+ t && (t.cease = !0) && t.stopPropagation ? t.stopPropagation() : t.cancelBubble = !0
978
+ }
979
+
980
+ t.exports = {
981
+ stop: function (t) {
982
+ return f(t), l(t), !1
983
+ }, stopPropagation: f, stopImmediatePropagation: function (t) {
984
+ t && (t.ceaseImmediately = !0, f(t), t.stopImmediatePropagation())
985
+ }, preventDefault: l, delegate: function (t, e, n, r) {
986
+ var o = a(t);
987
+ i[o] = i[o] || {}, i[o][e] || (i[o][e] = {}, d(t, e, i[o], o)), i[o][e][n] = i[o][e][n] || [], i[o][e][n].push(r)
988
+ }, simulate: function (t, e, n) {
989
+ var r = a(e), o = i[r] && i[r];
990
+ c.call(e, {target: n}, o[t])
991
+ }, removeDelegatesForWidget: function (t) {
992
+ var e = s[t];
993
+ e && (e.forEach(function (t) {
994
+ t.el.removeEventListener(t.type, t.listener, !1), delete i[t.rootId]
995
+ }), delete s[t])
996
+ }
997
+ }
998
+ }, function (t, e, n) {
999
+ var r = n(26), i = n(125);
1000
+ t.exports = r.build([i])
1001
+ }, function (t, e, n) {
1002
+ var r = n(8), i = n(78), o = n(0), s = i.getCanonicalURL() || r.href, a = s;
1003
+ t.exports = {
1004
+ isFramed: function () {
1005
+ return s !== a
1006
+ }, rootDocumentLocation: function (t) {
1007
+ return t && o.isType("string", t) && (s = t), s
1008
+ }, currentDocumentLocation: function () {
1009
+ return a
1010
+ }
1011
+ }
1012
+ }, function (t, e, n) {
1013
+ var r = n(77), i = n(104), o = n(80), s = n(34), a = new (n(112))(function (t) {
1014
+ var e, n, a;
1015
+ if (0 !== t.length) {
1016
+ if (u(t)) return c(t);
1017
+ e = r(t, function (t) {
1018
+ return s.noticeSeen(t.input.namespace)
1019
+ }), n = e.true, a = e.false, n && n.length > 0 && (n = n.slice(0, 1), o.canFlushOneItem(n[0]) || (n[0].input.data.message = ""), c(n)), a && (u(a) ? c : function (t) {
1020
+ i.init(), t.forEach(function (t) {
1021
+ var e = t.input.namespace, n = t.input.data, r = t.input.offsite,
1022
+ o = t.input.version;
1023
+ i.clientEvent(e, n, r, o)
1024
+ }), i.flush().then(function () {
1025
+ t.forEach(function (t) {
1026
+ t.taskDoneDeferred.resolve()
1027
+ })
1028
+ }, function () {
1029
+ t.forEach(function (t) {
1030
+ t.taskDoneDeferred.reject()
1031
+ })
1032
+ })
1033
+ })(a)
1034
+ }
1035
+ });
1036
+
1037
+ function u(t) {
1038
+ return 1 === t.length && o.canFlushOneItem(t[0])
1039
+ }
1040
+
1041
+ function c(t) {
1042
+ t.forEach(function (t) {
1043
+ var e = t.input.namespace, n = t.input.data, r = t.input.offsite, i = t.input.version;
1044
+ o.clientEvent(e, n, r, i), t.taskDoneDeferred.resolve()
1045
+ })
1046
+ }
1047
+
1048
+ t.exports = {
1049
+ scribe: function (t, e, n, r) {
1050
+ return a.add({namespace: t, data: e, offsite: n, version: r})
1051
+ }, pause: function () {
1052
+ a.pause()
1053
+ }, resume: function () {
1054
+ a.resume()
1055
+ }
1056
+ }
1057
+ }, function (t, e, n) {
1058
+ var r = n(105), i = n(106), o = n(0);
1059
+ t.exports = {
1060
+ couple: function () {
1061
+ return o.toRealArray(arguments)
1062
+ }, build: function (t, e, n) {
1063
+ var o = new t;
1064
+ return (e = i(r(e || []))).forEach(function (t) {
1065
+ t.call(null, o)
1066
+ }), o.build(n)
1067
+ }
1068
+ }
1069
+ }, function (t, e, n) {
1070
+ var r = n(108), i = n(0), o = n(42);
1071
+
1072
+ function s() {
1073
+ this.Component = this.factory(), this._adviceArgs = [], this._lastArgs = []
1074
+ }
1075
+
1076
+ i.aug(s.prototype, {
1077
+ factory: o, build: function (t) {
1078
+ var e = this;
1079
+ return this.Component, i.aug(this.Component.prototype.boundParams, t), this._adviceArgs.concat(this._lastArgs).forEach(function (t) {
1080
+ (function (t, e, n) {
1081
+ var r = this[e];
1082
+ if (!r) throw new Error(e + " does not exist");
1083
+ this[e] = t(r, n)
1084
+ }).apply(e.Component.prototype, t)
1085
+ }), delete this._lastArgs, delete this._adviceArgs, this.Component
1086
+ }, params: function (t) {
1087
+ var e = this.Component.prototype.paramConfigs;
1088
+ t = t || {}, this.Component.prototype.paramConfigs = i.aug({}, t, e)
1089
+ }, define: function (t, e) {
1090
+ if (t in this.Component.prototype) throw new Error(t + " has previously been defined");
1091
+ this.override(t, e)
1092
+ }, defineStatic: function (t, e) {
1093
+ this.Component[t] = e
1094
+ }, override: function (t, e) {
1095
+ this.Component.prototype[t] = e
1096
+ }, defineProperty: function (t, e) {
1097
+ if (t in this.Component.prototype) throw new Error(t + " has previously been defined");
1098
+ this.overrideProperty(t, e)
1099
+ }, overrideProperty: function (t, e) {
1100
+ var n = i.aug({configurable: !0}, e);
1101
+ Object.defineProperty(this.Component.prototype, t, n)
1102
+ }, before: function (t, e) {
1103
+ this._adviceArgs.push([r.before, t, e])
1104
+ }, after: function (t, e) {
1105
+ this._adviceArgs.push([r.after, t, e])
1106
+ }, around: function (t, e) {
1107
+ this._adviceArgs.push([r.around, t, e])
1108
+ }, last: function (t, e) {
1109
+ this._lastArgs.push([r.after, t, e])
1110
+ }
1111
+ }), t.exports = s
1112
+ }, function (t, e, n) {
1113
+ var r = n(0);
1114
+
1115
+ function i() {
1116
+ return !0
1117
+ }
1118
+
1119
+ function o(t) {
1120
+ return t
1121
+ }
1122
+
1123
+ t.exports = function () {
1124
+ function t(t) {
1125
+ var e = this;
1126
+ t = t || {}, this.params = Object.keys(this.paramConfigs).reduce(function (n, s) {
1127
+ var a = [], u = e.boundParams, c = e.paramConfigs[s], d = c.validate || i,
1128
+ l = c.transform || o;
1129
+ if (s in u && a.push(u[s]), s in t && a.push(t[s]), a = "fallback" in c ? a.concat(c.fallback) : a, n[s] = function (t, e, n) {
1130
+ var i = null;
1131
+ return t.some(function (t) {
1132
+ if (t = r.isType("function", t) ? t() : t, e(t)) return i = n(t), !0
1133
+ }), i
1134
+ }(a, d, l), c.required && null == n[s]) throw new Error(s + " is a required parameter");
1135
+ return n
1136
+ }, {}), this.initialize()
1137
+ }
1138
+
1139
+ return r.aug(t.prototype, {
1140
+ paramConfigs: {}, boundParams: {}, initialize: function () {
1141
+ }
1142
+ }), t
1143
+ }
1144
+ }, function (t, e, n) {
1145
+ var r = n(1).HTMLElement,
1146
+ i = r.prototype.matches || r.prototype.matchesSelector || r.prototype.webkitMatchesSelector || r.prototype.mozMatchesSelector || r.prototype.msMatchesSelector || r.prototype.oMatchesSelector;
1147
+ t.exports = function (t, e) {
1148
+ if (i) return i.call(t, e)
1149
+ }
1150
+ }, function (t) {
1151
+ t.exports = {version: "2a81c84:1568701398616"}
1152
+ }, function (t, e, n) {
1153
+ var r, i = n(10), o = n(4), s = n(1), a = n(33), u = n(54), c = n(5), d = n(23), l = "csptest";
1154
+ t.exports = {
1155
+ inlineStyle: function () {
1156
+ var t = l + d.generate(), e = o.createElement("div"), n = o.createElement("style"),
1157
+ f = "." + t + " { visibility: hidden; }";
1158
+ return !!o.body && (c.asBoolean(a.val("widgets:csp")) && (r = !1), void 0 !== r ? r : (e.style.display = "none", i.add(e, t), n.type = "text/css", n.appendChild(o.createTextNode(f)), o.body.appendChild(n), o.body.appendChild(e), r = "hidden" === s.getComputedStyle(e).visibility, u(e), u(n), r))
1159
+ }
1160
+ }
1161
+ }, function (t, e, n) {
1162
+ var r = n(1);
1163
+ t.exports = function (t, e, n) {
1164
+ var i, o = 0;
1165
+ return n = n || null, function s() {
1166
+ var a = n || this, u = arguments, c = +new Date;
1167
+ if (r.clearTimeout(i), c - o > e) return o = c, void t.apply(a, u);
1168
+ i = r.setTimeout(function () {
1169
+ s.apply(a, u)
1170
+ }, e)
1171
+ }
1172
+ }
1173
+ }, function (t, e) {
1174
+ t.exports = function (t) {
1175
+ var e = t.getBoundingClientRect();
1176
+ return {width: e.width, height: e.height}
1177
+ }
1178
+ }, function (t, e, n) {
1179
+ /*!
1180
+ * @overview es6-promise - a tiny implementation of Promises/A+.
1181
+ * @copyright Copyright (c) 2014 Yehuda Katz, Tom Dale, Stefan Penner and contributors (Conversion to ES6 API by Jake Archibald)
1182
+ * @license Licensed under MIT license
1183
+ * See https://raw.githubusercontent.com/stefanpenner/es6-promise/master/LICENSE
1184
+ * @version v4.2.5+7f2b526d
1185
+ */
1186
+ var r;
1187
+ r = function () {
1188
+ "use strict";
1189
+
1190
+ function t(t) {
1191
+ return "function" == typeof t
1192
+ }
1193
+
1194
+ var e = Array.isArray ? Array.isArray : function (t) {
1195
+ return "[object Array]" === Object.prototype.toString.call(t)
1196
+ }, n = 0, r = void 0, i = void 0, o = function (t, e) {
1197
+ f[n] = t, f[n + 1] = e, 2 === (n += 2) && (i ? i(h) : w())
1198
+ }, s = "undefined" != typeof window ? window : void 0, a = s || {},
1199
+ u = a.MutationObserver || a.WebKitMutationObserver,
1200
+ c = "undefined" == typeof self && "undefined" != typeof process && "[object process]" === {}.toString.call(process),
1201
+ d = "undefined" != typeof Uint8ClampedArray && "undefined" != typeof importScripts && "undefined" != typeof MessageChannel;
1202
+
1203
+ function l() {
1204
+ var t = setTimeout;
1205
+ return function () {
1206
+ return t(h, 1)
1207
+ }
1208
+ }
1209
+
1210
+ var f = new Array(1e3);
1211
+
1212
+ function h() {
1213
+ for (var t = 0; t < n; t += 2) (0, f[t])(f[t + 1]), f[t] = void 0, f[t + 1] = void 0;
1214
+ n = 0
1215
+ }
1216
+
1217
+ var p, m, v, g, w = void 0;
1218
+
1219
+ function y(t, e) {
1220
+ var n = this, r = new this.constructor(E);
1221
+ void 0 === r[_] && k(r);
1222
+ var i = n._state;
1223
+ if (i) {
1224
+ var s = arguments[i - 1];
1225
+ o(function () {
1226
+ return D(i, r, s, n._result)
1227
+ })
1228
+ } else I(n, r, t, e);
1229
+ return r
1230
+ }
1231
+
1232
+ function b(t) {
1233
+ if (t && "object" == typeof t && t.constructor === this) return t;
1234
+ var e = new this(E);
1235
+ return C(e, t), e
1236
+ }
1237
+
1238
+ c ? w = function () {
1239
+ return process.nextTick(h)
1240
+ } : u ? (m = 0, v = new u(h), g = document.createTextNode(""), v.observe(g, {characterData: !0}), w = function () {
1241
+ g.data = m = ++m % 2
1242
+ }) : d ? ((p = new MessageChannel).port1.onmessage = h, w = function () {
1243
+ return p.port2.postMessage(0)
1244
+ }) : w = void 0 === s ? function () {
1245
+ try {
1246
+ var t = Function("return this")().require("vertx");
1247
+ return void 0 !== (r = t.runOnLoop || t.runOnContext) ? function () {
1248
+ r(h)
1249
+ } : l()
1250
+ } catch (t) {
1251
+ return l()
1252
+ }
1253
+ }() : l();
1254
+ var _ = Math.random().toString(36).substring(2);
1255
+
1256
+ function E() {
1257
+ }
1258
+
1259
+ var x = void 0, T = 1, A = 2, S = {error: null};
1260
+
1261
+ function R(t) {
1262
+ try {
1263
+ return t.then
1264
+ } catch (t) {
1265
+ return S.error = t, S
1266
+ }
1267
+ }
1268
+
1269
+ function N(e, n, r) {
1270
+ n.constructor === e.constructor && r === y && n.constructor.resolve === b ? function (t, e) {
1271
+ e._state === T ? L(t, e._result) : e._state === A ? j(t, e._result) : I(e, void 0, function (e) {
1272
+ return C(t, e)
1273
+ }, function (e) {
1274
+ return j(t, e)
1275
+ })
1276
+ }(e, n) : r === S ? (j(e, S.error), S.error = null) : void 0 === r ? L(e, n) : t(r) ? function (t, e, n) {
1277
+ o(function (t) {
1278
+ var r = !1, i = function (t, e, n, r) {
1279
+ try {
1280
+ t.call(e, n, r)
1281
+ } catch (t) {
1282
+ return t
1283
+ }
1284
+ }(n, e, function (n) {
1285
+ r || (r = !0, e !== n ? C(t, n) : L(t, n))
1286
+ }, function (e) {
1287
+ r || (r = !0, j(t, e))
1288
+ }, t._label);
1289
+ !r && i && (r = !0, j(t, i))
1290
+ }, t)
1291
+ }(e, n, r) : L(e, n)
1292
+ }
1293
+
1294
+ function C(t, e) {
1295
+ var n, r;
1296
+ t === e ? j(t, new TypeError("You cannot resolve a promise with itself")) : (r = typeof (n = e), null === n || "object" !== r && "function" !== r ? L(t, e) : N(t, e, R(e)))
1297
+ }
1298
+
1299
+ function P(t) {
1300
+ t._onerror && t._onerror(t._result), O(t)
1301
+ }
1302
+
1303
+ function L(t, e) {
1304
+ t._state === x && (t._result = e, t._state = T, 0 !== t._subscribers.length && o(O, t))
1305
+ }
1306
+
1307
+ function j(t, e) {
1308
+ t._state === x && (t._state = A, t._result = e, o(P, t))
1309
+ }
1310
+
1311
+ function I(t, e, n, r) {
1312
+ var i = t._subscribers, s = i.length;
1313
+ t._onerror = null, i[s] = e, i[s + T] = n, i[s + A] = r, 0 === s && t._state && o(O, t)
1314
+ }
1315
+
1316
+ function O(t) {
1317
+ var e = t._subscribers, n = t._state;
1318
+ if (0 !== e.length) {
1319
+ for (var r = void 0, i = void 0, o = t._result, s = 0; s < e.length; s += 3) r = e[s], i = e[s + n], r ? D(n, r, i, o) : i(o);
1320
+ t._subscribers.length = 0
1321
+ }
1322
+ }
1323
+
1324
+ function D(e, n, r, i) {
1325
+ var o = t(r), s = void 0, a = void 0, u = void 0, c = void 0;
1326
+ if (o) {
1327
+ if ((s = function (t, e) {
1328
+ try {
1329
+ return t(e)
1330
+ } catch (t) {
1331
+ return S.error = t, S
1332
+ }
1333
+ }(r, i)) === S ? (c = !0, a = s.error, s.error = null) : u = !0, n === s) return void j(n, new TypeError("A promises callback cannot return that same promise."))
1334
+ } else s = i, u = !0;
1335
+ n._state !== x || (o && u ? C(n, s) : c ? j(n, a) : e === T ? L(n, s) : e === A && j(n, s))
1336
+ }
1337
+
1338
+ var z = 0;
1339
+
1340
+ function k(t) {
1341
+ t[_] = z++, t._state = void 0, t._result = void 0, t._subscribers = []
1342
+ }
1343
+
1344
+ var M = function () {
1345
+ function t(t, n) {
1346
+ this._instanceConstructor = t, this.promise = new t(E), this.promise[_] || k(this.promise), e(n) ? (this.length = n.length, this._remaining = n.length, this._result = new Array(this.length), 0 === this.length ? L(this.promise, this._result) : (this.length = this.length || 0, this._enumerate(n), 0 === this._remaining && L(this.promise, this._result))) : j(this.promise, new Error("Array Methods must be provided an Array"))
1347
+ }
1348
+
1349
+ return t.prototype._enumerate = function (t) {
1350
+ for (var e = 0; this._state === x && e < t.length; e++) this._eachEntry(t[e], e)
1351
+ }, t.prototype._eachEntry = function (t, e) {
1352
+ var n = this._instanceConstructor, r = n.resolve;
1353
+ if (r === b) {
1354
+ var i = R(t);
1355
+ if (i === y && t._state !== x) this._settledAt(t._state, e, t._result); else if ("function" != typeof i) this._remaining--, this._result[e] = t; else if (n === U) {
1356
+ var o = new n(E);
1357
+ N(o, t, i), this._willSettleAt(o, e)
1358
+ } else this._willSettleAt(new n(function (e) {
1359
+ return e(t)
1360
+ }), e)
1361
+ } else this._willSettleAt(r(t), e)
1362
+ }, t.prototype._settledAt = function (t, e, n) {
1363
+ var r = this.promise;
1364
+ r._state === x && (this._remaining--, t === A ? j(r, n) : this._result[e] = n), 0 === this._remaining && L(r, this._result)
1365
+ }, t.prototype._willSettleAt = function (t, e) {
1366
+ var n = this;
1367
+ I(t, void 0, function (t) {
1368
+ return n._settledAt(T, e, t)
1369
+ }, function (t) {
1370
+ return n._settledAt(A, e, t)
1371
+ })
1372
+ }, t
1373
+ }(), U = function () {
1374
+ function e(t) {
1375
+ this[_] = z++, this._result = this._state = void 0, this._subscribers = [], E !== t && ("function" != typeof t && function () {
1376
+ throw new TypeError("You must pass a resolver function as the first argument to the promise constructor")
1377
+ }(), this instanceof e ? function (t, e) {
1378
+ try {
1379
+ e(function (e) {
1380
+ C(t, e)
1381
+ }, function (e) {
1382
+ j(t, e)
1383
+ })
1384
+ } catch (e) {
1385
+ j(t, e)
1386
+ }
1387
+ }(this, t) : function () {
1388
+ throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.")
1389
+ }())
1390
+ }
1391
+
1392
+ return e.prototype.catch = function (t) {
1393
+ return this.then(null, t)
1394
+ }, e.prototype.finally = function (e) {
1395
+ var n = this.constructor;
1396
+ return t(e) ? this.then(function (t) {
1397
+ return n.resolve(e()).then(function () {
1398
+ return t
1399
+ })
1400
+ }, function (t) {
1401
+ return n.resolve(e()).then(function () {
1402
+ throw t
1403
+ })
1404
+ }) : this.then(e, e)
1405
+ }, e
1406
+ }();
1407
+ return U.prototype.then = y, U.all = function (t) {
1408
+ return new M(this, t).promise
1409
+ }, U.race = function (t) {
1410
+ var n = this;
1411
+ return e(t) ? new n(function (e, r) {
1412
+ for (var i = t.length, o = 0; o < i; o++) n.resolve(t[o]).then(e, r)
1413
+ }) : new n(function (t, e) {
1414
+ return e(new TypeError("You must pass an array to race."))
1415
+ })
1416
+ }, U.resolve = b, U.reject = function (t) {
1417
+ var e = new this(E);
1418
+ return j(e, t), e
1419
+ }, U._setScheduler = function (t) {
1420
+ i = t
1421
+ }, U._setAsap = function (t) {
1422
+ o = t
1423
+ }, U._asap = o, U.polyfill = function () {
1424
+ var t = void 0;
1425
+ if ("undefined" != typeof global) t = global; else if ("undefined" != typeof self) t = self; else try {
1426
+ t = Function("return this")()
1427
+ } catch (t) {
1428
+ throw new Error("polyfill failed because global object is unavailable in this environment")
1429
+ }
1430
+ var e = t.Promise;
1431
+ if (e) {
1432
+ var n = null;
1433
+ try {
1434
+ n = Object.prototype.toString.call(e.resolve())
1435
+ } catch (t) {
1436
+ }
1437
+ if ("[object Promise]" === n && !e.cast) return
1438
+ }
1439
+ t.Promise = U
1440
+ }, U.Promise = U, U
1441
+ }, t.exports = r()
1442
+ }, function (t, e, n) {
1443
+ var r = n(50);
1444
+ t.exports = new r("twttr")
1445
+ }, function (t, e, n) {
1446
+ var r = n(1), i = n(0);
1447
+
1448
+ function o(t) {
1449
+ return i.isType("string", t) ? t.split(".") : i.isType("array", t) ? t : []
1450
+ }
1451
+
1452
+ function s(t, e) {
1453
+ (e = e || r)[t] = e[t] || {}, Object.defineProperty(this, "base", {value: e[t]}), Object.defineProperty(this, "name", {value: t})
1454
+ }
1455
+
1456
+ i.aug(s.prototype, {
1457
+ get: function (t) {
1458
+ return o(t).reduce(function (t, e) {
1459
+ if (i.isObject(t)) return t[e]
1460
+ }, this.base)
1461
+ }, set: function (t, e, n) {
1462
+ var r = o(t), s = function (t, e) {
1463
+ var n = o(e).slice(0, -1);
1464
+ return n.reduce(function (t, e, r) {
1465
+ if (t[e] = t[e] || {}, !i.isObject(t[e])) throw new Error(n.slice(0, r + 1).join(".") + " is already defined with a value.");
1466
+ return t[e]
1467
+ }, t)
1468
+ }(this.base, t), a = r.slice(-1);
1469
+ return n && a in s ? s[a] : s[a] = e
1470
+ }, init: function (t, e) {
1471
+ return this.set(t, e, !0)
1472
+ }, unset: function (t) {
1473
+ var e = o(t), n = this.get(e.slice(0, -1));
1474
+ n && delete n[e.slice(-1)]
1475
+ }, aug: function (t) {
1476
+ var e = this.get(t), n = i.toRealArray(arguments).slice(1);
1477
+ if (e = void 0 !== e ? e : {}, n.unshift(e), !n.every(i.isObject)) throw new Error("Cannot augment non-object.");
1478
+ return this.set(t, i.aug.apply(null, n))
1479
+ }, call: function (t) {
1480
+ var e = this.get(t), n = i.toRealArray(arguments).slice(1);
1481
+ if (!i.isType("function", e)) throw new Error("Function " + t + "does not exist.");
1482
+ return e.apply(null, n)
1483
+ }, fullPath: function (t) {
1484
+ var e = o(t);
1485
+ return e.unshift(this.name), e.join(".")
1486
+ }
1487
+ }), t.exports = s
1488
+ }, function (t, e, n) {
1489
+ var r = n(0), i = n(7), o = {
1490
+ bind: function (t, e) {
1491
+ return this._handlers = this._handlers || {}, this._handlers[t] = this._handlers[t] || [], this._handlers[t].push(e)
1492
+ }, unbind: function (t, e) {
1493
+ var n;
1494
+ this._handlers && this._handlers[t] && (e ? (n = this._handlers[t].indexOf(e)) >= 0 && this._handlers[t].splice(n, 1) : this._handlers[t] = [])
1495
+ }, trigger: function (t, e) {
1496
+ var n = this._handlers && this._handlers[t];
1497
+ (e = e || {}).type = t, n && n.forEach(function (t) {
1498
+ r.async(i(t, this, e))
1499
+ })
1500
+ }
1501
+ };
1502
+ t.exports = {
1503
+ Emitter: o, makeEmitter: function () {
1504
+ return r.aug(function () {
1505
+ }, o)
1506
+ }
1507
+ }
1508
+ }, function (t, e) {
1509
+ var n = "tfw_horizon_tweet_embed_9555";
1510
+ t.exports = {
1511
+ isHorizonTweetEnabled: function (t) {
1512
+ return !(!t || !t[n] || "hte" !== t[n].bucket)
1513
+ }
1514
+ }
1515
+ }, function (t, e, n) {
1516
+ var r = n(101), i = n(76), o = n(6), s = n(22), a = n(7), u = n(0), c = new i(function (t) {
1517
+ var e = function (t) {
1518
+ return t.reduce(function (t, e) {
1519
+ return t[e._className] = t[e._className] || [], t[e._className].push(e), t
1520
+ }, {})
1521
+ }(t.map(r.fromRawTask));
1522
+ u.forIn(e, function (t, e) {
1523
+ s.allSettled(e.map(function (t) {
1524
+ return t.initialize()
1525
+ })).then(function () {
1526
+ e.forEach(function (t) {
1527
+ o.all([t.hydrate(), t.insertIntoDom()]).then(a(t.render, t)).then(a(t.success, t), a(t.fail, t))
1528
+ })
1529
+ })
1530
+ })
1531
+ });
1532
+ t.exports = {
1533
+ addWidget: function (t) {
1534
+ return c.add(t)
1535
+ }
1536
+ }
1537
+ }, function (t, e, n) {
1538
+ var r = n(18);
1539
+ t.exports = function (t) {
1540
+ return r.write(function () {
1541
+ t && t.parentNode && t.parentNode.removeChild(t)
1542
+ })
1543
+ }
1544
+ }, function (t, e, n) {
1545
+ n(12), t.exports = {
1546
+ log: function (t, e) {
1547
+ }
1548
+ }
1549
+ }, function (t, e, n) {
1550
+ var r = n(1);
1551
+
1552
+ function i(t) {
1553
+ return (t = t || r).getSelection && t.getSelection()
1554
+ }
1555
+
1556
+ t.exports = {
1557
+ getSelection: i, getSelectedText: function (t) {
1558
+ var e = i(t);
1559
+ return e ? e.toString() : ""
1560
+ }
1561
+ }
1562
+ }, function (t, e, n) {
1563
+ var r = n(4), i = n(1), o = n(2), s = 2e4;
1564
+ t.exports = function (t) {
1565
+ var e = new o, n = r.createElement("img");
1566
+ return n.onload = n.onerror = function () {
1567
+ i.setTimeout(e.resolve, 50)
1568
+ }, n.src = t, i.setTimeout(e.reject, s), e.promise
1569
+ }
1570
+ }, function (t, e, n) {
1571
+ var r = n(111);
1572
+ t.exports = function (t) {
1573
+ t.define("createElement", r), t.define("createFragment", r), t.define("htmlToElement", r), t.define("hasSelectedText", r), t.define("addRootClass", r), t.define("removeRootClass", r), t.define("hasRootClass", r), t.define("prependStyleSheet", r), t.define("appendStyleSheet", r), t.define("prependCss", r), t.define("appendCss", r), t.define("makeVisible", r), t.define("injectWidgetEl", r), t.define("matchHeightToContent", r), t.define("matchWidthToContent", r)
1574
+ }
1575
+ }, function (t, e) {
1576
+ t.exports = function (t) {
1577
+ var e, n = !1;
1578
+ return function () {
1579
+ return n ? e : (n = !0, e = t.apply(this, arguments))
1580
+ }
1581
+ }
1582
+ }, function (t, e, n) {
1583
+ var r = n(15), i = n(120), o = n(61), s = n(16);
1584
+ t.exports = function (t, e, n) {
1585
+ return new r(i, o, s.DM_BUTTON, t, e, n)
1586
+ }
1587
+ }, function (t, e, n) {
1588
+ var r = n(62), i = n(25);
1589
+ t.exports = r.isSupported() ? r : i
1590
+ }, function (t, e, n) {
1591
+ var r = n(26), i = n(121);
1592
+ t.exports = r.build([i])
1593
+ }, function (t, e, n) {
1594
+ var r = n(15), i = n(124), o = n(37), s = n(16);
1595
+ t.exports = function (t, e, n) {
1596
+ return new r(i, o, s.FOLLOW_BUTTON, t, e, n)
1597
+ }
1598
+ }, function (t, e, n) {
1599
+ var r = n(15), i = n(132), o = n(25), s = n(16);
1600
+ t.exports = function (t, e, n) {
1601
+ return new r(i, o, s.MOMENT, t, e, n)
1602
+ }
1603
+ }, function (t, e, n) {
1604
+ var r = n(15), i = n(134), o = n(25), s = n(16);
1605
+ t.exports = function (t, e, n) {
1606
+ return new r(i, o, s.PERISCOPE, t, e, n)
1607
+ }
1608
+ }, function (t, e, n) {
1609
+ var r = n(82), i = n(136), o = n(140), s = n(142), a = n(144), u = n(146),
1610
+ c = {collection: i, event: o, likes: s, list: a, profile: u, url: l}, d = [u, s, i, a, o];
1611
+
1612
+ function l(t) {
1613
+ return r(d, function (e) {
1614
+ try {
1615
+ return new e(t)
1616
+ } catch (t) {
1617
+ }
1618
+ })
1619
+ }
1620
+
1621
+ t.exports = function (t) {
1622
+ return t ? function (t) {
1623
+ var e, n;
1624
+ return e = (t.sourceType + "").toLowerCase(), (n = c[e]) ? new n(t) : null
1625
+ }(t) || l(t) : null
1626
+ }
1627
+ }, function (t, e, n) {
1628
+ var r = n(15), i = n(148), o = n(25), s = n(16);
1629
+ t.exports = function (t, e, n) {
1630
+ return new r(i, o, s.TIMELINE, t, e, n)
1631
+ }
1632
+ }, function (t, e, n) {
1633
+ var r = n(15), i = n(4), o = n(37), s = n(150), a = n(61), u = n(151), c = n(16);
1634
+ t.exports = function (t, e, n, d) {
1635
+ var l;
1636
+ return d ? (l = i.createElement("div"), new r(s, o, c.TWEET, t, e, n, {sandboxWrapperEl: l})) : new r(u, a, c.TWEET, t, e, n)
1637
+ }
1638
+ }, function (t, e, n) {
1639
+ var r = n(15), i = n(153), o = n(37), s = n(16);
1640
+ t.exports = function (t, e, n) {
1641
+ var a = t && t.type || "share",
1642
+ u = "hashtag" == a ? s.HASHTAG_BUTTON : "mention" == a ? s.MENTION_BUTTON : s.SHARE_BUTTON;
1643
+ return new r(i, o, u, t, e, n)
1644
+ }
1645
+ }, function (t, e, n) {
1646
+ var r = n(39), i = n(38), o = n(0);
1647
+ t.exports = function (t) {
1648
+ var e = {
1649
+ widget_origin: i.rootDocumentLocation(),
1650
+ widget_frame: i.isFramed() ? i.currentDocumentLocation() : null,
1651
+ duration_ms: t.duration,
1652
+ item_ids: t.widgetIds || []
1653
+ }, n = o.aug(t.namespace, {page: "page", component: "performance"});
1654
+ r.scribe(n, e)
1655
+ }
1656
+ }, function (t, e, n) {
1657
+ var r = n(0), i = n(137), o = ["ar", "fa", "he", "ur"];
1658
+ t.exports = {
1659
+ isRtlLang: function (t) {
1660
+ return t = String(t).toLowerCase(), r.contains(o, t)
1661
+ }, matchLanguage: function (t) {
1662
+ return t = (t = (t || "").toLowerCase()).replace("_", "-"), i(t) ? t : (t = t.replace(/-.*/, ""), i(t) ? t : "en")
1663
+ }
1664
+ }
1665
+ }, function (t) {
1666
+ t.exports = {
1667
+ tweetButtonHtmlPath: "/widgets/tweet_button.d6364fae9340b0be5f13818370141fd0.{{lang}}.html",
1668
+ followButtonHtmlPath: "/widgets/follow_button.d6364fae9340b0be5f13818370141fd0.{{lang}}.html",
1669
+ hubHtmlPath: "/widgets/hub.html",
1670
+ widgetIframeHtmlPath: "/widgets/widget_iframe.d6364fae9340b0be5f13818370141fd0.html",
1671
+ resourceBaseUrl: "https://platform.twitter.com"
1672
+ }
1673
+ }, function (t, e, n) {
1674
+ var r = n(3), i = n(98), o = n(24), s = n(11), a = {
1675
+ favorite: ["favorite", "like"],
1676
+ follow: ["follow"],
1677
+ like: ["favorite", "like"],
1678
+ retweet: ["retweet"],
1679
+ tweet: ["tweet"]
1680
+ };
1681
+
1682
+ function u(t) {
1683
+ this.srcEl = [], this.element = t
1684
+ }
1685
+
1686
+ u.open = function (t, e, n) {
1687
+ var u = (r.intentType(t) || "").toLowerCase();
1688
+ r.isTwitterURL(t) && (function (t, e) {
1689
+ i.open(t, {}, e)
1690
+ }(t, n), e && o.trigger("click", {
1691
+ target: e,
1692
+ region: "intent",
1693
+ type: "click",
1694
+ data: {}
1695
+ }), e && a[u] && a[u].forEach(function (n) {
1696
+ o.trigger(n, {
1697
+ target: e, region: "intent", type: n, data: function (t, e) {
1698
+ var n = s.decodeURL(e);
1699
+ switch (t) {
1700
+ case"favorite":
1701
+ case"like":
1702
+ return {tweet_id: n.tweet_id};
1703
+ case"follow":
1704
+ return {screen_name: n.screen_name, user_id: n.user_id};
1705
+ case"retweet":
1706
+ return {source_tweet_id: n.tweet_id};
1707
+ default:
1708
+ return {}
1709
+ }
1710
+ }(u, t)
1711
+ })
1712
+ }))
1713
+ }, t.exports = u
1714
+ }, function (t, e) {
1715
+ t.exports = {
1716
+ getTimezoneOffset: function () {
1717
+ var t = (new Date).toString().match(/(GMT[+-]?\d+)/);
1718
+ return t && t[0] || "GMT"
1719
+ }
1720
+ }
1721
+ }, function (t, e, n) {
1722
+ var r = n(4), i = n(9), o = n(2), s = n(0), a = n(11), u = "cb", c = 0;
1723
+ t.exports = {
1724
+ fetch: function (t, e, n, d) {
1725
+ var l, f, h;
1726
+ return d = function (t) {
1727
+ if (t) return t.replace(/[^\w$]/g, "_")
1728
+ }(d || u + c++), l = i.fullPath(["callbacks", d]), f = r.createElement("script"), h = new o, e = s.aug({}, e, {
1729
+ callback: l,
1730
+ suppress_response_codes: !0
1731
+ }), i.set(["callbacks", d], function (t) {
1732
+ var e;
1733
+ t = (e = n(t || !1)).resp, e.success ? h.resolve(t) : h.reject(t), f.onload = f.onreadystatechange = null, f.parentNode && f.parentNode.removeChild(f), i.unset(["callbacks", d])
1734
+ }), f.onerror = function () {
1735
+ h.reject(new Error("failed to fetch " + f.src))
1736
+ }, f.src = a.url(t, e), f.async = "async", r.body.appendChild(f), h.promise
1737
+ }
1738
+ }
1739
+ }, function (t, e, n) {
1740
+ var r = n(2), i = n(103), o = n(7);
1741
+
1742
+ function s(t) {
1743
+ this._inputsQueue = [], this._task = t, this._hasFlushBeenScheduled = !1
1744
+ }
1745
+
1746
+ s.prototype.add = function (t) {
1747
+ var e = new r;
1748
+ return this._inputsQueue.push({
1749
+ input: t,
1750
+ taskDoneDeferred: e
1751
+ }), this._hasFlushBeenScheduled || (this._hasFlushBeenScheduled = !0, i(o(this._flush, this))), e.promise
1752
+ }, s.prototype._flush = function () {
1753
+ try {
1754
+ this._task.call(null, this._inputsQueue)
1755
+ } catch (t) {
1756
+ this._inputsQueue.forEach(function (e) {
1757
+ e.taskDoneDeferred.reject(t)
1758
+ })
1759
+ }
1760
+ this._inputsQueue = [], this._hasFlushBeenScheduled = !1
1761
+ }, t.exports = s
1762
+ }, function (t, e) {
1763
+ t.exports = function (t, e) {
1764
+ return t.reduce(function (t, n) {
1765
+ var r = e(n);
1766
+ return t[r] = t[r] || [], t[r].push(n), t
1767
+ }, {})
1768
+ }
1769
+ }, function (t, e, n) {
1770
+ var r = n(4), i = n(8), o = n(3);
1771
+
1772
+ function s(t, e) {
1773
+ var n, r;
1774
+ return e = e || i, /^https?:\/\//.test(t) ? t : /^\/\//.test(t) ? e.protocol + t : (n = e.host + (e.port.length ? ":" + e.port : ""), 0 !== t.indexOf("/") && ((r = e.pathname.split("/")).pop(), r.push(t), t = "/" + r.join("/")), [e.protocol, "//", n, t].join(""))
1775
+ }
1776
+
1777
+ t.exports = {
1778
+ absolutize: s, getCanonicalURL: function () {
1779
+ for (var t, e = r.getElementsByTagName("link"), n = 0; e[n]; n++) if ("canonical" == (t = e[n]).rel) return s(t.href)
1780
+ }, getScreenNameFromPage: function () {
1781
+ for (var t, e, n, i = [r.getElementsByTagName("a"), r.getElementsByTagName("link")], s = 0, a = 0, u = /\bme\b/; t = i[s]; s++) for (a = 0; e = t[a]; a++) if (u.test(e.rel) && (n = o.screenName(e.href))) return n
1782
+ }
1783
+ }
1784
+ }, function (t, e, n) {
1785
+ var r = n(8), i = /^[^#?]*\.(gov|mil)(:\d+)?([#?].*)?$/i, o = {};
1786
+
1787
+ function s(t) {
1788
+ return t in o ? o[t] : o[t] = i.test(t)
1789
+ }
1790
+
1791
+ t.exports = {
1792
+ isUrlSensitive: s, isHostPageSensitive: function () {
1793
+ return s(r.host)
1794
+ }
1795
+ }
1796
+ }, function (t, e, n) {
1797
+ var r = n(20), i = n(55), o = n(11), s = n(34), a = n(0), u = n(9).get("scribeCallback"),
1798
+ c = 2083, d = [], l = o.url(s.CLIENT_EVENT_ENDPOINT, {dnt: 0, l: ""}),
1799
+ f = encodeURIComponent(l).length;
1800
+
1801
+ function h(t, e, n, r) {
1802
+ var i = !a.isObject(t), o = !!e && !a.isObject(e);
1803
+ i || o || (u && u(arguments), p(s.formatClientEventNamespace(t), s.formatClientEventData(e, n, r), s.CLIENT_EVENT_ENDPOINT))
1804
+ }
1805
+
1806
+ function p(t, e, n) {
1807
+ var r, u;
1808
+ n && a.isObject(t) && a.isObject(e) && (i.log(t, e), r = s.flattenClientEventPayload(t, e), u = {l: s.stringify(r)}, s.noticeSeen(t) && (u.notice_seen = !0), r.dnt && (u.dnt = 1), w(o.url(n, u)))
1809
+ }
1810
+
1811
+ function m(t, e, n, r) {
1812
+ var i = !a.isObject(t), o = !!e && !a.isObject(e);
1813
+ if (!i && !o) return v(s.flattenClientEventPayload(s.formatClientEventNamespace(t), s.formatClientEventData(e, n, r)))
1814
+ }
1815
+
1816
+ function v(t) {
1817
+ return d.push(t), d
1818
+ }
1819
+
1820
+ function g(t) {
1821
+ return encodeURIComponent(t).length + 3
1822
+ }
1823
+
1824
+ function w(t) {
1825
+ return (new Image).src = t
1826
+ }
1827
+
1828
+ t.exports = {
1829
+ canFlushOneItem: function (t) {
1830
+ var e = g(s.stringify(t));
1831
+ return f + e < c
1832
+ }, _enqueueRawObject: v, scribe: p, clientEvent: h, clientEvent2: function (t, e, n) {
1833
+ return h(t, e, n, 2)
1834
+ }, enqueueClientEvent: m, flushClientEvents: function () {
1835
+ var t;
1836
+ return d.length > 1 && m({
1837
+ page: "widgets_js",
1838
+ component: "scribe_pixel",
1839
+ action: "batch_log"
1840
+ }, {}), t = d, d = [], t.reduce(function (e, n, r) {
1841
+ var i = e.length, o = i && e[i - 1];
1842
+ return r + 1 == t.length && n.event_namespace && "batch_log" == n.event_namespace.action && (n.message = ["entries:" + r, "requests:" + i].join("/")), function t(e) {
1843
+ return Array.isArray(e) || (e = [e]), e.reduce(function (e, n) {
1844
+ var r, i = s.stringify(n), o = g(i);
1845
+ return f + o < c ? e = e.concat(i) : (r = s.splitLogEntry(n)).length > 1 && (e = e.concat(t(r))), e
1846
+ }, [])
1847
+ }(n).forEach(function (t) {
1848
+ var n = g(t);
1849
+ (!o || o.urlLength + n > c) && (o = {
1850
+ urlLength: f,
1851
+ items: []
1852
+ }, e.push(o)), o.urlLength += n, o.items.push(t)
1853
+ }), e
1854
+ }, []).map(function (t) {
1855
+ var e = {l: t.items};
1856
+ return r.enabled() && (e.dnt = 1), w(o.url(s.CLIENT_EVENT_ENDPOINT, e))
1857
+ })
1858
+ }, interaction: function (t, e, n, r) {
1859
+ var i = s.extractTermsFromDOM(t.target || t.srcElement);
1860
+ i.action = r || "click", h(i, e, n)
1861
+ }
1862
+ }
1863
+ }, function (t, e, n) {
1864
+ var r = n(0), i = n(43);
1865
+ t.exports = function (t, e) {
1866
+ return i(t, e) ? [t] : r.toRealArray(t.querySelectorAll(e))
1867
+ }
1868
+ }, function (t, e) {
1869
+ t.exports = function (t, e, n) {
1870
+ for (var r, i = 0; i < t.length; i++) if (r = e.call(n, t[i], i, t)) return r
1871
+ }
1872
+ }, function (t, e) {
1873
+ t.exports = function () {
1874
+ return Math.floor(+new Date / 9e5)
1875
+ }
1876
+ }, function (t, e, n) {
1877
+ var r = n(12);
1878
+ t.exports = function (t) {
1879
+ var e, n;
1880
+ return e = t.headers && t.headers.status, !(n = t && !t.error && 200 === e) && t.headers && t.headers.message && r.publicError(t.headers.message), {
1881
+ success: n,
1882
+ resp: t
1883
+ }
1884
+ }
1885
+ }, function (t, e, n) {
1886
+ var r, i = n(29), o = 0;
1887
+
1888
+ function s() {
1889
+ r && r.length === o && (i.emitter.trigger(i.ALL_WIDGETS_AND_IMAGES_LOADED, r), r = null)
1890
+ }
1891
+
1892
+ i.emitter.bind(i.ALL_WIDGETS_RENDER_END, function (t) {
1893
+ r = t.widgets, s()
1894
+ }), t.exports = {
1895
+ reportImagesLoadForAWidget: function () {
1896
+ o++, s()
1897
+ }
1898
+ }
1899
+ }, , , , , , , , , function (t, e, n) {
1900
+ var r, i = n(2), o = n(4), s = n(96), a = n(49), u = n(9), c = n(97), d = n(24), l = n(100),
1901
+ f = n(154), h = n(162), p = n(163), m = n(29), v = n(35);
1902
+ n(164), m.emitter.trigger(m.START), u.set("widgets.init", !0), a.set("init", !0), p(), r = new i, s.exposeReadyPromise(r.promise, a.base, "_e"), a.set("widgets", f), a.set("widgets.load", l.load), a.set("events", d), h(function () {
1903
+ v.load(), r.resolve(a.base), c.attachTo(o), l.loadPage()
1904
+ })
1905
+ }, function (t, e) {
1906
+ t.exports = navigator
1907
+ }, function (t, e, n) {
1908
+ var r = n(7);
1909
+ t.exports = {
1910
+ exposeReadyPromise: function (t, e, n) {
1911
+ e.ready = r(t.then, t), n && Array.isArray(e[n]) && (e[n].forEach(r(t.then, t)), delete e[n])
1912
+ }
1913
+ }
1914
+ }, function (t, e, n) {
1915
+ var r = n(8), i = n(36), o = n(32), s = n(73), a = n(3);
1916
+
1917
+ function u(t) {
1918
+ var e, n, u;
1919
+ t.altKey || t.metaKey || t.shiftKey || (e = o.closest(function (t) {
1920
+ return "A" === t.tagName || "AREA" === t.tagName
1921
+ }, t.target)) && a.isIntentURL(e.href) && (n = (n = (n = [u = e.href, "original_referer=" + r.href].join(-1 == u.indexOf("?") ? "?" : "&")).replace(/^http[:]/, "https:")).replace(/^\/\//, "https://"), s.open(n, e), i.preventDefault(t))
1922
+ }
1923
+
1924
+ t.exports = {
1925
+ attachTo: function (t) {
1926
+ t.addEventListener("click", u, !1)
1927
+ }
1928
+ }
1929
+ }, function (t, e, n) {
1930
+ var r, i = n(1), o = n(99), s = n(36), a = n(32), u = n(21), c = n(3), d = n(23), l = n(0),
1931
+ f = "intent_", h = i.screen.width, p = i.screen.height;
1932
+
1933
+ function m(t, e) {
1934
+ function n(t) {
1935
+ return Math.round(t / 2)
1936
+ }
1937
+
1938
+ return t > e ? {coordinate: 0, size: e} : {coordinate: n(e) - n(t), size: t}
1939
+ }
1940
+
1941
+ function v(t, e, n) {
1942
+ var i, o;
1943
+ e = r.parse(e), n = n || {}, i = m(e.width, n.width || h), e.left = i.coordinate, e.width = i.size, o = m(e.height, n.height || p), e.top = o.coordinate, e.height = o.size, this.win = t, this.features = function (t) {
1944
+ var e = [];
1945
+ return l.forIn(t, function (t, n) {
1946
+ e.push(t + "=" + n)
1947
+ }), e.join(",")
1948
+ }(e)
1949
+ }
1950
+
1951
+ r = (new o).defaults({
1952
+ width: 550,
1953
+ height: 520,
1954
+ personalbar: "0",
1955
+ toolbar: "0",
1956
+ location: "1",
1957
+ scrollbars: "1",
1958
+ resizable: "1"
1959
+ }), v.prototype.open = function (t, e) {
1960
+ var n = e && "click" == e.type && a.closest("a", e.target),
1961
+ r = e && (e.altKey || e.metaKey || e.shiftKey), i = n && (u.ios() || u.android());
1962
+ if (c.isTwitterURL(t)) return r || i ? this : (this.name = f + d.generate(), this.popup = this.win.open(t, this.name, this.features), e && s.preventDefault(e), this)
1963
+ }, v.open = function (t, e, n) {
1964
+ return new v(i, e).open(t, n)
1965
+ }, t.exports = v
1966
+ }, function (t, e, n) {
1967
+ var r = n(5), i = n(0);
1968
+
1969
+ function o() {
1970
+ this.assertions = [], this._defaults = {}
1971
+ }
1972
+
1973
+ o.prototype.assert = function (t, e) {
1974
+ return this.assertions.push({fn: t, msg: e || "assertion failed"}), this
1975
+ }, o.prototype.defaults = function (t) {
1976
+ return this._defaults = t || this._defaults, this
1977
+ }, o.prototype.require = function (t) {
1978
+ var e = this;
1979
+ return (t = Array.isArray(t) ? t : i.toRealArray(arguments)).forEach(function (t) {
1980
+ e.assert(function (t) {
1981
+ return function (e) {
1982
+ return r.hasValue(e[t])
1983
+ }
1984
+ }(t), "required: " + t)
1985
+ }), this
1986
+ }, o.prototype.parse = function (t) {
1987
+ var e, n;
1988
+ if (e = i.aug({}, this._defaults, t || {}), (n = this.assertions.reduce(function (t, n) {
1989
+ return n.fn(e) || t.push(n.msg), t
1990
+ }, [])).length > 0) throw new Error(n.join("\n"));
1991
+ return e
1992
+ }, t.exports = o
1993
+ }, function (t, e, n) {
1994
+ var r = n(4), i = n(52), o = n(6), s = n(22), a = n(53), u = n(33), c = n(9), d = n(39),
1995
+ l = n(24), f = n(5), h = n(0), p = n(35), m = n(117), v = n(29);
1996
+
1997
+ function g() {
1998
+ var t = u.val("widgets:autoload") || !0;
1999
+ return !f.isFalseValue(t) && (f.isTruthValue(t) ? r.body : r.querySelectorAll(t))
2000
+ }
2001
+
2002
+ function w(t, e) {
2003
+ var n, i;
2004
+ return t = (t = t || r.body).length ? h.toRealArray(t) : [t], d.pause(), i = function (t, e) {
2005
+ return t.reduce(function (t, n) {
2006
+ return t.concat(m.reduce(function (t, r) {
2007
+ return t.concat(r(n, e))
2008
+ }, []))
2009
+ }, [])
2010
+ }(t, e), v.emitter.trigger(v.ALL_WIDGETS_RENDER_START, {widgets: i}), n = s.allResolved(i.map(function (t) {
2011
+ return a.addWidget(t)
2012
+ })).then(function (t) {
2013
+ l.trigger("loaded", {widgets: t}), t && t.length && v.emitter.trigger(v.ALL_WIDGETS_RENDER_END, {widgets: t})
2014
+ }), s.always(n, function () {
2015
+ d.resume()
2016
+ }), n
2017
+ }
2018
+
2019
+ function y(t) {
2020
+ return p.getExperiments().then(function (e) {
2021
+ return w(t, i.isHorizonTweetEnabled(e))
2022
+ })
2023
+ }
2024
+
2025
+ t.exports = {
2026
+ load: y, loadPage: function () {
2027
+ var t = g();
2028
+ return !1 === t ? o.resolve() : (c.set("widgets.loaded", !0), y(t))
2029
+ }, _getPageLoadTarget: g
2030
+ }
2031
+ }, function (t, e, n) {
2032
+ var r = n(10), i = n(18), o = n(24), s = n(54), a = n(6), u = n(22);
2033
+
2034
+ function c(t, e) {
2035
+ this._widget = null, this._sandbox = null, this._hydrated = !1, this._insertedIntoDom = !1, this._Sandbox = t.Sandbox, this._factory = t.factory, this._widgetParams = t.parameters, this._resolve = e, this._className = t.className, this._renderedClassName = t.className + "-rendered", this._errorClassName = t.className + "-error", this._srcEl = t.srcEl, this._targetGlobal = function (t) {
2036
+ return (t.srcEl || t.targetEl).ownerDocument.defaultView
2037
+ }(t), this._sandboxWrapperEl = t.options ? t.options.sandboxWrapperEl : null, this._insertionStrategy = function (e) {
2038
+ var n, r = t.srcEl, i = t.targetEl, o = t.options ? t.options.sandboxWrapperEl : null;
2039
+ o ? (o.appendChild(e), n = o) : n = e, r ? i.insertBefore(n, r) : i.appendChild(n)
2040
+ }
2041
+ }
2042
+
2043
+ c.fromRawTask = function (t) {
2044
+ return new c(t.input, t.taskDoneDeferred.resolve)
2045
+ }, c.prototype.initialize = function () {
2046
+ var t = this, e = new this._Sandbox(this._targetGlobal);
2047
+ return this._factory(this._widgetParams, e).then(function (n) {
2048
+ return t._widget = n, t._sandbox = e, n._sandboxWrapperEl = t._sandboxWrapperEl, n
2049
+ })
2050
+ }, c.prototype.insertIntoDom = function () {
2051
+ var t = this;
2052
+ return this._widget ? this._sandbox.insert(this._widget.id, {class: [this._className, this._renderedClassName].join(" ")}, null, this._insertionStrategy).then(function () {
2053
+ t._insertedIntoDom = !0
2054
+ }) : a.reject(new Error("cannot insert widget into DOM before it is initialized"))
2055
+ }, c.prototype.hydrate = function () {
2056
+ var t = this;
2057
+ return this._widget ? this._widget.hydrate().then(function () {
2058
+ t._hydrated = !0
2059
+ }) : a.reject(new Error("cannot hydrate widget before it is initialized"))
2060
+ }, c.prototype.render = function () {
2061
+ var t = this;
2062
+
2063
+ function e(e) {
2064
+ return s(t._sandbox.sandboxEl).then(function () {
2065
+ return a.reject(e)
2066
+ })
2067
+ }
2068
+
2069
+ return this._hydrated ? this._insertedIntoDom ? t._widget.render(t._sandbox).then(function () {
2070
+ return t._sandbox.onResize(function () {
2071
+ return t._widget.resize().then(function () {
2072
+ o.trigger("resize", {target: t._sandbox.sandboxEl})
2073
+ })
2074
+ }), t._widget.show()
2075
+ }).then(function () {
2076
+ return s(t._srcEl).then(function () {
2077
+ return t._sandbox.sandboxEl
2078
+ })
2079
+ }, e) : e(new Error("cannot render widget before DOM insertion")) : e(new Error("cannot render widget before hydration"))
2080
+ }, c.prototype.fail = function () {
2081
+ var t = this;
2082
+ return this._srcEl ? u.always(i.write(function () {
2083
+ r.add(t._srcEl, t._errorClassName)
2084
+ }), function () {
2085
+ o.trigger("rendered", {target: t._srcEl}), t._resolve(t._srcEl)
2086
+ }) : (t._resolve(), a.resolve())
2087
+ }, c.prototype.success = function () {
2088
+ o.trigger("rendered", {target: this._sandbox.sandboxEl}), this._resolve(this._sandbox.sandboxEl)
2089
+ }, t.exports = c
2090
+ }, function (t, e, n) {
2091
+ var r;
2092
+ !function () {
2093
+ "use strict";
2094
+ var i = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.msRequestAnimationFrame || function (t) {
2095
+ return window.setTimeout(t, 1e3 / 60)
2096
+ };
2097
+
2098
+ function o() {
2099
+ this.frames = [], this.lastId = 0, this.raf = i, this.batch = {
2100
+ hash: {},
2101
+ read: [],
2102
+ write: [],
2103
+ mode: null
2104
+ }
2105
+ }
2106
+
2107
+ o.prototype.read = function (t, e) {
2108
+ var n = this.add("read", t, e), r = n.id;
2109
+ return this.batch.read.push(n.id), "reading" === this.batch.mode || this.batch.scheduled ? r : (this.scheduleBatch(), r)
2110
+ }, o.prototype.write = function (t, e) {
2111
+ var n = this.add("write", t, e), r = this.batch.mode, i = n.id;
2112
+ return this.batch.write.push(n.id), "writing" === r || "reading" === r || this.batch.scheduled ? i : (this.scheduleBatch(), i)
2113
+ }, o.prototype.defer = function (t, e, n) {
2114
+ "function" == typeof t && (n = e, e = t, t = 1);
2115
+ var r = this, i = t - 1;
2116
+ return this.schedule(i, function () {
2117
+ r.run({fn: e, ctx: n})
2118
+ })
2119
+ }, o.prototype.clear = function (t) {
2120
+ if ("function" == typeof t) return this.clearFrame(t);
2121
+ t = Number(t);
2122
+ var e = this.batch.hash[t];
2123
+ if (e) {
2124
+ var n = this.batch[e.type], r = n.indexOf(t);
2125
+ delete this.batch.hash[t], ~r && n.splice(r, 1)
2126
+ }
2127
+ }, o.prototype.clearFrame = function (t) {
2128
+ var e = this.frames.indexOf(t);
2129
+ ~e && this.frames.splice(e, 1)
2130
+ }, o.prototype.scheduleBatch = function () {
2131
+ var t = this;
2132
+ this.schedule(0, function () {
2133
+ t.batch.scheduled = !1, t.runBatch()
2134
+ }), this.batch.scheduled = !0
2135
+ }, o.prototype.uniqueId = function () {
2136
+ return ++this.lastId
2137
+ }, o.prototype.flush = function (t) {
2138
+ for (var e; e = t.shift();) this.run(this.batch.hash[e])
2139
+ }, o.prototype.runBatch = function () {
2140
+ try {
2141
+ this.batch.mode = "reading", this.flush(this.batch.read), this.batch.mode = "writing", this.flush(this.batch.write), this.batch.mode = null
2142
+ } catch (t) {
2143
+ throw this.runBatch(), t
2144
+ }
2145
+ }, o.prototype.add = function (t, e, n) {
2146
+ var r = this.uniqueId();
2147
+ return this.batch.hash[r] = {id: r, fn: e, ctx: n, type: t}
2148
+ }, o.prototype.run = function (t) {
2149
+ var e = t.ctx || this, n = t.fn;
2150
+ if (delete this.batch.hash[t.id], !this.onError) return n.call(e);
2151
+ try {
2152
+ n.call(e)
2153
+ } catch (t) {
2154
+ this.onError(t)
2155
+ }
2156
+ }, o.prototype.loop = function () {
2157
+ var t, e = this, n = this.raf, r = !1;
2158
+
2159
+ function i() {
2160
+ var t = e.frames.shift();
2161
+ e.frames.length ? n(i) : e.looping = !1, t && t()
2162
+ }
2163
+
2164
+ this.looping || (t = setTimeout(function () {
2165
+ r = !0, i()
2166
+ }, 500), n(function () {
2167
+ r || (clearTimeout(t), i())
2168
+ }), this.looping = !0)
2169
+ }, o.prototype.schedule = function (t, e) {
2170
+ return this.frames[t] ? this.schedule(t + 1, e) : (this.loop(), this.frames[t] = e)
2171
+ };
2172
+ var s = new o;
2173
+ void 0 !== t && t.exports ? t.exports = s : void 0 === (r = function () {
2174
+ return s
2175
+ }.call(e, n, e, t)) || (t.exports = r)
2176
+ }()
2177
+ }, function (t, e, n) {
2178
+ var r = n(48).Promise;
2179
+ t.exports = r._asap
2180
+ }, function (t, e, n) {
2181
+ var r, i, o, s = n(4), a = n(1), u = n(30), c = n(20), d = n(2), l = n(6), f = n(55), h = n(34),
2182
+ p = n(0), m = n(25), v = n(9).get("scribeCallback"),
2183
+ g = Math.floor(1e3 * Math.random()) + "_", w = "rufous-frame-" + g + "-",
2184
+ y = "rufous-form-" + g + "-", b = 0, _ = !1, E = new d;
2185
+
2186
+ function x() {
2187
+ var t = o.createElement("form"), e = o.createElement("input"), n = o.createElement("input");
2188
+ return b++, t.action = h.CLIENT_EVENT_ENDPOINT, t.method = "POST", t.target = w + b, t.id = y + b, e.type = "hidden", e.name = "dnt", e.value = c.enabled(), n.type = "hidden", n.name = "tfw_redirect", n.value = h.RUFOUS_REDIRECT, t.appendChild(e), t.appendChild(n), t
2189
+ }
2190
+
2191
+ function T() {
2192
+ var t = w + b;
2193
+ return u({id: t, name: t, width: 0, height: 0, border: 0}, {display: "none"}, o.doc)
2194
+ }
2195
+
2196
+ t.exports = {
2197
+ clientEvent: function (t, e, n, i) {
2198
+ (function (t, e) {
2199
+ var n = !p.isObject(t), r = !!e && !p.isObject(e), i = n || r;
2200
+ return i
2201
+ })(t, e) || (v && v(arguments), E.promise.then(function () {
2202
+ !function (t, e) {
2203
+ var n, i, s;
2204
+ p.isObject(t) && p.isObject(e) && (f.log(t, e), s = h.flattenClientEventPayload(t, e), (n = r.firstChild).value = +(+n.value || s.dnt || 0), (i = o.createElement("input")).type = "hidden", i.name = "l", i.value = h.stringify(s), r.appendChild(i))
2205
+ }(h.formatClientEventNamespace(t), h.formatClientEventData(e, n, i))
2206
+ }))
2207
+ }, flush: function () {
2208
+ return E.promise.then(function () {
2209
+ var t;
2210
+ return r.children.length <= 2 ? l.reject() : (t = l.all([o.doc.body.appendChild(r), o.doc.body.appendChild(i)]).then(function (t) {
2211
+ var e = t[0], n = t[1];
2212
+ return n.addEventListener("load", function () {
2213
+ !function (t, e) {
2214
+ return function () {
2215
+ var n = t.parentNode;
2216
+ n && (n.removeChild(t), n.removeChild(e))
2217
+ }
2218
+ }(e, n)()
2219
+ }), e.submit(), t
2220
+ }), r = x(), i = T(), t)
2221
+ })
2222
+ }, init: function () {
2223
+ return _ ? E.promise : ((o = new m(a)).insert("rufous-sandbox", null, {display: "none"}, function (t) {
2224
+ s.body.appendChild(t)
2225
+ }).then(function () {
2226
+ o.setTitle("Twitter analytics iframe"), r = x(), i = T(), E.resolve([r, i])
2227
+ }), _ = !0, E.promise)
2228
+ }
2229
+ }
2230
+ }, function (t, e, n) {
2231
+ var r = n(0);
2232
+ t.exports = function t(e) {
2233
+ var n = [];
2234
+ return e.forEach(function (e) {
2235
+ var i = r.isType("array", e) ? t(e) : [e];
2236
+ n = n.concat(i)
2237
+ }), n
2238
+ }
2239
+ }, function (t, e) {
2240
+ t.exports = function (t) {
2241
+ return t.filter(function (e, n) {
2242
+ return t.indexOf(e) === n
2243
+ })
2244
+ }
2245
+ }, function (t, e, n) {
2246
+ var r = n(41), i = n(0), o = n(109);
2247
+
2248
+ function s() {
2249
+ r.apply(this, arguments)
2250
+ }
2251
+
2252
+ s.prototype = Object.create(r.prototype), i.aug(s.prototype, {factory: o}), t.exports = s
2253
+ }, function (t, e, n) {
2254
+ var r = n(22), i = n(0), o = n(7);
2255
+ t.exports = {
2256
+ before: function (t, e) {
2257
+ return function () {
2258
+ var n, i = this, o = arguments;
2259
+ return n = e.apply(this, arguments), r.isPromise(n) ? n.then(function () {
2260
+ return t.apply(i, o)
2261
+ }) : t.apply(this, arguments)
2262
+ }
2263
+ }, after: function (t, e) {
2264
+ return function () {
2265
+ var n, i = this, o = arguments;
2266
+
2267
+ function s(t, e) {
2268
+ return r.isPromise(e) ? e.then(function () {
2269
+ return t
2270
+ }) : t
2271
+ }
2272
+
2273
+ return n = t.apply(this, arguments), r.isPromise(n) ? n.then(function (t) {
2274
+ return s(t, e.apply(i, o))
2275
+ }) : s(n, e.apply(this, arguments))
2276
+ }
2277
+ }, around: function (t, e) {
2278
+ return function () {
2279
+ var n = i.toRealArray(arguments);
2280
+ return n.unshift(o(t, this)), e.apply(this, n)
2281
+ }
2282
+ }
2283
+ }
2284
+ }, function (t, e, n) {
2285
+ var r = n(10), i = n(18), o = n(42), s = n(6), a = n(0);
2286
+ t.exports = function () {
2287
+ var t = o();
2288
+
2289
+ function e(e) {
2290
+ t.apply(this, arguments), Object.defineProperty(this, "targetGlobal", {value: e})
2291
+ }
2292
+
2293
+ return e.prototype = Object.create(t.prototype), a.aug(e.prototype, {
2294
+ id: null,
2295
+ initialized: !1,
2296
+ width: 0,
2297
+ height: 0,
2298
+ sandboxEl: null,
2299
+ insert: function () {
2300
+ return s.reject()
2301
+ },
2302
+ onResize: function () {
2303
+ },
2304
+ addClass: function (t) {
2305
+ var e = this.sandboxEl;
2306
+ return t = Array.isArray(t) ? t : [t], i.write(function () {
2307
+ t.forEach(function (t) {
2308
+ r.add(e, t)
2309
+ })
2310
+ })
2311
+ },
2312
+ removeClass: function (t) {
2313
+ var e = this.sandboxEl;
2314
+ return t = Array.isArray(t) ? t : [t], i.write(function () {
2315
+ t.forEach(function (t) {
2316
+ r.remove(e, t)
2317
+ })
2318
+ })
2319
+ },
2320
+ styleSelf: function (t) {
2321
+ var e = this;
2322
+ return i.write(function () {
2323
+ a.forIn(t, function (t, n) {
2324
+ e.sandboxEl.style[t] = n
2325
+ })
2326
+ })
2327
+ }
2328
+ }), e
2329
+ }
2330
+ }, function (t, e, n) {
2331
+ var r = n(4), i = n(10), o = n(18), s = n(56), a = n(26), u = n(57), c = n(45), d = n(46),
2332
+ l = n(30), f = n(12), h = n(47), p = n(2), m = n(6), v = n(0), g = n(9), w = n(23),
2333
+ y = n(7), b = {allowfullscreen: "true"}, _ = {
2334
+ position: "absolute",
2335
+ visibility: "hidden",
2336
+ display: "block",
2337
+ width: "0px",
2338
+ height: "0px",
2339
+ padding: "0",
2340
+ border: "none"
2341
+ }, E = {position: "static", visibility: "visible"}, x = "SandboxRoot",
2342
+ T = ".SandboxRoot { display: none; }", A = 50;
2343
+
2344
+ function S(t, e, n, r) {
2345
+ return e = v.aug({id: t}, b, e), n = v.aug({}, _, n), l(e, n, r)
2346
+ }
2347
+
2348
+ function R(t, e, n, i, s) {
2349
+ var a = new p, u = w.generate(), c = S(t, e, n, s);
2350
+ return g.set(["sandbox", u], function () {
2351
+ var t = c.contentWindow.document;
2352
+ o.write(function () {
2353
+ t.write("<!DOCTYPE html><html><head></head><body></body></html>")
2354
+ }).then(function () {
2355
+ t.close(), a.resolve(c)
2356
+ })
2357
+ }), c.src = ["javascript:", 'document.write("");', "try { window.parent.document; }", 'catch (e) { document.domain="' + r.domain + '"; }', "window.parent." + g.fullPath(["sandbox", u]) + "();"].join(""), c.addEventListener("error", a.reject, !1), o.write(function () {
2358
+ i.parentNode.replaceChild(c, i)
2359
+ }), a.promise
2360
+ }
2361
+
2362
+ t.exports = a.couple(n(58), function (t) {
2363
+ t.overrideProperty("id", {
2364
+ get: function () {
2365
+ return this.sandboxEl && this.sandboxEl.id
2366
+ }
2367
+ }), t.overrideProperty("initialized", {
2368
+ get: function () {
2369
+ return !!this.win
2370
+ }
2371
+ }), t.overrideProperty("width", {
2372
+ get: function () {
2373
+ return this._width
2374
+ }
2375
+ }), t.overrideProperty("height", {
2376
+ get: function () {
2377
+ return this._height
2378
+ }
2379
+ }), t.overrideProperty("sandboxEl", {
2380
+ get: function () {
2381
+ return this.iframeEl
2382
+ }
2383
+ }), t.defineProperty("iframeEl", {
2384
+ get: function () {
2385
+ return this._iframe
2386
+ }
2387
+ }), t.defineProperty("rootEl", {
2388
+ get: function () {
2389
+ return this.doc && this.doc.documentElement
2390
+ }
2391
+ }), t.defineProperty("widgetEl", {
2392
+ get: function () {
2393
+ return this.doc && this.doc.body.firstElementChild
2394
+ }
2395
+ }), t.defineProperty("win", {
2396
+ get: function () {
2397
+ return this.iframeEl && this.iframeEl.contentWindow
2398
+ }
2399
+ }), t.defineProperty("doc", {
2400
+ get: function () {
2401
+ return this.win && this.win.document
2402
+ }
2403
+ }), t.define("_updateCachedDimensions", function () {
2404
+ var t = this;
2405
+ return o.read(function () {
2406
+ var e, n = h(t.sandboxEl);
2407
+ "visible" == t.sandboxEl.style.visibility ? t._width = n.width : (e = h(t.sandboxEl.parentElement).width, t._width = Math.min(n.width, e)), t._height = n.height
2408
+ })
2409
+ }), t.define("_setTargetToBlank", function () {
2410
+ var t = this.createElement("base");
2411
+ t.target = "_blank", this.doc.head.appendChild(t)
2412
+ }), t.define("_didResize", function () {
2413
+ var t = this, e = this._resizeHandlers.slice(0);
2414
+ return this._updateCachedDimensions().then(function () {
2415
+ e.forEach(function (e) {
2416
+ e(t)
2417
+ })
2418
+ })
2419
+ }), t.define("setTitle", function (t) {
2420
+ this.iframeEl.title = t
2421
+ }), t.override("createElement", function (t) {
2422
+ return this.doc.createElement(t)
2423
+ }), t.override("createFragment", function () {
2424
+ return this.doc.createDocumentFragment()
2425
+ }), t.override("htmlToElement", function (t) {
2426
+ var e;
2427
+ return (e = this.createElement("div")).innerHTML = t, e.firstElementChild
2428
+ }), t.override("hasSelectedText", function () {
2429
+ return !!s.getSelectedText(this.win)
2430
+ }), t.override("addRootClass", function (t) {
2431
+ var e = this.rootEl;
2432
+ return t = Array.isArray(t) ? t : [t], this.initialized ? o.write(function () {
2433
+ t.forEach(function (t) {
2434
+ i.add(e, t)
2435
+ })
2436
+ }) : m.reject(new Error("sandbox not initialized"))
2437
+ }), t.override("removeRootClass", function (t) {
2438
+ var e = this.rootEl;
2439
+ return t = Array.isArray(t) ? t : [t], this.initialized ? o.write(function () {
2440
+ t.forEach(function (t) {
2441
+ i.remove(e, t)
2442
+ })
2443
+ }) : m.reject(new Error("sandbox not initialized"))
2444
+ }), t.override("hasRootClass", function (t) {
2445
+ return i.present(this.rootEl, t)
2446
+ }), t.define("addStyleSheet", function (t, e) {
2447
+ var n, r = new p;
2448
+ return this.initialized ? ((n = this.createElement("link")).type = "text/css", n.rel = "stylesheet", n.href = t, n.addEventListener("load", r.resolve, !1), n.addEventListener("error", r.reject, !1), o.write(y(e, null, n)).then(function () {
2449
+ return u(t).then(r.resolve, r.reject), r.promise
2450
+ })) : m.reject(new Error("sandbox not initialized"))
2451
+ }), t.override("prependStyleSheet", function (t) {
2452
+ var e = this.doc;
2453
+ return this.addStyleSheet(t, function (t) {
2454
+ var n = e.head.firstElementChild;
2455
+ return n ? e.head.insertBefore(t, n) : e.head.appendChild(t)
2456
+ })
2457
+ }), t.override("appendStyleSheet", function (t) {
2458
+ var e = this.doc;
2459
+ return this.addStyleSheet(t, function (t) {
2460
+ return e.head.appendChild(t)
2461
+ })
2462
+ }), t.define("addCss", function (t, e) {
2463
+ var n;
2464
+ return c.inlineStyle() ? ((n = this.createElement("style")).type = "text/css", n.appendChild(this.doc.createTextNode(t)), o.write(y(e, null, n))) : (f.devError("CSP enabled; cannot embed inline styles"), m.resolve())
2465
+ }), t.override("prependCss", function (t) {
2466
+ var e = this.doc;
2467
+ return this.addCss(t, function (t) {
2468
+ var n = e.head.firstElementChild;
2469
+ return n ? e.head.insertBefore(t, n) : e.head.appendChild(t)
2470
+ })
2471
+ }), t.override("appendCss", function (t) {
2472
+ var e = this.doc;
2473
+ return this.addCss(t, function (t) {
2474
+ return e.head.appendChild(t)
2475
+ })
2476
+ }), t.override("makeVisible", function () {
2477
+ var t = this;
2478
+ return this.styleSelf(E).then(function () {
2479
+ t._updateCachedDimensions()
2480
+ })
2481
+ }), t.override("injectWidgetEl", function (t) {
2482
+ var e = this;
2483
+ return this.initialized ? this.widgetEl ? m.reject(new Error("widget already injected")) : o.write(function () {
2484
+ e.doc.body.appendChild(t)
2485
+ }) : m.reject(new Error("sandbox not initialized"))
2486
+ }), t.override("matchHeightToContent", function () {
2487
+ var t, e = this;
2488
+ return o.read(function () {
2489
+ t = e.widgetEl ? h(e.widgetEl).height : 0
2490
+ }), o.write(function () {
2491
+ e.sandboxEl.style.height = t + "px"
2492
+ }).then(function () {
2493
+ return e._updateCachedDimensions()
2494
+ })
2495
+ }), t.override("matchWidthToContent", function () {
2496
+ var t, e = this;
2497
+ return o.read(function () {
2498
+ t = e.widgetEl ? h(e.widgetEl).width : 0
2499
+ }), o.write(function () {
2500
+ e.sandboxEl.style.width = t + "px"
2501
+ }).then(function () {
2502
+ return e._updateCachedDimensions()
2503
+ })
2504
+ }), t.after("initialize", function () {
2505
+ this._iframe = null, this._width = this._height = 0, this._resizeHandlers = []
2506
+ }), t.override("insert", function (t, e, n, r) {
2507
+ var i = this, s = new p, a = this.targetGlobal.document, u = S(t, e, n, a);
2508
+ return o.write(y(r, null, u)), u.addEventListener("load", function () {
2509
+ (function (t) {
2510
+ try {
2511
+ t.contentWindow.document
2512
+ } catch (t) {
2513
+ return m.reject(t)
2514
+ }
2515
+ return m.resolve(t)
2516
+ })(u).then(null, y(R, null, t, e, n, u, a)).then(s.resolve, s.reject)
2517
+ }, !1), u.addEventListener("error", s.reject, !1), s.promise.then(function (t) {
2518
+ var e = d(i._didResize, A, i);
2519
+ return i._iframe = t, i.win.addEventListener("resize", e, !1), m.all([i._setTargetToBlank(), i.addRootClass(x), i.prependCss(T)])
2520
+ })
2521
+ }), t.override("onResize", function (t) {
2522
+ this._resizeHandlers.push(t)
2523
+ }), t.after("styleSelf", function () {
2524
+ return this._updateCachedDimensions()
2525
+ })
2526
+ })
2527
+ }, function (t, e) {
2528
+ t.exports = function () {
2529
+ throw new Error("unimplemented method")
2530
+ }
2531
+ }, function (t, e, n) {
2532
+ var r = n(2), i = n(7), o = 100, s = 3e3;
2533
+
2534
+ function a(t, e) {
2535
+ this._inputsQueue = [], this._task = t, this._isPaused = !1, this._flushDelay = e && e.flushDelay || o, this._pauseLength = e && e.pauseLength || s, this._flushTimeout = void 0
2536
+ }
2537
+
2538
+ a.prototype.add = function (t) {
2539
+ var e = new r;
2540
+ return this._inputsQueue.push({
2541
+ input: t,
2542
+ taskDoneDeferred: e
2543
+ }), this._scheduleFlush(), e.promise
2544
+ }, a.prototype._scheduleFlush = function () {
2545
+ this._isPaused || (clearTimeout(this._flushTimeout), this._flushTimeout = setTimeout(i(this._flush, this), this._flushDelay))
2546
+ }, a.prototype._flush = function () {
2547
+ try {
2548
+ this._task.call(null, this._inputsQueue)
2549
+ } catch (t) {
2550
+ this._inputsQueue.forEach(function (e) {
2551
+ e.taskDoneDeferred.reject(t)
2552
+ })
2553
+ }
2554
+ this._inputsQueue = [], this._flushTimeout = void 0
2555
+ }, a.prototype.pause = function (t) {
2556
+ clearTimeout(this._flushTimeout), this._isPaused = !0, !t && this._pauseLength && setTimeout(i(this.resume, this), this._pauseLength)
2557
+ }, a.prototype.resume = function () {
2558
+ this._isPaused = !1, this._scheduleFlush()
2559
+ }, t.exports = a
2560
+ }, function (t, e, n) {
2561
+ var r, i = n(72), o = n(30), s = n(2), a = n(4), u = n(19), c = n(21), d = n(31), l = n(8),
2562
+ f = n(12), h = n(114), p = n(59), m = n(9), v = n(11), g = n(115), w = n(3), y = n(0),
2563
+ b = n(1), _ = p(function () {
2564
+ return new s
2565
+ });
2566
+
2567
+ function E(t) {
2568
+ var e = t || {should_obtain_cookie_consent: !0, experiments: {}};
2569
+ return new g(e.should_obtain_cookie_consent, e.experiments)
2570
+ }
2571
+
2572
+ t.exports = {
2573
+ load: function () {
2574
+ var t, e, n, s;
2575
+ if (c.ie9() || c.ie10() || "http:" !== l.protocol && "https:" !== l.protocol) return f.devError("Using default settings due to unsupported browser or protocol."), r = E(), void _().resolve();
2576
+ t = {origin: l.origin}, u.settings().indexOf("localhost") > -1 && (t.localSettings = !0), e = v.url(i.resourceBaseUrl + i.widgetIframeHtmlPath, t), n = function (t) {
2577
+ var n;
2578
+ if (e.substr(0, t.origin.length) === t.origin && w.isTwitterURL(t.origin)) try {
2579
+ (n = "string" == typeof t.data ? d.parse(t.data) : t.data).namespace === h.settings && (r = E(n.settings), _().resolve())
2580
+ } catch (t) {
2581
+ f.devError(t)
2582
+ }
2583
+ }, b.addEventListener("message", n), s = o({
2584
+ src: e,
2585
+ title: "Twitter settings iframe"
2586
+ }, {display: "none"}), a.body.appendChild(s)
2587
+ }, settingsLoaded: function () {
2588
+ var t, e, n;
2589
+ return t = new s, e = m.get("experimentOverride"), _().promise.then(function () {
2590
+ e && e.name && e.assignment && ((n = {})[e.name] = {bucket: e.assignment}, r.experiments = y.aug(r.experiments, n)), t.resolve(r)
2591
+ }).catch(function (e) {
2592
+ t.reject(e)
2593
+ }), t.promise
2594
+ }
2595
+ }
2596
+ }, function (t, e) {
2597
+ t.exports = {settings: "twttr.settings"}
2598
+ }, function (t, e) {
2599
+ t.exports = function (t, e) {
2600
+ this.shouldObtainCookieConsent = t, this.experiments = e || {}
2601
+ }
2602
+ }, function (t, e) {
2603
+ t.exports = function (t) {
2604
+ return t.split("").map(function (t) {
2605
+ return t.charCodeAt(0).toString(16)
2606
+ }).join("")
2607
+ }
2608
+ }, function (t, e, n) {
2609
+ t.exports = [n(118), n(123), n(131), n(133), n(135), n(149), n(152)]
2610
+ }, function (t, e, n) {
2611
+ var r = n(11), i = n(5), o = n(0), s = n(13), a = n(14)(), u = n(60), c = "a.twitter-dm-button";
2612
+ t.exports = function (t) {
2613
+ return a(t, c).map(function (t) {
2614
+ return u(function (t) {
2615
+ var e = t.getAttribute("data-show-screen-name"), n = s(t),
2616
+ a = t.getAttribute("href"), u = t.getAttribute("data-screen-name"),
2617
+ c = e ? i.asBoolean(e) : null, d = t.getAttribute("data-size"),
2618
+ l = r.decodeURL(a), f = l.recipient_id,
2619
+ h = t.getAttribute("data-text") || l.text,
2620
+ p = t.getAttribute("data-welcome-message-id") || l.welcomeMessageId;
2621
+ return o.aug(n, {
2622
+ screenName: u,
2623
+ showScreenName: c,
2624
+ size: d,
2625
+ text: h,
2626
+ userId: f,
2627
+ welcomeMessageId: p
2628
+ })
2629
+ }(t), t.parentNode, t)
2630
+ })
2631
+ }
2632
+ }, function (t, e, n) {
2633
+ var r = n(0);
2634
+ t.exports = function t(e) {
2635
+ var n;
2636
+ if (e) return n = e.lang || e.getAttribute("data-lang"), r.isType("string", n) ? n : t(e.parentElement)
2637
+ }
2638
+ }, function (t, e, n) {
2639
+ var r = n(2);
2640
+ t.exports = function (t, e) {
2641
+ var i = new r;
2642
+ return n.e(2).then(function (r) {
2643
+ var o;
2644
+ try {
2645
+ o = n(86), i.resolve(new o(t, e))
2646
+ } catch (t) {
2647
+ i.reject(t)
2648
+ }
2649
+ }.bind(null, n)).catch(function (t) {
2650
+ i.reject(t)
2651
+ }), i.promise
2652
+ }
2653
+ }, function (t, e, n) {
2654
+ var r = n(122), i = n(1), o = n(10), s = n(36), a = n(18), u = n(56), c = n(26), d = n(57),
2655
+ l = n(45), f = n(47), h = n(7), p = n(46), m = n(6), v = n(0), g = 50, w = {
2656
+ position: "absolute",
2657
+ visibility: "hidden",
2658
+ display: "block",
2659
+ transform: "rotate(0deg)"
2660
+ }, y = {position: "static", visibility: "visible"}, b = "twitter-widget", _ = "open",
2661
+ E = "SandboxRoot", x = ".SandboxRoot { display: none; max-height: 10000px; }";
2662
+ t.exports = c.couple(n(58), function (t) {
2663
+ t.defineStatic("isSupported", function () {
2664
+ return !!i.HTMLElement.prototype.attachShadow && l.inlineStyle()
2665
+ }), t.overrideProperty("id", {
2666
+ get: function () {
2667
+ return this.sandboxEl && this.sandboxEl.id
2668
+ }
2669
+ }), t.overrideProperty("initialized", {
2670
+ get: function () {
2671
+ return !!this._shadowHost
2672
+ }
2673
+ }), t.overrideProperty("width", {
2674
+ get: function () {
2675
+ return this._width
2676
+ }
2677
+ }), t.overrideProperty("height", {
2678
+ get: function () {
2679
+ return this._height
2680
+ }
2681
+ }), t.overrideProperty("sandboxEl", {
2682
+ get: function () {
2683
+ return this._shadowHost
2684
+ }
2685
+ }), t.define("_updateCachedDimensions", function () {
2686
+ var t = this;
2687
+ return a.read(function () {
2688
+ var e, n = f(t.sandboxEl);
2689
+ "visible" == t.sandboxEl.style.visibility ? t._width = n.width : (e = f(t.sandboxEl.parentElement).width, t._width = Math.min(n.width, e)), t._height = n.height
2690
+ })
2691
+ }), t.define("_didResize", function () {
2692
+ var t = this, e = this._resizeHandlers.slice(0);
2693
+ return this._updateCachedDimensions().then(function () {
2694
+ e.forEach(function (e) {
2695
+ e(t)
2696
+ })
2697
+ })
2698
+ }), t.override("createElement", function (t) {
2699
+ return this.targetGlobal.document.createElement(t)
2700
+ }), t.override("createFragment", function () {
2701
+ return this.targetGlobal.document.createDocumentFragment()
2702
+ }), t.override("htmlToElement", function (t) {
2703
+ var e;
2704
+ return (e = this.createElement("div")).innerHTML = t, e.firstElementChild
2705
+ }), t.override("hasSelectedText", function () {
2706
+ return !!u.getSelectedText(this.targetGlobal)
2707
+ }), t.override("addRootClass", function (t) {
2708
+ var e = this._shadowRootBody;
2709
+ return t = Array.isArray(t) ? t : [t], this.initialized ? a.write(function () {
2710
+ t.forEach(function (t) {
2711
+ o.add(e, t)
2712
+ })
2713
+ }) : m.reject(new Error("sandbox not initialized"))
2714
+ }), t.override("removeRootClass", function (t) {
2715
+ var e = this._shadowRootBody;
2716
+ return t = Array.isArray(t) ? t : [t], this.initialized ? a.write(function () {
2717
+ t.forEach(function (t) {
2718
+ o.remove(e, t)
2719
+ })
2720
+ }) : m.reject(new Error("sandbox not initialized"))
2721
+ }), t.override("hasRootClass", function (t) {
2722
+ return o.present(this._shadowRootBody, t)
2723
+ }), t.override("addStyleSheet", function (t, e) {
2724
+ return this.addCss('@import url("' + t + '");', e).then(function () {
2725
+ return d(t)
2726
+ })
2727
+ }), t.override("prependStyleSheet", function (t) {
2728
+ var e = this._shadowRoot;
2729
+ return this.addStyleSheet(t, function (t) {
2730
+ var n = e.firstElementChild;
2731
+ return n ? e.insertBefore(t, n) : e.appendChild(t)
2732
+ })
2733
+ }), t.override("appendStyleSheet", function (t) {
2734
+ var e = this._shadowRoot;
2735
+ return this.addStyleSheet(t, function (t) {
2736
+ return e.appendChild(t)
2737
+ })
2738
+ }), t.override("addCss", function (t, e) {
2739
+ var n;
2740
+ return this.initialized ? l.inlineStyle() ? ((n = this.createElement("style")).type = "text/css", n.appendChild(this.targetGlobal.document.createTextNode(t)), a.write(h(e, null, n))) : m.resolve() : m.reject(new Error("sandbox not initialized"))
2741
+ }), t.override("prependCss", function (t) {
2742
+ var e = this._shadowRoot;
2743
+ return this.addCss(t, function (t) {
2744
+ var n = e.firstElementChild;
2745
+ return n ? e.insertBefore(t, n) : e.appendChild(t)
2746
+ })
2747
+ }), t.override("appendCss", function (t) {
2748
+ var e = this._shadowRoot;
2749
+ return this.addCss(t, function (t) {
2750
+ return e.appendChild(t)
2751
+ })
2752
+ }), t.override("makeVisible", function () {
2753
+ return this.styleSelf(y)
2754
+ }), t.override("injectWidgetEl", function (t) {
2755
+ var e = this;
2756
+ return this.initialized ? this._shadowRootBody.firstElementChild ? m.reject(new Error("widget already injected")) : a.write(function () {
2757
+ e._shadowRootBody.appendChild(t)
2758
+ }).then(function () {
2759
+ return e._updateCachedDimensions()
2760
+ }).then(function () {
2761
+ var t = p(e._didResize, g, e);
2762
+ new r(e._shadowRootBody, t)
2763
+ }) : m.reject(new Error("sandbox not initialized"))
2764
+ }), t.override("matchHeightToContent", function () {
2765
+ return m.resolve()
2766
+ }), t.override("matchWidthToContent", function () {
2767
+ return m.resolve()
2768
+ }), t.override("insert", function (t, e, n, r) {
2769
+ var i = this.targetGlobal.document, o = this._shadowHost = i.createElement(b),
2770
+ u = this._shadowRoot = o.attachShadow({mode: _}),
2771
+ c = this._shadowRootBody = i.createElement("div");
2772
+ return v.forIn(e || {}, function (t, e) {
2773
+ o.setAttribute(t, e)
2774
+ }), o.id = t, u.appendChild(c), s.delegate(c, "click", "A", function (t, e) {
2775
+ e.hasAttribute("target") || e.setAttribute("target", "_blank")
2776
+ }), m.all([this.styleSelf(w), this.addRootClass(E), this.prependCss(x), a.write(r.bind(null, o))])
2777
+ }), t.override("onResize", function (t) {
2778
+ this._resizeHandlers.push(t)
2779
+ }), t.after("initialize", function () {
2780
+ this._shadowHost = this._shadowRoot = this._shadowRootBody = null, this._width = this._height = 0, this._resizeHandlers = []
2781
+ }), t.after("styleSelf", function () {
2782
+ return this._updateCachedDimensions()
2783
+ })
2784
+ })
2785
+ }, function (t, e) {
2786
+ var n;
2787
+ (n = function (t, e) {
2788
+ function r(t, e) {
2789
+ if (t.resizedAttached) {
2790
+ if (t.resizedAttached) return void t.resizedAttached.add(e)
2791
+ } else t.resizedAttached = new function () {
2792
+ var t, e;
2793
+ this.q = [], this.add = function (t) {
2794
+ this.q.push(t)
2795
+ }, this.call = function () {
2796
+ for (t = 0, e = this.q.length; t < e; t++) this.q[t].call()
2797
+ }
2798
+ }, t.resizedAttached.add(e);
2799
+ t.resizeSensor = document.createElement("div"), t.resizeSensor.className = "resize-sensor";
2800
+ var n = "position: absolute; left: 0; top: 0; right: 0; bottom: 0; overflow: hidden; z-index: -1; visibility: hidden;",
2801
+ r = "position: absolute; left: 0; top: 0; transition: 0s;";
2802
+ t.resizeSensor.style.cssText = n, t.resizeSensor.innerHTML = '<div class="resize-sensor-expand" style="' + n + '"><div style="' + r + '"></div></div><div class="resize-sensor-shrink" style="' + n + '"><div style="' + r + ' width: 200%; height: 200%"></div></div>', t.appendChild(t.resizeSensor), {
2803
+ fixed: 1,
2804
+ absolute: 1
2805
+ }[function (t, e) {
2806
+ return t.currentStyle ? t.currentStyle[e] : window.getComputedStyle ? window.getComputedStyle(t, null).getPropertyValue(e) : t.style[e]
2807
+ }(t, "position")] || (t.style.position = "relative");
2808
+ var i, o, s = t.resizeSensor.childNodes[0], a = s.childNodes[0],
2809
+ u = t.resizeSensor.childNodes[1], c = (u.childNodes[0], function () {
2810
+ a.style.width = s.offsetWidth + 10 + "px", a.style.height = s.offsetHeight + 10 + "px", s.scrollLeft = s.scrollWidth, s.scrollTop = s.scrollHeight, u.scrollLeft = u.scrollWidth, u.scrollTop = u.scrollHeight, i = t.offsetWidth, o = t.offsetHeight
2811
+ });
2812
+ c();
2813
+ var d = function (t, e, n) {
2814
+ t.attachEvent ? t.attachEvent("on" + e, n) : t.addEventListener(e, n)
2815
+ }, l = function () {
2816
+ t.offsetWidth == i && t.offsetHeight == o || t.resizedAttached && t.resizedAttached.call(), c()
2817
+ };
2818
+ d(s, "scroll", l), d(u, "scroll", l)
2819
+ }
2820
+
2821
+ var i = Object.prototype.toString.call(t),
2822
+ o = "[object Array]" === i || "[object NodeList]" === i || "[object HTMLCollection]" === i || "undefined" != typeof jQuery && t instanceof jQuery || "undefined" != typeof Elements && t instanceof Elements;
2823
+ if (o) for (var s = 0, a = t.length; s < a; s++) r(t[s], e); else r(t, e);
2824
+ this.detach = function () {
2825
+ if (o) for (var e = 0, r = t.length; e < r; e++) n.detach(t[e]); else n.detach(t)
2826
+ }
2827
+ }).detach = function (t) {
2828
+ t.resizeSensor && (t.removeChild(t.resizeSensor), delete t.resizeSensor, delete t.resizedAttached)
2829
+ }, void 0 !== t && void 0 !== t.exports ? t.exports = n : window.ResizeSensor = n
2830
+ }, function (t, e, n) {
2831
+ var r = n(3), i = n(0), o = n(13), s = n(14)(), a = n(63), u = n(5),
2832
+ c = "a.twitter-follow-button";
2833
+ t.exports = function (t) {
2834
+ return s(t, c).map(function (t) {
2835
+ return a(function (t) {
2836
+ var e = o(t), n = {
2837
+ screenName: r.screenName(t.href),
2838
+ showScreenName: "false" !== t.getAttribute("data-show-screen-name"),
2839
+ showCount: "false" !== t.getAttribute("data-show-count"),
2840
+ size: t.getAttribute("data-size"),
2841
+ count: t.getAttribute("data-count"),
2842
+ preview: t.getAttribute("data-preview")
2843
+ };
2844
+ return i.forIn(n, function (t, n) {
2845
+ var r = e[t];
2846
+ e[t] = u.hasValue(r) ? r : n
2847
+ }), e.screenName = e.screenName || e.screen_name, e
2848
+ }(t), t.parentNode, t)
2849
+ })
2850
+ }
2851
+ }, function (t, e, n) {
2852
+ var r = n(2);
2853
+ t.exports = function (t, e) {
2854
+ var i = new r;
2855
+ return n.e(3).then(function (r) {
2856
+ var o;
2857
+ try {
2858
+ o = n(87), i.resolve(new o(t, e))
2859
+ } catch (t) {
2860
+ i.reject(t)
2861
+ }
2862
+ }.bind(null, n)).catch(function (t) {
2863
+ i.reject(t)
2864
+ }), i.promise
2865
+ }
2866
+ }, function (t, e, n) {
2867
+ var r = n(18), i = n(126), o = n(59), s = n(30), a = n(2), u = n(6), c = n(7), d = n(0),
2868
+ l = {position: "absolute", visibility: "hidden", width: "0px", height: "0px"},
2869
+ f = {position: "static", visibility: "visible"}, h = {};
2870
+ i(function (t, e, n) {
2871
+ var r = h[t];
2872
+ if (r) return e = e || 1, n = n || 1, r.styleSelf({
2873
+ width: e + "px",
2874
+ height: n + "px"
2875
+ }).then(function () {
2876
+ r.didResize()
2877
+ })
2878
+ }), t.exports = function (t) {
2879
+ t.overrideProperty("id", {
2880
+ get: function () {
2881
+ return this.sandboxEl && this.sandboxEl.id
2882
+ }
2883
+ }), t.overrideProperty("initialized", {
2884
+ get: function () {
2885
+ return !!this.iframeEl
2886
+ }
2887
+ }), t.overrideProperty("width", {
2888
+ get: function () {
2889
+ return this._width
2890
+ }
2891
+ }), t.overrideProperty("height", {
2892
+ get: function () {
2893
+ return this._height
2894
+ }
2895
+ }), t.overrideProperty("sandboxEl", {
2896
+ get: function () {
2897
+ return this.iframeEl
2898
+ }
2899
+ }), t.defineProperty("iframeEl", {
2900
+ get: function () {
2901
+ return this._iframe
2902
+ }
2903
+ }), t.define("updateCachedDimensions", function () {
2904
+ var t = this;
2905
+ return this.initialized ? r.read(function () {
2906
+ t._width = t.sandboxEl.offsetWidth, t._height = t.sandboxEl.offsetHeight
2907
+ }) : u.resolve()
2908
+ }), t.define("setTitle", function (t) {
2909
+ this.iframeEl.title = t
2910
+ }), t.define("makeVisible", function () {
2911
+ return this.styleSelf(f)
2912
+ }), t.define("didResize", function () {
2913
+ var t = this, e = t._resizeHandlers.length > 0;
2914
+ return this.updateCachedDimensions().then(function () {
2915
+ e && t._resizeHandlers.forEach(function (e) {
2916
+ e(t)
2917
+ })
2918
+ })
2919
+ }), t.define("loadDocument", function (t) {
2920
+ var e = new a;
2921
+ return this.initialized ? this.iframeEl.src ? u.reject(new Error("widget already loaded")) : (this.iframeEl.addEventListener("load", e.resolve, !1), this.iframeEl.addEventListener("error", e.reject, !1), this.iframeEl.src = t, e.promise) : u.reject(new Error("sandbox not initialized"))
2922
+ }), t.after("initialize", function () {
2923
+ this._iframe = null, this._width = this._height = 0, this._resizeHandlers = []
2924
+ }), t.override("insert", function (t, e, n, i) {
2925
+ var a = this;
2926
+ return e = d.aug({id: t}, e), n = d.aug({}, l, n), this._iframe = s(e, n), h[t] = this, this.onResize(o(function () {
2927
+ a.makeVisible()
2928
+ })), r.write(c(i, null, this._iframe))
2929
+ }), t.override("onResize", function (t) {
2930
+ this._resizeHandlers.push(t)
2931
+ }), t.after("styleSelf", function () {
2932
+ return this.updateCachedDimensions()
2933
+ })
2934
+ }
2935
+ }, function (t, e, n) {
2936
+ var r = n(1), i = n(127), o = n(129), s = n(24), a = n(5), u = n(130);
2937
+ t.exports = function (t) {
2938
+ function e(t, e) {
2939
+ var n = u(this);
2940
+ s.trigger(t, {target: n, region: e, type: t, data: {}})
2941
+ }
2942
+
2943
+ function n(e) {
2944
+ var n = u(this), r = n && n.id, i = a.asInt(e.width), o = a.asInt(e.height);
2945
+ r && void 0 !== i && void 0 !== o && t(r, i, o)
2946
+ }
2947
+
2948
+ (new i).attachReceiver(new o.Receiver(r, "twttr.button")).bind("twttr.private.trigger", e).bind("twttr.private.resizeButton", n), (new i).attachReceiver(new o.Receiver(r, "twttr.embed")).bind("twttr.private.trigger", e).bind("twttr.private.rendered", n).bind("twttr.private.resize", n)
2949
+ }
2950
+ }, function (t, e, n) {
2951
+ var r = n(31), i = n(128), o = n(0), s = n(6), a = n(22), u = "2.0";
2952
+
2953
+ function c(t) {
2954
+ this.registry = t || {}
2955
+ }
2956
+
2957
+ function d(t) {
2958
+ var e, n;
2959
+ return e = o.isType("string", t), n = o.isType("number", t), e || n || null === t
2960
+ }
2961
+
2962
+ function l(t, e) {
2963
+ return {jsonrpc: u, id: d(t) ? t : null, error: e}
2964
+ }
2965
+
2966
+ c.prototype._invoke = function (t, e) {
2967
+ var n, r, i;
2968
+ n = this.registry[t.method], r = t.params || [], r = o.isType("array", r) ? r : [r];
2969
+ try {
2970
+ i = n.apply(e.source || null, r)
2971
+ } catch (t) {
2972
+ i = s.reject(t.message)
2973
+ }
2974
+ return a.isPromise(i) ? i : s.resolve(i)
2975
+ }, c.prototype._processRequest = function (t, e) {
2976
+ var n, r;
2977
+ return function (t) {
2978
+ var e, n, r;
2979
+ return !!o.isObject(t) && (e = t.jsonrpc === u, n = o.isType("string", t.method), r = !("id" in t) || d(t.id), e && n && r)
2980
+ }(t) ? (n = "params" in t && (r = t.params, !o.isObject(r) || o.isType("function", r)) ? s.resolve(l(t.id, i.INVALID_PARAMS)) : this.registry[t.method] ? this._invoke(t, {source: e}).then(function (e) {
2981
+ return n = t.id, {jsonrpc: u, id: n, result: e};
2982
+ var n
2983
+ }, function () {
2984
+ return l(t.id, i.INTERNAL_ERROR)
2985
+ }) : s.resolve(l(t.id, i.METHOD_NOT_FOUND)), null != t.id ? n : s.resolve()) : s.resolve(l(t.id, i.INVALID_REQUEST))
2986
+ }, c.prototype.attachReceiver = function (t) {
2987
+ return t.attachTo(this), this
2988
+ }, c.prototype.bind = function (t, e) {
2989
+ return this.registry[t] = e, this
2990
+ }, c.prototype.receive = function (t, e) {
2991
+ var n, a, u, c = this;
2992
+ try {
2993
+ u = t, t = o.isType("string", u) ? r.parse(u) : u
2994
+ } catch (t) {
2995
+ return s.resolve(l(null, i.PARSE_ERROR))
2996
+ }
2997
+ return e = e || null, a = ((n = o.isType("array", t)) ? t : [t]).map(function (t) {
2998
+ return c._processRequest(t, e)
2999
+ }), n ? function (t) {
3000
+ return s.all(t).then(function (t) {
3001
+ return (t = t.filter(function (t) {
3002
+ return void 0 !== t
3003
+ })).length ? t : void 0
3004
+ })
3005
+ }(a) : a[0]
3006
+ }, t.exports = c
3007
+ }, function (t) {
3008
+ t.exports = {
3009
+ PARSE_ERROR: {code: -32700, message: "Parse error"},
3010
+ INVALID_REQUEST: {code: -32600, message: "Invalid Request"},
3011
+ INVALID_PARAMS: {code: -32602, message: "Invalid params"},
3012
+ METHOD_NOT_FOUND: {code: -32601, message: "Method not found"},
3013
+ INTERNAL_ERROR: {code: -32603, message: "Internal error"}
3014
+ }
3015
+ }, function (t, e, n) {
3016
+ var r = n(8), i = n(1), o = n(31), s = n(2), a = n(21), u = n(0), c = n(3), d = n(7),
3017
+ l = a.ie9();
3018
+
3019
+ function f(t, e, n) {
3020
+ var r;
3021
+ t && t.postMessage && (l ? r = (n || "") + o.stringify(e) : n ? (r = {})[n] = e : r = e, t.postMessage(r, "*"))
3022
+ }
3023
+
3024
+ function h(t) {
3025
+ return u.isType("string", t) ? t : "JSONRPC"
3026
+ }
3027
+
3028
+ function p(t, e) {
3029
+ return e ? u.isType("string", t) && 0 === t.indexOf(e) ? t.substring(e.length) : t && t[e] ? t[e] : void 0 : t
3030
+ }
3031
+
3032
+ function m(t, e) {
3033
+ var n = t.document;
3034
+ this.filter = h(e), this.server = null, this.isTwitterFrame = c.isTwitterURL(n.location.href), t.addEventListener("message", d(this._onMessage, this), !1)
3035
+ }
3036
+
3037
+ function v(t, e) {
3038
+ this.pending = {}, this.target = t, this.isTwitterHost = c.isTwitterURL(r.href), this.filter = h(e), i.addEventListener("message", d(this._onMessage, this), !1)
3039
+ }
3040
+
3041
+ u.aug(m.prototype, {
3042
+ _onMessage: function (t) {
3043
+ var e, n = this;
3044
+ this.server && (this.isTwitterFrame && !c.isTwitterURL(t.origin) || (e = p(t.data, this.filter)) && this.server.receive(e, t.source).then(function (e) {
3045
+ e && f(t.source, e, n.filter)
3046
+ }))
3047
+ }, attachTo: function (t) {
3048
+ this.server = t
3049
+ }, detach: function () {
3050
+ this.server = null
3051
+ }
3052
+ }), u.aug(v.prototype, {
3053
+ _processResponse: function (t) {
3054
+ var e = this.pending[t.id];
3055
+ e && (e.resolve(t), delete this.pending[t.id])
3056
+ }, _onMessage: function (t) {
3057
+ var e;
3058
+ if ((!this.isTwitterHost || c.isTwitterURL(t.origin)) && (e = p(t.data, this.filter))) {
3059
+ if (u.isType("string", e)) try {
3060
+ e = o.parse(e)
3061
+ } catch (t) {
3062
+ return
3063
+ }
3064
+ (e = u.isType("array", e) ? e : [e]).forEach(d(this._processResponse, this))
3065
+ }
3066
+ }, send: function (t) {
3067
+ var e = new s;
3068
+ return t.id ? this.pending[t.id] = e : e.resolve(), f(this.target, t, this.filter), e.promise
3069
+ }
3070
+ }), t.exports = {
3071
+ Receiver: m, Dispatcher: v, _stringifyPayload: function (t) {
3072
+ return arguments.length > 0 && (l = !!t), l
3073
+ }
3074
+ }
3075
+ }, function (t, e, n) {
3076
+ var r = n(4);
3077
+ t.exports = function (t) {
3078
+ for (var e, n = r.getElementsByTagName("iframe"), i = 0; n[i]; i++) if ((e = n[i]).contentWindow === t) return e
3079
+ }
3080
+ }, function (t, e, n) {
3081
+ var r = n(5), i = n(0), o = n(3), s = n(13), a = n(14)(), u = n(64), c = "a.twitter-moment";
3082
+ t.exports = function (t) {
3083
+ return a(t, c).map(function (t) {
3084
+ return u(function (t) {
3085
+ var e = s(t), n = {
3086
+ momentId: o.momentId(t.href),
3087
+ chrome: t.getAttribute("data-chrome"),
3088
+ limit: t.getAttribute("data-limit")
3089
+ };
3090
+ return i.forIn(n, function (t, n) {
3091
+ var i = e[t];
3092
+ e[t] = r.hasValue(i) ? i : n
3093
+ }), e
3094
+ }(t), t.parentNode, t)
3095
+ })
3096
+ }
3097
+ }, function (t, e, n) {
3098
+ var r = n(2);
3099
+ t.exports = function (t, e) {
3100
+ var i = new r;
3101
+ return Promise.all([n.e(0), n.e(4)]).then(function (r) {
3102
+ var o;
3103
+ try {
3104
+ o = n(88), i.resolve(new o(t, e))
3105
+ } catch (t) {
3106
+ i.reject(t)
3107
+ }
3108
+ }.bind(null, n)).catch(function (t) {
3109
+ i.reject(t)
3110
+ }), i.promise
3111
+ }
3112
+ }, function (t, e, n) {
3113
+ var r = n(0), i = n(13), o = n(14)(), s = n(65), a = "a.periscope-on-air",
3114
+ u = /^https?:\/\/(?:www\.)?(?:periscope|pscp)\.tv\/@?([a-zA-Z0-9_]+)\/?$/i;
3115
+ t.exports = function (t) {
3116
+ return o(t, a).map(function (t) {
3117
+ return s(function (t) {
3118
+ var e = i(t), n = t.getAttribute("href"), o = t.getAttribute("data-size"),
3119
+ s = u.exec(n)[1];
3120
+ return r.aug(e, {username: s, size: o})
3121
+ }(t), t.parentNode, t)
3122
+ })
3123
+ }
3124
+ }, function (t, e, n) {
3125
+ var r = n(2);
3126
+ t.exports = function (t, e) {
3127
+ var i = new r;
3128
+ return n.e(5).then(function (r) {
3129
+ var o;
3130
+ try {
3131
+ o = n(89), i.resolve(new o(t, e))
3132
+ } catch (t) {
3133
+ i.reject(t)
3134
+ }
3135
+ }.bind(null, n)).catch(function (t) {
3136
+ i.reject(t)
3137
+ }), i.promise
3138
+ }
3139
+ }, function (t, e, n) {
3140
+ var r = n(5), i = n(0), o = n(66), s = n(13), a = n(14)(), u = n(67), c = n(3), d = n(12),
3141
+ l = "a.twitter-timeline,div.twitter-timeline,a.twitter-grid",
3142
+ f = "Embedded Search timelines have been deprecated. See https://twittercommunity.com/t/deprecating-widget-settings/102295.",
3143
+ h = "You may have been affected by an update to settings in embedded timelines. See https://twittercommunity.com/t/deprecating-widget-settings/102295.",
3144
+ p = "Embedded grids have been deprecated and will now render as timelines. Please update your embed code to use the twitter-timeline class. More info: https://twittercommunity.com/t/update-on-the-embedded-grid-display-type/119564.";
3145
+ t.exports = function (t) {
3146
+ return a(t, l).map(function (t) {
3147
+ return u(function (t) {
3148
+ var e = s(t), n = t.getAttribute("data-show-replies"), a = {
3149
+ isPreconfigured: !!t.getAttribute("data-widget-id"),
3150
+ chrome: t.getAttribute("data-chrome"),
3151
+ tweetLimit: t.getAttribute("data-tweet-limit") || t.getAttribute("data-limit"),
3152
+ ariaLive: t.getAttribute("data-aria-polite"),
3153
+ theme: t.getAttribute("data-theme"),
3154
+ linkColor: t.getAttribute("data-link-color"),
3155
+ borderColor: t.getAttribute("data-border-color"),
3156
+ showReplies: n ? r.asBoolean(n) : null,
3157
+ profileScreenName: t.getAttribute("data-screen-name"),
3158
+ profileUserId: t.getAttribute("data-user-id"),
3159
+ favoritesScreenName: t.getAttribute("data-favorites-screen-name"),
3160
+ favoritesUserId: t.getAttribute("data-favorites-user-id"),
3161
+ likesScreenName: t.getAttribute("data-likes-screen-name"),
3162
+ likesUserId: t.getAttribute("data-likes-user-id"),
3163
+ listOwnerScreenName: t.getAttribute("data-list-owner-screen-name"),
3164
+ listOwnerUserId: t.getAttribute("data-list-owner-id"),
3165
+ listId: t.getAttribute("data-list-id"),
3166
+ listSlug: t.getAttribute("data-list-slug"),
3167
+ customTimelineId: t.getAttribute("data-custom-timeline-id"),
3168
+ staticContent: t.getAttribute("data-static-content"),
3169
+ url: t.href
3170
+ };
3171
+ return a.isPreconfigured && (c.isSearchUrl(a.url) ? d.publicError(f, t) : d.publicLog(h, t)), "twitter-grid" === t.className && d.publicLog(p, t), (a = i.aug(a, e)).dataSource = o(a), a.id = a.dataSource && a.dataSource.id, a
3172
+ }(t), t.parentNode, t)
3173
+ })
3174
+ }
3175
+ }, function (t, e, n) {
3176
+ var r = n(27);
3177
+ t.exports = r.build([n(28), n(139)])
3178
+ }, function (t, e, n) {
3179
+ var r = n(0), i = n(138);
3180
+ t.exports = function (t) {
3181
+ return "en" === t || r.contains(i, t)
3182
+ }
3183
+ }, function (t, e) {
3184
+ t.exports = ["hi", "zh-cn", "fr", "zh-tw", "msa", "fil", "fi", "sv", "pl", "ja", "ko", "de", "it", "pt", "es", "ru", "id", "tr", "da", "no", "nl", "hu", "fa", "ar", "ur", "he", "th", "cs", "uk", "vi", "ro", "bn", "el", "en-gb", "gu", "kn", "mr", "ta", "bg", "ca", "hr", "sr", "sk"]
3185
+ }, function (t, e, n) {
3186
+ var r = n(3), i = n(0), o = n(19), s = "collection:";
3187
+
3188
+ function a(t, e) {
3189
+ return r.collectionId(t) || e
3190
+ }
3191
+
3192
+ t.exports = function (t) {
3193
+ t.params({id: {}, url: {}}), t.overrideProperty("id", {
3194
+ get: function () {
3195
+ var t = a(this.params.url, this.params.id);
3196
+ return s + t
3197
+ }
3198
+ }), t.overrideProperty("endpoint", {
3199
+ get: function () {
3200
+ return o.timeline(["collection"])
3201
+ }
3202
+ }), t.around("queryParams", function (t) {
3203
+ return i.aug(t(), {collection_id: a(this.params.url, this.params.id)})
3204
+ }), t.before("initialize", function () {
3205
+ if (!a(this.params.url, this.params.id)) throw new Error("one of url or id is required")
3206
+ })
3207
+ }
3208
+ }, function (t, e, n) {
3209
+ var r = n(27);
3210
+ t.exports = r.build([n(28), n(141)])
3211
+ }, function (t, e, n) {
3212
+ var r = n(3), i = n(0), o = n(19), s = "event:";
3213
+
3214
+ function a(t, e) {
3215
+ return r.eventId(t) || e
3216
+ }
3217
+
3218
+ t.exports = function (t) {
3219
+ t.params({id: {}, url: {}}), t.overrideProperty("id", {
3220
+ get: function () {
3221
+ var t = a(this.params.url, this.params.id);
3222
+ return s + t
3223
+ }
3224
+ }), t.overrideProperty("endpoint", {
3225
+ get: function () {
3226
+ return o.timeline(["event"])
3227
+ }
3228
+ }), t.around("queryParams", function (t) {
3229
+ return i.aug(t(), {event_id: a(this.params.url, this.params.id)})
3230
+ }), t.before("initialize", function () {
3231
+ if (!a(this.params.url, this.params.id)) throw new Error("one of url or id is required")
3232
+ })
3233
+ }
3234
+ }, function (t, e, n) {
3235
+ var r = n(27);
3236
+ t.exports = r.build([n(28), n(143)])
3237
+ }, function (t, e, n) {
3238
+ var r = n(3), i = n(0), o = n(19), s = "likes:";
3239
+
3240
+ function a(t) {
3241
+ return r.likesScreenName(t.url) || t.screenName
3242
+ }
3243
+
3244
+ t.exports = function (t) {
3245
+ t.params({
3246
+ screenName: {},
3247
+ userId: {},
3248
+ url: {}
3249
+ }), t.overrideProperty("id", {
3250
+ get: function () {
3251
+ var t = a(this.params) || this.params.userId;
3252
+ return s + t
3253
+ }
3254
+ }), t.overrideProperty("endpoint", {
3255
+ get: function () {
3256
+ return o.timeline(["likes"])
3257
+ }
3258
+ }), t.define("_getLikesQueryParam", function () {
3259
+ var t = a(this.params);
3260
+ return t ? {screen_name: t} : {user_id: this.params.userId}
3261
+ }), t.around("queryParams", function (t) {
3262
+ return i.aug(t(), this._getLikesQueryParam())
3263
+ }), t.before("initialize", function () {
3264
+ if (!a(this.params) && !this.params.userId) throw new Error("screen name or user id is required")
3265
+ })
3266
+ }
3267
+ }, function (t, e, n) {
3268
+ var r = n(27);
3269
+ t.exports = r.build([n(28), n(145)])
3270
+ }, function (t, e, n) {
3271
+ var r = n(3), i = n(0), o = n(19), s = "list:";
3272
+
3273
+ function a(t) {
3274
+ var e = r.listScreenNameAndSlug(t.url) || t;
3275
+ return i.compact({
3276
+ screen_name: e.ownerScreenName,
3277
+ user_id: e.ownerUserId,
3278
+ list_slug: e.slug
3279
+ })
3280
+ }
3281
+
3282
+ t.exports = function (t) {
3283
+ t.params({
3284
+ id: {},
3285
+ ownerScreenName: {},
3286
+ ownerUserId: {},
3287
+ slug: {},
3288
+ url: {}
3289
+ }), t.overrideProperty("id", {
3290
+ get: function () {
3291
+ var t, e, n;
3292
+ return this.params.id ? s + this.params.id : (e = (t = a(this.params)) && t.list_slug.replace(/-/g, "_"), n = t && (t.screen_name || t.user_id), s + (n + ":") + e)
3293
+ }
3294
+ }), t.overrideProperty("endpoint", {
3295
+ get: function () {
3296
+ return o.timeline(["list"])
3297
+ }
3298
+ }), t.define("_getListQueryParam", function () {
3299
+ return this.params.id ? {list_id: this.params.id} : a(this.params)
3300
+ }), t.around("queryParams", function (t) {
3301
+ return i.aug(t(), this._getListQueryParam())
3302
+ }), t.before("initialize", function () {
3303
+ var t = a(this.params);
3304
+ if (i.isEmptyObject(t) && !this.params.id) throw new Error("qualified slug or list id required")
3305
+ })
3306
+ }
3307
+ }, function (t, e, n) {
3308
+ var r = n(27);
3309
+ t.exports = r.build([n(28), n(147)])
3310
+ }, function (t, e, n) {
3311
+ var r = n(3), i = n(5), o = n(0), s = n(19), a = "profile:";
3312
+
3313
+ function u(t, e) {
3314
+ return r.screenName(t) || e
3315
+ }
3316
+
3317
+ t.exports = function (t) {
3318
+ t.params({
3319
+ showReplies: {fallback: !1, transform: i.asBoolean},
3320
+ screenName: {},
3321
+ userId: {},
3322
+ url: {}
3323
+ }), t.overrideProperty("id", {
3324
+ get: function () {
3325
+ var t = u(this.params.url, this.params.screenName);
3326
+ return a + (t || this.params.userId)
3327
+ }
3328
+ }), t.overrideProperty("endpoint", {
3329
+ get: function () {
3330
+ return s.timeline(["profile"])
3331
+ }
3332
+ }), t.define("_getProfileQueryParam", function () {
3333
+ var t = u(this.params.url, this.params.screenName),
3334
+ e = t ? {screen_name: t} : {user_id: this.params.userId};
3335
+ return o.aug(e, {with_replies: this.params.showReplies ? "true" : "false"})
3336
+ }), t.around("queryParams", function (t) {
3337
+ return o.aug(t(), this._getProfileQueryParam())
3338
+ }), t.before("initialize", function () {
3339
+ if (!u(this.params.url, this.params.screenName) && !this.params.userId) throw new Error("screen name or user id is required")
3340
+ })
3341
+ }
3342
+ }, function (t, e, n) {
3343
+ var r = n(2);
3344
+ t.exports = function (t, e) {
3345
+ var i = new r;
3346
+ return Promise.all([n.e(0), n.e(6)]).then(function (r) {
3347
+ var o;
3348
+ try {
3349
+ o = n(90), i.resolve(new o(t, e))
3350
+ } catch (t) {
3351
+ i.reject(t)
3352
+ }
3353
+ }.bind(null, n)).catch(function (t) {
3354
+ i.reject(t)
3355
+ }), i.promise
3356
+ }
3357
+ }, function (t, e, n) {
3358
+ var r = n(10), i = n(3), o = n(0), s = n(13), a = n(14)(), u = n(68),
3359
+ c = "blockquote.twitter-tweet, blockquote.twitter-video",
3360
+ d = /\btw-align-(left|right|center)\b/;
3361
+ t.exports = function (t, e) {
3362
+ return a(t, c).map(function (t) {
3363
+ return u(function (t) {
3364
+ var e = s(t), n = t.getElementsByTagName("A"), a = n && n[n.length - 1],
3365
+ u = a && i.status(a.href), c = t.getAttribute("data-conversation"),
3366
+ l = "none" == c || "hidden" == c || r.present(t, "tw-hide-thread"),
3367
+ f = t.getAttribute("data-cards"),
3368
+ h = "none" == f || "hidden" == f || r.present(t, "tw-hide-media"),
3369
+ p = t.getAttribute("data-align") || t.getAttribute("align"),
3370
+ m = t.getAttribute("data-link-color"), v = t.getAttribute("data-theme");
3371
+ return !p && d.test(t.className) && (p = RegExp.$1), o.aug(e, {
3372
+ tweetId: u,
3373
+ hideThread: l,
3374
+ hideCard: h,
3375
+ align: p,
3376
+ linkColor: m,
3377
+ theme: v,
3378
+ id: u
3379
+ })
3380
+ }(t), t.parentNode, t, e)
3381
+ })
3382
+ }
3383
+ }, function (t, e, n) {
3384
+ var r = n(2);
3385
+ t.exports = function (t, e) {
3386
+ var i = new r;
3387
+ return Promise.all([n.e(0), n.e(7)]).then(function (r) {
3388
+ var o;
3389
+ try {
3390
+ o = n(91), i.resolve(new o(t, e))
3391
+ } catch (t) {
3392
+ i.reject(t)
3393
+ }
3394
+ }.bind(null, n)).catch(function (t) {
3395
+ i.reject(t)
3396
+ }), i.promise
3397
+ }
3398
+ }, function (t, e, n) {
3399
+ var r = n(2);
3400
+ t.exports = function (t, e) {
3401
+ var i = new r;
3402
+ return Promise.all([n.e(0), n.e(7)]).then(function (r) {
3403
+ var o;
3404
+ try {
3405
+ o = n(92), i.resolve(new o(t, e))
3406
+ } catch (t) {
3407
+ i.reject(t)
3408
+ }
3409
+ }.bind(null, n)).catch(function (t) {
3410
+ i.reject(t)
3411
+ }), i.promise
3412
+ }
3413
+ }, function (t, e, n) {
3414
+ var r = n(10), i = n(0), o = n(13), s = n(14)(), a = n(69), u = n(5),
3415
+ c = "a.twitter-share-button, a.twitter-mention-button, a.twitter-hashtag-button",
3416
+ d = "twitter-hashtag-button", l = "twitter-mention-button";
3417
+ t.exports = function (t) {
3418
+ return s(t, c).map(function (t) {
3419
+ return a(function (t) {
3420
+ var e = o(t), n = {
3421
+ screenName: t.getAttribute("data-button-screen-name"),
3422
+ text: t.getAttribute("data-text"),
3423
+ type: t.getAttribute("data-type"),
3424
+ size: t.getAttribute("data-size"),
3425
+ url: t.getAttribute("data-url"),
3426
+ hashtags: t.getAttribute("data-hashtags"),
3427
+ via: t.getAttribute("data-via"),
3428
+ buttonHashtag: t.getAttribute("data-button-hashtag")
3429
+ };
3430
+ return i.forIn(n, function (t, n) {
3431
+ var r = e[t];
3432
+ e[t] = u.hasValue(r) ? r : n
3433
+ }), e.screenName = e.screenName || e.screen_name, e.buttonHashtag = e.buttonHashtag || e.button_hashtag || e.hashtag, r.present(t, d) && (e.type = "hashtag"), r.present(t, l) && (e.type = "mention"), e
3434
+ }(t), t.parentNode, t)
3435
+ })
3436
+ }
3437
+ }, function (t, e, n) {
3438
+ var r = n(2);
3439
+ t.exports = function (t, e) {
3440
+ var i = new r;
3441
+ return n.e(3).then(function (r) {
3442
+ var o;
3443
+ try {
3444
+ o = n(93), i.resolve(new o(t, e))
3445
+ } catch (t) {
3446
+ i.reject(t)
3447
+ }
3448
+ }.bind(null, n)).catch(function (t) {
3449
+ i.reject(t)
3450
+ }), i.promise
3451
+ }
3452
+ }, function (t, e, n) {
3453
+ var r = n(0);
3454
+ t.exports = r.aug({}, n(155), n(156), n(157), n(158), n(159), n(160), n(161))
3455
+ }, function (t, e, n) {
3456
+ var r = n(60), i = n(17)(["userId"], {}, r);
3457
+ t.exports = {createDMButton: i}
3458
+ }, function (t, e, n) {
3459
+ var r = n(63), i = n(17)(["screenName"], {}, r);
3460
+ t.exports = {createFollowButton: i}
3461
+ }, function (t, e, n) {
3462
+ var r = n(64), i = n(17)(["momentId"], {}, r);
3463
+ t.exports = {createMoment: i}
3464
+ }, function (t, e, n) {
3465
+ var r = n(65), i = n(17)(["username"], {}, r);
3466
+ t.exports = {createPeriscopeOnAirButton: i}
3467
+ }, function (t, e, n) {
3468
+ var r = n(8), i = n(12), o = n(3), s = n(0), a = n(5), u = n(66), c = n(67),
3469
+ d = n(17)([], {}, c), l = n(6),
3470
+ f = "Embedded grids have been deprecated. Please use twttr.widgets.createTimeline instead. More info: https://twittercommunity.com/t/update-on-the-embedded-grid-display-type/119564.",
3471
+ h = {
3472
+ createTimeline: p, createGridFromCollection: function (t) {
3473
+ var e = s.toRealArray(arguments).slice(1), n = {sourceType: "collection", id: t};
3474
+ return e.unshift(n), i.publicLog(f), p.apply(this, e)
3475
+ }
3476
+ };
3477
+
3478
+ function p(t) {
3479
+ var e, n = s.toRealArray(arguments).slice(1);
3480
+ return a.isString(t) || a.isNumber(t) ? l.reject("Embedded timelines with widget settings have been deprecated. See https://twittercommunity.com/t/deprecating-widget-settings/102295.") : s.isObject(t) ? (t = t || {}, n.forEach(function (t) {
3481
+ s.isType("object", t) && function (t) {
3482
+ t.ariaLive = t.ariaPolite
3483
+ }(e = t)
3484
+ }), e || (e = {}, n.push(e)), t.lang = e.lang, t.tweetLimit = e.tweetLimit, t.showReplies = e.showReplies, e.dataSource = u(t), d.apply(this, n)) : l.reject("data source must be an object.")
3485
+ }
3486
+
3487
+ o.isTwitterURL(r.href) && (h.createTimelinePreview = function (t, e, n) {
3488
+ var r = {previewParams: t, useLegacyDefaults: !0, isPreviewTimeline: !0};
3489
+ return r.dataSource = u(r), d(e, r, n)
3490
+ }), t.exports = h
3491
+ }, function (t, e, n) {
3492
+ var r, i = n(0), o = n(68), s = n(17), a = (r = s(["tweetId"], {}, o), function () {
3493
+ return i.toRealArray(arguments).slice(1).forEach(function (t) {
3494
+ i.isType("object", t) && (t.hideCard = "none" == t.cards || "hidden" == t.cards, t.hideThread = "none" == t.conversation || "hidden" == t.conversation)
3495
+ }), r.apply(this, arguments)
3496
+ });
3497
+ t.exports = {createTweet: a, createTweetEmbed: a, createVideo: a}
3498
+ }, function (t, e, n) {
3499
+ var r = n(0), i = n(69), o = n(17), s = o(["url"], {type: "share"}, i),
3500
+ a = o(["buttonHashtag"], {type: "hashtag"}, i), u = o(["screenName"], {type: "mention"}, i);
3501
+
3502
+ function c(t) {
3503
+ return function () {
3504
+ return r.toRealArray(arguments).slice(1).forEach(function (t) {
3505
+ r.isType("object", t) && (t.screenName = t.screenName || t.screen_name, t.buttonHashtag = t.buttonHashtag || t.button_hashtag || t.hashtag)
3506
+ }), t.apply(this, arguments)
3507
+ }
3508
+ }
3509
+
3510
+ t.exports = {createShareButton: c(s), createHashtagButton: c(a), createMentionButton: c(u)}
3511
+ }, function (t, e, n) {
3512
+ var r, i, o, s = n(4), a = n(1), u = 0, c = [], d = s.createElement("a");
3513
+
3514
+ function l() {
3515
+ var t, e;
3516
+ for (u = 1, t = 0, e = c.length; t < e; t++) c[t]()
3517
+ }
3518
+
3519
+ /^loade|c/.test(s.readyState) && (u = 1), s.addEventListener && s.addEventListener("DOMContentLoaded", i = function () {
3520
+ s.removeEventListener("DOMContentLoaded", i, !1), l()
3521
+ }, !1), d.doScroll && s.attachEvent("onreadystatechange", r = function () {
3522
+ /^c/.test(s.readyState) && (s.detachEvent("onreadystatechange", r), l())
3523
+ }), o = d.doScroll ? function (t) {
3524
+ a.self != a.top ? u ? t() : c.push(t) : function () {
3525
+ try {
3526
+ d.doScroll("left")
3527
+ } catch (e) {
3528
+ return setTimeout(function () {
3529
+ o(t)
3530
+ }, 50)
3531
+ }
3532
+ t()
3533
+ }()
3534
+ } : function (t) {
3535
+ u ? t() : c.push(t)
3536
+ }, t.exports = o
3537
+ }, function (t, e, n) {
3538
+ var r = n(44), i = n(9);
3539
+ t.exports = function () {
3540
+ i.set("buildVersion", r.version)
3541
+ }
3542
+ }, function (t, e, n) {
3543
+ n(165), n(85), n(168)
3544
+ }, function (t, e, n) {
3545
+ var r = n(166), i = n(29), o = n(70), s = new r, a = function (t) {
3546
+ t.widgets && 1 === t.widgets.length && (s.start(), i.emitter.unbind(i.ALL_WIDGETS_RENDER_START, a))
3547
+ }, u = function (t) {
3548
+ var e;
3549
+ t.widgets && 1 === t.widgets.length && (e = t.widgets[0], s.end(), e.dataset && e.dataset.tweetId && o({
3550
+ duration: s.duration(),
3551
+ namespace: {element: "tweet", action: "render"},
3552
+ widgetIds: [e.dataset.tweetId]
3553
+ })), i.emitter.unbind(i.ALL_WIDGETS_RENDER_END, u)
3554
+ };
3555
+ i.emitter.bind(i.ALL_WIDGETS_RENDER_START, a), i.emitter.bind(i.ALL_WIDGETS_RENDER_END, u)
3556
+ }, function (t, e, n) {
3557
+ var r = n(167);
3558
+
3559
+ function i() {
3560
+ }
3561
+
3562
+ i.prototype.start = function () {
3563
+ this._startTime = r()
3564
+ }, i.prototype.end = function () {
3565
+ this._duration = r() - this._startTime
3566
+ }, i.prototype.duration = function () {
3567
+ return this._duration
3568
+ }, t.exports = i
3569
+ }, function (t, e, n) {
3570
+ var r = n(1);
3571
+ t.exports = function () {
3572
+ return r.performance && r.performance.now ? r.performance.now() : Date.now()
3573
+ }
3574
+ }, function (t, e, n) {
3575
+ var r = n(29), i = n(70), o = n(169), s = n(3), a = n(1), u = n(0), c = n(21), d = n(62);
3576
+
3577
+ function l(t) {
3578
+ return t.performance.getEntriesByType("resource").filter(function (t) {
3579
+ return s.isTwimgURL(t.name) || s.isTwitterURL(t.name)
3580
+ }).reduce(function (t, e) {
3581
+ return t[e.name] = e.duration, t
3582
+ }, {})
3583
+ }
3584
+
3585
+ r.emitter.bind(r.ALL_WIDGETS_AND_IMAGES_LOADED, function (t) {
3586
+ var e, n, r = [];
3587
+ c.hasPerformanceInformation() && (e = l(a), d.isSupported() || (r = function (t) {
3588
+ return t.reduce(function (t, e) {
3589
+ return u.aug(t, l(e.contentDocument.defaultView))
3590
+ }, {})
3591
+ }(t)), n = u.aug({}, e, r), Object.keys(o).forEach(function (t) {
3592
+ !function (t, e, n) {
3593
+ var r = Object.keys(t).reduce(function (e, r) {
3594
+ return n(r) ? e + t[r] : e
3595
+ }, 0);
3596
+ i({duration: r, namespace: {element: e, action: "resource"}})
3597
+ }(n, t, o[t])
3598
+ }))
3599
+ })
3600
+ }, function (t, e, n) {
3601
+ var r = n(3), i = {
3602
+ all: function () {
3603
+ return !0
3604
+ }, image: function (t) {
3605
+ return r.isTwimgURL(t)
3606
+ }, settings: function (t) {
3607
+ return r.isSettingsURL(t)
3608
+ }, widget_iframe: function (t) {
3609
+ return r.isWidgetIframeURL(t)
3610
+ }
3611
+ };
3612
+ t.exports = i
3613
+ }])));
3614
+ }
3615
+ });
3616
+
3617
+
3618
+ //Runs every time new tweets are loaded
3619
+ function ctfScripts($ctf) {
3620
+ $ctf.addClass('ctf_is_initialized');
3621
+ //Loop through each newly loaded tweet
3622
+ $ctf.find('.ctf-item.ctf-new').each(function () {
3623
+
3624
+ var $ctfItem = $(this),
3625
+ $ctfTextMedia = $ctfItem.find('.ctf-tweet-text-media-wrap'),
3626
+ $ctfText = $ctfItem.find('.ctf-tweet-text').remove('.ctf-tweet-text-media-wrap'),
3627
+ ctfTextStr = ' ' + $ctfText.html();
3628
+
3629
+ if ($ctf.attr('data-ctfdisablelinks') != 'true' && typeof ctfTextStr !== 'undefined' && !$ctf.find('.ctf-tweet-text-link').length) {
3630
+
3631
+ var ctfLinkColor = $ctf.attr('data-ctflinktextcolor'),
3632
+ ctfLinkColorHex = '';
3633
+ if (ctfLinkColor) ctfLinkColorHex = ctfLinkColor.replace(';', '').split("#")[1];
3634
+
3635
+ //Link URLs
3636
+ window.ctfLinkify = (function () {
3637
+ var k = "[a-z\\d.-]+://",
3638
+ h = "(?:(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])\\.){3}(?:[0-9]|[1-9]\\d|1\\d{2}|2[0-4]\\d|25[0-5])",
3639
+ c = "(?:(?:[^\\s!@#$%^&*()_=+[\\]{}\\\\|;:'\",.<>/?]+)\\.)+",
3640
+ n = "(?:ac|ad|aero|ae|af|ag|ai|al|am|an|ao|aq|arpa|ar|asia|as|at|au|aw|ax|az|ba|bb|bd|be|bf|bg|bh|biz|bi|bj|bm|bn|bo|br|bs|bt|bv|bw|by|bz|cat|ca|cc|cd|cf|cg|ch|ci|ck|cl|cm|cn|coop|com|co|cr|cu|cv|cx|cy|cz|de|dj|dk|dm|do|dz|ec|edu|ee|eg|er|es|et|eu|fi|fj|fk|fm|fo|fr|ga|gb|gd|ge|gf|gg|gh|gi|gl|gm|gn|gov|gp|gq|gr|gs|gt|gu|gw|gy|hk|hm|hn|hr|ht|hu|id|ie|il|im|info|int|in|io|iq|ir|is|it|je|jm|jobs|jo|jp|ke|kg|kh|ki|km|kn|kp|kr|kw|ky|kz|la|lb|lc|li|lk|lr|ls|lt|lu|lv|ly|ma|mc|md|me|mg|mh|mil|mk|ml|mm|mn|mobi|mo|mp|mq|mr|ms|mt|museum|mu|mv|mw|mx|my|mz|name|na|nc|net|ne|nf|ng|ni|nl|no|np|nr|nu|nz|om|org|pa|pe|pf|pg|ph|pk|pl|pm|pn|pro|pr|ps|pt|pw|py|qa|re|ro|rs|ru|rw|sa|sb|sc|sd|se|sg|sh|si|sj|sk|sl|sm|sn|so|sr|st|su|sv|sy|sz|tc|td|tel|tf|tg|th|tj|tk|tl|tm|tn|to|tp|travel|tr|tt|tv|tw|tz|ua|ug|uk|um|us|uy|uz|va|vc|ve|vg|vi|vn|vu|wf|ws|xn--0zwm56d|xn--11b5bs3a9aj6g|xn--80akhbyknj4f|xn--9t4b11yi5a|xn--deba0ad|xn--g6w251d|xn--hgbk6aj7f53bba|xn--hlcj6aya9esc7a|xn--jxalpdlp|xn--kgbechtv|xn--zckzah|ye|yt|yu|za|zm|zw)",
3641
+ f = "(?:" + c + n + "|" + h + ")", o = "(?:[;/][^#?<>\\s]*)?",
3642
+ e = "(?:\\?[^#<>\\s]*)?(?:#[^<>\\s]*)?", d = "\\b" + k + "[^<>\\s]+",
3643
+ a = "\\b" + f + o + e + "(?!\\w)", m = "mailto:",
3644
+ j = "(?:" + m + ")?[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@" + f + e + "(?!\\w)",
3645
+ l = new RegExp("(?:" + d + "|" + a + "|" + j + ")", "ig"), g = new RegExp("^" + k, "i"),
3646
+ b = {"'": "`", ">": "<", ")": "(", "]": "[", "}": "{", "B;": "B+", "b:": "b9"}, i = {
3647
+ callback: function (q, p) {
3648
+ return p ? '<a href="' + p + '" title="' + p + '" target="_blank">' + q + "</a>" : q
3649
+ },
3650
+ punct_regexp: /(?:[!?.,:;'"]|(?:&|&amp;)(?:lt|gt|quot|apos|raquo|laquo|rsaquo|lsaquo);)$/
3651
+ };
3652
+ return function (u, z) {
3653
+ z = z || {};
3654
+ var w, v, A, p, x = "", t = [], s, E, C, y, q, D, B, r;
3655
+ for (v in i) {
3656
+ if (z[v] === undefined) {
3657
+ z[v] = i[v]
3658
+ }
3659
+ }
3660
+ while (w = l.exec(u)) {
3661
+ A = w[0];
3662
+ E = l.lastIndex;
3663
+ C = E - A.length;
3664
+ if (/[\/:]/.test(u.charAt(C - 1))) {
3665
+ continue
3666
+ }
3667
+ do {
3668
+ y = A;
3669
+ r = A.substr(-1);
3670
+ B = b[r];
3671
+ if (B) {
3672
+ q = A.match(new RegExp("\\" + B + "(?!$)", "g"));
3673
+ D = A.match(new RegExp("\\" + r, "g"));
3674
+ if ((q ? q.length : 0) < (D ? D.length : 0)) {
3675
+ A = A.substr(0, A.length - 1);
3676
+ E--
3677
+ }
3678
+ }
3679
+ if (z.punct_regexp) {
3680
+ A = A.replace(z.punct_regexp, function (F) {
3681
+ E -= F.length;
3682
+ return ""
3683
+ })
3684
+ }
3685
+ } while (A.length && A !== y);
3686
+ p = A;
3687
+ if (!g.test(p)) {
3688
+ p = (p.indexOf("@") !== -1 ? (!p.indexOf(m) ? "" : m) : !p.indexOf("irc.") ? "irc://" : !p.indexOf("ftp.") ? "ftp://" : "http://") + p
3689
+ }
3690
+ if (s != C) {
3691
+ t.push([u.slice(s, C)]);
3692
+ s = E
3693
+ }
3694
+ t.push([A, p])
3695
+ }
3696
+ t.push([u.substr(s)]);
3697
+ for (v = 0; v < t.length; v++) {
3698
+ x += z.callback.apply(window, t[v])
3699
+ }
3700
+ return x || u
3701
+ }
3702
+ })();
3703
+ if (!$ctfText.find('a').length) {
3704
+ $ctfText.find('.emoji').each(function() {
3705
+ $(this).replaceWith($(this).attr('alt'));
3706
+ });
3707
+ ctfTextStr = ' ' +$ctfText.html();
3708
+ ctfTextStr = ctfLinkify(ctfTextStr);
3709
+ }
3710
+ //Link hashtags
3711
+ var ctfHashRegex = /(^|\s)#(\w*[\u0041-\u005A\u0061-\u007A\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+\w*)/gi;
3712
+
3713
+ function ctfHashReplacer(hash) {
3714
+ //Remove white space at beginning of hash
3715
+ var replacementString = jQuery.trim(hash);
3716
+ //If the hash is a hex code then don't replace it with a link as it's likely in the style attr, eg: "color: #ff0000"
3717
+ if (/^#[0-9A-F]{6}$/i.test(replacementString)) {
3718
+ return replacementString;
3719
+ } else {
3720
+ return ' <a href="https://twitter.com/hashtag/' + replacementString.substring(1) + '" target="_blank" rel="nofollow">' + replacementString + '</a>';
3721
+ }
3722
+ }
3723
+
3724
+ //Replace hashtags in text
3725
+ if (ctfTextStr.length > 0) {
3726
+ //Add a space after all <br> tags so that #hashtags immediately after them are also converted to hashtag links. Without the space they aren't captured by the regex.
3727
+ ctfTextStr = ctfTextStr.replace(/<br>/g, "<br> ");
3728
+ ctfTextStr = ctfTextStr.replace(ctfHashRegex, ctfHashReplacer);
3729
+ }
3730
+
3731
+ //Link @tags
3732
+ function ctfReplaceTags(tag) {
3733
+ var replacementString = jQuery.trim(tag);
3734
+ return ' <a href="https://twitter.com/' + replacementString.substring(1) + '" target="_blank" rel="nofollow">' + replacementString + '</a>';
3735
+ }
3736
+
3737
+ var tagRegex = /[\s][@]+[A-Za-z0-9-_]+/g;
3738
+ ctfTextStr = ctfTextStr.replace(tagRegex, ctfReplaceTags);
3739
+
3740
+
3741
+ //Replace text with linked version
3742
+ $ctfText.html(ctfTextStr.trim());
3743
+ $ctfText.append($ctfTextMedia);
3744
+
3745
+ //Add link color
3746
+ $ctfText.find('a').css('color', '#' + ctfLinkColorHex);
3747
+ $ctfTextMedia.css('color', '#' + ctfLinkColorHex);
3748
+
3749
+ } // End "ctfdata-disablelinks" check
3750
+
3751
+ // shorten long urls in tweets
3752
+ $ctfItem.find('.ctf-tweet-text a').each(function () {
3753
+ if (jQuery(this).text().indexOf('http') > -1 && jQuery(this).text().length > 63) {
3754
+ jQuery(this).text(jQuery(this).text().substring(0, 60) + '...');
3755
+ }
3756
+ });
3757
+
3758
+ }); // End .ctfItem loop
3759
+
3760
+ //Change color of retweet icon to match text
3761
+ // $ctf.find('.ctf-retweet-icon').css({'background' : $ctf.find('.ctf-tweet-text a').css('color')}); //This doesn't work well if the link color is set to white as the default color of the icon text is also white
3762
+
3763
+