WordPress Landing Pages - Version 2.0.4

Version Description

  • fix issue with pausing variation A and misbehaving statistics when A is deleted.
  • fix issue with dropcap conversion area not displaying
Download this release

Release Info

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

Code changes from version 2.0.3 to 2.0.4

Files changed (59) hide show
  1. LICENSE +339 -339
  2. README.md +359 -359
  3. assets/images/image.php +62 -62
  4. assets/images/index.php +1 -1
  5. assets/js/admin/admin.global-settings.js +72 -72
  6. assets/js/admin/admin.install-plugins.js +30 -30
  7. assets/js/admin/admin.landing-page-list.js +160 -160
  8. assets/js/admin/admin.post-edit-ab-testing.js +63 -63
  9. assets/js/admin/admin.post-edit.js +322 -322
  10. assets/js/admin/admin.post-new.js +143 -143
  11. assets/js/admin/admin.post.js +34 -34
  12. assets/js/admin/admin.store.js +4 -4
  13. assets/js/admin/admin.templates-upload.js +24 -24
  14. assets/js/admin/index.php +1 -1
  15. assets/js/admin/intro.js +758 -758
  16. assets/js/admin/tour/tour.post-edit.js +8 -8
  17. assets/js/admin/tour/tour.post-list.js +7 -7
  18. assets/js/index.php +1 -1
  19. assets/js/jquery.bindfirst.js +14 -14
  20. assets/js/jquery.easing.min.js +44 -44
  21. assets/js/jquery.tablesorter.js +1030 -1030
  22. assets/js/jquery.total-storage.min.js +21 -21
  23. assets/js/wordpress/index.php +1 -1
  24. assets/lang/.tx/config +7 -7
  25. assets/lang/landing-pages.po +243 -243
  26. assets/libraries/class-tgm-plugin-activation.php +3540 -3540
  27. assets/libraries/datetimepicker/MIT-LICENSE.txt +18 -18
  28. assets/libraries/datetimepicker/README.md +28 -28
  29. assets/libraries/datetimepicker/bower.json +52 -52
  30. assets/libraries/datetimepicker/datetimepicker.jquery.json +46 -46
  31. assets/libraries/datetimepicker/jquery.datetimepicker.css +545 -545
  32. assets/libraries/datetimepicker/jquery.datetimepicker.js +2073 -2073
  33. assets/libraries/datetimepicker/package.json +28 -28
  34. assets/libraries/datetimepicker/picker_functions.js +55 -55
  35. assets/libraries/index.php +1 -1
  36. assets/libraries/jpicker/css/jPicker-1.1.6.css +231 -231
  37. assets/libraries/jpicker/css/jPicker.css +16 -16
  38. assets/libraries/jquery-qtip/jquery.qtip.min.js +1 -1
  39. assets/libraries/jquery-qtip/load.qtip.js +55 -55
  40. assets/libraries/jquery.zoomer.js +440 -440
  41. assets/libraries/script.js +36 -36
  42. assets/libraries/shareme/index.php +1 -1
  43. assets/libraries/shareme/sharrre/index.php +1 -1
  44. assets/libraries/shareme/sharrre/jquery.sharrre-1.3.3.js +584 -584
  45. assets/tests/bin/phantomjs.exe +0 -0
  46. assets/tests/bin/phantomloader +0 -7
  47. assets/tests/bin/wpcept +0 -7
  48. assets/tests/bin/wpcept.bat +0 -3
  49. assets/tests/build/php.conf +0 -4
  50. assets/tests/build/php.load +0 -1
  51. assets/tests/build/scratchpad.sh +0 -1
  52. assets/tests/build/travis-ci-apache +0 -23
  53. assets/tests/codeception/_bootstrap.php +0 -14
  54. assets/tests/codeception/_data/dump.sql +0 -1
  55. assets/tests/codeception/_support/AcceptanceHelper.php +0 -10
  56. assets/tests/codeception/_support/FunctionalHelper.php +0 -10
  57. assets/tests/codeception/_support/UnitHelper.php +0 -10
  58. assets/tests/codeception/acceptance.suite.yml +0 -25
  59. assets/tests/codeception/acceptance/AcceptanceTester.php +0 -1717
LICENSE CHANGED
@@ -1,339 +1,339 @@
1
- GNU GENERAL PUBLIC LICENSE
2
- Version 2, June 1991
3
-
4
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
- Everyone is permitted to copy and distribute verbatim copies
7
- of this license document, but changing it is not allowed.
8
-
9
- Preamble
10
-
11
- The licenses for most software are designed to take away your
12
- freedom to share and change it. By contrast, the GNU General Public
13
- License is intended to guarantee your freedom to share and change free
14
- software--to make sure the software is free for all its users. This
15
- General Public License applies to most of the Free Software
16
- Foundation's software and to any other program whose authors commit to
17
- using it. (Some other Free Software Foundation software is covered by
18
- the GNU Lesser General Public License instead.) You can apply it to
19
- your programs, too.
20
-
21
- When we speak of free software, we are referring to freedom, not
22
- price. Our General Public Licenses are designed to make sure that you
23
- have the freedom to distribute copies of free software (and charge for
24
- this service if you wish), that you receive source code or can get it
25
- if you want it, that you can change the software or use pieces of it
26
- in new free programs; and that you know you can do these things.
27
-
28
- To protect your rights, we need to make restrictions that forbid
29
- anyone to deny you these rights or to ask you to surrender the rights.
30
- These restrictions translate to certain responsibilities for you if you
31
- distribute copies of the software, or if you modify it.
32
-
33
- For example, if you distribute copies of such a program, whether
34
- gratis or for a fee, you must give the recipients all the rights that
35
- you have. You must make sure that they, too, receive or can get the
36
- source code. And you must show them these terms so they know their
37
- rights.
38
-
39
- We protect your rights with two steps: (1) copyright the software, and
40
- (2) offer you this license which gives you legal permission to copy,
41
- distribute and/or modify the software.
42
-
43
- Also, for each author's protection and ours, we want to make certain
44
- that everyone understands that there is no warranty for this free
45
- software. If the software is modified by someone else and passed on, we
46
- want its recipients to know that what they have is not the original, so
47
- that any problems introduced by others will not reflect on the original
48
- authors' reputations.
49
-
50
- Finally, any free program is threatened constantly by software
51
- patents. We wish to avoid the danger that redistributors of a free
52
- program will individually obtain patent licenses, in effect making the
53
- program proprietary. To prevent this, we have made it clear that any
54
- patent must be licensed for everyone's free use or not licensed at all.
55
-
56
- The precise terms and conditions for copying, distribution and
57
- modification follow.
58
-
59
- GNU GENERAL PUBLIC LICENSE
60
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
-
62
- 0. This License applies to any program or other work which contains
63
- a notice placed by the copyright holder saying it may be distributed
64
- under the terms of this General Public License. The "Program", below,
65
- refers to any such program or work, and a "work based on the Program"
66
- means either the Program or any derivative work under copyright law:
67
- that is to say, a work containing the Program or a portion of it,
68
- either verbatim or with modifications and/or translated into another
69
- language. (Hereinafter, translation is included without limitation in
70
- the term "modification".) Each licensee is addressed as "you".
71
-
72
- Activities other than copying, distribution and modification are not
73
- covered by this License; they are outside its scope. The act of
74
- running the Program is not restricted, and the output from the Program
75
- is covered only if its contents constitute a work based on the
76
- Program (independent of having been made by running the Program).
77
- Whether that is true depends on what the Program does.
78
-
79
- 1. You may copy and distribute verbatim copies of the Program's
80
- source code as you receive it, in any medium, provided that you
81
- conspicuously and appropriately publish on each copy an appropriate
82
- copyright notice and disclaimer of warranty; keep intact all the
83
- notices that refer to this License and to the absence of any warranty;
84
- and give any other recipients of the Program a copy of this License
85
- along with the Program.
86
-
87
- You may charge a fee for the physical act of transferring a copy, and
88
- you may at your option offer warranty protection in exchange for a fee.
89
-
90
- 2. You may modify your copy or copies of the Program or any portion
91
- of it, thus forming a work based on the Program, and copy and
92
- distribute such modifications or work under the terms of Section 1
93
- above, provided that you also meet all of these conditions:
94
-
95
- a) You must cause the modified files to carry prominent notices
96
- stating that you changed the files and the date of any change.
97
-
98
- b) You must cause any work that you distribute or publish, that in
99
- whole or in part contains or is derived from the Program or any
100
- part thereof, to be licensed as a whole at no charge to all third
101
- parties under the terms of this License.
102
-
103
- c) If the modified program normally reads commands interactively
104
- when run, you must cause it, when started running for such
105
- interactive use in the most ordinary way, to print or display an
106
- announcement including an appropriate copyright notice and a
107
- notice that there is no warranty (or else, saying that you provide
108
- a warranty) and that users may redistribute the program under
109
- these conditions, and telling the user how to view a copy of this
110
- License. (Exception: if the Program itself is interactive but
111
- does not normally print such an announcement, your work based on
112
- the Program is not required to print an announcement.)
113
-
114
- These requirements apply to the modified work as a whole. If
115
- identifiable sections of that work are not derived from the Program,
116
- and can be reasonably considered independent and separate works in
117
- themselves, then this License, and its terms, do not apply to those
118
- sections when you distribute them as separate works. But when you
119
- distribute the same sections as part of a whole which is a work based
120
- on the Program, the distribution of the whole must be on the terms of
121
- this License, whose permissions for other licensees extend to the
122
- entire whole, and thus to each and every part regardless of who wrote it.
123
-
124
- Thus, it is not the intent of this section to claim rights or contest
125
- your rights to work written entirely by you; rather, the intent is to
126
- exercise the right to control the distribution of derivative or
127
- collective works based on the Program.
128
-
129
- In addition, mere aggregation of another work not based on the Program
130
- with the Program (or with a work based on the Program) on a volume of
131
- a storage or distribution medium does not bring the other work under
132
- the scope of this License.
133
-
134
- 3. You may copy and distribute the Program (or a work based on it,
135
- under Section 2) in object code or executable form under the terms of
136
- Sections 1 and 2 above provided that you also do one of the following:
137
-
138
- a) Accompany it with the complete corresponding machine-readable
139
- source code, which must be distributed under the terms of Sections
140
- 1 and 2 above on a medium customarily used for software interchange; or,
141
-
142
- b) Accompany it with a written offer, valid for at least three
143
- years, to give any third party, for a charge no more than your
144
- cost of physically performing source distribution, a complete
145
- machine-readable copy of the corresponding source code, to be
146
- distributed under the terms of Sections 1 and 2 above on a medium
147
- customarily used for software interchange; or,
148
-
149
- c) Accompany it with the information you received as to the offer
150
- to distribute corresponding source code. (This alternative is
151
- allowed only for noncommercial distribution and only if you
152
- received the program in object code or executable form with such
153
- an offer, in accord with Subsection b above.)
154
-
155
- The source code for a work means the preferred form of the work for
156
- making modifications to it. For an executable work, complete source
157
- code means all the source code for all modules it contains, plus any
158
- associated interface definition files, plus the scripts used to
159
- control compilation and installation of the executable. However, as a
160
- special exception, the source code distributed need not include
161
- anything that is normally distributed (in either source or binary
162
- form) with the major components (compiler, kernel, and so on) of the
163
- operating system on which the executable runs, unless that component
164
- itself accompanies the executable.
165
-
166
- If distribution of executable or object code is made by offering
167
- access to copy from a designated place, then offering equivalent
168
- access to copy the source code from the same place counts as
169
- distribution of the source code, even though third parties are not
170
- compelled to copy the source along with the object code.
171
-
172
- 4. You may not copy, modify, sublicense, or distribute the Program
173
- except as expressly provided under this License. Any attempt
174
- otherwise to copy, modify, sublicense or distribute the Program is
175
- void, and will automatically terminate your rights under this License.
176
- However, parties who have received copies, or rights, from you under
177
- this License will not have their licenses terminated so long as such
178
- parties remain in full compliance.
179
-
180
- 5. You are not required to accept this License, since you have not
181
- signed it. However, nothing else grants you permission to modify or
182
- distribute the Program or its derivative works. These actions are
183
- prohibited by law if you do not accept this License. Therefore, by
184
- modifying or distributing the Program (or any work based on the
185
- Program), you indicate your acceptance of this License to do so, and
186
- all its terms and conditions for copying, distributing or modifying
187
- the Program or works based on it.
188
-
189
- 6. Each time you redistribute the Program (or any work based on the
190
- Program), the recipient automatically receives a license from the
191
- original licensor to copy, distribute or modify the Program subject to
192
- these terms and conditions. You may not impose any further
193
- restrictions on the recipients' exercise of the rights granted herein.
194
- You are not responsible for enforcing compliance by third parties to
195
- this License.
196
-
197
- 7. If, as a consequence of a court judgment or allegation of patent
198
- infringement or for any other reason (not limited to patent issues),
199
- conditions are imposed on you (whether by court order, agreement or
200
- otherwise) that contradict the conditions of this License, they do not
201
- excuse you from the conditions of this License. If you cannot
202
- distribute so as to satisfy simultaneously your obligations under this
203
- License and any other pertinent obligations, then as a consequence you
204
- may not distribute the Program at all. For example, if a patent
205
- license would not permit royalty-free redistribution of the Program by
206
- all those who receive copies directly or indirectly through you, then
207
- the only way you could satisfy both it and this License would be to
208
- refrain entirely from distribution of the Program.
209
-
210
- If any portion of this section is held invalid or unenforceable under
211
- any particular circumstance, the balance of the section is intended to
212
- apply and the section as a whole is intended to apply in other
213
- circumstances.
214
-
215
- It is not the purpose of this section to induce you to infringe any
216
- patents or other property right claims or to contest validity of any
217
- such claims; this section has the sole purpose of protecting the
218
- integrity of the free software distribution system, which is
219
- implemented by public license practices. Many people have made
220
- generous contributions to the wide range of software distributed
221
- through that system in reliance on consistent application of that
222
- system; it is up to the author/donor to decide if he or she is willing
223
- to distribute software through any other system and a licensee cannot
224
- impose that choice.
225
-
226
- This section is intended to make thoroughly clear what is believed to
227
- be a consequence of the rest of this License.
228
-
229
- 8. If the distribution and/or use of the Program is restricted in
230
- certain countries either by patents or by copyrighted interfaces, the
231
- original copyright holder who places the Program under this License
232
- may add an explicit geographical distribution limitation excluding
233
- those countries, so that distribution is permitted only in or among
234
- countries not thus excluded. In such case, this License incorporates
235
- the limitation as if written in the body of this License.
236
-
237
- 9. The Free Software Foundation may publish revised and/or new versions
238
- of the General Public License from time to time. Such new versions will
239
- be similar in spirit to the present version, but may differ in detail to
240
- address new problems or concerns.
241
-
242
- Each version is given a distinguishing version number. If the Program
243
- specifies a version number of this License which applies to it and "any
244
- later version", you have the option of following the terms and conditions
245
- either of that version or of any later version published by the Free
246
- Software Foundation. If the Program does not specify a version number of
247
- this License, you may choose any version ever published by the Free Software
248
- Foundation.
249
-
250
- 10. If you wish to incorporate parts of the Program into other free
251
- programs whose distribution conditions are different, write to the author
252
- to ask for permission. For software which is copyrighted by the Free
253
- Software Foundation, write to the Free Software Foundation; we sometimes
254
- make exceptions for this. Our decision will be guided by the two goals
255
- of preserving the free status of all derivatives of our free software and
256
- of promoting the sharing and reuse of software generally.
257
-
258
- NO WARRANTY
259
-
260
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
- OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
- MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
- TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
- PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
- REPAIR OR CORRECTION.
269
-
270
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
- INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
- OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
- TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
- YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
- PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
- POSSIBILITY OF SUCH DAMAGES.
279
-
280
- END OF TERMS AND CONDITIONS
281
-
282
- How to Apply These Terms to Your New Programs
283
-
284
- If you develop a new program, and you want it to be of the greatest
285
- possible use to the public, the best way to achieve this is to make it
286
- free software which everyone can redistribute and change under these terms.
287
-
288
- To do so, attach the following notices to the program. It is safest
289
- to attach them to the start of each source file to most effectively
290
- convey the exclusion of warranty; and each file should have at least
291
- the "copyright" line and a pointer to where the full notice is found.
292
-
293
- WordPress Landing Pages
294
- Copyright (C) 2013 Inbound Now
295
-
296
- This program is free software; you can redistribute it and/or modify
297
- it under the terms of the GNU General Public License as published by
298
- the Free Software Foundation; either version 2 of the License, or
299
- (at your option) any later version.
300
-
301
- This program is distributed in the hope that it will be useful,
302
- but WITHOUT ANY WARRANTY; without even the implied warranty of
303
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
- GNU General Public License for more details.
305
-
306
- You should have received a copy of the GNU General Public License along
307
- with this program; if not, write to the Free Software Foundation, Inc.,
308
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
-
310
- Also add information on how to contact you by electronic and paper mail.
311
-
312
- If the program is interactive, make it output a short notice like this
313
- when it starts in an interactive mode:
314
-
315
- Gnomovision version 69, Copyright (C) year name of author
316
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
- This is free software, and you are welcome to redistribute it
318
- under certain conditions; type `show c' for details.
319
-
320
- The hypothetical commands `show w' and `show c' should show the appropriate
321
- parts of the General Public License. Of course, the commands you use may
322
- be called something other than `show w' and `show c'; they could even be
323
- mouse-clicks or menu items--whatever suits your program.
324
-
325
- You should also get your employer (if you work as a programmer) or your
326
- school, if any, to sign a "copyright disclaimer" for the program, if
327
- necessary. Here is a sample; alter the names:
328
-
329
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
-
332
- {signature of Ty Coon}, 1 April 1989
333
- Ty Coon, President of Vice
334
-
335
- This General Public License does not permit incorporating your program into
336
- proprietary programs. If your program is a subroutine library, you may
337
- consider it more useful to permit linking proprietary applications with the
338
- library. If this is what you want to do, use the GNU Lesser General
339
- Public License instead of this License.
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6
+ Everyone is permitted to copy and distribute verbatim copies
7
+ of this license document, but changing it is not allowed.
8
+
9
+ Preamble
10
+
11
+ The licenses for most software are designed to take away your
12
+ freedom to share and change it. By contrast, the GNU General Public
13
+ License is intended to guarantee your freedom to share and change free
14
+ software--to make sure the software is free for all its users. This
15
+ General Public License applies to most of the Free Software
16
+ Foundation's software and to any other program whose authors commit to
17
+ using it. (Some other Free Software Foundation software is covered by
18
+ the GNU Lesser General Public License instead.) You can apply it to
19
+ your programs, too.
20
+
21
+ When we speak of free software, we are referring to freedom, not
22
+ price. Our General Public Licenses are designed to make sure that you
23
+ have the freedom to distribute copies of free software (and charge for
24
+ this service if you wish), that you receive source code or can get it
25
+ if you want it, that you can change the software or use pieces of it
26
+ in new free programs; and that you know you can do these things.
27
+
28
+ To protect your rights, we need to make restrictions that forbid
29
+ anyone to deny you these rights or to ask you to surrender the rights.
30
+ These restrictions translate to certain responsibilities for you if you
31
+ distribute copies of the software, or if you modify it.
32
+
33
+ For example, if you distribute copies of such a program, whether
34
+ gratis or for a fee, you must give the recipients all the rights that
35
+ you have. You must make sure that they, too, receive or can get the
36
+ source code. And you must show them these terms so they know their
37
+ rights.
38
+
39
+ We protect your rights with two steps: (1) copyright the software, and
40
+ (2) offer you this license which gives you legal permission to copy,
41
+ distribute and/or modify the software.
42
+
43
+ Also, for each author's protection and ours, we want to make certain
44
+ that everyone understands that there is no warranty for this free
45
+ software. If the software is modified by someone else and passed on, we
46
+ want its recipients to know that what they have is not the original, so
47
+ that any problems introduced by others will not reflect on the original
48
+ authors' reputations.
49
+
50
+ Finally, any free program is threatened constantly by software
51
+ patents. We wish to avoid the danger that redistributors of a free
52
+ program will individually obtain patent licenses, in effect making the
53
+ program proprietary. To prevent this, we have made it clear that any
54
+ patent must be licensed for everyone's free use or not licensed at all.
55
+
56
+ The precise terms and conditions for copying, distribution and
57
+ modification follow.
58
+
59
+ GNU GENERAL PUBLIC LICENSE
60
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61
+
62
+ 0. This License applies to any program or other work which contains
63
+ a notice placed by the copyright holder saying it may be distributed
64
+ under the terms of this General Public License. The "Program", below,
65
+ refers to any such program or work, and a "work based on the Program"
66
+ means either the Program or any derivative work under copyright law:
67
+ that is to say, a work containing the Program or a portion of it,
68
+ either verbatim or with modifications and/or translated into another
69
+ language. (Hereinafter, translation is included without limitation in
70
+ the term "modification".) Each licensee is addressed as "you".
71
+
72
+ Activities other than copying, distribution and modification are not
73
+ covered by this License; they are outside its scope. The act of
74
+ running the Program is not restricted, and the output from the Program
75
+ is covered only if its contents constitute a work based on the
76
+ Program (independent of having been made by running the Program).
77
+ Whether that is true depends on what the Program does.
78
+
79
+ 1. You may copy and distribute verbatim copies of the Program's
80
+ source code as you receive it, in any medium, provided that you
81
+ conspicuously and appropriately publish on each copy an appropriate
82
+ copyright notice and disclaimer of warranty; keep intact all the
83
+ notices that refer to this License and to the absence of any warranty;
84
+ and give any other recipients of the Program a copy of this License
85
+ along with the Program.
86
+
87
+ You may charge a fee for the physical act of transferring a copy, and
88
+ you may at your option offer warranty protection in exchange for a fee.
89
+
90
+ 2. You may modify your copy or copies of the Program or any portion
91
+ of it, thus forming a work based on the Program, and copy and
92
+ distribute such modifications or work under the terms of Section 1
93
+ above, provided that you also meet all of these conditions:
94
+
95
+ a) You must cause the modified files to carry prominent notices
96
+ stating that you changed the files and the date of any change.
97
+
98
+ b) You must cause any work that you distribute or publish, that in
99
+ whole or in part contains or is derived from the Program or any
100
+ part thereof, to be licensed as a whole at no charge to all third
101
+ parties under the terms of this License.
102
+
103
+ c) If the modified program normally reads commands interactively
104
+ when run, you must cause it, when started running for such
105
+ interactive use in the most ordinary way, to print or display an
106
+ announcement including an appropriate copyright notice and a
107
+ notice that there is no warranty (or else, saying that you provide
108
+ a warranty) and that users may redistribute the program under
109
+ these conditions, and telling the user how to view a copy of this
110
+ License. (Exception: if the Program itself is interactive but
111
+ does not normally print such an announcement, your work based on
112
+ the Program is not required to print an announcement.)
113
+
114
+ These requirements apply to the modified work as a whole. If
115
+ identifiable sections of that work are not derived from the Program,
116
+ and can be reasonably considered independent and separate works in
117
+ themselves, then this License, and its terms, do not apply to those
118
+ sections when you distribute them as separate works. But when you
119
+ distribute the same sections as part of a whole which is a work based
120
+ on the Program, the distribution of the whole must be on the terms of
121
+ this License, whose permissions for other licensees extend to the
122
+ entire whole, and thus to each and every part regardless of who wrote it.
123
+
124
+ Thus, it is not the intent of this section to claim rights or contest
125
+ your rights to work written entirely by you; rather, the intent is to
126
+ exercise the right to control the distribution of derivative or
127
+ collective works based on the Program.
128
+
129
+ In addition, mere aggregation of another work not based on the Program
130
+ with the Program (or with a work based on the Program) on a volume of
131
+ a storage or distribution medium does not bring the other work under
132
+ the scope of this License.
133
+
134
+ 3. You may copy and distribute the Program (or a work based on it,
135
+ under Section 2) in object code or executable form under the terms of
136
+ Sections 1 and 2 above provided that you also do one of the following:
137
+
138
+ a) Accompany it with the complete corresponding machine-readable
139
+ source code, which must be distributed under the terms of Sections
140
+ 1 and 2 above on a medium customarily used for software interchange; or,
141
+
142
+ b) Accompany it with a written offer, valid for at least three
143
+ years, to give any third party, for a charge no more than your
144
+ cost of physically performing source distribution, a complete
145
+ machine-readable copy of the corresponding source code, to be
146
+ distributed under the terms of Sections 1 and 2 above on a medium
147
+ customarily used for software interchange; or,
148
+
149
+ c) Accompany it with the information you received as to the offer
150
+ to distribute corresponding source code. (This alternative is
151
+ allowed only for noncommercial distribution and only if you
152
+ received the program in object code or executable form with such
153
+ an offer, in accord with Subsection b above.)
154
+
155
+ The source code for a work means the preferred form of the work for
156
+ making modifications to it. For an executable work, complete source
157
+ code means all the source code for all modules it contains, plus any
158
+ associated interface definition files, plus the scripts used to
159
+ control compilation and installation of the executable. However, as a
160
+ special exception, the source code distributed need not include
161
+ anything that is normally distributed (in either source or binary
162
+ form) with the major components (compiler, kernel, and so on) of the
163
+ operating system on which the executable runs, unless that component
164
+ itself accompanies the executable.
165
+
166
+ If distribution of executable or object code is made by offering
167
+ access to copy from a designated place, then offering equivalent
168
+ access to copy the source code from the same place counts as
169
+ distribution of the source code, even though third parties are not
170
+ compelled to copy the source along with the object code.
171
+
172
+ 4. You may not copy, modify, sublicense, or distribute the Program
173
+ except as expressly provided under this License. Any attempt
174
+ otherwise to copy, modify, sublicense or distribute the Program is
175
+ void, and will automatically terminate your rights under this License.
176
+ However, parties who have received copies, or rights, from you under
177
+ this License will not have their licenses terminated so long as such
178
+ parties remain in full compliance.
179
+
180
+ 5. You are not required to accept this License, since you have not
181
+ signed it. However, nothing else grants you permission to modify or
182
+ distribute the Program or its derivative works. These actions are
183
+ prohibited by law if you do not accept this License. Therefore, by
184
+ modifying or distributing the Program (or any work based on the
185
+ Program), you indicate your acceptance of this License to do so, and
186
+ all its terms and conditions for copying, distributing or modifying
187
+ the Program or works based on it.
188
+
189
+ 6. Each time you redistribute the Program (or any work based on the
190
+ Program), the recipient automatically receives a license from the
191
+ original licensor to copy, distribute or modify the Program subject to
192
+ these terms and conditions. You may not impose any further
193
+ restrictions on the recipients' exercise of the rights granted herein.
194
+ You are not responsible for enforcing compliance by third parties to
195
+ this License.
196
+
197
+ 7. If, as a consequence of a court judgment or allegation of patent
198
+ infringement or for any other reason (not limited to patent issues),
199
+ conditions are imposed on you (whether by court order, agreement or
200
+ otherwise) that contradict the conditions of this License, they do not
201
+ excuse you from the conditions of this License. If you cannot
202
+ distribute so as to satisfy simultaneously your obligations under this
203
+ License and any other pertinent obligations, then as a consequence you
204
+ may not distribute the Program at all. For example, if a patent
205
+ license would not permit royalty-free redistribution of the Program by
206
+ all those who receive copies directly or indirectly through you, then
207
+ the only way you could satisfy both it and this License would be to
208
+ refrain entirely from distribution of the Program.
209
+
210
+ If any portion of this section is held invalid or unenforceable under
211
+ any particular circumstance, the balance of the section is intended to
212
+ apply and the section as a whole is intended to apply in other
213
+ circumstances.
214
+
215
+ It is not the purpose of this section to induce you to infringe any
216
+ patents or other property right claims or to contest validity of any
217
+ such claims; this section has the sole purpose of protecting the
218
+ integrity of the free software distribution system, which is
219
+ implemented by public license practices. Many people have made
220
+ generous contributions to the wide range of software distributed
221
+ through that system in reliance on consistent application of that
222
+ system; it is up to the author/donor to decide if he or she is willing
223
+ to distribute software through any other system and a licensee cannot
224
+ impose that choice.
225
+
226
+ This section is intended to make thoroughly clear what is believed to
227
+ be a consequence of the rest of this License.
228
+
229
+ 8. If the distribution and/or use of the Program is restricted in
230
+ certain countries either by patents or by copyrighted interfaces, the
231
+ original copyright holder who places the Program under this License
232
+ may add an explicit geographical distribution limitation excluding
233
+ those countries, so that distribution is permitted only in or among
234
+ countries not thus excluded. In such case, this License incorporates
235
+ the limitation as if written in the body of this License.
236
+
237
+ 9. The Free Software Foundation may publish revised and/or new versions
238
+ of the General Public License from time to time. Such new versions will
239
+ be similar in spirit to the present version, but may differ in detail to
240
+ address new problems or concerns.
241
+
242
+ Each version is given a distinguishing version number. If the Program
243
+ specifies a version number of this License which applies to it and "any
244
+ later version", you have the option of following the terms and conditions
245
+ either of that version or of any later version published by the Free
246
+ Software Foundation. If the Program does not specify a version number of
247
+ this License, you may choose any version ever published by the Free Software
248
+ Foundation.
249
+
250
+ 10. If you wish to incorporate parts of the Program into other free
251
+ programs whose distribution conditions are different, write to the author
252
+ to ask for permission. For software which is copyrighted by the Free
253
+ Software Foundation, write to the Free Software Foundation; we sometimes
254
+ make exceptions for this. Our decision will be guided by the two goals
255
+ of preserving the free status of all derivatives of our free software and
256
+ of promoting the sharing and reuse of software generally.
257
+
258
+ NO WARRANTY
259
+
260
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261
+ FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262
+ OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263
+ PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264
+ OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265
+ MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266
+ TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267
+ PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268
+ REPAIR OR CORRECTION.
269
+
270
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271
+ WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272
+ REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273
+ INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274
+ OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275
+ TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276
+ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277
+ PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278
+ POSSIBILITY OF SUCH DAMAGES.
279
+
280
+ END OF TERMS AND CONDITIONS
281
+
282
+ How to Apply These Terms to Your New Programs
283
+
284
+ If you develop a new program, and you want it to be of the greatest
285
+ possible use to the public, the best way to achieve this is to make it
286
+ free software which everyone can redistribute and change under these terms.
287
+
288
+ To do so, attach the following notices to the program. It is safest
289
+ to attach them to the start of each source file to most effectively
290
+ convey the exclusion of warranty; and each file should have at least
291
+ the "copyright" line and a pointer to where the full notice is found.
292
+
293
+ WordPress Landing Pages
294
+ Copyright (C) 2013 Inbound Now
295
+
296
+ This program is free software; you can redistribute it and/or modify
297
+ it under the terms of the GNU General Public License as published by
298
+ the Free Software Foundation; either version 2 of the License, or
299
+ (at your option) any later version.
300
+
301
+ This program is distributed in the hope that it will be useful,
302
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
303
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304
+ GNU General Public License for more details.
305
+
306
+ You should have received a copy of the GNU General Public License along
307
+ with this program; if not, write to the Free Software Foundation, Inc.,
308
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309
+
310
+ Also add information on how to contact you by electronic and paper mail.
311
+
312
+ If the program is interactive, make it output a short notice like this
313
+ when it starts in an interactive mode:
314
+
315
+ Gnomovision version 69, Copyright (C) year name of author
316
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317
+ This is free software, and you are welcome to redistribute it
318
+ under certain conditions; type `show c' for details.
319
+
320
+ The hypothetical commands `show w' and `show c' should show the appropriate
321
+ parts of the General Public License. Of course, the commands you use may
322
+ be called something other than `show w' and `show c'; they could even be
323
+ mouse-clicks or menu items--whatever suits your program.
324
+
325
+ You should also get your employer (if you work as a programmer) or your
326
+ school, if any, to sign a "copyright disclaimer" for the program, if
327
+ necessary. Here is a sample; alter the names:
328
+
329
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
331
+
332
+ {signature of Ty Coon}, 1 April 1989
333
+ Ty Coon, President of Vice
334
+
335
+ This General Public License does not permit incorporating your program into
336
+ proprietary programs. If your program is a subroutine library, you may
337
+ consider it more useful to permit linking proprietary applications with the
338
+ library. If this is what you want to do, use the GNU Lesser General
339
+ Public License instead of this License.
README.md CHANGED
@@ -1,359 +1,359 @@
1
- ![](https://travis-ci.org/inboundnow/landing-pages.svg?branch=master)
2
-
3
- # WordPress Landing Pages #
4
-
5
- **Contributors:** David Wells, Hudson Atwell
6
- **Donate link:** mailto:marketplace@inboundnow.com
7
- **License:** GPLv2 or later
8
- **License URI:** http://www.gnu.org/licenses/gpl-2.0.html
9
- **Tags:** landing pages, inbound marketing, conversion pages, split testing, a b test, a b testing, a/b test, a/b testing, coming soon page, email list, landing page, list building, maintenance page, squeeze page, inbound now, landing-pages, splash pages, cpa, click tracking, goal tracking, analytics, free landing page templates
10
- **Requires at least:** 3.8
11
- **Tested up to:** 4.1
12
- **Stable Tag:** 1.8.0
13
-
14
- Create landing pages for your WordPress site. Monitor and improve conversion rates, run A/B split tests, customize your own templates and more.
15
-
16
- ## Description ##
17
-
18
- > WordPress Landing Pages works as a standalone plugin or hand in hand with [WordPress Calls to Action](http://wordpress.org/plugins/cta/ "Learn more about Calls to Action") & [WordPress Leads](http://wordpress.org/plugins/leads/ "Learn more about WordPress Leads") to create a powerful & free lead generation system for your business.
19
-
20
- This plugin creates landing pages (a.k.a. conversion or splash pages) for your WordPress site. It gives site owners the ability to monitor and track conversion rates, run a/b or multivariate split tests on landing pages, and most importantly increase lead flow!
21
-
22
- The landing page plugin was specifically designed with inbound marketing best practices in mind and will help you drive & convert more leads on your site.
23
-
24
- Landing pages are an ideal way to convert more of your passive website visitors into active leads or email list subscribers.
25
-
26
- ### Highlights ###
27
-
28
- * Create beautiful Landing Pages on your WordPress site.
29
- * Visual Editor to view changes being made on the fly!
30
- * Track conversion rates on your landing pages for continual optimization.
31
- * Easily clone existing landing pages and run A/B Split tests on variations.
32
- * Use your current WordPress theme or choose from our library of custom landing page designs.
33
- * Pre-populate Forms with visitor information to increase conversion rates
34
- * Gather lead intelligence and track lead activity with <a href="http://wordpress.org/plugins/leads/screenshots/">WordPress Leads</a>
35
- * Extend functionality with our growing repository of <a href="http://www.inboundnow.com/market/category/landing-pages/extensions/">third party add ons</a>.
36
- * Easily implement your own custom landing page design.
37
-
38
- This plugin is form agnostic meaning it will work with any form system you use.
39
-
40
- Recommended form plugins (Gravity forms, Ninja Forms or Contact form 7)
41
-
42
- ### About the Plugin ###
43
-
44
- http://www.youtube.com/watch?v=flEd0sRTFUo
45
-
46
- ### Developers & Designers ###
47
-
48
- We built the landing page plugin as a framework! Need A/B testing out of the box implemented for your existing designs? Use WordPress Landing Pages to quickly spin up new landing pages that have all the functionality your clients will need.
49
-
50
- You can quickly take your existing designs and implement them using our <a href="http://docs.inboundnow.com/section/developer/">templating framework</a>.
51
-
52
- The plugin is also fully extendable and has a number of actions, filters, and hooks available for use. If a hook doesn't exist, simply ask and we can implement custom changes.
53
-
54
-
55
- [Follow Development on GitHub ](https://github.com/inboundnow/landing-pages "Follow & Contribute to core development on GitHub")
56
- |
57
- [Follow Development on Twitter ](https://twitter.com/gitlandingpages "See our latest development commits on Twitter")
58
-
59
-
60
- ## Installation ##
61
-
62
- 1. Upload `landing-pages` folder to the `/wp-content/plugins/` directory
63
- 1. Activate the plugin through the 'Plugins' menu in WordPress
64
-
65
- ## Frequently Asked Questions ##
66
- *Can I create my own landing page designs?,
67
- *Yes! You can learn how to <a href="http://docs.inboundnow.com/guide/creating-landing-page-templates/">create your own landing page template here</a>.
68
-
69
- ## Screenshots ##
70
-
71
- ### 1. Landing Page Custom Post Type ###
72
- ![Landing Page Custom Post Type](screenshot-1.jpg)
73
-
74
- ### 2. Track conversion rates and continuously improve your landing pages ###
75
- ![Track conversion rates and continuously improve your landing pages](screenshot-2.jpg)
76
-
77
- ### 3. Manage Split Testing Page ###
78
- ![Manage Split Testing Page](screenshot-3.jpg)
79
-
80
- ### 4. Choose from a ton of pre-made templates, use your existing design, or design your own theme! ###
81
- ![Choose from a ton of pre-made templates, use your existing design, or design your own theme!](screenshot-4.jpg)
82
-
83
-
84
- ## Changelog ##
85
- ### 1.8.0 ###
86
- * Fixing addon store
87
-
88
- ### 1.7.9 ###
89
- * Even more security updates! Security for the win!
90
-
91
- ### 1.7.8 ###
92
- * Security Patch
93
-
94
- ### 1.7.7 ###
95
- * Fix double lead notification email
96
-
97
- ### 1.7.6 ###
98
- * Fixed double email submission on contact form 7
99
-
100
- ### 1.7.5 ###
101
- * Added form field exclusions to ignore sensitive data
102
-
103
- ### 1.7.3 ###
104
- * See changelog here: https://github.com/inboundnow/landing-pages/issues?q=is%3Aissue+is%3Aclosed+label%3Av1.7.3
105
-
106
- ### 1.7.2 ###
107
- * Improved form email typo detection
108
- * Improved Template Styles
109
- * Fixed content wysiwyg scroll freezing bug
110
-
111
- ### 1.7.1 ###
112
- * removed iframe of inbound now addon store. For addons please visit: http://inboundnow.com/market
113
-
114
- ### 1.7.0 ###
115
- * Removed anonymous PHP functions for PHP 5.2 support
116
- * Updated template creation standards
117
- * Converted varition modules to CLASS based system & documented
118
- * Move /lang/ file outside of shared
119
-
120
- ### 1.6.2 ###
121
- * Bug Fix: Fix with lead email notifications
122
-
123
- ### 1.5.9 ###
124
- * Various bug fixes.
125
- * Refactored main plugin file to class loader.
126
- * Improved localization systems.
127
-
128
- ### 1.5.8 ###
129
- * Bug Fix: Check all required fields
130
-
131
- ### 1.5.7 ###
132
- * Improvement: All core template now use new consolidated settings system.
133
- * Improvement: Leads Dashboard styling & stats
134
- * Improvement: Screenshots on local installation replaced with template thumbnails.
135
-
136
-
137
- ### 1.5.6 ###
138
- * Fix to insert marketing shortcode popup
139
-
140
- ### 1.5.5 ###
141
- * Added events to lead tracking
142
- * Bug Fix: Marketing Button
143
- * Optimized CTA Tracking JS.
144
- * Expanded impression/conversion analytics to all post types.
145
-
146
- ### 1.5.4 ###
147
- * Impression tracking bug fix.
148
- * Bringing Inbound Tracking to All Posts/Pages
149
-
150
- ### 1.5.3 ###
151
- * Temporary fix for shortcodes disappearing from wordpress 3.8 to 3.9
152
- * Performance improvements on analytics and lead tracking
153
-
154
- ### 1.5.1 ###
155
- * Misc bug fixes
156
-
157
- ### 1.5.0 ###
158
- * fixed field mapping bug
159
- * Added better compability for js conflicts
160
- * Prepping for marketing automation
161
-
162
- ### 1.4.9 ###
163
- * Fixed and improved default landing page templates
164
- * Updates to work with V2 of the CTA plugins
165
- * Improved form compatibilty with contact form 7, gravity forms, and ninja forms
166
- * Numerous bug files and code improvements
167
-
168
- ### 1.4.8 ###
169
- * Added Google Analytics Custom Event Tracking for form submissions
170
- * Added Ability: automatically sort leads into lists on form completions
171
- * Added Ability: Send lead notification emails to multiple people. Use comma separated values
172
- * Improved Social Media Buttons called with lp_social_media() function
173
- * Fixed qTranslate plugin bug
174
- * Fixed Genesis Title tag conflict
175
- * Added improved asset loader
176
- * Updated main docs.inboundnow.com site. Check it out!
177
-
178
- ### 1.4.7 ###
179
- * GPL fix with js library
180
-
181
- ### 1.4.6 ###
182
- * New Feature: Bulk Lead management with leads plugin wordpress.org/plugins/leads/
183
- * Added tags to lead profiles for improved management/categorization
184
- * Added new compatibility options to fix third party plugin conflicts!
185
- * Added new debugging javascript debugging tools for users
186
- * Fixed Email Sending Error on forms
187
- * Improved support for master license keys
188
-
189
- ### 1.4.5 ###
190
- * Added New HTML Lead Email Template with clickable links for faster lead management
191
- * Added Button Shortcodes!
192
- * Added HTML field option to form tool
193
- * Added Divider Option to Form tool
194
- * Added multi column support to icon list shortcode
195
- * Added Font Awesome Icons option to Inbound Form Submit buttons
196
- * Added Social Sharing Shortcode
197
- * Bug fix - emails not sending after form conversion fixed
198
-
199
- ### 1.4.1 ###
200
- * Bug fix - missing trackingObj
201
-
202
- ### 1.4.0 ###
203
- * Added feature request form to all plugin admin pages. Submit your feature requests today! =)
204
-
205
- ### 1.3.9 ###
206
- * Bug fixes for form creation issues
207
- * Bug fixes for safari page tracking not firing
208
- * Added quick menu to WP admin bar for quicker marketing!
209
-
210
- ### 1.3.8 ###
211
- * Updated styles to 3.8 wordpress
212
- * Streamlined form creation
213
- * fixed rogue PHP errors
214
-
215
- ### 1.3.7 ###
216
- * Added: Shortcode now automatically render in landing page option echos in templates
217
- * Updated: Visual Editor tool
218
- * Updated: Template selection interface
219
- * Updated: Major updates to core templates, CSS tweaks and fixes
220
- * Fixed: Shortcode insert into correct editor box
221
- * Fixed: editor always on HTML view
222
-
223
- ### 1.3.6 ###
224
-
225
- * Added: New Shortcodes! Fancy List and Column shortcodes
226
- * Added: Added email confirmation support to Inbound Forms tool
227
- * Added: Added New Welcome Page with Tutorial Video on Getting Started
228
- * Added: New Debug Tab for faster support requests/debugging
229
- * Fixed: CSS conflicts with button classes
230
-
231
- ### 1.3.1 ###
232
-
233
- * Added: Added InboundNow form creation and management system (beta)
234
- * Added: Support for InboundNow cross plugin extensions
235
- * Added: 'Sticky Variations' to global settings.
236
- * Added: Easier way for extension developers to license their extensions.
237
- * Added: 'header' setting component to global settings.
238
- * Fixed: Security issues
239
- * Improvement: Improved data management for global settings, metaboxes, and extensions.
240
-
241
- ### 1.2.3 ###
242
-
243
- * Fixed: Security issue with vulnerability to sql injection.
244
-
245
- ### 1.2.1 ###
246
-
247
- * Fixed: Issues with shortcodes rendering in wp-admin for variations.
248
-
249
- ### 1.1.9 ###
250
-
251
- * Fixed: Issues with navigation menu items breaking on landing pages with the default template selected.
252
-
253
- ### 1.1.8 ###
254
-
255
- * Fixed: Issue with post_content not saving for variations.
256
- * Added: [lp_conversion_area] Shortcode. It renders form conversion area anywhere on landing page
257
- * Fixed: Restored the ability to delete custom templates from 'Templates' section.
258
-
259
- ### 1.1.7 ###
260
-
261
- * Fixed: Issue with extension license keys not validating
262
- * Fixed: Issue with shortcodes not firing on select core templates
263
- * Improvement: Converted global settings data array to new easier to read format for development, added in legacy support.
264
-
265
- ### 1.1.0.1 ###
266
-
267
- * Fixed: Variation saves for custom css and custom js.
268
- * Fixed: jQuery error related to wysiwyg content formatting.
269
-
270
- ### 1.0.9.9 ###
271
- * Improved extension metabox loading for quicker load times and optimized meta data storage.
272
- * Phased out more 'old method' split testing components.
273
- * Improved .htaccess parsing.
274
- * Addressed issue with line breaks being removed from WYSIWYG editors.
275
-
276
- ### 1.0.9.4 ###
277
- * Added in tours for the edit screen and the list of landing page screen for new users to learn how to use the tool quickly and easily
278
- * Updated conversion tracking for wp-leads addon plugin
279
- * Added in option for default templates to toggle on/off navigation
280
-
281
- ### 1.0.9.3 ###
282
-
283
- * Removed old A/B split testing and the new system is fully in place!
284
-
285
- ### 1.0.9.0 ###
286
-
287
- * Added in A/B stats to the main landing page list view
288
-
289
- ### 1.0.8.6 ###
290
-
291
- * Release new and improved version of A/B testing!
292
- * Ajax saving on landing page options for faster page edits
293
- * Frontend Visual Editor to see what you are editing/changing
294
- * Enabled frontend editor for use on normal pages and posts
295
-
296
- ### 1.0.8.5 ###
297
-
298
- Providing better conversion and impression tracking for landing pages that are set as homepage.
299
-
300
- ### 1.0.8.4 ###
301
-
302
- Fixing activation bug
303
-
304
- ### 1.0.8.1 ###
305
-
306
- Fixing issue with jquery submission errors.
307
-
308
- ### 1.0.7.9 ###
309
-
310
- Added capability to activate and update license keys for premium extensions. Added ability to define white listed HTML elements for Form Standardization process.
311
-
312
- ### 1.0.7.3 ###
313
-
314
- Fixed issue with WP_List_table causing posts to to save or edit propperly Attempt 001
315
-
316
- ### 1.0.7.1 ###
317
-
318
- Added cookie based auto-field population & lead data collection to core.
319
-
320
- ### 1.0.5.6 ###
321
-
322
- Fixed issue with global setting's radio buttons not holding new set values.
323
-
324
- ### 1.0.5.3 ###
325
-
326
- Solutions for custom post type wp rewrite issue on activation.
327
-
328
- ### 1.0.5.1 ###
329
-
330
- Introducing version control system for extensions.
331
-
332
- ### 1.0.4.4 ###
333
-
334
- Migrating store to new location. Updating version control systems
335
-
336
- ### 1.0.4.2 ###
337
-
338
- Added new defitions to form standardization parser. Limited .htaccess rewrites to plugin activation to try and mitigate .htaccess corruptions.
339
-
340
- ### 1.0.4.1 ###
341
-
342
- Fixed issue with conversions not recording on some servers by forcing form submittal to wait until ajax has completely finnished loading before continuing to process form.
343
-
344
- ### 1.0.3.9 ###
345
-
346
- Fixed issue with plugins and wp core refusing to update on some installations when landing page plugin is activated.
347
-
348
- ### 1.0.3.8 ###
349
-
350
- Debugging cross browser impressions and conversion tracking. Implemented soltion for url-to-postid conversions that's compatible with the /slug/ removal extension for landing pages plugin.
351
- Added email validation check to prevent false positives when form standardization is turned on.
352
-
353
- ### 1.0.3.7 ###
354
-
355
- **Bug Fix:** 'Clear Stats' button.
356
-
357
- ### 1.1 ###
358
-
359
- Released
1
+ ![](https://travis-ci.org/inboundnow/landing-pages.svg?branch=master)
2
+
3
+ # WordPress Landing Pages #
4
+
5
+ **Contributors:** David Wells, Hudson Atwell
6
+ **Donate link:** mailto:marketplace@inboundnow.com
7
+ **License:** GPLv2 or later
8
+ **License URI:** http://www.gnu.org/licenses/gpl-2.0.html
9
+ **Tags:** landing pages, inbound marketing, conversion pages, split testing, a b test, a b testing, a/b test, a/b testing, coming soon page, email list, landing page, list building, maintenance page, squeeze page, inbound now, landing-pages, splash pages, cpa, click tracking, goal tracking, analytics, free landing page templates
10
+ **Requires at least:** 3.8
11
+ **Tested up to:** 4.1
12
+ **Stable Tag:** 1.8.0
13
+
14
+ Create landing pages for your WordPress site. Monitor and improve conversion rates, run A/B split tests, customize your own templates and more.
15
+
16
+ ## Description ##
17
+
18
+ > WordPress Landing Pages works as a standalone plugin or hand in hand with [WordPress Calls to Action](http://wordpress.org/plugins/cta/ "Learn more about Calls to Action") & [WordPress Leads](http://wordpress.org/plugins/leads/ "Learn more about WordPress Leads") to create a powerful & free lead generation system for your business.
19
+
20
+ This plugin creates landing pages (a.k.a. conversion or splash pages) for your WordPress site. It gives site owners the ability to monitor and track conversion rates, run a/b or multivariate split tests on landing pages, and most importantly increase lead flow!
21
+
22
+ The landing page plugin was specifically designed with inbound marketing best practices in mind and will help you drive & convert more leads on your site.
23
+
24
+ Landing pages are an ideal way to convert more of your passive website visitors into active leads or email list subscribers.
25
+
26
+ ### Highlights ###
27
+
28
+ * Create beautiful Landing Pages on your WordPress site.
29
+ * Visual Editor to view changes being made on the fly!
30
+ * Track conversion rates on your landing pages for continual optimization.
31
+ * Easily clone existing landing pages and run A/B Split tests on variations.
32
+ * Use your current WordPress theme or choose from our library of custom landing page designs.
33
+ * Pre-populate Forms with visitor information to increase conversion rates
34
+ * Gather lead intelligence and track lead activity with <a href="http://wordpress.org/plugins/leads/screenshots/">WordPress Leads</a>
35
+ * Extend functionality with our growing repository of <a href="http://www.inboundnow.com/market/category/landing-pages/extensions/">third party add ons</a>.
36
+ * Easily implement your own custom landing page design.
37
+
38
+ This plugin is form agnostic meaning it will work with any form system you use.
39
+
40
+ Recommended form plugins (Gravity forms, Ninja Forms or Contact form 7)
41
+
42
+ ### About the Plugin ###
43
+
44
+ http://www.youtube.com/watch?v=flEd0sRTFUo
45
+
46
+ ### Developers & Designers ###
47
+
48
+ We built the landing page plugin as a framework! Need A/B testing out of the box implemented for your existing designs? Use WordPress Landing Pages to quickly spin up new landing pages that have all the functionality your clients will need.
49
+
50
+ You can quickly take your existing designs and implement them using our <a href="http://docs.inboundnow.com/section/developer/">templating framework</a>.
51
+
52
+ The plugin is also fully extendable and has a number of actions, filters, and hooks available for use. If a hook doesn't exist, simply ask and we can implement custom changes.
53
+
54
+
55
+ [Follow Development on GitHub ](https://github.com/inboundnow/landing-pages "Follow & Contribute to core development on GitHub")
56
+ |
57
+ [Follow Development on Twitter ](https://twitter.com/gitlandingpages "See our latest development commits on Twitter")
58
+
59
+
60
+ ## Installation ##
61
+
62
+ 1. Upload `landing-pages` folder to the `/wp-content/plugins/` directory
63
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
64
+
65
+ ## Frequently Asked Questions ##
66
+ *Can I create my own landing page designs?,
67
+ *Yes! You can learn how to <a href="http://docs.inboundnow.com/guide/creating-landing-page-templates/">create your own landing page template here</a>.
68
+
69
+ ## Screenshots ##
70
+
71
+ ### 1. Landing Page Custom Post Type ###
72
+ ![Landing Page Custom Post Type](screenshot-1.jpg)
73
+
74
+ ### 2. Track conversion rates and continuously improve your landing pages ###
75
+ ![Track conversion rates and continuously improve your landing pages](screenshot-2.jpg)
76
+
77
+ ### 3. Manage Split Testing Page ###
78
+ ![Manage Split Testing Page](screenshot-3.jpg)
79
+
80
+ ### 4. Choose from a ton of pre-made templates, use your existing design, or design your own theme! ###
81
+ ![Choose from a ton of pre-made templates, use your existing design, or design your own theme!](screenshot-4.jpg)
82
+
83
+
84
+ ## Changelog ##
85
+ ### 1.8.0 ###
86
+ * Fixing addon store
87
+
88
+ ### 1.7.9 ###
89
+ * Even more security updates! Security for the win!
90
+
91
+ ### 1.7.8 ###
92
+ * Security Patch
93
+
94
+ ### 1.7.7 ###
95
+ * Fix double lead notification email
96
+
97
+ ### 1.7.6 ###
98
+ * Fixed double email submission on contact form 7
99
+
100
+ ### 1.7.5 ###
101
+ * Added form field exclusions to ignore sensitive data
102
+
103
+ ### 1.7.3 ###
104
+ * See changelog here: https://github.com/inboundnow/landing-pages/issues?q=is%3Aissue+is%3Aclosed+label%3Av1.7.3
105
+
106
+ ### 1.7.2 ###
107
+ * Improved form email typo detection
108
+ * Improved Template Styles
109
+ * Fixed content wysiwyg scroll freezing bug
110
+
111
+ ### 1.7.1 ###
112
+ * removed iframe of inbound now addon store. For addons please visit: http://inboundnow.com/market
113
+
114
+ ### 1.7.0 ###
115
+ * Removed anonymous PHP functions for PHP 5.2 support
116
+ * Updated template creation standards
117
+ * Converted varition modules to CLASS based system & documented
118
+ * Move /lang/ file outside of shared
119
+
120
+ ### 1.6.2 ###
121
+ * Bug Fix: Fix with lead email notifications
122
+
123
+ ### 1.5.9 ###
124
+ * Various bug fixes.
125
+ * Refactored main plugin file to class loader.
126
+ * Improved localization systems.
127
+
128
+ ### 1.5.8 ###
129
+ * Bug Fix: Check all required fields
130
+
131
+ ### 1.5.7 ###
132
+ * Improvement: All core template now use new consolidated settings system.
133
+ * Improvement: Leads Dashboard styling & stats
134
+ * Improvement: Screenshots on local installation replaced with template thumbnails.
135
+
136
+
137
+ ### 1.5.6 ###
138
+ * Fix to insert marketing shortcode popup
139
+
140
+ ### 1.5.5 ###
141
+ * Added events to lead tracking
142
+ * Bug Fix: Marketing Button
143
+ * Optimized CTA Tracking JS.
144
+ * Expanded impression/conversion analytics to all post types.
145
+
146
+ ### 1.5.4 ###
147
+ * Impression tracking bug fix.
148
+ * Bringing Inbound Tracking to All Posts/Pages
149
+
150
+ ### 1.5.3 ###
151
+ * Temporary fix for shortcodes disappearing from wordpress 3.8 to 3.9
152
+ * Performance improvements on analytics and lead tracking
153
+
154
+ ### 1.5.1 ###
155
+ * Misc bug fixes
156
+
157
+ ### 1.5.0 ###
158
+ * fixed field mapping bug
159
+ * Added better compability for js conflicts
160
+ * Prepping for marketing automation
161
+
162
+ ### 1.4.9 ###
163
+ * Fixed and improved default landing page templates
164
+ * Updates to work with V2 of the CTA plugins
165
+ * Improved form compatibilty with contact form 7, gravity forms, and ninja forms
166
+ * Numerous bug files and code improvements
167
+
168
+ ### 1.4.8 ###
169
+ * Added Google Analytics Custom Event Tracking for form submissions
170
+ * Added Ability: automatically sort leads into lists on form completions
171
+ * Added Ability: Send lead notification emails to multiple people. Use comma separated values
172
+ * Improved Social Media Buttons called with lp_social_media() function
173
+ * Fixed qTranslate plugin bug
174
+ * Fixed Genesis Title tag conflict
175
+ * Added improved asset loader
176
+ * Updated main docs.inboundnow.com site. Check it out!
177
+
178
+ ### 1.4.7 ###
179
+ * GPL fix with js library
180
+
181
+ ### 1.4.6 ###
182
+ * New Feature: Bulk Lead management with leads plugin wordpress.org/plugins/leads/
183
+ * Added tags to lead profiles for improved management/categorization
184
+ * Added new compatibility options to fix third party plugin conflicts!
185
+ * Added new debugging javascript debugging tools for users
186
+ * Fixed Email Sending Error on forms
187
+ * Improved support for master license keys
188
+
189
+ ### 1.4.5 ###
190
+ * Added New HTML Lead Email Template with clickable links for faster lead management
191
+ * Added Button Shortcodes!
192
+ * Added HTML field option to form tool
193
+ * Added Divider Option to Form tool
194
+ * Added multi column support to icon list shortcode
195
+ * Added Font Awesome Icons option to Inbound Form Submit buttons
196
+ * Added Social Sharing Shortcode
197
+ * Bug fix - emails not sending after form conversion fixed
198
+
199
+ ### 1.4.1 ###
200
+ * Bug fix - missing trackingObj
201
+
202
+ ### 1.4.0 ###
203
+ * Added feature request form to all plugin admin pages. Submit your feature requests today! =)
204
+
205
+ ### 1.3.9 ###
206
+ * Bug fixes for form creation issues
207
+ * Bug fixes for safari page tracking not firing
208
+ * Added quick menu to WP admin bar for quicker marketing!
209
+
210
+ ### 1.3.8 ###
211
+ * Updated styles to 3.8 wordpress
212
+ * Streamlined form creation
213
+ * fixed rogue PHP errors
214
+
215
+ ### 1.3.7 ###
216
+ * Added: Shortcode now automatically render in landing page option echos in templates
217
+ * Updated: Visual Editor tool
218
+ * Updated: Template selection interface
219
+ * Updated: Major updates to core templates, CSS tweaks and fixes
220
+ * Fixed: Shortcode insert into correct editor box
221
+ * Fixed: editor always on HTML view
222
+
223
+ ### 1.3.6 ###
224
+
225
+ * Added: New Shortcodes! Fancy List and Column shortcodes
226
+ * Added: Added email confirmation support to Inbound Forms tool
227
+ * Added: Added New Welcome Page with Tutorial Video on Getting Started
228
+ * Added: New Debug Tab for faster support requests/debugging
229
+ * Fixed: CSS conflicts with button classes
230
+
231
+ ### 1.3.1 ###
232
+
233
+ * Added: Added InboundNow form creation and management system (beta)
234
+ * Added: Support for InboundNow cross plugin extensions
235
+ * Added: 'Sticky Variations' to global settings.
236
+ * Added: Easier way for extension developers to license their extensions.
237
+ * Added: 'header' setting component to global settings.
238
+ * Fixed: Security issues
239
+ * Improvement: Improved data management for global settings, metaboxes, and extensions.
240
+
241
+ ### 1.2.3 ###
242
+
243
+ * Fixed: Security issue with vulnerability to sql injection.
244
+
245
+ ### 1.2.1 ###
246
+
247
+ * Fixed: Issues with shortcodes rendering in wp-admin for variations.
248
+
249
+ ### 1.1.9 ###
250
+
251
+ * Fixed: Issues with navigation menu items breaking on landing pages with the default template selected.
252
+
253
+ ### 1.1.8 ###
254
+
255
+ * Fixed: Issue with post_content not saving for variations.
256
+ * Added: [lp_conversion_area] Shortcode. It renders form conversion area anywhere on landing page
257
+ * Fixed: Restored the ability to delete custom templates from 'Templates' section.
258
+
259
+ ### 1.1.7 ###
260
+
261
+ * Fixed: Issue with extension license keys not validating
262
+ * Fixed: Issue with shortcodes not firing on select core templates
263
+ * Improvement: Converted global settings data array to new easier to read format for development, added in legacy support.
264
+
265
+ ### 1.1.0.1 ###
266
+
267
+ * Fixed: Variation saves for custom css and custom js.
268
+ * Fixed: jQuery error related to wysiwyg content formatting.
269
+
270
+ ### 1.0.9.9 ###
271
+ * Improved extension metabox loading for quicker load times and optimized meta data storage.
272
+ * Phased out more 'old method' split testing components.
273
+ * Improved .htaccess parsing.
274
+ * Addressed issue with line breaks being removed from WYSIWYG editors.
275
+
276
+ ### 1.0.9.4 ###
277
+ * Added in tours for the edit screen and the list of landing page screen for new users to learn how to use the tool quickly and easily
278
+ * Updated conversion tracking for wp-leads addon plugin
279
+ * Added in option for default templates to toggle on/off navigation
280
+
281
+ ### 1.0.9.3 ###
282
+
283
+ * Removed old A/B split testing and the new system is fully in place!
284
+
285
+ ### 1.0.9.0 ###
286
+
287
+ * Added in A/B stats to the main landing page list view
288
+
289
+ ### 1.0.8.6 ###
290
+
291
+ * Release new and improved version of A/B testing!
292
+ * Ajax saving on landing page options for faster page edits
293
+ * Frontend Visual Editor to see what you are editing/changing
294
+ * Enabled frontend editor for use on normal pages and posts
295
+
296
+ ### 1.0.8.5 ###
297
+
298
+ Providing better conversion and impression tracking for landing pages that are set as homepage.
299
+
300
+ ### 1.0.8.4 ###
301
+
302
+ Fixing activation bug
303
+
304
+ ### 1.0.8.1 ###
305
+
306
+ Fixing issue with jquery submission errors.
307
+
308
+ ### 1.0.7.9 ###
309
+
310
+ Added capability to activate and update license keys for premium extensions. Added ability to define white listed HTML elements for Form Standardization process.
311
+
312
+ ### 1.0.7.3 ###
313
+
314
+ Fixed issue with WP_List_table causing posts to to save or edit propperly Attempt 001
315
+
316
+ ### 1.0.7.1 ###
317
+
318
+ Added cookie based auto-field population & lead data collection to core.
319
+
320
+ ### 1.0.5.6 ###
321
+
322
+ Fixed issue with global setting's radio buttons not holding new set values.
323
+
324
+ ### 1.0.5.3 ###
325
+
326
+ Solutions for custom post type wp rewrite issue on activation.
327
+
328
+ ### 1.0.5.1 ###
329
+
330
+ Introducing version control system for extensions.
331
+
332
+ ### 1.0.4.4 ###
333
+
334
+ Migrating store to new location. Updating version control systems
335
+
336
+ ### 1.0.4.2 ###
337
+
338
+ Added new defitions to form standardization parser. Limited .htaccess rewrites to plugin activation to try and mitigate .htaccess corruptions.
339
+
340
+ ### 1.0.4.1 ###
341
+
342
+ Fixed issue with conversions not recording on some servers by forcing form submittal to wait until ajax has completely finnished loading before continuing to process form.
343
+
344
+ ### 1.0.3.9 ###
345
+
346
+ Fixed issue with plugins and wp core refusing to update on some installations when landing page plugin is activated.
347
+
348
+ ### 1.0.3.8 ###
349
+
350
+ Debugging cross browser impressions and conversion tracking. Implemented soltion for url-to-postid conversions that's compatible with the /slug/ removal extension for landing pages plugin.
351
+ Added email validation check to prevent false positives when form standardization is turned on.
352
+
353
+ ### 1.0.3.7 ###
354
+
355
+ **Bug Fix:** 'Clear Stats' button.
356
+
357
+ ### 1.1 ###
358
+
359
+ Released
assets/images/image.php CHANGED
@@ -1,63 +1,63 @@
1
- <?php
2
-
3
- // file: image.php
4
- // Dynamically Create a clear png for css background opacities
5
- header("Content-type: image/png");
6
-
7
- $hex_value = $_GET['hex'];
8
-
9
- if (isset($_GET['trans'])) {
10
- $trans_value = $_GET['trans'];
11
- }
12
- else {
13
- $trans_value = 50;
14
- }
15
-
16
- if (!function_exists('_inbound_HexToRGB')) {
17
- // Convert Hex to RGB Value
18
- function _inbound_HexToRGB($hex) {
19
- $hex = preg_replace("/#/", "", $hex);
20
- $color = array();
21
-
22
- if(strlen($hex) == 3) {
23
- $color['r'] = hexdec(substr($hex, 0, 1) . $r);
24
- $color['g'] = hexdec(substr($hex, 1, 1) . $g);
25
- $color['b'] = hexdec(substr($hex, 2, 1) . $b);
26
- }
27
- else if(strlen($hex) == 6) {
28
- $color['r'] = hexdec(substr($hex, 0, 2));
29
- $color['g'] = hexdec(substr($hex, 2, 2));
30
- $color['b'] = hexdec(substr($hex, 4, 2));
31
- }
32
-
33
- return $color;
34
-
35
- }
36
- }
37
-
38
- $RBG_array = _inbound_HexToRGB($hex_value);
39
-
40
- if(isset($RBG_array)) {
41
- $red = (isset($RBG_array['r'])) ? $RBG_array['r'] : '0';
42
- $green = (isset($RBG_array['g'])) ? $RBG_array['g'] : '0';
43
- $blue = (isset($RBG_array['b'])) ? $RBG_array['b'] : '0';
44
-
45
- // Set the image
46
- $img = imagecreatetruecolor(10,10); // 10 x 10 px
47
- imagesavealpha($img, true);
48
-
49
- // Fill the image with transparent color
50
- $color = imagecolorallocatealpha($img,$red,$green,$blue,$trans_value);
51
- imagefill($img, 0, 0, $color);
52
-
53
- // Return the image
54
- imagepng($img);
55
-
56
- // Destroy image
57
- imagedestroy($img);
58
-
59
- }
60
- // usage in html: <image src="path-to-file/image.php?hex=HEXCOLOR">
61
- // Make sure to add in the HEX GET Parameters with ?hex= and ?trans= for transparency
62
- // example: <image src="path-to-file/image.php?hex=ffffff"> will call white transparent png
63
  ?>
1
+ <?php
2
+
3
+ // file: image.php
4
+ // Dynamically Create a clear png for css background opacities
5
+ header("Content-type: image/png");
6
+
7
+ $hex_value = $_GET['hex'];
8
+
9
+ if (isset($_GET['trans'])) {
10
+ $trans_value = $_GET['trans'];
11
+ }
12
+ else {
13
+ $trans_value = 50;
14
+ }
15
+
16
+ if (!function_exists('_inbound_HexToRGB')) {
17
+ // Convert Hex to RGB Value
18
+ function _inbound_HexToRGB($hex) {
19
+ $hex = preg_replace("/#/", "", $hex);
20
+ $color = array();
21
+
22
+ if(strlen($hex) == 3) {
23
+ $color['r'] = hexdec(substr($hex, 0, 1) . $r);
24
+ $color['g'] = hexdec(substr($hex, 1, 1) . $g);
25
+ $color['b'] = hexdec(substr($hex, 2, 1) . $b);
26
+ }
27
+ else if(strlen($hex) == 6) {
28
+ $color['r'] = hexdec(substr($hex, 0, 2));
29
+ $color['g'] = hexdec(substr($hex, 2, 2));
30
+ $color['b'] = hexdec(substr($hex, 4, 2));
31
+ }
32
+
33
+ return $color;
34
+
35
+ }
36
+ }
37
+
38
+ $RBG_array = _inbound_HexToRGB($hex_value);
39
+
40
+ if(isset($RBG_array)) {
41
+ $red = (isset($RBG_array['r'])) ? $RBG_array['r'] : '0';
42
+ $green = (isset($RBG_array['g'])) ? $RBG_array['g'] : '0';
43
+ $blue = (isset($RBG_array['b'])) ? $RBG_array['b'] : '0';
44
+
45
+ // Set the image
46
+ $img = imagecreatetruecolor(10,10); // 10 x 10 px
47
+ imagesavealpha($img, true);
48
+
49
+ // Fill the image with transparent color
50
+ $color = imagecolorallocatealpha($img,$red,$green,$blue,$trans_value);
51
+ imagefill($img, 0, 0, $color);
52
+
53
+ // Return the image
54
+ imagepng($img);
55
+
56
+ // Destroy image
57
+ imagedestroy($img);
58
+
59
+ }
60
+ // usage in html: <image src="path-to-file/image.php?hex=HEXCOLOR">
61
+ // Make sure to add in the HEX GET Parameters with ?hex= and ?trans= for transparency
62
+ // example: <image src="path-to-file/image.php?hex=ffffff"> will call white transparent png
63
  ?>
assets/images/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/js/admin/admin.global-settings.js CHANGED
@@ -1,73 +1,73 @@
1
- function getUrlVars() {
2
- var vars = [], hash;
3
- var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
4
- for (var i = 0; i < hashes.length; i++) {
5
- hash = hashes[i].split('=');
6
- vars.push(hash[0]);
7
- vars[hash[0]] = hash[1];
8
- }
9
- return vars;
10
- };
11
-
12
- function getUrlVar(name) {
13
- return getUrlVars()[name];
14
- };
15
-
16
- jQuery(document).ready(function ($) {
17
-
18
- jQuery(document).ready(function() {
19
- jQuery('.tooltip').tooltipster({
20
- contentAsHTML: true,
21
- interactive: true,
22
- maxWidth: 350,
23
- position: "right",
24
- theme: "tooltipster-noir"
25
- });
26
- });
27
-
28
- // Getting URL var by its nam
29
- var byName = getUrlVar('tab');
30
-
31
- // Set setting Tab
32
- setTimeout(function () {
33
- jQuery("#" + byName).click();
34
- }, 300);
35
-
36
- /* Update Setting URL */
37
- jQuery("body").on('click', '.nav-tab', function () {
38
- var this_id = jQuery(this).attr('id');
39
- if (history.pushState) {
40
- var newurl = window.location.href.replace(/tab=([^"]*)/g, 'tab=' + this_id);
41
- var current_tab = newurl.match(/tab=([^"]*)/g);
42
- if (typeof (current_tab) != "undefined" && current_tab != null && current_tab != "") {
43
- var current_tab = current_tab[0].replace("tab=", "");
44
- window.history.pushState({path: newurl}, '', newurl);
45
- } else {
46
- var newurl = window.location.href + '&tab=' + this_id;
47
- window.history.pushState({path: newurl}, '', newurl);
48
- }
49
-
50
- }
51
- });
52
-
53
- setTimeout(function() {
54
- var getoption = document.URL.split('&option=')[1];
55
- var showoption = "#" + getoption;
56
- jQuery(showoption).click();
57
- }, 100);
58
-
59
- /* Navigate tabs */
60
- jQuery('.lp-nav-tab').live('click', function() {
61
- var this_id = this.id.replace('tabs-','');
62
- jQuery('.lp-tab-display').css('display','none');
63
- jQuery('#'+this_id).css('display','block');
64
- jQuery('.lp-nav-tab').removeClass('nav-tab-special-active');
65
- jQuery('.lp-nav-tab').addClass('nav-tab-special-inactive');
66
- jQuery('#tabs-'+this_id).addClass('nav-tab-special-active');
67
- jQuery('#id-open-tab').val(this_id);
68
- });
69
- var form_sys = jQuery("#sys-inbound-form");
70
- jQuery("#in-sys-info").after(form_sys);
71
- jQuery("#sys-inbound-form").show();
72
-
73
  });
1
+ function getUrlVars() {
2
+ var vars = [], hash;
3
+ var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
4
+ for (var i = 0; i < hashes.length; i++) {
5
+ hash = hashes[i].split('=');
6
+ vars.push(hash[0]);
7
+ vars[hash[0]] = hash[1];
8
+ }
9
+ return vars;
10
+ };
11
+
12
+ function getUrlVar(name) {
13
+ return getUrlVars()[name];
14
+ };
15
+
16
+ jQuery(document).ready(function ($) {
17
+
18
+ jQuery(document).ready(function() {
19
+ jQuery('.tooltip').tooltipster({
20
+ contentAsHTML: true,
21
+ interactive: true,
22
+ maxWidth: 350,
23
+ position: "right",
24
+ theme: "tooltipster-noir"
25
+ });
26
+ });
27
+
28
+ // Getting URL var by its nam
29
+ var byName = getUrlVar('tab');
30
+
31
+ // Set setting Tab
32
+ setTimeout(function () {
33
+ jQuery("#" + byName).click();
34
+ }, 300);
35
+
36
+ /* Update Setting URL */
37
+ jQuery("body").on('click', '.nav-tab', function () {
38
+ var this_id = jQuery(this).attr('id');
39
+ if (history.pushState) {
40
+ var newurl = window.location.href.replace(/tab=([^"]*)/g, 'tab=' + this_id);
41
+ var current_tab = newurl.match(/tab=([^"]*)/g);
42
+ if (typeof (current_tab) != "undefined" && current_tab != null && current_tab != "") {
43
+ var current_tab = current_tab[0].replace("tab=", "");
44
+ window.history.pushState({path: newurl}, '', newurl);
45
+ } else {
46
+ var newurl = window.location.href + '&tab=' + this_id;
47
+ window.history.pushState({path: newurl}, '', newurl);
48
+ }
49
+
50
+ }
51
+ });
52
+
53
+ setTimeout(function() {
54
+ var getoption = document.URL.split('&option=')[1];
55
+ var showoption = "#" + getoption;
56
+ jQuery(showoption).click();
57
+ }, 100);
58
+
59
+ /* Navigate tabs */
60
+ jQuery('.lp-nav-tab').live('click', function() {
61
+ var this_id = this.id.replace('tabs-','');
62
+ jQuery('.lp-tab-display').css('display','none');
63
+ jQuery('#'+this_id).css('display','block');
64
+ jQuery('.lp-nav-tab').removeClass('nav-tab-special-active');
65
+ jQuery('.lp-nav-tab').addClass('nav-tab-special-inactive');
66
+ jQuery('#tabs-'+this_id).addClass('nav-tab-special-active');
67
+ jQuery('#id-open-tab').val(this_id);
68
+ });
69
+ var form_sys = jQuery("#sys-inbound-form");
70
+ jQuery("#in-sys-info").after(form_sys);
71
+ jQuery("#sys-inbound-form").show();
72
+
73
  });
assets/js/admin/admin.install-plugins.js CHANGED
@@ -1,30 +1,30 @@
1
- jQuery(document).ready(function($) {
2
- /* Loads on /themes.php?page=install-inbound-plugins */
3
- var install_status = jQuery("#the-list td.status.column-status").text();
4
- var click_apply = "<h2 class='click-to-activate'><span>←</span>Click Apply to Install</h2>";
5
- var activate_apply = "<h2 class='click-to-activate-bulk'><span>←</span>Click Apply to Bulk Activate Plugins</h2>";
6
- jQuery(".alignleft.actions.bulkactions").after(click_apply);
7
- jQuery(".alignleft.actions.bulkactions").after(activate_apply);
8
- console.log(install_status);
9
- jQuery('#the-list td.status.column-status').each(function(){
10
- var installed_on = $(this).text();
11
- if (installed_on === "Not Installed") {
12
- $(this).parent().find("input[type=checkbox]").attr("checked", "on");
13
- if ( $(".click-to-activate-bulk").is(":hidden") ) {
14
- jQuery('.click-to-activate').show();
15
- jQuery(".alignleft.actions.bulkactions select").val('tgmpa-bulk-install');
16
- }
17
-
18
- }
19
- if (installed_on === "Installed But Not Activated") {
20
- $(this).parent().find("input[type=checkbox]").attr("checked", "on");
21
- if ( $(".click-to-activate").is(":hidden") ) {
22
- jQuery('.click-to-activate-bulk').show();
23
- jQuery(".alignleft.actions.bulkactions select").val('tgmpa-bulk-activate');
24
- }
25
-
26
- }
27
- });
28
- jQuery("#cb-select-all-1, #cb-select-all-2").attr("checked", "on");
29
-
30
- });
1
+ jQuery(document).ready(function($) {
2
+ /* Loads on /themes.php?page=install-inbound-plugins */
3
+ var install_status = jQuery("#the-list td.status.column-status").text();
4
+ var click_apply = "<h2 class='click-to-activate'><span>←</span>Click Apply to Install</h2>";
5
+ var activate_apply = "<h2 class='click-to-activate-bulk'><span>←</span>Click Apply to Bulk Activate Plugins</h2>";
6
+ jQuery(".alignleft.actions.bulkactions").after(click_apply);
7
+ jQuery(".alignleft.actions.bulkactions").after(activate_apply);
8
+ console.log(install_status);
9
+ jQuery('#the-list td.status.column-status').each(function(){
10
+ var installed_on = $(this).text();
11
+ if (installed_on === "Not Installed") {
12
+ $(this).parent().find("input[type=checkbox]").attr("checked", "on");
13
+ if ( $(".click-to-activate-bulk").is(":hidden") ) {
14
+ jQuery('.click-to-activate').show();
15
+ jQuery(".alignleft.actions.bulkactions select").val('tgmpa-bulk-install');
16
+ }
17
+
18
+ }
19
+ if (installed_on === "Installed But Not Activated") {
20
+ $(this).parent().find("input[type=checkbox]").attr("checked", "on");
21
+ if ( $(".click-to-activate").is(":hidden") ) {
22
+ jQuery('.click-to-activate-bulk').show();
23
+ jQuery(".alignleft.actions.bulkactions select").val('tgmpa-bulk-activate');
24
+ }
25
+
26
+ }
27
+ });
28
+ jQuery("#cb-select-all-1, #cb-select-all-2").attr("checked", "on");
29
+
30
+ });
assets/js/admin/admin.landing-page-list.js CHANGED
@@ -1,161 +1,161 @@
1
- jQuery(document).ready(function($) {
2
- // Code for landing page list view
3
- var cats = jQuery("#landing_page_category option").length;
4
- if ( cats === 0 ){
5
- jQuery("#landing_page_category").hide();
6
- }
7
-
8
- jQuery('.lp-letter').each(function(){
9
- var draft = jQuery(this).text();
10
- if ( draft === "" ){
11
- jQuery(this).parent().parent().hide();
12
- }
13
- });
14
-
15
- jQuery(".lp-impress-num").each(function(){
16
- var empty = jQuery(this).text();
17
- if ( empty === "" || empty === "0" ){
18
- jQuery(this).parent().parent().find(".lp-letter").css("color", "#ccc");
19
- jQuery(this).parent().html("<span class='lp-no-stats'>no stats yet</span>");
20
- }
21
- });
22
- /* List tour */
23
- var tourbutton = '<a class="" id="lp-tour" style="font-size:13px;">Need help? Take the tour</a>';
24
- jQuery(tourbutton).appendTo(".wrap h1");
25
- jQuery("body").on('click', '#lp-tour', function () {
26
- jQuery(this).hide();
27
- var tour = jQuery("#lp-tour-style").length;
28
- if ( tour === 0 ) {
29
- jQuery('head').append("<link rel='stylesheet' id='lp-tour-style' href='/wp-content/plugins/landing-pages/assets/css/admin-tour.css' type='text/css' /><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/tour/tour.post-list.js'></script><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/intro.js'></script>");
30
- }
31
- setTimeout(function() {
32
- introJs().start(); // start tour
33
- }, 300);
34
-
35
- });
36
- /*jQuery(".lp-varation-stat-ul").each(function(){
37
- var length = jQuery(this).find("li").length;
38
- if ( length < 3 ){
39
- jQuery(this).find("li").first().css("padding-top", "18px");
40
- }
41
- });
42
- */
43
- jQuery("body").on('mouseenter', 'tr.type-landing-page', function () {
44
- jQuery(this).find(".no-stats-yet").show();
45
- });
46
- jQuery("body").on('mouseleave', 'tr.type-landing-page', function () {
47
- jQuery(this).find(".no-stats-yet").hide();
48
- });
49
- jQuery(".variation-winner-is").each(function(){
50
- var target = jQuery(this).text();
51
- jQuery("." + target).addClass("winner-lp").attr("data-lp", "Current Winner");
52
- });
53
-
54
- var hidestats = "<span id='hide-stats'>(Hide Stats)</span><span class='show-stats show-stats-top'>Show Stats</span>";
55
- jQuery("#stats").append(hidestats);
56
-
57
- jQuery("body").on('click', '#hide-stats', function () {
58
- jQuery(".lp-varation-stat-ul").each(function(){
59
- jQuery(this).hide();
60
- });
61
- jQuery(".show-stats").show();
62
- jQuery("#hide-stats").hide();
63
- });
64
-
65
- jQuery("body").on('click', '.show-stats-top', function () {
66
- jQuery(".lp-varation-stat-ul").each(function(){
67
- jQuery(this).show();
68
- });
69
- jQuery(".show-stats").hide();
70
- jQuery("#hide-stats").show();
71
- });
72
-
73
- jQuery("body").on('click', '.show-stats', function () {
74
- jQuery(this).hide();
75
- jQuery(this).parent().find(".lp-varation-stat-ul").show();
76
- });
77
-
78
- jQuery('.lp-letter, .cr-number, .qtip').on('mouseenter', function(event) {
79
- // Bind the qTip within the event handler
80
- var text_in_tip = jQuery(this).attr("data-notes");
81
- var letter = jQuery(this).attr("data-letter");
82
- var status = "<span class='lp-paused'>" + jQuery(this).parent().attr("rel") + "</span>";
83
- var winner = "<span class='lp-win'>" + jQuery(this).parent().attr("data-lp") + "</span>";
84
- jQuery(this).qtip({
85
- overwrite: false, // Make sure the tooltip won't be overridden once created
86
- content: {
87
- text: text_in_tip,
88
- title: {
89
- text: 'Variation ' + letter + "<span class='lp-extra'>" + status + winner + "</span>" + "<span class='lp-pop-close'>close</span>"
90
- }
91
- },
92
- position: {
93
- my: 'bottom center', // Use the corner...
94
- at: 'top center', // ...and opposite corner
95
- viewport: jQuery(window)
96
- },
97
- style: {
98
- classes: 'qtip-shadow qtip-jtools',
99
- },
100
- show: {
101
- event: event.type, // Use the same show event as the one that triggered the event handler
102
- ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
103
- solo: true
104
- },
105
- hide: 'unfocus'
106
- //hide: { when: { event: 'inactive' }, delay: 1200 }
107
- }, event); // Pass through our original event to qTip
108
- })
109
-
110
- jQuery('.lp-letter').on('mouseleave', function(event) {
111
-
112
-
113
- });
114
-
115
- jQuery("body").on("click", ".lp-pop-close", function(event) {
116
- jQuery(this).parent().parent().parent().hide();
117
- });
118
-
119
- jQuery("body").on("click", ".lp-pop-preview a", function(event) {
120
- jQuery(this).parent().parent().parent().parent().hide();
121
- });
122
-
123
- // Fix Thickbox width/hieght
124
- jQuery(function($) {
125
- tb_position = function() {
126
- var tbWindow = $('#TB_window');
127
- var width = $(window).width();
128
- var H = $(window).height();
129
- var W = ( 1720 < width ) ? 1720 : width;
130
-
131
- if ( tbWindow.size() ) {
132
- tbWindow.width( W - 50 ).height( H - 45 );
133
- $('#TB_iframeContent').width( W - 50 ).height( H - 75 );
134
- tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
135
- if ( typeof document.body.style.maxWidth != 'undefined' )
136
- tbWindow.css({'top':'40px','margin-top':'0'});
137
- //$('#TB_title').css({'background-color':'#fff','color':'#cfcfcf'});
138
- };
139
-
140
- return $('a.thickbox').each( function() {
141
- var href = $(this).attr('href');
142
- if ( ! href ) return;
143
- href = href.replace(/&width=[0-9]+/g, '');
144
- href = href.replace(/&height=[0-9]+/g, '');
145
- $(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ) );
146
- });
147
-
148
- };
149
-
150
- jQuery('a.thickbox').click(function(){
151
- if ( typeof tinyMCE != 'undefined' && tinyMCE.activeEditor ) {
152
- tinyMCE.get('content').focus();
153
- tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
154
- }
155
-
156
- });
157
-
158
- $(window).resize( function() { tb_position() } );
159
- });
160
-
161
  });
1
+ jQuery(document).ready(function($) {
2
+ // Code for landing page list view
3
+ var cats = jQuery("#landing_page_category option").length;
4
+ if ( cats === 0 ){
5
+ jQuery("#landing_page_category").hide();
6
+ }
7
+
8
+ jQuery('.lp-letter').each(function(){
9
+ var draft = jQuery(this).text();
10
+ if ( draft === "" ){
11
+ jQuery(this).parent().parent().hide();
12
+ }
13
+ });
14
+
15
+ jQuery(".lp-impress-num").each(function(){
16
+ var empty = jQuery(this).text();
17
+ if ( empty === "" || empty === "0" ){
18
+ jQuery(this).parent().parent().find(".lp-letter").css("color", "#ccc");
19
+ jQuery(this).parent().html("<span class='lp-no-stats'>no stats yet</span>");
20
+ }
21
+ });
22
+ /* List tour */
23
+ var tourbutton = '<a class="" id="lp-tour" style="font-size:13px;">Need help? Take the tour</a>';
24
+ jQuery(tourbutton).appendTo(".wrap h1");
25
+ jQuery("body").on('click', '#lp-tour', function () {
26
+ jQuery(this).hide();
27
+ var tour = jQuery("#lp-tour-style").length;
28
+ if ( tour === 0 ) {
29
+ jQuery('head').append("<link rel='stylesheet' id='lp-tour-style' href='/wp-content/plugins/landing-pages/assets/css/admin-tour.css' type='text/css' /><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/tour/tour.post-list.js'></script><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/intro.js'></script>");
30
+ }
31
+ setTimeout(function() {
32
+ introJs().start(); // start tour
33
+ }, 300);
34
+
35
+ });
36
+ /*jQuery(".lp-varation-stat-ul").each(function(){
37
+ var length = jQuery(this).find("li").length;
38
+ if ( length < 3 ){
39
+ jQuery(this).find("li").first().css("padding-top", "18px");
40
+ }
41
+ });
42
+ */
43
+ jQuery("body").on('mouseenter', 'tr.type-landing-page', function () {
44
+ jQuery(this).find(".no-stats-yet").show();
45
+ });
46
+ jQuery("body").on('mouseleave', 'tr.type-landing-page', function () {
47
+ jQuery(this).find(".no-stats-yet").hide();
48
+ });
49
+ jQuery(".variation-winner-is").each(function(){
50
+ var target = jQuery(this).text();
51
+ jQuery("." + target).addClass("winner-lp").attr("data-lp", "Current Winner");
52
+ });
53
+
54
+ var hidestats = "<span id='hide-stats'>(Hide Stats)</span><span class='show-stats show-stats-top'>Show Stats</span>";
55
+ jQuery("#stats").append(hidestats);
56
+
57
+ jQuery("body").on('click', '#hide-stats', function () {
58
+ jQuery(".lp-varation-stat-ul").each(function(){
59
+ jQuery(this).hide();
60
+ });
61
+ jQuery(".show-stats").show();
62
+ jQuery("#hide-stats").hide();
63
+ });
64
+
65
+ jQuery("body").on('click', '.show-stats-top', function () {
66
+ jQuery(".lp-varation-stat-ul").each(function(){
67
+ jQuery(this).show();
68
+ });
69
+ jQuery(".show-stats").hide();
70
+ jQuery("#hide-stats").show();
71
+ });
72
+
73
+ jQuery("body").on('click', '.show-stats', function () {
74
+ jQuery(this).hide();
75
+ jQuery(this).parent().find(".lp-varation-stat-ul").show();
76
+ });
77
+
78
+ jQuery('.lp-letter, .cr-number, .qtip').on('mouseenter', function(event) {
79
+ // Bind the qTip within the event handler
80
+ var text_in_tip = jQuery(this).attr("data-notes");
81
+ var letter = jQuery(this).attr("data-letter");
82
+ var status = "<span class='lp-paused'>" + jQuery(this).parent().attr("rel") + "</span>";
83
+ var winner = "<span class='lp-win'>" + jQuery(this).parent().attr("data-lp") + "</span>";
84
+ jQuery(this).qtip({
85
+ overwrite: false, // Make sure the tooltip won't be overridden once created
86
+ content: {
87
+ text: text_in_tip,
88
+ title: {
89
+ text: 'Variation ' + letter + "<span class='lp-extra'>" + status + winner + "</span>" + "<span class='lp-pop-close'>close</span>"
90
+ }
91
+ },
92
+ position: {
93
+ my: 'bottom center', // Use the corner...
94
+ at: 'top center', // ...and opposite corner
95
+ viewport: jQuery(window)
96
+ },
97
+ style: {
98
+ classes: 'qtip-shadow qtip-jtools',
99
+ },
100
+ show: {
101
+ event: event.type, // Use the same show event as the one that triggered the event handler
102
+ ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
103
+ solo: true
104
+ },
105
+ hide: 'unfocus'
106
+ //hide: { when: { event: 'inactive' }, delay: 1200 }
107
+ }, event); // Pass through our original event to qTip
108
+ })
109
+
110
+ jQuery('.lp-letter').on('mouseleave', function(event) {
111
+
112
+
113
+ });
114
+
115
+ jQuery("body").on("click", ".lp-pop-close", function(event) {
116
+ jQuery(this).parent().parent().parent().hide();
117
+ });
118
+
119
+ jQuery("body").on("click", ".lp-pop-preview a", function(event) {
120
+ jQuery(this).parent().parent().parent().parent().hide();
121
+ });
122
+
123
+ // Fix Thickbox width/hieght
124
+ jQuery(function($) {
125
+ tb_position = function() {
126
+ var tbWindow = $('#TB_window');
127
+ var width = $(window).width();
128
+ var H = $(window).height();
129
+ var W = ( 1720 < width ) ? 1720 : width;
130
+
131
+ if ( tbWindow.size() ) {
132
+ tbWindow.width( W - 50 ).height( H - 45 );
133
+ $('#TB_iframeContent').width( W - 50 ).height( H - 75 );
134
+ tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
135
+ if ( typeof document.body.style.maxWidth != 'undefined' )
136
+ tbWindow.css({'top':'40px','margin-top':'0'});
137
+ //$('#TB_title').css({'background-color':'#fff','color':'#cfcfcf'});
138
+ };
139
+
140
+ return $('a.thickbox').each( function() {
141
+ var href = $(this).attr('href');
142
+ if ( ! href ) return;
143
+ href = href.replace(/&width=[0-9]+/g, '');
144
+ href = href.replace(/&height=[0-9]+/g, '');
145
+ $(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ) );
146
+ });
147
+
148
+ };
149
+
150
+ jQuery('a.thickbox').click(function(){
151
+ if ( typeof tinyMCE != 'undefined' && tinyMCE.activeEditor ) {
152
+ tinyMCE.get('content').focus();
153
+ tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
154
+ }
155
+
156
+ });
157
+
158
+ $(window).resize( function() { tb_position() } );
159
+ });
160
+
161
  });
assets/js/admin/admin.post-edit-ab-testing.js CHANGED
@@ -1,64 +1,64 @@
1
- jQuery(document).ready(function ($) {
2
- var variations = new Array();
3
- var has_variations = 0;
4
- variations = variation.variations;
5
-
6
-
7
- var replace_slash = '\\';
8
-
9
- //variation.content_area = variation.content_area.replace(/\n/g, replace_slash);
10
- //variation.content_area = variation.content_area.replace(/\r\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/\n/g, "<br/>");
11
-
12
- jQuery("#wp-content-editor-container textarea").val(variation.content_area);
13
- jQuery("#content_ifr").contents().find("body").html(variation.content_area);
14
-
15
-
16
- var html;
17
- if (variation.vid>0&&variation.new_variation!=1)
18
- {
19
- html = '<a class="add-new-h2" href="?post='+variation.pid+'&action=edit&lp-variation-id='+variation.vid+'&ab-action=delete-variation">Delete This Variation</a>';
20
- jQuery('.wrap h2:first').append(html);
21
- }
22
-
23
- if (variation.vid>0)
24
- {
25
- jQuery('#delete-action').remove();
26
- }
27
-
28
- //alter preview and customizer buttons based on open variation
29
- var preview_href = jQuery('#post-preview').attr('href');
30
- jQuery('#post-preview').attr('href',preview_href+'?lp-variation-id='+variation.vid);
31
-
32
- //setup timer and and navigation change events
33
- var input_change = jQuery("#switch-lp").text();
34
- jQuery('.wrap').on('keyup change', jQuery('form').find('input[type=text],textarea,select'), function() {
35
- jQuery("#switch-lp").text("1");
36
- console.log("change");
37
- });
38
-
39
- /*setTimeout(function () {
40
- input_change = 1;
41
- }, 15000);
42
- */
43
- jQuery('.wrap').on('click', '.nav-tab-wrapper a', function(e) {
44
- var this_id = this.id.replace('tabs-','');
45
- if (input_change==1)
46
- {
47
- var answer = confirm('Do you want to change variations without saving the changes made here?');
48
- if (answer){
49
- // do the default action
50
- } else {
51
- e.preventDefault();
52
- }
53
- }
54
-
55
- jQuery('.lp-tab-display').css('display','none');
56
- jQuery('#'+this_id).css('display','block');
57
- jQuery('.lp-nav-tab').removeClass('nav-tab-special-active');
58
- jQuery('.lp-nav-tab').addClass('nav-tab-special-inactive');
59
- jQuery('#tabs-'+this_id).addClass('nav-tab-special-active');
60
- jQuery('#id-open-tab').val(this_id);
61
-
62
- });
63
-
64
  });
1
+ jQuery(document).ready(function ($) {
2
+ var variations = new Array();
3
+ var has_variations = 0;
4
+ variations = variation.variations;
5
+
6
+
7
+ var replace_slash = '\\';
8
+
9
+ //variation.content_area = variation.content_area.replace(/\n/g, replace_slash);
10
+ //variation.content_area = variation.content_area.replace(/\r\n/g, "<br/>").replace(/\r/g, "<br/>").replace(/\n/g, "<br/>");
11
+
12
+ jQuery("#wp-content-editor-container textarea").val(variation.content_area);
13
+ jQuery("#content_ifr").contents().find("body").html(variation.content_area);
14
+
15
+
16
+ var html;
17
+ if (variation.vid>0&&variation.new_variation!=1)
18
+ {
19
+ html = '<a class="add-new-h2" href="?post='+variation.pid+'&action=edit&lp-variation-id='+variation.vid+'&ab-action=delete-variation">Delete This Variation</a>';
20
+ jQuery('.wrap h2:first').append(html);
21
+ }
22
+
23
+ if (variation.vid>0)
24
+ {
25
+ jQuery('#delete-action').remove();
26
+ }
27
+
28
+ //alter preview and customizer buttons based on open variation
29
+ var preview_href = jQuery('#post-preview').attr('href');
30
+ jQuery('#post-preview').attr('href',preview_href+'?lp-variation-id='+variation.vid);
31
+
32
+ //setup timer and and navigation change events
33
+ var input_change = jQuery("#switch-lp").text();
34
+ jQuery('.wrap').on('keyup change', jQuery('form').find('input[type=text],textarea,select'), function() {
35
+ jQuery("#switch-lp").text("1");
36
+ console.log("change");
37
+ });
38
+
39
+ /*setTimeout(function () {
40
+ input_change = 1;
41
+ }, 15000);
42
+ */
43
+ jQuery('.wrap').on('click', '.nav-tab-wrapper a', function(e) {
44
+ var this_id = this.id.replace('tabs-','');
45
+ if (input_change==1)
46
+ {
47
+ var answer = confirm('Do you want to change variations without saving the changes made here?');
48
+ if (answer){
49
+ // do the default action
50
+ } else {
51
+ e.preventDefault();
52
+ }
53
+ }
54
+
55
+ jQuery('.lp-tab-display').css('display','none');
56
+ jQuery('#'+this_id).css('display','block');
57
+ jQuery('.lp-nav-tab').removeClass('nav-tab-special-active');
58
+ jQuery('.lp-nav-tab').addClass('nav-tab-special-inactive');
59
+ jQuery('#tabs-'+this_id).addClass('nav-tab-special-active');
60
+ jQuery('#id-open-tab').val(this_id);
61
+
62
+ });
63
+
64
  });
assets/js/admin/admin.post-edit.js CHANGED
@@ -1,322 +1,322 @@
1
- jQuery(document).ready(function ($) {
2
-
3
- var cookies = (typeof (jQuery.cookie) != "undefined" ? true : false); // Check for JQuery Cookie
4
- function cookie_notice() {
5
- alert('Oh no! jQuery Cookie not loaded. Your Server Might be Blocking this. Some functionality may be impaired');
6
- }
7
-
8
- jQuery('.button.button-small').each(function () {
9
- var $this = jQuery(this);
10
- var text = $this.text();
11
- if (text === "Get Shortlink") {
12
- $this.hide();
13
- }
14
- });
15
-
16
- var width = jQuery("#lp-thumbnail-sidebar-preview").width();
17
- jQuery('#zoomer').zoomer({width: width, height: 225, zoom: 0.27, tranformOrigin: '0px 40px 1px',});
18
-
19
- // Filter Styling
20
- jQuery('#template-filter li').first().addClass('button-primary');
21
- // filter items when filter link is clicked
22
- jQuery('#template-filter a').click(function () {
23
- var selector = jQuery(this).attr('data-filter');
24
- jQuery("ul#template-filter li").removeClass('button-primary');
25
- jQuery(this).parent().addClass('button-primary');
26
- $(".template-item-boxes").fadeOut(500);
27
- setTimeout(function () {
28
- $(selector).fadeIn(500);
29
- }, 500);
30
-
31
- return false;
32
- });
33
-
34
-
35
- jQuery("body").on('click', '#content-tmce, .wp-switch-editor.switch-tmce', function () {
36
- if (cookies) {
37
- $.cookie("lp-edit-view-choice", "editor", {path: '/', expires: 7});
38
- } else {
39
- cookie_notice();
40
- }
41
- });
42
-
43
- jQuery("body").on('click', '#content-html, .wp-switch-editor.switch-html', function () {
44
- if (cookies) {
45
- $.cookie("lp-edit-view-choice", "html", {path: '/', expires: 7});
46
- } else {
47
- cookie_notice();
48
- }
49
- });
50
-
51
- if (cookies) {
52
- var which_editor = $.cookie("lp-edit-view-choice");
53
- } else {
54
- var which_editor = 'editor';
55
- cookie_notice();
56
- }
57
- if (which_editor === null) {
58
- setTimeout(function () {
59
- //jQuery("#content-tmce").click();
60
- //jQuery(".wp-switch-editor.switch-tmce").click();
61
- }, 1000);
62
-
63
- }
64
-
65
- if (which_editor === 'editor') {
66
- setTimeout(function () {
67
-
68
- jQuery('.switch-tmce').each(function () {
69
- jQuery(this).click();
70
- });
71
-
72
- }, 1000);
73
- }
74
-
75
- /* Tour Start JS */
76
- var tourbutton = '<a class="" id="lp-tour" style="font-size:13px;">Need help? Take the tour</a>';
77
- jQuery(tourbutton).appendTo("h2:eq(0)");
78
- jQuery("body").on('click', '#lp-tour', function () {
79
- jQuery(this).hide();
80
- var tour = jQuery("#lp-tour-style").length;
81
- if (tour === 0) {
82
- jQuery('head').append("<link rel='stylesheet' id='lp-tour-style' href='/wp-content/plugins/landing-pages/assets/css/admin-tour.css' type='text/css' /><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/tour/tour.post-edit.js'></script><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/intro.js'></script>");
83
- }
84
- setTimeout(function () {
85
- introJs().start(); // start tour
86
- }, 300);
87
-
88
- });
89
-
90
- var current_a_tab = jQuery("#tabs-0").hasClass('nav-tab-special-active');
91
- if (current_a_tab === true) {
92
- var url_norm = jQuery("#view-post-btn a").attr('href');
93
- var new_url = url_norm + "?lp-variation-id=0";
94
- jQuery("#view-post-btn a").attr('href', new_url);
95
- }
96
-
97
- /* Fix inactivate theme display */
98
- jQuery("#template-box a").live('click', function () {
99
-
100
- setTimeout(function () {
101
- jQuery('#TB_window iframe').contents().find("#customize-controls").hide();
102
- jQuery('#TB_window iframe').contents().find(".wp-full-overlay.expanded").css("margin-left", "0px");
103
- }, 600);
104
-
105
- });
106
-
107
- /* Fix Split testing iframe size */
108
- jQuery("#lp-metabox-splittesting a.thickbox, #leads-table-container-inside .column-details a").live('click', function () {
109
- jQuery('#TB_iframeContent, #TB_window').hide();
110
- setTimeout(function () {
111
-
112
- jQuery('#TB_iframeContent, #TB_window').width(640).height(800).css("margin-left", "0px").css("left", "35%");
113
- jQuery('#TB_iframeContent, #TB_window').show();
114
- }, 600);
115
- });
116
-
117
- /* Load meta box in correct position on page load */
118
- var current_template = jQuery("input#lp_select_template ").val();
119
- var current_template_meta = "#lp_" + current_template + "_custom_meta_box";
120
- jQuery(current_template_meta).removeClass("postbox").appendTo("#template-display-options").addClass("Old-Template");
121
- var current_template_h3 = "#lp_" + current_template + "_custom_meta_box h3";
122
-
123
- /* jQuery(current_template_h3).css("background","#f8f8f8"); */
124
- jQuery(current_template_meta + ' .handlediv').hide();
125
- jQuery(current_template_meta + ' .hndle').css('cursor', 'default');
126
-
127
-
128
- /* Fix Thickbox width/hieght */
129
- jQuery(function ($) {
130
- tb_position = function () {
131
- var tbWindow = $('#TB_window');
132
- var width = $(window).width();
133
- var H = $(window).height();
134
- var W = ( 1720 < width ) ? 1720 : width;
135
-
136
- if (tbWindow.size()) {
137
- tbWindow.width(W - 50).height(H - 45);
138
- $('#TB_iframeContent').width(W - 50).height(H - 75);
139
- tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2), 10) + 'px'});
140
- if (typeof document.body.style.maxWidth != 'undefined') {
141
- tbWindow.css({'top': '40px', 'margin-top': '0'});
142
- }
143
-
144
- }
145
- ;
146
-
147
- return $('a.thickbox').each(function () {
148
- var href = $(this).attr('href');
149
- if (!href) return;
150
- href = href.replace(/&width=[0-9]+/g, '');
151
- href = href.replace(/&height=[0-9]+/g, '');
152
- $(this).attr('href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ));
153
-
154
- });
155
-
156
- };
157
-
158
- jQuery('a.thickbox').click(function () {
159
- if (typeof tinyMCE != 'undefined' && tinyMCE.activeEditor) {
160
- tinyMCE.get('content').focus();
161
- tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
162
- }
163
-
164
- });
165
-
166
- $(window).resize(function () {
167
- tb_position()
168
- });
169
- });
170
-
171
-
172
- /* the_content default overwrite */
173
- jQuery('body').on('click', '#overwrite-content', function () {
174
- if (confirm('Are you sure you want to overwrite what is currently in the main edit box above?')) {
175
- var ctmce = jQuery('#content-tmce');
176
- switchEditors.switchto(ctmce[0]); // switch to tinymce
177
- setTimeout(function () {
178
- var default_content = jQuery(".inbound-default-content-option textarea").first().text();
179
- jQuery("#content_ifr").contents().find("body").html(default_content);
180
- }, 500);
181
-
182
- }
183
- });
184
-
185
- /* Colorpicker fix */
186
- jQuery('.jpicker').one('mouseenter', function () {
187
- jQuery(this).jPicker({
188
- window: // used to define the position of the popup window only useful in binded mode
189
- {
190
- title: null, // any title for the jPicker window itself - displays "Drag Markers To Pick A Color" if left null
191
- position: {
192
- x: 'screenCenter', // acceptable values "left", "center", "right", "screenCenter", or relative px value
193
- y: 'center', // acceptable values "top", "bottom", "center", or relative px value
194
- },
195
- expandable: false, // default to large static picker - set to true to make an expandable picker (small icon with popup) - set
196
- // automatically when binded to input element
197
- liveUpdate: true, // set false if you want the user to click "OK" before the binded input box updates values (always "true"
198
- // for expandable picker)
199
- alphaSupport: false, // set to true to enable alpha picking
200
- alphaPrecision: 0, // set decimal precision for alpha percentage display - hex codes do not map directly to percentage
201
- // integers - range 0-2
202
- updateInputColor: true // set to false to prevent binded input colors from changing
203
- }
204
- },
205
- function (color, context) {
206
- var all = color.val('all');
207
- // alert('Color chosen - hex: ' + (all && '#' + all.hex || 'none') + ' - alpha: ' + (all && all.a + '%' || 'none'));
208
- //jQuery(this).attr('rel', all.hex);
209
-
210
- jQuery(this).parent().find(".lp-success-message").remove();
211
- //jQuery(this).parent().find(".new-save-lp").show();
212
- //jQuery(this).parent().find(".new-save-lp-frontend").show();
213
- //jQuery(this).attr('value', all.hex);
214
- });
215
- });
216
-
217
-
218
- if (jQuery(".lp-template-selector-container").css("display") == "none") {
219
- jQuery(".currently_selected").hide();
220
- } else {
221
- jQuery(".currently_selected").show();
222
- }
223
-
224
- /* Add current title of template to selector */
225
- var selected_template = jQuery('#lp_select_template').val();
226
- var selected_template_id = "#" + selected_template;
227
- var clean_template_name = selected_template.replace(/-/g, ' ');
228
-
229
- function capitaliseFirstLetter(string) {
230
- return string.charAt(0).toUpperCase() + string.slice(1);
231
- }
232
-
233
- var currentlabel = jQuery(".currently_selected");
234
- jQuery(selected_template_id).parent().addClass("default_template_highlight").prepend(currentlabel);
235
-
236
- jQuery('#lp-change-template-button').live('click', function () {
237
- jQuery('.acf-postbox').remove();
238
- jQuery(".wrap").fadeOut(500, function () {
239
-
240
- jQuery(".lp-template-selector-container").fadeIn(500, function () {
241
- jQuery(".currently_selected").show();
242
- jQuery('#lp-cancel-selection').show();
243
- });
244
-
245
- });
246
- });
247
-
248
-
249
- jQuery('.background-style').on('change', function () {
250
- var input = jQuery(".background-style option:selected").val();
251
- if (input == 'color') {
252
- jQuery('.background-color').show();
253
- jQuery('.background-image').hide();
254
- jQuery('.background_tip').hide();
255
- }
256
- else if (input == 'default') {
257
- jQuery('.background-color').hide();
258
- jQuery('.background-image').hide();
259
- jQuery('.background_tip').hide();
260
- }
261
- else if (input == 'custom') {
262
- var obj = jQuery(".background-style .lp_tooltip");
263
- obj.removeClass("lp_tooltip").addClass("background_tip").html("Use the custom css block at the bottom of this page to set up custom CSS rules");
264
- jQuery('.background_tip').show();
265
- }
266
- else {
267
- jQuery('.background-color').hide();
268
- jQuery('.background-image').show();
269
- jQuery('.background_tip').hide();
270
- }
271
-
272
- });
273
-
274
- /* Check BG options on page load */
275
- jQuery(document).ready(function () {
276
- var input = jQuery(".background-style option:selected").val();
277
- if (input == 'color') {
278
- jQuery('.background-color').show();
279
- jQuery('.background-image').hide();
280
- jQuery('.background_tip').hide();
281
- }
282
- else if (input == 'default') {
283
- jQuery('.background-color').hide();
284
- jQuery('.background-image').hide();
285
- jQuery('.background_tip').hide();
286
- }
287
- else if (input == 'custom') {
288
- var obj = jQuery(".background-style .lp_tooltip");
289
- obj.removeClass("lp_tooltip").addClass("background_tip").html("Use the custom css block at the bottom of this page to set up custom CSS rules");
290
- jQuery('.background_tip').show();
291
- }
292
- else {
293
- jQuery('.background-color').hide();
294
- jQuery('.background-image').show();
295
- jQuery('.background_tip').hide();
296
- }
297
- });
298
-
299
- /* Stylize lead's wp-list-table */
300
- var cnt = $("#leads-table-container").contents();
301
- $("#lp_conversion_log_metabox").replaceWith(cnt);
302
-
303
- /* remove inputs from wp-list-table */
304
- jQuery('#leads-table-container-inside input').each(function () {
305
- jQuery(this).remove();
306
- });
307
-
308
- var post_status = jQuery("#original_post_status").val();
309
-
310
- if (post_status === "draft") {
311
- jQuery(".new-save-lp-frontend").on("click", function (event) {
312
- event.preventDefault();
313
- alert("Must publish this page before you can use the visual editor!");
314
- });
315
- var subbox = jQuery("#submitdiv");
316
- jQuery("#lp_ab_display_stats_metabox").before(subbox)
317
- } else {
318
- jQuery("#publish").val("Update All");
319
- }
320
-
321
-
322
- });
1
+ jQuery(document).ready(function ($) {
2
+
3
+ var cookies = (typeof (jQuery.cookie) != "undefined" ? true : false); // Check for JQuery Cookie
4
+ function cookie_notice() {
5
+ alert('Oh no! jQuery Cookie not loaded. Your Server Might be Blocking this. Some functionality may be impaired');
6
+ }
7
+
8
+ jQuery('.button.button-small').each(function () {
9
+ var $this = jQuery(this);
10
+ var text = $this.text();
11
+ if (text === "Get Shortlink") {
12
+ $this.hide();
13
+ }
14
+ });
15
+
16
+ var width = jQuery("#lp-thumbnail-sidebar-preview").width();
17
+ jQuery('#zoomer').zoomer({width: width, height: 225, zoom: 0.27, tranformOrigin: '0px 40px 1px',});
18
+
19
+ // Filter Styling
20
+ jQuery('#template-filter li').first().addClass('button-primary');
21
+ // filter items when filter link is clicked
22
+ jQuery('#template-filter a').click(function () {
23
+ var selector = jQuery(this).attr('data-filter');
24
+ jQuery("ul#template-filter li").removeClass('button-primary');
25
+ jQuery(this).parent().addClass('button-primary');
26
+ $(".template-item-boxes").fadeOut(500);
27
+ setTimeout(function () {
28
+ $(selector).fadeIn(500);
29
+ }, 500);
30
+
31
+ return false;
32
+ });
33
+
34
+
35
+ jQuery("body").on('click', '#content-tmce, .wp-switch-editor.switch-tmce', function () {
36
+ if (cookies) {
37
+ $.cookie("lp-edit-view-choice", "editor", {path: '/', expires: 7});
38
+ } else {
39
+ cookie_notice();
40
+ }
41
+ });
42
+
43
+ jQuery("body").on('click', '#content-html, .wp-switch-editor.switch-html', function () {
44
+ if (cookies) {
45
+ $.cookie("lp-edit-view-choice", "html", {path: '/', expires: 7});
46
+ } else {
47
+ cookie_notice();
48
+ }
49
+ });
50
+
51
+ if (cookies) {
52
+ var which_editor = $.cookie("lp-edit-view-choice");
53
+ } else {
54
+ var which_editor = 'editor';
55
+ cookie_notice();
56
+ }
57
+ if (which_editor === null) {
58
+ setTimeout(function () {
59
+ //jQuery("#content-tmce").click();
60
+ //jQuery(".wp-switch-editor.switch-tmce").click();
61
+ }, 1000);
62
+
63
+ }
64
+
65
+ if (which_editor === 'editor') {
66
+ setTimeout(function () {
67
+
68
+ jQuery('.switch-tmce').each(function () {
69
+ jQuery(this).click();
70
+ });
71
+
72
+ }, 1000);
73
+ }
74
+
75
+ /* Tour Start JS */
76
+ var tourbutton = '<a class="" id="lp-tour" style="font-size:13px;position: absolute;bottom: 5px;">Need help? Take the tour</a>';
77
+ jQuery(tourbutton).appendTo("h2:eq(0)");
78
+ jQuery("body").on('click', '#lp-tour', function () {
79
+ jQuery(this).hide();
80
+ var tour = jQuery("#lp-tour-style").length;
81
+ if (tour === 0) {
82
+ jQuery('head').append("<link rel='stylesheet' id='lp-tour-style' href='/wp-content/plugins/landing-pages/assets/css/admin-tour.css' type='text/css' /><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/tour/tour.post-edit.js'></script><script type='text/javascript' src='/wp-content/plugins/landing-pages/assets/js/admin/intro.js'></script>");
83
+ }
84
+ setTimeout(function () {
85
+ introJs().start(); // start tour
86
+ }, 300);
87
+
88
+ });
89
+
90
+ var current_a_tab = jQuery("#tabs-0").hasClass('nav-tab-special-active');
91
+ if (current_a_tab === true) {
92
+ var url_norm = jQuery("#view-post-btn a").attr('href');
93
+ var new_url = url_norm + "?lp-variation-id=0";
94
+ jQuery("#view-post-btn a").attr('href', new_url);
95
+ }
96
+
97
+ /* Fix inactivate theme display */
98
+ jQuery("#template-box a").live('click', function () {
99
+
100
+ setTimeout(function () {
101
+ jQuery('#TB_window iframe').contents().find("#customize-controls").hide();
102
+ jQuery('#TB_window iframe').contents().find(".wp-full-overlay.expanded").css("margin-left", "0px");
103
+ }, 600);
104
+
105
+ });
106
+
107
+ /* Fix Split testing iframe size */
108
+ jQuery("#lp-metabox-splittesting a.thickbox, #leads-table-container-inside .column-details a").live('click', function () {
109
+ jQuery('#TB_iframeContent, #TB_window').hide();
110
+ setTimeout(function () {
111
+
112
+ jQuery('#TB_iframeContent, #TB_window').width(640).height(800).css("margin-left", "0px").css("left", "35%");
113
+ jQuery('#TB_iframeContent, #TB_window').show();
114
+ }, 600);
115
+ });
116
+
117
+ /* Load meta box in correct position on page load */
118
+ var current_template = jQuery("input#lp_select_template ").val();
119
+ var current_template_meta = "#lp_" + current_template + "_custom_meta_box";
120
+ jQuery(current_template_meta).removeClass("postbox").appendTo("#template-display-options").addClass("Old-Template");
121
+ var current_template_h3 = "#lp_" + current_template + "_custom_meta_box h3";
122
+
123
+ /* jQuery(current_template_h3).css("background","#f8f8f8"); */
124
+ jQuery(current_template_meta + ' .handlediv').hide();
125
+ jQuery(current_template_meta + ' .hndle').css('cursor', 'default');
126
+
127
+
128
+ /* Fix Thickbox width/hieght */
129
+ jQuery(function ($) {
130
+ tb_position = function () {
131
+ var tbWindow = $('#TB_window');
132
+ var width = $(window).width();
133
+ var H = $(window).height();
134
+ var W = ( 1720 < width ) ? 1720 : width;
135
+
136
+ if (tbWindow.size()) {
137
+ tbWindow.width(W - 50).height(H - 45);
138
+ $('#TB_iframeContent').width(W - 50).height(H - 75);
139
+ tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2), 10) + 'px'});
140
+ if (typeof document.body.style.maxWidth != 'undefined') {
141
+ tbWindow.css({'top': '40px', 'margin-top': '0'});
142
+ }
143
+
144
+ }
145
+ ;
146
+
147
+ return $('a.thickbox').each(function () {
148
+ var href = $(this).attr('href');
149
+ if (!href) return;
150
+ href = href.replace(/&width=[0-9]+/g, '');
151
+ href = href.replace(/&height=[0-9]+/g, '');
152
+ $(this).attr('href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ));
153
+
154
+ });
155
+
156
+ };
157
+
158
+ jQuery('a.thickbox').click(function () {
159
+ if (typeof tinyMCE != 'undefined' && tinyMCE.activeEditor) {
160
+ tinyMCE.get('content').focus();
161
+ tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
162
+ }
163
+
164
+ });
165
+
166
+ $(window).resize(function () {
167
+ tb_position()
168
+ });
169
+ });
170
+
171
+
172
+ /* the_content default overwrite */
173
+ jQuery('body').on('click', '#overwrite-content', function () {
174
+ if (confirm('Are you sure you want to overwrite what is currently in the main edit box above?')) {
175
+ var ctmce = jQuery('#content-tmce');
176
+ switchEditors.switchto(ctmce[0]); // switch to tinymce
177
+ setTimeout(function () {
178
+ var default_content = jQuery(".inbound-default-content-option textarea").first().text();
179
+ jQuery("#content_ifr").contents().find("body").html(default_content);
180
+ }, 500);
181
+
182
+ }
183
+ });
184
+
185
+ /* Colorpicker fix */
186
+ jQuery('.jpicker').one('mouseenter', function () {
187
+ jQuery(this).jPicker({
188
+ window: // used to define the position of the popup window only useful in binded mode
189
+ {
190
+ title: null, // any title for the jPicker window itself - displays "Drag Markers To Pick A Color" if left null
191
+ position: {
192
+ x: 'screenCenter', // acceptable values "left", "center", "right", "screenCenter", or relative px value
193
+ y: 'center', // acceptable values "top", "bottom", "center", or relative px value
194
+ },
195
+ expandable: false, // default to large static picker - set to true to make an expandable picker (small icon with popup) - set
196
+ // automatically when binded to input element
197
+ liveUpdate: true, // set false if you want the user to click "OK" before the binded input box updates values (always "true"
198
+ // for expandable picker)
199
+ alphaSupport: false, // set to true to enable alpha picking
200
+ alphaPrecision: 0, // set decimal precision for alpha percentage display - hex codes do not map directly to percentage
201
+ // integers - range 0-2
202
+ updateInputColor: true // set to false to prevent binded input colors from changing
203
+ }
204
+ },
205
+ function (color, context) {
206
+ var all = color.val('all');
207
+ // alert('Color chosen - hex: ' + (all && '#' + all.hex || 'none') + ' - alpha: ' + (all && all.a + '%' || 'none'));
208
+ //jQuery(this).attr('rel', all.hex);
209
+
210
+ jQuery(this).parent().find(".lp-success-message").remove();
211
+ //jQuery(this).parent().find(".new-save-lp").show();
212
+ //jQuery(this).parent().find(".new-save-lp-frontend").show();
213
+ //jQuery(this).attr('value', all.hex);
214
+ });
215
+ });
216
+
217
+
218
+ if (jQuery(".lp-template-selector-container").css("display") == "none") {
219
+ jQuery(".currently_selected").hide();
220
+ } else {
221
+ jQuery(".currently_selected").show();
222
+ }
223
+
224
+ /* Add current title of template to selector */
225
+ var selected_template = jQuery('#lp_select_template').val();
226
+ var selected_template_id = "#" + selected_template;
227
+ var clean_template_name = selected_template.replace(/-/g, ' ');
228
+
229
+ function capitaliseFirstLetter(string) {
230
+ return string.charAt(0).toUpperCase() + string.slice(1);
231
+ }
232
+
233
+ var currentlabel = jQuery(".currently_selected");
234
+ jQuery(selected_template_id).parent().addClass("default_template_highlight").prepend(currentlabel);
235
+
236
+ jQuery('#lp-change-template-button').live('click', function () {
237
+ jQuery('.acf-postbox').remove();
238
+ jQuery(".wrap").fadeOut(500, function () {
239
+
240
+ jQuery(".lp-template-selector-container").fadeIn(500, function () {
241
+ jQuery(".currently_selected").show();
242
+ jQuery('#lp-cancel-selection').show();
243
+ });
244
+
245
+ });
246
+ });
247
+
248
+
249
+ jQuery('.background-style').on('change', function () {
250
+ var input = jQuery(".background-style option:selected").val();
251
+ if (input == 'color') {
252
+ jQuery('.background-color').show();
253
+ jQuery('.background-image').hide();
254
+ jQuery('.background_tip').hide();
255
+ }
256
+ else if (input == 'default') {
257
+ jQuery('.background-color').hide();
258
+ jQuery('.background-image').hide();
259
+ jQuery('.background_tip').hide();
260
+ }
261
+ else if (input == 'custom') {
262
+ var obj = jQuery(".background-style .lp_tooltip");
263
+ obj.removeClass("lp_tooltip").addClass("background_tip").html("Use the custom css block at the bottom of this page to set up custom CSS rules");
264
+ jQuery('.background_tip').show();
265
+ }
266
+ else {
267
+ jQuery('.background-color').hide();
268
+ jQuery('.background-image').show();
269
+ jQuery('.background_tip').hide();
270
+ }
271
+
272
+ });
273
+
274
+ /* Check BG options on page load */
275
+ jQuery(document).ready(function () {
276
+ var input = jQuery(".background-style option:selected").val();
277
+ if (input == 'color') {
278
+ jQuery('.background-color').show();
279
+ jQuery('.background-image').hide();
280
+ jQuery('.background_tip').hide();
281
+ }
282
+ else if (input == 'default') {
283
+ jQuery('.background-color').hide();
284
+ jQuery('.background-image').hide();
285
+ jQuery('.background_tip').hide();
286
+ }
287
+ else if (input == 'custom') {
288
+ var obj = jQuery(".background-style .lp_tooltip");
289
+ obj.removeClass("lp_tooltip").addClass("background_tip").html("Use the custom css block at the bottom of this page to set up custom CSS rules");
290
+ jQuery('.background_tip').show();
291
+ }
292
+ else {
293
+ jQuery('.background-color').hide();
294
+ jQuery('.background-image').show();
295
+ jQuery('.background_tip').hide();
296
+ }
297
+ });
298
+
299
+ /* Stylize lead's wp-list-table */
300
+ var cnt = $("#leads-table-container").contents();
301
+ $("#lp_conversion_log_metabox").replaceWith(cnt);
302
+
303
+ /* remove inputs from wp-list-table */
304
+ jQuery('#leads-table-container-inside input').each(function () {
305
+ jQuery(this).remove();
306
+ });
307
+
308
+ var post_status = jQuery("#original_post_status").val();
309
+
310
+ if (post_status === "draft") {
311
+ jQuery(".new-save-lp-frontend").on("click", function (event) {
312
+ event.preventDefault();
313
+ alert("Must publish this page before you can use the visual editor!");
314
+ });
315
+ var subbox = jQuery("#submitdiv");
316
+ jQuery("#lp_ab_display_stats_metabox").before(subbox)
317
+ } else {
318
+ jQuery("#publish").val("Update All");
319
+ }
320
+
321
+
322
+ });
assets/js/admin/admin.post-new.js CHANGED
@@ -1,144 +1,144 @@
1
- jQuery(document).ready(function ($) {
2
-
3
- jQuery('#lp-template-selector-container').css('display','block');
4
-
5
- //remove inputs from wp-list-table
6
- jQuery('#leads-table-container-inside input').each(function(){
7
- jQuery(this).remove();
8
- });
9
-
10
- jQuery("#submitdiv").siblings().hide();
11
-
12
- jQuery("#title-prompt-text").text("Name Your New Landing Page");
13
- jQuery("#title").attr("required","required");
14
-
15
- var titledescription = jQuery("<span id='descriptor'>This will be the administrative title your landing page, the main headline is created in the next step</span>");
16
- jQuery(titledescription).appendTo("#titlewrap");
17
-
18
- jQuery("#save-action input").addClass("button-primary button-large").css("margin-bottom", "10px").attr("value", "Create Landing Page");
19
-
20
- var sidebar = jQuery("#side-sortables");
21
- jQuery(sidebar).appendTo("#titlediv");
22
-
23
- var tempdiv = jQuery("<div id='templates' class='postbox'><h3 class='hndle'>Current Template: <span id='ctemp'></span></h3><div id='lp_the_image'><span id='timage'><img src='' id='c_temp'></span></div><div id='template_current'></div></div>");
24
-
25
- jQuery(tempdiv).appendTo("#titlewrap");
26
- var changebutton = jQuery("#lp_template_change");
27
-
28
- jQuery(changebutton).appendTo("#templates");
29
- jQuery("#lp_template_change a").removeClass("button-primary").addClass("button");
30
-
31
- // New Sidebar
32
- jQuery("#postbox-container-1").html("<div class='postbox'><center><h3>Download Additional Templates</h3><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store'><img src='"+lp_post_new_ui.LANDINGPAGES_URLPATH+"assets/images/get-wordpress-templates.png'></a><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store' class='button new-lp-button button-primary button-large'>Download Landing Page Templates</a></center></div><div class='postbox'><center><h3>Need Custom Template Design?</h3><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store'><img src='"+lp_post_new_ui.LANDINGPAGES_URLPATH+"assets/images/get-custom-setup.png'></a><a target='_blank' href='http://www.inboundnow.com/landing-pages/custom-wordpress-landing-page-setup/' class='button new-lp-button button-primary button-large'>Get Custom Template Setup</a></center></div>");
33
-
34
- jQuery('.lp_select_template').click(function(){
35
- jQuery(".mceIframeContainer iframe#content_ifr").css("height", "100%");
36
- jQuery("#wp-content-editor-container .mceStatusbar").css("display", "none");
37
- });
38
-
39
- jQuery('.lp_select_template').click(function(){
40
-
41
- var template = jQuery(this).attr('id');
42
- var selected_template_id = "#" + template;
43
- var label = jQuery(this).attr('label');
44
- var template_image = "#" + template + " .template-thumbnail";
45
- var template_img_obj = jQuery(template_image).attr("src");
46
-
47
- jQuery("#ctemp").text(label);
48
- jQuery("#template_current").html('<input type="hidden" name="lp-selected-template" value="'+template+'"><input type="hidden" value="1" name="lp_post_new">');
49
- jQuery("#timage #c_temp").attr("src", template_img_obj);
50
- jQuery("#submitdiv .hndle span").text("Create Landing Page");
51
-
52
- });
53
-
54
- jQuery('#lp-change-template-button').live('click', function () {
55
- jQuery(".wrap").fadeOut(500,function(){
56
-
57
- jQuery(".lp-template-selector-container").fadeIn(500, function(){
58
- jQuery('#lp-cancel-selection').show();
59
- });
60
- jQuery("#template-filter li a").first().click();
61
- });
62
- });
63
-
64
- // filter items when filter link is clicked
65
- jQuery('#template-filter a').click(function(){
66
- var selector = jQuery(this).attr('data-filter');
67
- jQuery("ul#template-filter li").removeClass('button-primary');
68
- jQuery(this).parent().addClass('button-primary');
69
- $(".template-item-boxes").fadeOut(500);
70
- setTimeout(function() {
71
- $(selector).fadeIn(500);
72
- }, 500);
73
-
74
- return false;
75
- });
76
-
77
- jQuery('.lp_select_template').click(function(){
78
- var template = jQuery(this).attr('id');
79
- var label = jQuery(this).attr('label');
80
- jQuery(".lp-template-selector-container").fadeOut(500,function(){
81
- jQuery(".wrap").fadeIn(500, function(){
82
- });
83
- });
84
-
85
- jQuery('#lp_metabox_select_template h3').html('Current Active Template: '+label);
86
- jQuery('#lp_select_template').val(template);
87
- //alert(template);
88
- //alert(label);
89
- });
90
-
91
-
92
- jQuery("#template-box a").live('click', function () {
93
-
94
- setTimeout(function() {
95
- jQuery('#TB_window iframe').contents().find("#customize-controls").hide();
96
- jQuery('#TB_window iframe').contents().find(".wp-full-overlay.expanded").css("margin-left", "0px");
97
- }, 1200);
98
-
99
- });
100
-
101
- // Fix Thickbox width
102
- jQuery(function($) {
103
- tb_position = function() {
104
- var tbWindow = $('#TB_window');
105
- var width = $(window).width();
106
- var H = $(window).height();
107
- var W = ( 1720 < width ) ? 1720 : width;
108
-
109
- if ( tbWindow.size() ) {
110
- tbWindow.width( W - 50 ).height( H - 45 );
111
- $('#TB_iframeContent').width( W - 50 ).height( H - 75 );
112
- tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
113
- if ( typeof document.body.style.maxWidth != 'undefined' )
114
- tbWindow.css({'top':'40px','margin-top':'0'});
115
- //$('#TB_title').css({'background-color':'#fff','color':'#cfcfcf'});
116
- };
117
-
118
- return $('a.thickbox').each( function() {
119
- var href = $(this).attr('href');
120
- if ( ! href ) return;
121
- href = href.replace(/&width=[0-9]+/g, '');
122
- href = href.replace(/&height=[0-9]+/g, '');
123
- $(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ) );
124
- });
125
- };
126
-
127
- jQuery('a.thickbox').click(function(){
128
- if ( typeof tinyMCE != 'undefined' && tinyMCE.activeEditor ) {
129
- tinyMCE.get('content').focus();
130
- tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
131
- }
132
-
133
- });
134
-
135
- $(window).resize( function() { tb_position() } );
136
- });
137
-
138
- var nonce_val = lp_post_new_ui.wp_landing_page_meta_nonce; // NEED CORRECT NONCE
139
-
140
- var nonce_html = '<input type="hidden" value="74910e3045" name="wp-landing-page-meta-nonce">';
141
-
142
- jQuery('form').prepend(nonce_html);
143
-
144
  });
1
+ jQuery(document).ready(function ($) {
2
+
3
+ jQuery('#lp-template-selector-container').css('display','block');
4
+
5
+ //remove inputs from wp-list-table
6
+ jQuery('#leads-table-container-inside input').each(function(){
7
+ jQuery(this).remove();
8
+ });
9
+
10
+ jQuery("#submitdiv").siblings().hide();
11
+
12
+ jQuery("#title-prompt-text").text("Name Your New Landing Page");
13
+ jQuery("#title").attr("required","required");
14
+
15
+ var titledescription = jQuery("<span id='descriptor'>This will be the administrative title your landing page, the main headline is created in the next step</span>");
16
+ jQuery(titledescription).appendTo("#titlewrap");
17
+
18
+ jQuery("#save-action input").addClass("button-primary button-large").css("margin-bottom", "10px").attr("value", "Create Landing Page");
19
+
20
+ var sidebar = jQuery("#side-sortables");
21
+ jQuery(sidebar).appendTo("#titlediv");
22
+
23
+ var tempdiv = jQuery("<div id='templates' class='postbox'><h3 class='hndle'>Current Template: <span id='ctemp'></span></h3><div id='lp_the_image'><span id='timage'><img src='' id='c_temp'></span></div><div id='template_current'></div></div>");
24
+
25
+ jQuery(tempdiv).appendTo("#titlewrap");
26
+ var changebutton = jQuery("#lp_template_change");
27
+
28
+ jQuery(changebutton).appendTo("#templates");
29
+ jQuery("#lp_template_change a").removeClass("button-primary").addClass("button");
30
+
31
+ // New Sidebar
32
+ jQuery("#postbox-container-1").html("<div class='postbox'><center><h3>Download Additional Templates</h3><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store'><img src='"+lp_post_new_ui.LANDINGPAGES_URLPATH+"assets/images/get-wordpress-templates.png'></a><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store' class='button new-lp-button button-primary button-large'>Download Landing Page Templates</a></center></div><div class='postbox'><center><h3>Need Custom Template Design?</h3><a target='_blank' href='/wp-admin/edit.php?post_type=landing-page&page=lp_store'><img src='"+lp_post_new_ui.LANDINGPAGES_URLPATH+"assets/images/get-custom-setup.png'></a><a target='_blank' href='http://www.inboundnow.com/landing-pages/custom-wordpress-landing-page-setup/' class='button new-lp-button button-primary button-large'>Get Custom Template Setup</a></center></div>");
33
+
34
+ jQuery('.lp_select_template').click(function(){
35
+ jQuery(".mceIframeContainer iframe#content_ifr").css("height", "100%");
36
+ jQuery("#wp-content-editor-container .mceStatusbar").css("display", "none");
37
+ });
38
+
39
+ jQuery('.lp_select_template').click(function(){
40
+
41
+ var template = jQuery(this).attr('id');
42
+ var selected_template_id = "#" + template;
43
+ var label = jQuery(this).attr('label');
44
+ var template_image = "#" + template + " .template-thumbnail";
45
+ var template_img_obj = jQuery(template_image).attr("src");
46
+
47
+ jQuery("#ctemp").text(label);
48
+ jQuery("#template_current").html('<input type="hidden" name="lp-selected-template" value="'+template+'"><input type="hidden" value="1" name="lp_post_new">');
49
+ jQuery("#timage #c_temp").attr("src", template_img_obj);
50
+ jQuery("#submitdiv .hndle span").text("Create Landing Page");
51
+
52
+ });
53
+
54
+ jQuery('#lp-change-template-button').live('click', function () {
55
+ jQuery(".wrap").fadeOut(500,function(){
56
+
57
+ jQuery(".lp-template-selector-container").fadeIn(500, function(){
58
+ jQuery('#lp-cancel-selection').show();
59
+ });
60
+ jQuery("#template-filter li a").first().click();
61
+ });
62
+ });
63
+
64
+ // filter items when filter link is clicked
65
+ jQuery('#template-filter a').click(function(){
66
+ var selector = jQuery(this).attr('data-filter');
67
+ jQuery("ul#template-filter li").removeClass('button-primary');
68
+ jQuery(this).parent().addClass('button-primary');
69
+ $(".template-item-boxes").fadeOut(500);
70
+ setTimeout(function() {
71
+ $(selector).fadeIn(500);
72
+ }, 500);
73
+
74
+ return false;
75
+ });
76
+
77
+ jQuery('.lp_select_template').click(function(){
78
+ var template = jQuery(this).attr('id');
79
+ var label = jQuery(this).attr('label');
80
+ jQuery(".lp-template-selector-container").fadeOut(500,function(){
81
+ jQuery(".wrap").fadeIn(500, function(){
82
+ });
83
+ });
84
+
85
+ jQuery('#lp_metabox_select_template h3').html('Current Active Template: '+label);
86
+ jQuery('#lp_select_template').val(template);
87
+ //alert(template);
88
+ //alert(label);
89
+ });
90
+
91
+
92
+ jQuery("#template-box a").live('click', function () {
93
+
94
+ setTimeout(function() {
95
+ jQuery('#TB_window iframe').contents().find("#customize-controls").hide();
96
+ jQuery('#TB_window iframe').contents().find(".wp-full-overlay.expanded").css("margin-left", "0px");
97
+ }, 1200);
98
+
99
+ });
100
+
101
+ // Fix Thickbox width
102
+ jQuery(function($) {
103
+ tb_position = function() {
104
+ var tbWindow = $('#TB_window');
105
+ var width = $(window).width();
106
+ var H = $(window).height();
107
+ var W = ( 1720 < width ) ? 1720 : width;
108
+
109
+ if ( tbWindow.size() ) {
110
+ tbWindow.width( W - 50 ).height( H - 45 );
111
+ $('#TB_iframeContent').width( W - 50 ).height( H - 75 );
112
+ tbWindow.css({'margin-left': '-' + parseInt((( W - 50 ) / 2),10) + 'px'});
113
+ if ( typeof document.body.style.maxWidth != 'undefined' )
114
+ tbWindow.css({'top':'40px','margin-top':'0'});
115
+ //$('#TB_title').css({'background-color':'#fff','color':'#cfcfcf'});
116
+ };
117
+
118
+ return $('a.thickbox').each( function() {
119
+ var href = $(this).attr('href');
120
+ if ( ! href ) return;
121
+ href = href.replace(/&width=[0-9]+/g, '');
122
+ href = href.replace(/&height=[0-9]+/g, '');
123
+ $(this).attr( 'href', href + '&width=' + ( W - 80 ) + '&height=' + ( H - 85 ) );
124
+ });
125
+ };
126
+
127
+ jQuery('a.thickbox').click(function(){
128
+ if ( typeof tinyMCE != 'undefined' && tinyMCE.activeEditor ) {
129
+ tinyMCE.get('content').focus();
130
+ tinyMCE.activeEditor.windowManager.bookmark = tinyMCE.activeEditor.selection.getBookmark('simple');
131
+ }
132
+
133
+ });
134
+
135
+ $(window).resize( function() { tb_position() } );
136
+ });
137
+
138
+ var nonce_val = lp_post_new_ui.wp_landing_page_meta_nonce; // NEED CORRECT NONCE
139
+
140
+ var nonce_html = '<input type="hidden" value="74910e3045" name="wp-landing-page-meta-nonce">';
141
+
142
+ jQuery('form').prepend(nonce_html);
143
+
144
  });
assets/js/admin/admin.post.js CHANGED
@@ -1,35 +1,35 @@
1
- jQuery(document).ready(function ($) {
2
- jQuery('body').on( 'click' , '.lp_select_template' , function() {
3
- var this_template = jQuery(this);
4
- swal({
5
- title: sweetalert.title,
6
- text: sweetalert.text,
7
- type: "info",
8
- showCancelButton: true,
9
- confirmButtonColor: "#2ea2cc",
10
- confirmButtonText: sweetalert.confirmButtonText,
11
- closeOnConfirm: false
12
- }, function () {
13
- swal({
14
- title: sweetalert.waitTitle,
15
- text: sweetalert.waitText,
16
- imageUrl: sweetalert.waitImage
17
- });
18
-
19
- var template = this_template.attr('id');
20
- jQuery('#lp_select_template').val(template);
21
-
22
- /* save post */
23
- jQuery('#publish').click();
24
- });
25
- });
26
-
27
-
28
- jQuery('#lp-cancel-selection').click(function(){
29
- jQuery(".lp-template-selector-container").fadeOut(500,function(){
30
- jQuery(".wrap").fadeIn(500, function(){
31
- });
32
- });
33
-
34
- });
35
  });
1
+ jQuery(document).ready(function ($) {
2
+ jQuery('body').on( 'click' , '.lp_select_template' , function() {
3
+ var this_template = jQuery(this);
4
+ swal({
5
+ title: sweetalert.title,
6
+ text: sweetalert.text,
7
+ type: "info",
8
+ showCancelButton: true,
9
+ confirmButtonColor: "#2ea2cc",
10
+ confirmButtonText: sweetalert.confirmButtonText,
11
+ closeOnConfirm: false
12
+ }, function () {
13
+ swal({
14
+ title: sweetalert.waitTitle,
15
+ text: sweetalert.waitText,
16
+ imageUrl: sweetalert.waitImage
17
+ });
18
+
19
+ var template = this_template.attr('id');
20
+ jQuery('#lp_select_template').val(template);
21
+
22
+ /* save post */
23
+ jQuery('#publish').click();
24
+ });
25
+ });
26
+
27
+
28
+ jQuery('#lp-cancel-selection').click(function(){
29
+ jQuery(".lp-template-selector-container").fadeOut(500,function(){
30
+ jQuery(".wrap").fadeIn(500, function(){
31
+ });
32
+ });
33
+
34
+ });
35
  });
assets/js/admin/admin.store.js CHANGED
@@ -1,5 +1,5 @@
1
- jQuery(document).ready(function()
2
- {
3
- // Store page jquery not in use
4
- });
5
 
1
+ jQuery(document).ready(function()
2
+ {
3
+ // Store page jquery not in use
4
+ });
5
 
assets/js/admin/admin.templates-upload.js CHANGED
@@ -1,25 +1,25 @@
1
- jQuery(document).ready(function($) {
2
-
3
- jQuery('.subsubsub li a').live('click', function () {
4
-
5
- var id = jQuery(this).attr('id');
6
- //alert (id);
7
- if (id == 'menu_upload') {
8
- jQuery('.templates_search').hide();
9
- jQuery('.templates_search').removeClass('current');
10
-
11
- jQuery('.templates_upload').show();
12
- jQuery('.templates_upload').addClass('current');
13
- }
14
- else if (id == 'menu_search')
15
- {
16
- jQuery('.templates_upload').hide();
17
- jQuery('.templates_upload').removeClass('current');
18
-
19
- jQuery('.templates_search').show();
20
- jQuery('.templates_search').addClass('current');
21
- }
22
-
23
- });
24
-
25
  });
1
+ jQuery(document).ready(function($) {
2
+
3
+ jQuery('.subsubsub li a').live('click', function () {
4
+
5
+ var id = jQuery(this).attr('id');
6
+ //alert (id);
7
+ if (id == 'menu_upload') {
8
+ jQuery('.templates_search').hide();
9
+ jQuery('.templates_search').removeClass('current');
10
+
11
+ jQuery('.templates_upload').show();
12
+ jQuery('.templates_upload').addClass('current');
13
+ }
14
+ else if (id == 'menu_search')
15
+ {
16
+ jQuery('.templates_upload').hide();
17
+ jQuery('.templates_upload').removeClass('current');
18
+
19
+ jQuery('.templates_search').show();
20
+ jQuery('.templates_search').addClass('current');
21
+ }
22
+
23
+ });
24
+
25
  });
assets/js/admin/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/js/admin/intro.js CHANGED
@@ -1,758 +1,758 @@
1
- /**
2
- * Intro.js v0.4.0
3
- * https://github.com/usablica/intro.js
4
- * MIT licensed
5
- *
6
- * Copyright (C) 2013 usabli.ca - A weekend project by Afshin Mehrabani (@afshinmeh)
7
- */
8
-
9
- (function (root, factory) {
10
- if (typeof exports === 'object') {
11
- // CommonJS
12
- factory(exports);
13
- } else if (typeof define === 'function' && define.amd) {
14
- // AMD. Register as an anonymous module.
15
- define(['exports'], factory);
16
- } else {
17
- // Browser globals
18
- factory(root);
19
- }
20
- } (this, function (exports) {
21
- //Default config/variables
22
- var VERSION = '0.4.0';
23
-
24
- /**
25
- * IntroJs main class
26
- *
27
- * @class IntroJs
28
- */
29
- function IntroJs(obj) {
30
- this._targetElement = obj;
31
-
32
- this._options = {
33
- nextLabel: 'Next &rarr;',
34
- prevLabel: '&larr; Back',
35
- skipLabel: 'Skip',
36
- doneLabel: 'Done',
37
- tooltipPosition: 'bottom',
38
- exitOnEsc: true,
39
- exitOnOverlayClick: true,
40
- showStepNumbers: true
41
- };
42
- }
43
-
44
- /**
45
- * Initiate a new introduction/guide from an element in the page
46
- *
47
- * @api private
48
- * @method _introForElement
49
- * @param {Object} targetElm
50
- * @returns {Boolean} Success or not?
51
- */
52
- function _introForElement(targetElm) {
53
- var introItems = [],
54
- self = this;
55
-
56
- if (this._options.steps) {
57
- //use steps passed programmatically
58
- var allIntroSteps = [];
59
-
60
- for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) {
61
- var currentItem = this._options.steps[i];
62
- //set the step
63
- currentItem.step = i + 1;
64
- //grab the element with given selector from the page
65
- currentItem.element = document.querySelector(currentItem.element);
66
- introItems.push(currentItem);
67
- }
68
-
69
- } else {
70
- //use steps from data-* annotations
71
-
72
- var allIntroSteps = targetElm.querySelectorAll('*[data-intro]');
73
- //if there's no element to intro
74
- if (allIntroSteps.length < 1) {
75
- return false;
76
- }
77
-
78
- for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
79
- var currentElement = allIntroSteps[i];
80
- introItems.push({
81
- element: currentElement,
82
- intro: currentElement.getAttribute('data-intro'),
83
- step: parseInt(currentElement.getAttribute('data-step'), 10),
84
- position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
85
- });
86
- }
87
- }
88
-
89
- //Ok, sort all items with given steps
90
- introItems.sort(function (a, b) {
91
- return a.step - b.step;
92
- });
93
-
94
- //set it to the introJs object
95
- self._introItems = introItems;
96
-
97
- //add overlay layer to the page
98
- if(_addOverlayLayer.call(self, targetElm)) {
99
- //then, start the show
100
- _nextStep.call(self);
101
-
102
- var skipButton = targetElm.querySelector('.introjs-skipbutton'),
103
- nextStepButton = targetElm.querySelector('.introjs-nextbutton');
104
-
105
- self._onKeyDown = function(e) {
106
- if (e.keyCode === 27 && self._options.exitOnEsc == true) {
107
- //escape key pressed, exit the intro
108
- _exitIntro.call(self, targetElm);
109
- //check if any callback is defined
110
- if (self._introExitCallback != undefined) {
111
- self._introExitCallback.call(self);
112
- }
113
- } else if(e.keyCode === 37) {
114
- //left arrow
115
- _previousStep.call(self);
116
- } else if (e.keyCode === 39 || e.keyCode === 13) {
117
- //right arrow or enter
118
- _nextStep.call(self);
119
- //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
120
- if(e.preventDefault) {
121
- e.preventDefault();
122
- } else {
123
- e.returnValue = false;
124
- }
125
- }
126
- };
127
-
128
- self._onResize = function(e) {
129
- _setHelperLayerPosition.call(self, document.querySelector('.introjs-helperLayer'));
130
- };
131
-
132
- if (window.addEventListener) {
133
- window.addEventListener('keydown', self._onKeyDown, true);
134
- //for window resize
135
- window.addEventListener("resize", self._onResize, true);
136
- } else if (document.attachEvent) { //IE
137
- document.attachEvent('onkeydown', self._onKeyDown);
138
- //for window resize
139
- document.attachEvent("onresize", self._onResize);
140
- }
141
- }
142
- return false;
143
- }
144
-
145
- /**
146
- * Go to specific step of introduction
147
- *
148
- * @api private
149
- * @method _goToStep
150
- */
151
- function _goToStep(step) {
152
- //because steps starts with zero
153
- this._currentStep = step - 2;
154
- if(typeof (this._introItems) !== 'undefined') {
155
- _nextStep.call(this);
156
- }
157
- }
158
-
159
- /**
160
- * Go to next step on intro
161
- *
162
- * @api private
163
- * @method _nextStep
164
- */
165
- function _nextStep() {
166
- if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
167
- this._introBeforeChangeCallback.call(this, this._targetElement);
168
- }
169
-
170
- if (typeof (this._currentStep) === 'undefined') {
171
- this._currentStep = 0;
172
- } else {
173
- ++this._currentStep;
174
- }
175
-
176
- if((this._introItems.length) <= this._currentStep) {
177
- //end of the intro
178
- //check if any callback is defined
179
- if (typeof (this._introCompleteCallback) === 'function') {
180
- this._introCompleteCallback.call(this);
181
- }
182
- _exitIntro.call(this, this._targetElement);
183
- return;
184
- }
185
-
186
- _showElement.call(this, this._introItems[this._currentStep]);
187
- }
188
-
189
- /**
190
- * Go to previous step on intro
191
- *
192
- * @api private
193
- * @method _nextStep
194
- */
195
- function _previousStep() {
196
- if (this._currentStep === 0) {
197
- return false;
198
- }
199
-
200
- if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
201
- this._introBeforeChangeCallback.call(this, this._targetElement);
202
- }
203
-
204
- _showElement.call(this, this._introItems[--this._currentStep]);
205
- }
206
-
207
- /**
208
- * Exit from intro
209
- *
210
- * @api private
211
- * @method _exitIntro
212
- * @param {Object} targetElement
213
- */
214
- function _exitIntro(targetElement) {
215
- //remove overlay layer from the page
216
- var overlayLayer = targetElement.querySelector('.introjs-overlay');
217
- //for fade-out animation
218
- overlayLayer.style.opacity = 0;
219
- setTimeout(function () {
220
- if (overlayLayer.parentNode) {
221
- overlayLayer.parentNode.removeChild(overlayLayer);
222
- }
223
- }, 500);
224
- //remove all helper layers
225
- var helperLayer = targetElement.querySelector('.introjs-helperLayer');
226
- if (helperLayer) {
227
- helperLayer.parentNode.removeChild(helperLayer);
228
- }
229
- //remove `introjs-showElement` class from the element
230
- var showElement = document.querySelector('.introjs-showElement');
231
- if (showElement) {
232
- showElement.className = showElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, ''); // This is a manual trim.
233
- }
234
-
235
- //remove `introjs-fixParent` class from the elements
236
- var fixParents = document.querySelectorAll('.introjs-fixParent');
237
- if (fixParents && fixParents.length > 0) {
238
- for (var i = fixParents.length - 1; i >= 0; i--) {
239
- fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
240
- };
241
- }
242
- //clean listeners
243
- if (window.removeEventListener) {
244
- window.removeEventListener('keydown', this._onKeyDown, true);
245
- } else if (document.detachEvent) { //IE
246
- document.detachEvent('onkeydown', this._onKeyDown);
247
- }
248
- //set the step to zero
249
- this._currentStep = undefined;
250
- }
251
-
252
- /**
253
- * Render tooltip box in the page
254
- *
255
- * @api private
256
- * @method _placeTooltip
257
- * @param {Object} targetElement
258
- * @param {Object} tooltipLayer
259
- * @param {Object} arrowLayer
260
- */
261
- function _placeTooltip(targetElement, tooltipLayer, arrowLayer) {
262
- //reset the old style
263
- tooltipLayer.style.top = null;
264
- tooltipLayer.style.right = null;
265
- tooltipLayer.style.bottom = null;
266
- tooltipLayer.style.left = null;
267
-
268
- //prevent error when `this._currentStep` is undefined
269
- if(!this._introItems[this._currentStep]) return;
270
-
271
- var currentTooltipPosition = this._introItems[this._currentStep].position;
272
- switch (currentTooltipPosition) {
273
- case 'top':
274
- tooltipLayer.style.left = '15px';
275
- tooltipLayer.style.top = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
276
- arrowLayer.className = 'introjs-arrow bottom';
277
- break;
278
- case 'right':
279
- tooltipLayer.style.left = (_getOffset(targetElement).width + 20) + 'px';
280
- arrowLayer.className = 'introjs-arrow left';
281
- break;
282
- case 'left':
283
- tooltipLayer.style.top = '15px';
284
- tooltipLayer.style.right = (_getOffset(targetElement).width + 20) + 'px';
285
- arrowLayer.className = 'introjs-arrow right';
286
- break;
287
- case 'bottom':
288
- // Bottom going to follow the default behavior
289
- default:
290
- tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
291
- arrowLayer.className = 'introjs-arrow top';
292
- break;
293
- }
294
- }
295
-
296
- /**
297
- * Update the position of the helper layer on the screen
298
- *
299
- * @api private
300
- * @method _setHelperLayerPosition
301
- * @param {Object} helperLayer
302
- */
303
- function _setHelperLayerPosition(helperLayer) {
304
- if(helperLayer) {
305
- //prevent error when `this._currentStep` in undefined
306
- if(!this._introItems[this._currentStep]) return;
307
-
308
- var elementPosition = _getOffset(this._introItems[this._currentStep].element);
309
- //set new position to helper layer
310
- helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + 10) + 'px; ' +
311
- 'height:' + (elementPosition.height + 10) + 'px; ' +
312
- 'top:' + (elementPosition.top - 5) + 'px;' +
313
- 'left: ' + (elementPosition.left - 5) + 'px;');
314
- }
315
- }
316
-
317
- /**
318
- * Show an element on the page
319
- *
320
- * @api private
321
- * @method _showElement
322
- * @param {Object} targetElement
323
- */
324
- function _showElement(targetElement) {
325
-
326
- if (typeof (this._introChangeCallback) !== 'undefined') {
327
- this._introChangeCallback.call(this, targetElement.element);
328
- }
329
-
330
- var self = this,
331
- oldHelperLayer = document.querySelector('.introjs-helperLayer'),
332
- elementPosition = _getOffset(targetElement.element);
333
-
334
- if(oldHelperLayer != null) {
335
- var oldHelperNumberLayer = oldHelperLayer.querySelector('.introjs-helperNumberLayer'),
336
- oldtooltipLayer = oldHelperLayer.querySelector('.introjs-tooltiptext'),
337
- oldArrowLayer = oldHelperLayer.querySelector('.introjs-arrow'),
338
- oldtooltipContainer = oldHelperLayer.querySelector('.introjs-tooltip'),
339
- skipTooltipButton = oldHelperLayer.querySelector('.introjs-skipbutton'),
340
- prevTooltipButton = oldHelperLayer.querySelector('.introjs-prevbutton'),
341
- nextTooltipButton = oldHelperLayer.querySelector('.introjs-nextbutton');
342
-
343
- //hide the tooltip
344
- oldtooltipContainer.style.opacity = 0;
345
-
346
- //set new position to helper layer
347
- _setHelperLayerPosition.call(self, oldHelperLayer);
348
-
349
- //remove `introjs-fixParent` class from the elements
350
- var fixParents = document.querySelectorAll('.introjs-fixParent');
351
- if (fixParents && fixParents.length > 0) {
352
- for (var i = fixParents.length - 1; i >= 0; i--) {
353
- fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
354
- };
355
- }
356
-
357
- //remove old classes
358
- var oldShowElement = document.querySelector('.introjs-showElement');
359
- oldShowElement.className = oldShowElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, '');
360
- //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
361
- if (self._lastShowElementTimer) {
362
- clearTimeout(self._lastShowElementTimer);
363
- }
364
- self._lastShowElementTimer = setTimeout(function() {
365
- //set current step to the label
366
- if(oldHelperNumberLayer != null) {
367
- oldHelperNumberLayer.innerHTML = targetElement.step;
368
- }
369
- //set current tooltip text
370
- oldtooltipLayer.innerHTML = targetElement.intro;
371
- //set the tooltip position
372
- _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer);
373
- //show the tooltip
374
- oldtooltipContainer.style.opacity = 1;
375
- }, 350);
376
-
377
- } else {
378
- var helperLayer = document.createElement('div'),
379
- arrowLayer = document.createElement('div'),
380
- tooltipLayer = document.createElement('div');
381
-
382
- helperLayer.className = 'introjs-helperLayer';
383
-
384
- //set new position to helper layer
385
- _setHelperLayerPosition.call(self, helperLayer);
386
-
387
- //add helper layer to target element
388
- this._targetElement.appendChild(helperLayer);
389
-
390
- arrowLayer.className = 'introjs-arrow';
391
- tooltipLayer.className = 'introjs-tooltip';
392
-
393
-
394
- tooltipLayer.innerHTML = '<div class="introjs-tooltiptext">' +
395
- targetElement.intro +
396
- '</div><div class="introjs-tooltipbuttons"></div>';
397
-
398
- //add helper layer number
399
- if (this._options.showStepNumbers) {
400
- var helperNumberLayer = document.createElement('span');
401
- helperNumberLayer.className = 'introjs-helperNumberLayer';
402
- helperNumberLayer.innerHTML = targetElement.step;
403
- helperLayer.appendChild(helperNumberLayer);
404
- }
405
- tooltipLayer.appendChild(arrowLayer);
406
- helperLayer.appendChild(tooltipLayer);
407
-
408
- //next button
409
- var nextTooltipButton = document.createElement('a');
410
-
411
- nextTooltipButton.onclick = function() {
412
- if(self._introItems.length - 1 != self._currentStep) {
413
- _nextStep.call(self);
414
- }
415
- };
416
-
417
- nextTooltipButton.href = 'javascript:void(0);';
418
- nextTooltipButton.innerHTML = this._options.nextLabel;
419
-
420
- //previous button
421
- var prevTooltipButton = document.createElement('a');
422
-
423
- prevTooltipButton.onclick = function() {
424
- if(self._currentStep != 0) {
425
- _previousStep.call(self);
426
- }
427
- };
428
-
429
- prevTooltipButton.href = 'javascript:void(0);';
430
- prevTooltipButton.innerHTML = this._options.prevLabel;
431
-
432
- //skip button
433
- var skipTooltipButton = document.createElement('a');
434
- skipTooltipButton.className = 'introjs-button introjs-skipbutton';
435
- skipTooltipButton.href = 'javascript:void(0);';
436
- skipTooltipButton.innerHTML = this._options.skipLabel;
437
-
438
- skipTooltipButton.onclick = function() {
439
- if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
440
- self._introCompleteCallback.call(self);
441
- }
442
-
443
- if (self._introItems.length - 1 != self._currentStep && typeof (self._introExitCallback) === 'function') {
444
- self._introExitCallback.call(self);
445
- }
446
-
447
- _exitIntro.call(self, self._targetElement);
448
- };
449
-
450
- var tooltipButtonsLayer = tooltipLayer.querySelector('.introjs-tooltipbuttons');
451
- tooltipButtonsLayer.appendChild(skipTooltipButton);
452
- tooltipButtonsLayer.appendChild(prevTooltipButton);
453
- tooltipButtonsLayer.appendChild(nextTooltipButton);
454
-
455
- //set proper position
456
- _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer);
457
- }
458
-
459
- if (this._currentStep == 0) {
460
- prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
461
- nextTooltipButton.className = 'introjs-button introjs-nextbutton';
462
- skipTooltipButton.innerHTML = this._options.skipLabel;
463
- } else if (this._introItems.length - 1 == this._currentStep) {
464
- skipTooltipButton.innerHTML = this._options.doneLabel;
465
- prevTooltipButton.className = 'introjs-button introjs-prevbutton';
466
- nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
467
- } else {
468
- prevTooltipButton.className = 'introjs-button introjs-prevbutton';
469
- nextTooltipButton.className = 'introjs-button introjs-nextbutton';
470
- skipTooltipButton.innerHTML = this._options.skipLabel;
471
- }
472
-
473
- //Set focus on "next" button, so that hitting Enter always moves you onto the next step
474
- nextTooltipButton.focus();
475
-
476
- //add target element position style
477
- targetElement.element.className += ' introjs-showElement';
478
-
479
- var currentElementPosition = _getPropValue(targetElement.element, 'position');
480
- if (currentElementPosition !== 'absolute' &&
481
- currentElementPosition !== 'relative') {
482
- //change to new intro item
483
- targetElement.element.className += ' introjs-relativePosition';
484
- }
485
-
486
- var parentElm = targetElement.element.parentNode;
487
- while(parentElm != null) {
488
- if(parentElm.tagName.toLowerCase() === 'body') break;
489
-
490
- var zIndex = _getPropValue(parentElm, 'z-index');
491
- if(/[0-9]+/.test(zIndex)) {
492
- parentElm.className += ' introjs-fixParent';
493
- }
494
- parentElm = parentElm.parentNode;
495
- }
496
-
497
- if (!_elementInViewport(targetElement.element)) {
498
- var rect = targetElement.element.getBoundingClientRect(),
499
- top = rect.bottom - (rect.bottom - rect.top),
500
- bottom = rect.bottom - _getWinSize().height;
501
-
502
- // Scroll up
503
- if (top < 0) {
504
- window.scrollBy(0, top - 30); // 30px padding from edge to look nice
505
-
506
- // Scroll down
507
- } else {
508
- window.scrollBy(0, bottom + 100); // 70px + 30px padding from edge to look nice
509
- }
510
- }
511
- }
512
-
513
- /**
514
- * Get an element CSS property on the page
515
- * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
516
- *
517
- * @api private
518
- * @method _getPropValue
519
- * @param {Object} element
520
- * @param {String} propName
521
- * @returns Element's property value
522
- */
523
- function _getPropValue (element, propName) {
524
- var propValue = '';
525
- if (element.currentStyle) { //IE
526
- propValue = element.currentStyle[propName];
527
- } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
528
- propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
529
- }
530
-
531
- //Prevent exception in IE
532
- if(propValue.toLowerCase) {
533
- return propValue.toLowerCase();
534
- } else {
535
- return propValue;
536
- }
537
- }
538
-
539
- /**
540
- * Provides a cross-browser way to get the screen dimensions
541
- * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
542
- *
543
- * @api private
544
- * @method _getWinSize
545
- * @returns {Object} width and height attributes
546
- */
547
- function _getWinSize() {
548
- if (window.innerWidth != undefined) {
549
- return { width: window.innerWidth, height: window.innerHeight };
550
- } else {
551
- var D = document.documentElement;
552
- return { width: D.clientWidth, height: D.clientHeight };
553
- }
554
- }
555
-
556
- /**
557
- * Add overlay layer to the page
558
- * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
559
- *
560
- * @api private
561
- * @method _elementInViewport
562
- * @param {Object} el
563
- */
564
- function _elementInViewport(el) {
565
- var rect = el.getBoundingClientRect();
566
-
567
- return (
568
- rect.top >= 0 &&
569
- rect.left >= 0 &&
570
- (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
571
- rect.right <= window.innerWidth
572
- );
573
- }
574
-
575
- /**
576
- * Add overlay layer to the page
577
- *
578
- * @api private
579
- * @method _addOverlayLayer
580
- * @param {Object} targetElm
581
- */
582
- function _addOverlayLayer(targetElm) {
583
- var overlayLayer = document.createElement('div'),
584
- styleText = '',
585
- self = this;
586
-
587
- //set css class name
588
- overlayLayer.className = 'introjs-overlay';
589
-
590
- //check if the target element is body, we should calculate the size of overlay layer in a better way
591
- if (targetElm.tagName.toLowerCase() === 'body') {
592
- styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
593
- overlayLayer.setAttribute('style', styleText);
594
- } else {
595
- //set overlay layer position
596
- var elementPosition = _getOffset(targetElm);
597
- if(elementPosition) {
598
- styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
599
- overlayLayer.setAttribute('style', styleText);
600
- }
601
- }
602
-
603
- targetElm.appendChild(overlayLayer);
604
-
605
- overlayLayer.onclick = function() {
606
- if(self._options.exitOnOverlayClick == true) {
607
- _exitIntro.call(self, targetElm);
608
- }
609
- //check if any callback is defined
610
- if (self._introExitCallback != undefined) {
611
- self._introExitCallback.call(self);
612
- }
613
- };
614
-
615
- setTimeout(function() {
616
- styleText += 'opacity: .8;';
617
- overlayLayer.setAttribute('style', styleText);
618
- }, 10);
619
- return true;
620
- }
621
-
622
- /**
623
- * Get an element position on the page
624
- * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
625
- *
626
- * @api private
627
- * @method _getOffset
628
- * @param {Object} element
629
- * @returns Element's position info
630
- */
631
- function _getOffset(element) {
632
- var elementPosition = {};
633
-
634
- //set width
635
- elementPosition.width = element.offsetWidth;
636
-
637
- //set height
638
- elementPosition.height = element.offsetHeight;
639
-
640
- //calculate element top and left
641
- var _x = 0;
642
- var _y = 0;
643
- while(element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
644
- _x += element.offsetLeft;
645
- _y += element.offsetTop;
646
- element = element.offsetParent;
647
- }
648
- //set top
649
- elementPosition.top = _y;
650
- //set left
651
- elementPosition.left = _x;
652
-
653
- return elementPosition;
654
- }
655
-
656
- /**
657
- * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
658
- * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
659
- *
660
- * @param obj1
661
- * @param obj2
662
- * @returns obj3 a new object based on obj1 and obj2
663
- */
664
- function _mergeOptions(obj1,obj2) {
665
- var obj3 = {};
666
- for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
667
- for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
668
- return obj3;
669
- }
670
-
671
- var introJs = function (targetElm) {
672
- if (typeof (targetElm) === 'object') {
673
- //Ok, create a new instance
674
- return new IntroJs(targetElm);
675
-
676
- } else if (typeof (targetElm) === 'string') {
677
- //select the target element with query selector
678
- var targetElement = document.querySelector(targetElm);
679
-
680
- if (targetElement) {
681
- return new IntroJs(targetElement);
682
- } else {
683
- throw new Error('There is no element with given selector.');
684
- }
685
- } else {
686
- return new IntroJs(document.body);
687
- }
688
- };
689
-
690
- /**
691
- * Current IntroJs version
692
- *
693
- * @property version
694
- * @type String
695
- */
696
- introJs.version = VERSION;
697
-
698
- //Prototype
699
- introJs.fn = IntroJs.prototype = {
700
- clone: function () {
701
- return new IntroJs(this);
702
- },
703
- setOption: function(option, value) {
704
- this._options[option] = value;
705
- return this;
706
- },
707
- setOptions: function(options) {
708
- this._options = _mergeOptions(this._options, options);
709
- return this;
710
- },
711
- start: function () {
712
- _introForElement.call(this, this._targetElement);
713
- return this;
714
- },
715
- goToStep: function(step) {
716
- _goToStep.call(this, step);
717
- return this;
718
- },
719
- exit: function() {
720
- _exitIntro.call(this, this._targetElement);
721
- },
722
- onbeforechange: function(providedCallback) {
723
- if (typeof (providedCallback) === 'function') {
724
- this._introBeforeChangeCallback = providedCallback;
725
- } else {
726
- throw new Error('Provided callback for onbeforechange was not a function');
727
- }
728
- return this;
729
- },
730
- onchange: function(providedCallback) {
731
- if (typeof (providedCallback) === 'function') {
732
- this._introChangeCallback = providedCallback;
733
- } else {
734
- throw new Error('Provided callback for onchange was not a function.');
735
- }
736
- return this;
737
- },
738
- oncomplete: function(providedCallback) {
739
- if (typeof (providedCallback) === 'function') {
740
- this._introCompleteCallback = providedCallback;
741
- } else {
742
- throw new Error('Provided callback for oncomplete was not a function.');
743
- }
744
- return this;
745
- },
746
- onexit: function(providedCallback) {
747
- if (typeof (providedCallback) === 'function') {
748
- this._introExitCallback = providedCallback;
749
- } else {
750
- throw new Error('Provided callback for onexit was not a function.');
751
- }
752
- return this;
753
- }
754
- };
755
-
756
- exports.introJs = introJs;
757
- return introJs;
758
- }));
1
+ /**
2
+ * Intro.js v0.4.0
3
+ * https://github.com/usablica/intro.js
4
+ * MIT licensed
5
+ *
6
+ * Copyright (C) 2013 usabli.ca - A weekend project by Afshin Mehrabani (@afshinmeh)
7
+ */
8
+
9
+ (function (root, factory) {
10
+ if (typeof exports === 'object') {
11
+ // CommonJS
12
+ factory(exports);
13
+ } else if (typeof define === 'function' && define.amd) {
14
+ // AMD. Register as an anonymous module.
15
+ define(['exports'], factory);
16
+ } else {
17
+ // Browser globals
18
+ factory(root);
19
+ }
20
+ } (this, function (exports) {
21
+ //Default config/variables
22
+ var VERSION = '0.4.0';
23
+
24
+ /**
25
+ * IntroJs main class
26
+ *
27
+ * @class IntroJs
28
+ */
29
+ function IntroJs(obj) {
30
+ this._targetElement = obj;
31
+
32
+ this._options = {
33
+ nextLabel: 'Next &rarr;',
34
+ prevLabel: '&larr; Back',
35
+ skipLabel: 'Skip',
36
+ doneLabel: 'Done',
37
+ tooltipPosition: 'bottom',
38
+ exitOnEsc: true,
39
+ exitOnOverlayClick: true,
40
+ showStepNumbers: true
41
+ };
42
+ }
43
+
44
+ /**
45
+ * Initiate a new introduction/guide from an element in the page
46
+ *
47
+ * @api private
48
+ * @method _introForElement
49
+ * @param {Object} targetElm
50
+ * @returns {Boolean} Success or not?
51
+ */
52
+ function _introForElement(targetElm) {
53
+ var introItems = [],
54
+ self = this;
55
+
56
+ if (this._options.steps) {
57
+ //use steps passed programmatically
58
+ var allIntroSteps = [];
59
+
60
+ for (var i = 0, stepsLength = this._options.steps.length; i < stepsLength; i++) {
61
+ var currentItem = this._options.steps[i];
62
+ //set the step
63
+ currentItem.step = i + 1;
64
+ //grab the element with given selector from the page
65
+ currentItem.element = document.querySelector(currentItem.element);
66
+ introItems.push(currentItem);
67
+ }
68
+
69
+ } else {
70
+ //use steps from data-* annotations
71
+
72
+ var allIntroSteps = targetElm.querySelectorAll('*[data-intro]');
73
+ //if there's no element to intro
74
+ if (allIntroSteps.length < 1) {
75
+ return false;
76
+ }
77
+
78
+ for (var i = 0, elmsLength = allIntroSteps.length; i < elmsLength; i++) {
79
+ var currentElement = allIntroSteps[i];
80
+ introItems.push({
81
+ element: currentElement,
82
+ intro: currentElement.getAttribute('data-intro'),
83
+ step: parseInt(currentElement.getAttribute('data-step'), 10),
84
+ position: currentElement.getAttribute('data-position') || this._options.tooltipPosition
85
+ });
86
+ }
87
+ }
88
+
89
+ //Ok, sort all items with given steps
90
+ introItems.sort(function (a, b) {
91
+ return a.step - b.step;
92
+ });
93
+
94
+ //set it to the introJs object
95
+ self._introItems = introItems;
96
+
97
+ //add overlay layer to the page
98
+ if(_addOverlayLayer.call(self, targetElm)) {
99
+ //then, start the show
100
+ _nextStep.call(self);
101
+
102
+ var skipButton = targetElm.querySelector('.introjs-skipbutton'),
103
+ nextStepButton = targetElm.querySelector('.introjs-nextbutton');
104
+
105
+ self._onKeyDown = function(e) {
106
+ if (e.keyCode === 27 && self._options.exitOnEsc == true) {
107
+ //escape key pressed, exit the intro
108
+ _exitIntro.call(self, targetElm);
109
+ //check if any callback is defined
110
+ if (self._introExitCallback != undefined) {
111
+ self._introExitCallback.call(self);
112
+ }
113
+ } else if(e.keyCode === 37) {
114
+ //left arrow
115
+ _previousStep.call(self);
116
+ } else if (e.keyCode === 39 || e.keyCode === 13) {
117
+ //right arrow or enter
118
+ _nextStep.call(self);
119
+ //prevent default behaviour on hitting Enter, to prevent steps being skipped in some browsers
120
+ if(e.preventDefault) {
121
+ e.preventDefault();
122
+ } else {
123
+ e.returnValue = false;
124
+ }
125
+ }
126
+ };
127
+
128
+ self._onResize = function(e) {
129
+ _setHelperLayerPosition.call(self, document.querySelector('.introjs-helperLayer'));
130
+ };
131
+
132
+ if (window.addEventListener) {
133
+ window.addEventListener('keydown', self._onKeyDown, true);
134
+ //for window resize
135
+ window.addEventListener("resize", self._onResize, true);
136
+ } else if (document.attachEvent) { //IE
137
+ document.attachEvent('onkeydown', self._onKeyDown);
138
+ //for window resize
139
+ document.attachEvent("onresize", self._onResize);
140
+ }
141
+ }
142
+ return false;
143
+ }
144
+
145
+ /**
146
+ * Go to specific step of introduction
147
+ *
148
+ * @api private
149
+ * @method _goToStep
150
+ */
151
+ function _goToStep(step) {
152
+ //because steps starts with zero
153
+ this._currentStep = step - 2;
154
+ if(typeof (this._introItems) !== 'undefined') {
155
+ _nextStep.call(this);
156
+ }
157
+ }
158
+
159
+ /**
160
+ * Go to next step on intro
161
+ *
162
+ * @api private
163
+ * @method _nextStep
164
+ */
165
+ function _nextStep() {
166
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
167
+ this._introBeforeChangeCallback.call(this, this._targetElement);
168
+ }
169
+
170
+ if (typeof (this._currentStep) === 'undefined') {
171
+ this._currentStep = 0;
172
+ } else {
173
+ ++this._currentStep;
174
+ }
175
+
176
+ if((this._introItems.length) <= this._currentStep) {
177
+ //end of the intro
178
+ //check if any callback is defined
179
+ if (typeof (this._introCompleteCallback) === 'function') {
180
+ this._introCompleteCallback.call(this);
181
+ }
182
+ _exitIntro.call(this, this._targetElement);
183
+ return;
184
+ }
185
+
186
+ _showElement.call(this, this._introItems[this._currentStep]);
187
+ }
188
+
189
+ /**
190
+ * Go to previous step on intro
191
+ *
192
+ * @api private
193
+ * @method _nextStep
194
+ */
195
+ function _previousStep() {
196
+ if (this._currentStep === 0) {
197
+ return false;
198
+ }
199
+
200
+ if (typeof (this._introBeforeChangeCallback) !== 'undefined') {
201
+ this._introBeforeChangeCallback.call(this, this._targetElement);
202
+ }
203
+
204
+ _showElement.call(this, this._introItems[--this._currentStep]);
205
+ }
206
+
207
+ /**
208
+ * Exit from intro
209
+ *
210
+ * @api private
211
+ * @method _exitIntro
212
+ * @param {Object} targetElement
213
+ */
214
+ function _exitIntro(targetElement) {
215
+ //remove overlay layer from the page
216
+ var overlayLayer = targetElement.querySelector('.introjs-overlay');
217
+ //for fade-out animation
218
+ overlayLayer.style.opacity = 0;
219
+ setTimeout(function () {
220
+ if (overlayLayer.parentNode) {
221
+ overlayLayer.parentNode.removeChild(overlayLayer);
222
+ }
223
+ }, 500);
224
+ //remove all helper layers
225
+ var helperLayer = targetElement.querySelector('.introjs-helperLayer');
226
+ if (helperLayer) {
227
+ helperLayer.parentNode.removeChild(helperLayer);
228
+ }
229
+ //remove `introjs-showElement` class from the element
230
+ var showElement = document.querySelector('.introjs-showElement');
231
+ if (showElement) {
232
+ showElement.className = showElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, ''); // This is a manual trim.
233
+ }
234
+
235
+ //remove `introjs-fixParent` class from the elements
236
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
237
+ if (fixParents && fixParents.length > 0) {
238
+ for (var i = fixParents.length - 1; i >= 0; i--) {
239
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
240
+ };
241
+ }
242
+ //clean listeners
243
+ if (window.removeEventListener) {
244
+ window.removeEventListener('keydown', this._onKeyDown, true);
245
+ } else if (document.detachEvent) { //IE
246
+ document.detachEvent('onkeydown', this._onKeyDown);
247
+ }
248
+ //set the step to zero
249
+ this._currentStep = undefined;
250
+ }
251
+
252
+ /**
253
+ * Render tooltip box in the page
254
+ *
255
+ * @api private
256
+ * @method _placeTooltip
257
+ * @param {Object} targetElement
258
+ * @param {Object} tooltipLayer
259
+ * @param {Object} arrowLayer
260
+ */
261
+ function _placeTooltip(targetElement, tooltipLayer, arrowLayer) {
262
+ //reset the old style
263
+ tooltipLayer.style.top = null;
264
+ tooltipLayer.style.right = null;
265
+ tooltipLayer.style.bottom = null;
266
+ tooltipLayer.style.left = null;
267
+
268
+ //prevent error when `this._currentStep` is undefined
269
+ if(!this._introItems[this._currentStep]) return;
270
+
271
+ var currentTooltipPosition = this._introItems[this._currentStep].position;
272
+ switch (currentTooltipPosition) {
273
+ case 'top':
274
+ tooltipLayer.style.left = '15px';
275
+ tooltipLayer.style.top = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
276
+ arrowLayer.className = 'introjs-arrow bottom';
277
+ break;
278
+ case 'right':
279
+ tooltipLayer.style.left = (_getOffset(targetElement).width + 20) + 'px';
280
+ arrowLayer.className = 'introjs-arrow left';
281
+ break;
282
+ case 'left':
283
+ tooltipLayer.style.top = '15px';
284
+ tooltipLayer.style.right = (_getOffset(targetElement).width + 20) + 'px';
285
+ arrowLayer.className = 'introjs-arrow right';
286
+ break;
287
+ case 'bottom':
288
+ // Bottom going to follow the default behavior
289
+ default:
290
+ tooltipLayer.style.bottom = '-' + (_getOffset(tooltipLayer).height + 10) + 'px';
291
+ arrowLayer.className = 'introjs-arrow top';
292
+ break;
293
+ }
294
+ }
295
+
296
+ /**
297
+ * Update the position of the helper layer on the screen
298
+ *
299
+ * @api private
300
+ * @method _setHelperLayerPosition
301
+ * @param {Object} helperLayer
302
+ */
303
+ function _setHelperLayerPosition(helperLayer) {
304
+ if(helperLayer) {
305
+ //prevent error when `this._currentStep` in undefined
306
+ if(!this._introItems[this._currentStep]) return;
307
+
308
+ var elementPosition = _getOffset(this._introItems[this._currentStep].element);
309
+ //set new position to helper layer
310
+ helperLayer.setAttribute('style', 'width: ' + (elementPosition.width + 10) + 'px; ' +
311
+ 'height:' + (elementPosition.height + 10) + 'px; ' +
312
+ 'top:' + (elementPosition.top - 5) + 'px;' +
313
+ 'left: ' + (elementPosition.left - 5) + 'px;');
314
+ }
315
+ }
316
+
317
+ /**
318
+ * Show an element on the page
319
+ *
320
+ * @api private
321
+ * @method _showElement
322
+ * @param {Object} targetElement
323
+ */
324
+ function _showElement(targetElement) {
325
+
326
+ if (typeof (this._introChangeCallback) !== 'undefined') {
327
+ this._introChangeCallback.call(this, targetElement.element);
328
+ }
329
+
330
+ var self = this,
331
+ oldHelperLayer = document.querySelector('.introjs-helperLayer'),
332
+ elementPosition = _getOffset(targetElement.element);
333
+
334
+ if(oldHelperLayer != null) {
335
+ var oldHelperNumberLayer = oldHelperLayer.querySelector('.introjs-helperNumberLayer'),
336
+ oldtooltipLayer = oldHelperLayer.querySelector('.introjs-tooltiptext'),
337
+ oldArrowLayer = oldHelperLayer.querySelector('.introjs-arrow'),
338
+ oldtooltipContainer = oldHelperLayer.querySelector('.introjs-tooltip'),
339
+ skipTooltipButton = oldHelperLayer.querySelector('.introjs-skipbutton'),
340
+ prevTooltipButton = oldHelperLayer.querySelector('.introjs-prevbutton'),
341
+ nextTooltipButton = oldHelperLayer.querySelector('.introjs-nextbutton');
342
+
343
+ //hide the tooltip
344
+ oldtooltipContainer.style.opacity = 0;
345
+
346
+ //set new position to helper layer
347
+ _setHelperLayerPosition.call(self, oldHelperLayer);
348
+
349
+ //remove `introjs-fixParent` class from the elements
350
+ var fixParents = document.querySelectorAll('.introjs-fixParent');
351
+ if (fixParents && fixParents.length > 0) {
352
+ for (var i = fixParents.length - 1; i >= 0; i--) {
353
+ fixParents[i].className = fixParents[i].className.replace(/introjs-fixParent/g, '').replace(/^\s+|\s+$/g, '');
354
+ };
355
+ }
356
+
357
+ //remove old classes
358
+ var oldShowElement = document.querySelector('.introjs-showElement');
359
+ oldShowElement.className = oldShowElement.className.replace(/introjs-[a-zA-Z]+/g, '').replace(/^\s+|\s+$/g, '');
360
+ //we should wait until the CSS3 transition is competed (it's 0.3 sec) to prevent incorrect `height` and `width` calculation
361
+ if (self._lastShowElementTimer) {
362
+ clearTimeout(self._lastShowElementTimer);
363
+ }
364
+ self._lastShowElementTimer = setTimeout(function() {
365
+ //set current step to the label
366
+ if(oldHelperNumberLayer != null) {
367
+ oldHelperNumberLayer.innerHTML = targetElement.step;
368
+ }
369
+ //set current tooltip text
370
+ oldtooltipLayer.innerHTML = targetElement.intro;
371
+ //set the tooltip position
372
+ _placeTooltip.call(self, targetElement.element, oldtooltipContainer, oldArrowLayer);
373
+ //show the tooltip
374
+ oldtooltipContainer.style.opacity = 1;
375
+ }, 350);
376
+
377
+ } else {
378
+ var helperLayer = document.createElement('div'),
379
+ arrowLayer = document.createElement('div'),
380
+ tooltipLayer = document.createElement('div');
381
+
382
+ helperLayer.className = 'introjs-helperLayer';
383
+
384
+ //set new position to helper layer
385
+ _setHelperLayerPosition.call(self, helperLayer);
386
+
387
+ //add helper layer to target element
388
+ this._targetElement.appendChild(helperLayer);
389
+
390
+ arrowLayer.className = 'introjs-arrow';
391
+ tooltipLayer.className = 'introjs-tooltip';
392
+
393
+
394
+ tooltipLayer.innerHTML = '<div class="introjs-tooltiptext">' +
395
+ targetElement.intro +
396
+ '</div><div class="introjs-tooltipbuttons"></div>';
397
+
398
+ //add helper layer number
399
+ if (this._options.showStepNumbers) {
400
+ var helperNumberLayer = document.createElement('span');
401
+ helperNumberLayer.className = 'introjs-helperNumberLayer';
402
+ helperNumberLayer.innerHTML = targetElement.step;
403
+ helperLayer.appendChild(helperNumberLayer);
404
+ }
405
+ tooltipLayer.appendChild(arrowLayer);
406
+ helperLayer.appendChild(tooltipLayer);
407
+
408
+ //next button
409
+ var nextTooltipButton = document.createElement('a');
410
+
411
+ nextTooltipButton.onclick = function() {
412
+ if(self._introItems.length - 1 != self._currentStep) {
413
+ _nextStep.call(self);
414
+ }
415
+ };
416
+
417
+ nextTooltipButton.href = 'javascript:void(0);';
418
+ nextTooltipButton.innerHTML = this._options.nextLabel;
419
+
420
+ //previous button
421
+ var prevTooltipButton = document.createElement('a');
422
+
423
+ prevTooltipButton.onclick = function() {
424
+ if(self._currentStep != 0) {
425
+ _previousStep.call(self);
426
+ }
427
+ };
428
+
429
+ prevTooltipButton.href = 'javascript:void(0);';
430
+ prevTooltipButton.innerHTML = this._options.prevLabel;
431
+
432
+ //skip button
433
+ var skipTooltipButton = document.createElement('a');
434
+ skipTooltipButton.className = 'introjs-button introjs-skipbutton';
435
+ skipTooltipButton.href = 'javascript:void(0);';
436
+ skipTooltipButton.innerHTML = this._options.skipLabel;
437
+
438
+ skipTooltipButton.onclick = function() {
439
+ if (self._introItems.length - 1 == self._currentStep && typeof (self._introCompleteCallback) === 'function') {
440
+ self._introCompleteCallback.call(self);
441
+ }
442
+
443
+ if (self._introItems.length - 1 != self._currentStep && typeof (self._introExitCallback) === 'function') {
444
+ self._introExitCallback.call(self);
445
+ }
446
+
447
+ _exitIntro.call(self, self._targetElement);
448
+ };
449
+
450
+ var tooltipButtonsLayer = tooltipLayer.querySelector('.introjs-tooltipbuttons');
451
+ tooltipButtonsLayer.appendChild(skipTooltipButton);
452
+ tooltipButtonsLayer.appendChild(prevTooltipButton);
453
+ tooltipButtonsLayer.appendChild(nextTooltipButton);
454
+
455
+ //set proper position
456
+ _placeTooltip.call(self, targetElement.element, tooltipLayer, arrowLayer);
457
+ }
458
+
459
+ if (this._currentStep == 0) {
460
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton introjs-disabled';
461
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
462
+ skipTooltipButton.innerHTML = this._options.skipLabel;
463
+ } else if (this._introItems.length - 1 == this._currentStep) {
464
+ skipTooltipButton.innerHTML = this._options.doneLabel;
465
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
466
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton introjs-disabled';
467
+ } else {
468
+ prevTooltipButton.className = 'introjs-button introjs-prevbutton';
469
+ nextTooltipButton.className = 'introjs-button introjs-nextbutton';
470
+ skipTooltipButton.innerHTML = this._options.skipLabel;
471
+ }
472
+
473
+ //Set focus on "next" button, so that hitting Enter always moves you onto the next step
474
+ nextTooltipButton.focus();
475
+
476
+ //add target element position style
477
+ targetElement.element.className += ' introjs-showElement';
478
+
479
+ var currentElementPosition = _getPropValue(targetElement.element, 'position');
480
+ if (currentElementPosition !== 'absolute' &&
481
+ currentElementPosition !== 'relative') {
482
+ //change to new intro item
483
+ targetElement.element.className += ' introjs-relativePosition';
484
+ }
485
+
486
+ var parentElm = targetElement.element.parentNode;
487
+ while(parentElm != null) {
488
+ if(parentElm.tagName.toLowerCase() === 'body') break;
489
+
490
+ var zIndex = _getPropValue(parentElm, 'z-index');
491
+ if(/[0-9]+/.test(zIndex)) {
492
+ parentElm.className += ' introjs-fixParent';
493
+ }
494
+ parentElm = parentElm.parentNode;
495
+ }
496
+
497
+ if (!_elementInViewport(targetElement.element)) {
498
+ var rect = targetElement.element.getBoundingClientRect(),
499
+ top = rect.bottom - (rect.bottom - rect.top),
500
+ bottom = rect.bottom - _getWinSize().height;
501
+
502
+ // Scroll up
503
+ if (top < 0) {
504
+ window.scrollBy(0, top - 30); // 30px padding from edge to look nice
505
+
506
+ // Scroll down
507
+ } else {
508
+ window.scrollBy(0, bottom + 100); // 70px + 30px padding from edge to look nice
509
+ }
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Get an element CSS property on the page
515
+ * Thanks to JavaScript Kit: http://www.javascriptkit.com/dhtmltutors/dhtmlcascade4.shtml
516
+ *
517
+ * @api private
518
+ * @method _getPropValue
519
+ * @param {Object} element
520
+ * @param {String} propName
521
+ * @returns Element's property value
522
+ */
523
+ function _getPropValue (element, propName) {
524
+ var propValue = '';
525
+ if (element.currentStyle) { //IE
526
+ propValue = element.currentStyle[propName];
527
+ } else if (document.defaultView && document.defaultView.getComputedStyle) { //Others
528
+ propValue = document.defaultView.getComputedStyle(element, null).getPropertyValue(propName);
529
+ }
530
+
531
+ //Prevent exception in IE
532
+ if(propValue.toLowerCase) {
533
+ return propValue.toLowerCase();
534
+ } else {
535
+ return propValue;
536
+ }
537
+ }
538
+
539
+ /**
540
+ * Provides a cross-browser way to get the screen dimensions
541
+ * via: http://stackoverflow.com/questions/5864467/internet-explorer-innerheight
542
+ *
543
+ * @api private
544
+ * @method _getWinSize
545
+ * @returns {Object} width and height attributes
546
+ */
547
+ function _getWinSize() {
548
+ if (window.innerWidth != undefined) {
549
+ return { width: window.innerWidth, height: window.innerHeight };
550
+ } else {
551
+ var D = document.documentElement;
552
+ return { width: D.clientWidth, height: D.clientHeight };
553
+ }
554
+ }
555
+
556
+ /**
557
+ * Add overlay layer to the page
558
+ * http://stackoverflow.com/questions/123999/how-to-tell-if-a-dom-element-is-visible-in-the-current-viewport
559
+ *
560
+ * @api private
561
+ * @method _elementInViewport
562
+ * @param {Object} el
563
+ */
564
+ function _elementInViewport(el) {
565
+ var rect = el.getBoundingClientRect();
566
+
567
+ return (
568
+ rect.top >= 0 &&
569
+ rect.left >= 0 &&
570
+ (rect.bottom+80) <= window.innerHeight && // add 80 to get the text right
571
+ rect.right <= window.innerWidth
572
+ );
573
+ }
574
+
575
+ /**
576
+ * Add overlay layer to the page
577
+ *
578
+ * @api private
579
+ * @method _addOverlayLayer
580
+ * @param {Object} targetElm
581
+ */
582
+ function _addOverlayLayer(targetElm) {
583
+ var overlayLayer = document.createElement('div'),
584
+ styleText = '',
585
+ self = this;
586
+
587
+ //set css class name
588
+ overlayLayer.className = 'introjs-overlay';
589
+
590
+ //check if the target element is body, we should calculate the size of overlay layer in a better way
591
+ if (targetElm.tagName.toLowerCase() === 'body') {
592
+ styleText += 'top: 0;bottom: 0; left: 0;right: 0;position: fixed;';
593
+ overlayLayer.setAttribute('style', styleText);
594
+ } else {
595
+ //set overlay layer position
596
+ var elementPosition = _getOffset(targetElm);
597
+ if(elementPosition) {
598
+ styleText += 'width: ' + elementPosition.width + 'px; height:' + elementPosition.height + 'px; top:' + elementPosition.top + 'px;left: ' + elementPosition.left + 'px;';
599
+ overlayLayer.setAttribute('style', styleText);
600
+ }
601
+ }
602
+
603
+ targetElm.appendChild(overlayLayer);
604
+
605
+ overlayLayer.onclick = function() {
606
+ if(self._options.exitOnOverlayClick == true) {
607
+ _exitIntro.call(self, targetElm);
608
+ }
609
+ //check if any callback is defined
610
+ if (self._introExitCallback != undefined) {
611
+ self._introExitCallback.call(self);
612
+ }
613
+ };
614
+
615
+ setTimeout(function() {
616
+ styleText += 'opacity: .8;';
617
+ overlayLayer.setAttribute('style', styleText);
618
+ }, 10);
619
+ return true;
620
+ }
621
+
622
+ /**
623
+ * Get an element position on the page
624
+ * Thanks to `meouw`: http://stackoverflow.com/a/442474/375966
625
+ *
626
+ * @api private
627
+ * @method _getOffset
628
+ * @param {Object} element
629
+ * @returns Element's position info
630
+ */
631
+ function _getOffset(element) {
632
+ var elementPosition = {};
633
+
634
+ //set width
635
+ elementPosition.width = element.offsetWidth;
636
+
637
+ //set height
638
+ elementPosition.height = element.offsetHeight;
639
+
640
+ //calculate element top and left
641
+ var _x = 0;
642
+ var _y = 0;
643
+ while(element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) {
644
+ _x += element.offsetLeft;
645
+ _y += element.offsetTop;
646
+ element = element.offsetParent;
647
+ }
648
+ //set top
649
+ elementPosition.top = _y;
650
+ //set left
651
+ elementPosition.left = _x;
652
+
653
+ return elementPosition;
654
+ }
655
+
656
+ /**
657
+ * Overwrites obj1's values with obj2's and adds obj2's if non existent in obj1
658
+ * via: http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically
659
+ *
660
+ * @param obj1
661
+ * @param obj2
662
+ * @returns obj3 a new object based on obj1 and obj2
663
+ */
664
+ function _mergeOptions(obj1,obj2) {
665
+ var obj3 = {};
666
+ for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
667
+ for (var attrname in obj2) { obj3[attrname] = obj2[attrname]; }
668
+ return obj3;
669
+ }
670
+
671
+ var introJs = function (targetElm) {
672
+ if (typeof (targetElm) === 'object') {
673
+ //Ok, create a new instance
674
+ return new IntroJs(targetElm);
675
+
676
+ } else if (typeof (targetElm) === 'string') {
677
+ //select the target element with query selector
678
+ var targetElement = document.querySelector(targetElm);
679
+
680
+ if (targetElement) {
681
+ return new IntroJs(targetElement);
682
+ } else {
683
+ throw new Error('There is no element with given selector.');
684
+ }
685
+ } else {
686
+ return new IntroJs(document.body);
687
+ }
688
+ };
689
+
690
+ /**
691
+ * Current IntroJs version
692
+ *
693
+ * @property version
694
+ * @type String
695
+ */
696
+ introJs.version = VERSION;
697
+
698
+ //Prototype
699
+ introJs.fn = IntroJs.prototype = {
700
+ clone: function () {
701
+ return new IntroJs(this);
702
+ },
703
+ setOption: function(option, value) {
704
+ this._options[option] = value;
705
+ return this;
706
+ },
707
+ setOptions: function(options) {
708
+ this._options = _mergeOptions(this._options, options);
709
+ return this;
710
+ },
711
+ start: function () {
712
+ _introForElement.call(this, this._targetElement);
713
+ return this;
714
+ },
715
+ goToStep: function(step) {
716
+ _goToStep.call(this, step);
717
+ return this;
718
+ },
719
+ exit: function() {
720
+ _exitIntro.call(this, this._targetElement);
721
+ },
722
+ onbeforechange: function(providedCallback) {
723
+ if (typeof (providedCallback) === 'function') {
724
+ this._introBeforeChangeCallback = providedCallback;
725
+ } else {
726
+ throw new Error('Provided callback for onbeforechange was not a function');
727
+ }
728
+ return this;
729
+ },
730
+ onchange: function(providedCallback) {
731
+ if (typeof (providedCallback) === 'function') {
732
+ this._introChangeCallback = providedCallback;
733
+ } else {
734
+ throw new Error('Provided callback for onchange was not a function.');
735
+ }
736
+ return this;
737
+ },
738
+ oncomplete: function(providedCallback) {
739
+ if (typeof (providedCallback) === 'function') {
740
+ this._introCompleteCallback = providedCallback;
741
+ } else {
742
+ throw new Error('Provided callback for oncomplete was not a function.');
743
+ }
744
+ return this;
745
+ },
746
+ onexit: function(providedCallback) {
747
+ if (typeof (providedCallback) === 'function') {
748
+ this._introExitCallback = providedCallback;
749
+ } else {
750
+ throw new Error('Provided callback for onexit was not a function.');
751
+ }
752
+ return this;
753
+ }
754
+ };
755
+
756
+ exports.introJs = introJs;
757
+ return introJs;
758
+ }));
assets/js/admin/tour/tour.post-edit.js CHANGED
@@ -1,9 +1,9 @@
1
- // Post Edit Screen Tour
2
- jQuery("#titlewrap").attr({"data-step": "1", "data-intro": 'This is the adminstrative title of your landing. <br>Visitors will not be able to see this. To edit this, simply click on the text.'});
3
- jQuery(".nav-tab-wrapper").attr({"data-step": "2", "data-intro": 'This controls the A/B testing functionality of the page.<br>You can toggle back and forth between variations or click the add new variation to start an A/B test.'});
4
- jQuery(".new-save-lp-frontend").attr({"data-step": "3", "data-intro": 'This lauches the frontend editor that will allow you to see live previews of your landing page variations and edit the settings on the same screen!'});
5
- jQuery(".lp-notes").attr({"data-step": "4", "data-intro": 'Add notes to each of your A/B test variations to keep track of what you are testing.'});
6
- jQuery("#main-title-area").attr({"data-step": "5", "data-intro": 'This is the main headline area of your landing page. Make sure your headlines are catchy and have a clear value proposition'});
7
- jQuery("#content_InboundShortcodesButton_action").attr({"data-step": "6", "data-intro": '<p>This is the inbound shortcode tool. You can use it to <strong>build forms</strong> and do a number of other cool things!</p>'});
8
- jQuery("#lp_ab_display_stats_metabox").attr({"data-step": "7", "data-intro": '<p>This is the main stats box for your landing page variations. Here you will find all of your page views, conversions, and conversion rate numbers.</p><p>A/B Variation controls are also available here.</p><p><strong>Pause:</strong> Paused the current variation from a/b testing</p><p><strong>Edit:</strong> Jump to edit screen of selected variation</p><p><strong>Preview:</strong> Pop open preview window</p><p><strong>Clone:</strong> Clones exact copy of landing page in a new variation</p><p><strong>Delete:</strong> Deletes the variation from landing page</p>'});
9
  jQuery("#lp_metabox_select_template").attr({"data-step": "8", "data-intro": 'These are the main options that control the currently selected template.'});
1
+ // Post Edit Screen Tour
2
+ jQuery("#titlewrap").attr({"data-step": "1", "data-intro": 'This is the adminstrative title of your landing. <br>Visitors will not be able to see this. To edit this, simply click on the text.'});
3
+ jQuery(".nav-tab-wrapper").attr({"data-step": "2", "data-intro": 'This controls the A/B testing functionality of the page.<br>You can toggle back and forth between variations or click the add new variation to start an A/B test.'});
4
+ jQuery(".new-save-lp-frontend").attr({"data-step": "3", "data-intro": 'This lauches the frontend editor that will allow you to see live previews of your landing page variations and edit the settings on the same screen!'});
5
+ jQuery(".lp-notes").attr({"data-step": "4", "data-intro": 'Add notes to each of your A/B test variations to keep track of what you are testing.'});
6
+ jQuery("#main-title-area").attr({"data-step": "5", "data-intro": 'This is the main headline area of your landing page. Make sure your headlines are catchy and have a clear value proposition'});
7
+ jQuery("#content_InboundShortcodesButton_action").attr({"data-step": "6", "data-intro": '<p>This is the inbound shortcode tool. You can use it to <strong>build forms</strong> and do a number of other cool things!</p>'});
8
+ jQuery("#lp_ab_display_stats_metabox").attr({"data-step": "7", "data-intro": '<p>This is the main stats box for your landing page variations. Here you will find all of your page views, conversions, and conversion rate numbers.</p><p>A/B Variation controls are also available here.</p><p><strong>Pause:</strong> Paused the current variation from a/b testing</p><p><strong>Edit:</strong> Jump to edit screen of selected variation</p><p><strong>Preview:</strong> Pop open preview window</p><p><strong>Clone:</strong> Clones exact copy of landing page in a new variation</p><p><strong>Delete:</strong> Deletes the variation from landing page</p>'});
9
  jQuery("#lp_metabox_select_template").attr({"data-step": "8", "data-intro": 'These are the main options that control the currently selected template.'});
assets/js/admin/tour/tour.post-list.js CHANGED
@@ -1,8 +1,8 @@
1
- // Post Edit Screen Tour
2
- jQuery(".thumbnail-lander.column-thumbnail-lander:eq(0)").attr({"data-step": "1", "data-intro": 'This is a quick screenshot of your landing page.<br><br> Click on it to see the live version in a popup window here.<br><br> You can preview your a/b variations directly from this view.'});
3
- jQuery(".post-title.page-title.column-title:eq(0)").attr({"data-step": "2", "data-intro": '<p>This is the admin title of the landing page. This isn\'t visible to visitors</p><p>You can delete, clone, or clears all of the page stats from here.</p><p><strong>Trash:</strong> Deletes this landing page and places it in the trash</p><p><strong>Edit:</strong> Jump to edit screen of this landing page</p><p><strong>Preview:</strong> Pop open preview window</p><p><strong>Clone:</strong> Clones exact copy of landing page. This includes all current variations.</p><p><strong>Clear Stats:</strong> This will clear all stats for each variation. This cannot be undone.</p>'});
4
- jQuery(".stats.column-stats:eq(0)").attr({"data-step": "3", "data-intro": '<p>These are the current stats of your landing page variations.</p><p>If you <strong>hover over the letter</strong> you will be able to preview or jump directly to the edit screen of that particular variation.</p><p>You can also clear indiviudal variation stats from there.</p>'});
5
- jQuery("#impressions").attr({"data-step": "4", "data-intro": '<p>This column shows the total number of page views the landing page has. This include all current A/B testing variations.</p>'});
6
- jQuery("#actions").attr({"data-step": "5", "data-intro": '<p>This column shows the total number of conversions the landing page has. This include all current A/B testing variations.</p>'});
7
- jQuery("#cr").attr({"data-step": "6", "data-intro": '<p>This column shows the total aggregate conversion rate of the landing page. This include all current A/B testing variations.</p>'});
8
  jQuery(".add-new-h2").attr({"data-step": "7", "data-intro": '<p>Thats all folks! Go ahead and create a new landing page and get started!</p>'});
1
+ // Post Edit Screen Tour
2
+ jQuery(".thumbnail-lander.column-thumbnail-lander:eq(0)").attr({"data-step": "1", "data-intro": 'This is a quick screenshot of your landing page.<br><br> Click on it to see the live version in a popup window here.<br><br> You can preview your a/b variations directly from this view.'});
3
+ jQuery(".post-title.page-title.column-title:eq(0)").attr({"data-step": "2", "data-intro": '<p>This is the admin title of the landing page. This isn\'t visible to visitors</p><p>You can delete, clone, or clears all of the page stats from here.</p><p><strong>Trash:</strong> Deletes this landing page and places it in the trash</p><p><strong>Edit:</strong> Jump to edit screen of this landing page</p><p><strong>Preview:</strong> Pop open preview window</p><p><strong>Clone:</strong> Clones exact copy of landing page. This includes all current variations.</p><p><strong>Clear Stats:</strong> This will clear all stats for each variation. This cannot be undone.</p>'});
4
+ jQuery(".stats.column-stats:eq(0)").attr({"data-step": "3", "data-intro": '<p>These are the current stats of your landing page variations.</p><p>If you <strong>hover over the letter</strong> you will be able to preview or jump directly to the edit screen of that particular variation.</p><p>You can also clear indiviudal variation stats from there.</p>'});
5
+ jQuery("#impressions").attr({"data-step": "4", "data-intro": '<p>This column shows the total number of page views the landing page has. This include all current A/B testing variations.</p>'});
6
+ jQuery("#actions").attr({"data-step": "5", "data-intro": '<p>This column shows the total number of conversions the landing page has. This include all current A/B testing variations.</p>'});
7
+ jQuery("#cr").attr({"data-step": "6", "data-intro": '<p>This column shows the total aggregate conversion rate of the landing page. This include all current A/B testing variations.</p>'});
8
  jQuery(".add-new-h2").attr({"data-step": "7", "data-intro": '<p>Thats all folks! Go ahead and create a new landing page and get started!</p>'});
assets/js/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/js/jquery.bindfirst.js CHANGED
@@ -1,15 +1,15 @@
1
- jQuery.fn.bindFirst = function(name, fn) {
2
- // bind as you normally would
3
- // don't want to miss out on any jQuery magic
4
- this.on(name, fn);
5
-
6
- // Thanks to a comment by @Martin, adding support for
7
- // namespaced events too.
8
- this.each(function() {
9
- var handlers = jQuery._data(this, 'events')[name.split('.')[0]];
10
- // take out the handler we just inserted from the end
11
- var handler = handlers.pop();
12
- // move it at the beginning
13
- handlers.splice(0, 0, handler);
14
- });
15
  };
1
+ jQuery.fn.bindFirst = function(name, fn) {
2
+ // bind as you normally would
3
+ // don't want to miss out on any jQuery magic
4
+ this.on(name, fn);
5
+
6
+ // Thanks to a comment by @Martin, adding support for
7
+ // namespaced events too.
8
+ this.each(function() {
9
+ var handlers = jQuery._data(this, 'events')[name.split('.')[0]];
10
+ // take out the handler we just inserted from the end
11
+ var handler = handlers.pop();
12
+ // move it at the beginning
13
+ handlers.splice(0, 0, handler);
14
+ });
15
  };
assets/js/jquery.easing.min.js CHANGED
@@ -1,44 +1,44 @@
1
- /*
2
- * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
3
- *
4
- * Uses the built in easing capabilities added In jQuery 1.1
5
- * to offer multiple easing options
6
- *
7
- * TERMS OF USE - EASING EQUATIONS
8
- *
9
- * Open source under the BSD License.
10
- *
11
- * Copyright © 2001 Robert Penner
12
- * All rights reserved.
13
- *
14
- * TERMS OF USE - jQuery Easing
15
- *
16
- * Open source under the BSD License.
17
- *
18
- * Copyright © 2008 George McGinley Smith
19
- * All rights reserved.
20
- *
21
- * Redistribution and use in source and binary forms, with or without modification,
22
- * are permitted provided that the following conditions are met:
23
- *
24
- * Redistributions of source code must retain the above copyright notice, this list of
25
- * conditions and the following disclaimer.
26
- * Redistributions in binary form must reproduce the above copyright notice, this list
27
- * of conditions and the following disclaimer in the documentation and/or other materials
28
- * provided with the distribution.
29
- *
30
- * Neither the name of the author nor the names of contributors may be used to endorse
31
- * or promote products derived from this software without specific prior written permission.
32
- *
33
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
34
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
35
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
36
- * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38
- * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
39
- * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41
- * OF THE POSSIBILITY OF SUCH DAMAGE.
42
- *
43
- */
44
- jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}});
1
+ /*
2
+ * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
3
+ *
4
+ * Uses the built in easing capabilities added In jQuery 1.1
5
+ * to offer multiple easing options
6
+ *
7
+ * TERMS OF USE - EASING EQUATIONS
8
+ *
9
+ * Open source under the BSD License.
10
+ *
11
+ * Copyright © 2001 Robert Penner
12
+ * All rights reserved.
13
+ *
14
+ * TERMS OF USE - jQuery Easing
15
+ *
16
+ * Open source under the BSD License.
17
+ *
18
+ * Copyright © 2008 George McGinley Smith
19
+ * All rights reserved.
20
+ *
21
+ * Redistribution and use in source and binary forms, with or without modification,
22
+ * are permitted provided that the following conditions are met:
23
+ *
24
+ * Redistributions of source code must retain the above copyright notice, this list of
25
+ * conditions and the following disclaimer.
26
+ * Redistributions in binary form must reproduce the above copyright notice, this list
27
+ * of conditions and the following disclaimer in the documentation and/or other materials
28
+ * provided with the distribution.
29
+ *
30
+ * Neither the name of the author nor the names of contributors may be used to endorse
31
+ * or promote products derived from this software without specific prior written permission.
32
+ *
33
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
34
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
35
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
36
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
37
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
38
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
39
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
40
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
41
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
42
+ *
43
+ */
44
+ jQuery.easing.jswing=jQuery.easing.swing;jQuery.extend(jQuery.easing,{def:"easeOutQuad",swing:function(e,f,a,h,g){return jQuery.easing[jQuery.easing.def](e,f,a,h,g)},easeInQuad:function(e,f,a,h,g){return h*(f/=g)*f+a},easeOutQuad:function(e,f,a,h,g){return -h*(f/=g)*(f-2)+a},easeInOutQuad:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f+a}return -h/2*((--f)*(f-2)-1)+a},easeInCubic:function(e,f,a,h,g){return h*(f/=g)*f*f+a},easeOutCubic:function(e,f,a,h,g){return h*((f=f/g-1)*f*f+1)+a},easeInOutCubic:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f+a}return h/2*((f-=2)*f*f+2)+a},easeInQuart:function(e,f,a,h,g){return h*(f/=g)*f*f*f+a},easeOutQuart:function(e,f,a,h,g){return -h*((f=f/g-1)*f*f*f-1)+a},easeInOutQuart:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+a}return -h/2*((f-=2)*f*f*f-2)+a},easeInQuint:function(e,f,a,h,g){return h*(f/=g)*f*f*f*f+a},easeOutQuint:function(e,f,a,h,g){return h*((f=f/g-1)*f*f*f*f+1)+a},easeInOutQuint:function(e,f,a,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+a}return h/2*((f-=2)*f*f*f*f+2)+a},easeInSine:function(e,f,a,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+a},easeOutSine:function(e,f,a,h,g){return h*Math.sin(f/g*(Math.PI/2))+a},easeInOutSine:function(e,f,a,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+a},easeInExpo:function(e,f,a,h,g){return(f==0)?a:h*Math.pow(2,10*(f/g-1))+a},easeOutExpo:function(e,f,a,h,g){return(f==g)?a+h:h*(-Math.pow(2,-10*f/g)+1)+a},easeInOutExpo:function(e,f,a,h,g){if(f==0){return a}if(f==g){return a+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+a}return h/2*(-Math.pow(2,-10*--f)+2)+a},easeInCirc:function(e,f,a,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+a},easeOutCirc:function(e,f,a,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+a},easeInOutCirc:function(e,f,a,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+a}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+a},easeInElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return -(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e},easeOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k)==1){return e+l}if(!j){j=k*0.3}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}return g*Math.pow(2,-10*h)*Math.sin((h*k-i)*(2*Math.PI)/j)+l+e},easeInOutElastic:function(f,h,e,l,k){var i=1.70158;var j=0;var g=l;if(h==0){return e}if((h/=k/2)==2){return e+l}if(!j){j=k*(0.3*1.5)}if(g<Math.abs(l)){g=l;var i=j/4}else{var i=j/(2*Math.PI)*Math.asin(l/g)}if(h<1){return -0.5*(g*Math.pow(2,10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j))+e}return g*Math.pow(2,-10*(h-=1))*Math.sin((h*k-i)*(2*Math.PI)/j)*0.5+l+e},easeInBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+a},easeOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+a},easeInOutBack:function(e,f,a,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+a}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+a},easeInBounce:function(e,f,a,h,g){return h-jQuery.easing.easeOutBounce(e,g-f,0,h,g)+a},easeOutBounce:function(e,f,a,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+a}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+a}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+a}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+a}}}},easeInOutBounce:function(e,f,a,h,g){if(f<g/2){return jQuery.easing.easeInBounce(e,f*2,0,h,g)*0.5+a}return jQuery.easing.easeOutBounce(e,f*2-g,0,h,g)*0.5+h*0.5+a}});
assets/js/jquery.tablesorter.js CHANGED
@@ -1,1031 +1,1031 @@
1
- /*
2
- *
3
- * TableSorter 2.0 - Client-side table sorting with ease!
4
- * Version 2.0.5b
5
- * @requires jQuery v1.2.3
6
- *
7
- * Copyright (c) 2007 Christian Bach
8
- * Examples and docs at: http://tablesorter.com
9
- * Dual licensed under the MIT and GPL licenses:
10
- * http://www.opensource.org/licenses/mit-license.php
11
- * http://www.gnu.org/licenses/gpl.html
12
- *
13
- */
14
- /**
15
- *
16
- * @description Create a sortable table with multi-column sorting capabilitys
17
- *
18
- * @example $('table').tablesorter();
19
- * @desc Create a simple tablesorter interface.
20
- *
21
- * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
22
- * @desc Create a tablesorter interface and sort on the first and secound column column headers.
23
- *
24
- * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
25
- *
26
- * @desc Create a tablesorter interface and disableing the first and second column headers.
27
- *
28
- *
29
- * @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
30
- *
31
- * @desc Create a tablesorter interface and set a column parser for the first
32
- * and second column.
33
- *
34
- *
35
- * @param Object
36
- * settings An object literal containing key/value pairs to provide
37
- * optional settings.
38
- *
39
- *
40
- * @option String cssHeader (optional) A string of the class name to be appended
41
- * to sortable tr elements in the thead of the table. Default value:
42
- * "header"
43
- *
44
- * @option String cssAsc (optional) A string of the class name to be appended to
45
- * sortable tr elements in the thead on a ascending sort. Default value:
46
- * "headerSortUp"
47
- *
48
- * @option String cssDesc (optional) A string of the class name to be appended
49
- * to sortable tr elements in the thead on a descending sort. Default
50
- * value: "headerSortDown"
51
- *
52
- * @option String sortInitialOrder (optional) A string of the inital sorting
53
- * order can be asc or desc. Default value: "asc"
54
- *
55
- * @option String sortMultisortKey (optional) A string of the multi-column sort
56
- * key. Default value: "shiftKey"
57
- *
58
- * @option String textExtraction (optional) A string of the text-extraction
59
- * method to use. For complex html structures inside td cell set this
60
- * option to "complex", on large tables the complex option can be slow.
61
- * Default value: "simple"
62
- *
63
- * @option Object headers (optional) An array containing the forces sorting
64
- * rules. This option let's you specify a default sorting rule. Default
65
- * value: null
66
- *
67
- * @option Array sortList (optional) An array containing the forces sorting
68
- * rules. This option let's you specify a default sorting rule. Default
69
- * value: null
70
- *
71
- * @option Array sortForce (optional) An array containing forced sorting rules.
72
- * This option let's you specify a default sorting rule, which is
73
- * prepended to user-selected rules. Default value: null
74
- *
75
- * @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever
76
- * to use String.localeCampare method or not. Default set to true.
77
- *
78
- *
79
- * @option Array sortAppend (optional) An array containing forced sorting rules.
80
- * This option let's you specify a default sorting rule, which is
81
- * appended to user-selected rules. Default value: null
82
- *
83
- * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter
84
- * should apply fixed widths to the table columns. This is usefull when
85
- * using the pager companion plugin. This options requires the dimension
86
- * jquery plugin. Default value: false
87
- *
88
- * @option Boolean cancelSelection (optional) Boolean flag indicating if
89
- * tablesorter should cancel selection of the table headers text.
90
- * Default value: true
91
- *
92
- * @option Boolean debug (optional) Boolean flag indicating if tablesorter
93
- * should display debuging information usefull for development.
94
- *
95
- * @type jQuery
96
- *
97
- * @name tablesorter
98
- *
99
- * @cat Plugins/Tablesorter
100
- *
101
- * @author Christian Bach/christian.bach@polyester.se
102
- */
103
-
104
- (function ($) {
105
- $.extend({
106
- tablesorter: new
107
- function () {
108
-
109
- var parsers = [],
110
- widgets = [];
111
-
112
- this.defaults = {
113
- cssHeader: "header",
114
- cssAsc: "headerSortUp",
115
- cssDesc: "headerSortDown",
116
- cssChildRow: "expand-child",
117
- sortInitialOrder: "asc",
118
- sortMultiSortKey: "shiftKey",
119
- sortForce: null,
120
- sortAppend: null,
121
- sortLocaleCompare: true,
122
- textExtraction: "simple",
123
- parsers: {}, widgets: [],
124
- widgetZebra: {
125
- css: ["even", "odd"]
126
- }, headers: {}, widthFixed: false,
127
- cancelSelection: true,
128
- sortList: [],
129
- headerList: [],
130
- dateFormat: "us",
131
- decimal: '/\.|\,/g',
132
- onRenderHeader: null,
133
- selectorHeaders: 'thead th',
134
- debug: false
135
- };
136
-
137
- /* debuging utils */
138
-
139
- function benchmark(s, d) {
140
- log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
141
- }
142
-
143
- this.benchmark = benchmark;
144
-
145
- function log(s) {
146
- if (typeof console != "undefined" && typeof console.debug != "undefined") {
147
- console.log(s);
148
- } else {
149
- alert(s);
150
- }
151
- }
152
-
153
- /* parsers utils */
154
-
155
- function buildParserCache(table, $headers) {
156
-
157
- if (table.config.debug) {
158
- var parsersDebug = "";
159
- }
160
-
161
- if (table.tBodies.length == 0) return; // In the case of empty tables
162
- var rows = table.tBodies[0].rows;
163
-
164
- if (rows[0]) {
165
-
166
- var list = [],
167
- cells = rows[0].cells,
168
- l = cells.length;
169
-
170
- for (var i = 0; i < l; i++) {
171
-
172
- var p = false;
173
-
174
- if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) {
175
-
176
- p = getParserById($($headers[i]).metadata().sorter);
177
-
178
- } else if ((table.config.headers[i] && table.config.headers[i].sorter)) {
179
-
180
- p = getParserById(table.config.headers[i].sorter);
181
- }
182
- if (!p) {
183
-
184
- p = detectParserForColumn(table, rows, -1, i);
185
- }
186
-
187
- if (table.config.debug) {
188
- parsersDebug += "column:" + i + " parser:" + p.id + "\n";
189
- }
190
-
191
- list.push(p);
192
- }
193
- }
194
-
195
- if (table.config.debug) {
196
- log(parsersDebug);
197
- }
198
-
199
- return list;
200
- };
201
-
202
- function detectParserForColumn(table, rows, rowIndex, cellIndex) {
203
- var l = parsers.length,
204
- node = false,
205
- nodeValue = false,
206
- keepLooking = true;
207
- while (nodeValue == '' && keepLooking) {
208
- rowIndex++;
209
- if (rows[rowIndex]) {
210
- node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex);
211
- nodeValue = trimAndGetNodeText(table.config, node);
212
- if (table.config.debug) {
213
- log('Checking if value was empty on row:' + rowIndex);
214
- }
215
- } else {
216
- keepLooking = false;
217
- }
218
- }
219
- for (var i = 1; i < l; i++) {
220
- if (parsers[i].is(nodeValue, table, node)) {
221
- return parsers[i];
222
- }
223
- }
224
- // 0 is always the generic parser (text)
225
- return parsers[0];
226
- }
227
-
228
- function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
229
- return rows[rowIndex].cells[cellIndex];
230
- }
231
-
232
- function trimAndGetNodeText(config, node) {
233
- return $.trim(getElementText(config, node));
234
- }
235
-
236
- function getParserById(name) {
237
- var l = parsers.length;
238
- for (var i = 0; i < l; i++) {
239
- if (parsers[i].id.toLowerCase() == name.toLowerCase()) {
240
- return parsers[i];
241
- }
242
- }
243
- return false;
244
- }
245
-
246
- /* utils */
247
-
248
- function buildCache(table) {
249
-
250
- if (table.config.debug) {
251
- var cacheTime = new Date();
252
- }
253
-
254
- var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
255
- totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
256
- parsers = table.config.parsers,
257
- cache = {
258
- row: [],
259
- normalized: []
260
- };
261
-
262
- for (var i = 0; i < totalRows; ++i) {
263
-
264
- /** Add the table data to main data array */
265
- var c = $(table.tBodies[0].rows[i]),
266
- cols = [];
267
-
268
- // if this is a child row, add it to the last row's children and
269
- // continue to the next row
270
- if (c.hasClass(table.config.cssChildRow)) {
271
- cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
272
- // go to the next for loop
273
- continue;
274
- }
275
-
276
- cache.row.push(c);
277
-
278
- for (var j = 0; j < totalCells; ++j) {
279
- cols.push(parsers[j].format(getElementText(table.config, c[0].cells[j]), table, c[0].cells[j]));
280
- }
281
-
282
- cols.push(cache.normalized.length); // add position for rowCache
283
- cache.normalized.push(cols);
284
- cols = null;
285
- };
286
-
287
- if (table.config.debug) {
288
- benchmark("Building cache for " + totalRows + " rows:", cacheTime);
289
- }
290
-
291
- return cache;
292
- };
293
-
294
- function getElementText(config, node) {
295
-
296
- var text = "";
297
-
298
- if (!node) return "";
299
-
300
- if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false;
301
-
302
- if (config.textExtraction == "simple") {
303
- if (config.supportsTextContent) {
304
- text = node.textContent;
305
- } else {
306
- if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
307
- text = node.childNodes[0].innerHTML;
308
- } else {
309
- text = node.innerHTML;
310
- }
311
- }
312
- } else {
313
- if (typeof(config.textExtraction) == "function") {
314
- text = config.textExtraction(node);
315
- } else {
316
- text = $(node).text();
317
- }
318
- }
319
- return text;
320
- }
321
-
322
- function appendToTable(table, cache) {
323
-
324
- if (table.config.debug) {
325
- var appendTime = new Date()
326
- }
327
-
328
- var c = cache,
329
- r = c.row,
330
- n = c.normalized,
331
- totalRows = n.length,
332
- checkCell = (n[0].length - 1),
333
- tableBody = $(table.tBodies[0]),
334
- rows = [];
335
-
336
-
337
- for (var i = 0; i < totalRows; i++) {
338
- var pos = n[i][checkCell];
339
-
340
- rows.push(r[pos]);
341
-
342
- if (!table.config.appender) {
343
-
344
- //var o = ;
345
- var l = r[pos].length;
346
- for (var j = 0; j < l; j++) {
347
- tableBody[0].appendChild(r[pos][j]);
348
- }
349
-
350
- //
351
- }
352
- }
353
-
354
-
355
-
356
- if (table.config.appender) {
357
-
358
- table.config.appender(table, rows);
359
- }
360
-
361
- rows = null;
362
-
363
- if (table.config.debug) {
364
- benchmark("Rebuilt table:", appendTime);
365
- }
366
-
367
- // apply table widgets
368
- applyWidget(table);
369
-
370
- // trigger sortend
371
- setTimeout(function () {
372
- $(table).trigger("sortEnd");
373
- }, 0);
374
-
375
- };
376
-
377
- function buildHeaders(table) {
378
-
379
- if (table.config.debug) {
380
- var time = new Date();
381
- }
382
-
383
- var meta = ($.metadata) ? true : false;
384
-
385
- var header_index = computeTableHeaderCellIndexes(table);
386
-
387
- $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) {
388
-
389
- this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
390
- // this.column = index;
391
- this.order = formatSortingOrder(table.config.sortInitialOrder);
392
-
393
-
394
- this.count = this.order;
395
-
396
- if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true;
397
- if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index);
398
-
399
- if (!this.sortDisabled) {
400
- var $th = $(this).addClass(table.config.cssHeader);
401
- if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th);
402
- }
403
-
404
- // add cell to headerList
405
- table.config.headerList[index] = this;
406
- });
407
-
408
- if (table.config.debug) {
409
- benchmark("Built headers:", time);
410
- log($tableHeaders);
411
- }
412
-
413
- return $tableHeaders;
414
-
415
- };
416
-
417
- // from:
418
- // http://www.javascripttoolbox.com/lib/table/examples.php
419
- // http://www.javascripttoolbox.com/temp/table_cellindex.html
420
-
421
-
422
- function computeTableHeaderCellIndexes(t) {
423
- var matrix = [];
424
- var lookup = {};
425
- var thead = t.getElementsByTagName('THEAD')[0];
426
- var trs = thead.getElementsByTagName('TR');
427
-
428
- for (var i = 0; i < trs.length; i++) {
429
- var cells = trs[i].cells;
430
- for (var j = 0; j < cells.length; j++) {
431
- var c = cells[j];
432
-
433
- var rowIndex = c.parentNode.rowIndex;
434
- var cellId = rowIndex + "-" + c.cellIndex;
435
- var rowSpan = c.rowSpan || 1;
436
- var colSpan = c.colSpan || 1
437
- var firstAvailCol;
438
- if (typeof(matrix[rowIndex]) == "undefined") {
439
- matrix[rowIndex] = [];
440
- }
441
- // Find first available column in the first row
442
- for (var k = 0; k < matrix[rowIndex].length + 1; k++) {
443
- if (typeof(matrix[rowIndex][k]) == "undefined") {
444
- firstAvailCol = k;
445
- break;
446
- }
447
- }
448
- lookup[cellId] = firstAvailCol;
449
- for (var k = rowIndex; k < rowIndex + rowSpan; k++) {
450
- if (typeof(matrix[k]) == "undefined") {
451
- matrix[k] = [];
452
- }
453
- var matrixrow = matrix[k];
454
- for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
455
- matrixrow[l] = "x";
456
- }
457
- }
458
- }
459
- }
460
- return lookup;
461
- }
462
-
463
- function checkCellColSpan(table, rows, row) {
464
- var arr = [],
465
- r = table.tHead.rows,
466
- c = r[row].cells;
467
-
468
- for (var i = 0; i < c.length; i++) {
469
- var cell = c[i];
470
-
471
- if (cell.colSpan > 1) {
472
- arr = arr.concat(checkCellColSpan(table, headerArr, row++));
473
- } else {
474
- if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) {
475
- arr.push(cell);
476
- }
477
- // headerArr[row] = (i+row);
478
- }
479
- }
480
- return arr;
481
- };
482
-
483
- function checkHeaderMetadata(cell) {
484
- if (($.metadata) && ($(cell).metadata().sorter === false)) {
485
- return true;
486
- };
487
- return false;
488
- }
489
-
490
- function checkHeaderOptions(table, i) {
491
- if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) {
492
- return true;
493
- };
494
- return false;
495
- }
496
-
497
- function checkHeaderOptionsSortingLocked(table, i) {
498
- if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder;
499
- return false;
500
- }
501
-
502
- function applyWidget(table) {
503
- var c = table.config.widgets;
504
- var l = c.length;
505
- for (var i = 0; i < l; i++) {
506
-
507
- getWidgetById(c[i]).format(table);
508
- }
509
-
510
- }
511
-
512
- function getWidgetById(name) {
513
- var l = widgets.length;
514
- for (var i = 0; i < l; i++) {
515
- if (widgets[i].id.toLowerCase() == name.toLowerCase()) {
516
- return widgets[i];
517
- }
518
- }
519
- };
520
-
521
- function formatSortingOrder(v) {
522
- if (typeof(v) != "Number") {
523
- return (v.toLowerCase() == "desc") ? 1 : 0;
524
- } else {
525
- return (v == 1) ? 1 : 0;
526
- }
527
- }
528
-
529
- function isValueInArray(v, a) {
530
- var l = a.length;
531
- for (var i = 0; i < l; i++) {
532
- if (a[i][0] == v) {
533
- return true;
534
- }
535
- }
536
- return false;
537
- }
538
-
539
- function setHeadersCss(table, $headers, list, css) {
540
- // remove all header information
541
- $headers.removeClass(css[0]).removeClass(css[1]);
542
-
543
- var h = [];
544
- $headers.each(function (offset) {
545
- if (!this.sortDisabled) {
546
- h[this.column] = $(this);
547
- }
548
- });
549
-
550
- var l = list.length;
551
- for (var i = 0; i < l; i++) {
552
- h[list[i][0]].addClass(css[list[i][1]]);
553
- }
554
- }
555
-
556
- function fixColumnWidth(table, $headers) {
557
- var c = table.config;
558
- if (c.widthFixed) {
559
- var colgroup = $('<colgroup>');
560
- $("tr:first td", table.tBodies[0]).each(function () {
561
- colgroup.append($('<col>').css('width', $(this).width()));
562
- });
563
- $(table).prepend(colgroup);
564
- };
565
- }
566
-
567
- function updateHeaderSortCount(table, sortList) {
568
- var c = table.config,
569
- l = sortList.length;
570
- for (var i = 0; i < l; i++) {
571
- var s = sortList[i],
572
- o = c.headerList[s[0]];
573
- o.count = s[1];
574
- o.count++;
575
- }
576
- }
577
-
578
- /* sorting methods */
579
-
580
- function multisort(table, sortList, cache) {
581
-
582
- if (table.config.debug) {
583
- var sortTime = new Date();
584
- }
585
-
586
- var dynamicExp = "var sortWrapper = function(a,b) {",
587
- l = sortList.length;
588
-
589
- // TODO: inline functions.
590
- for (var i = 0; i < l; i++) {
591
-
592
- var c = sortList[i][0];
593
- var order = sortList[i][1];
594
- // var s = (getCachedSortType(table.config.parsers,c) == "text") ?
595
- // ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ?
596
- // "sortNumeric" : "sortNumericDesc");
597
- // var s = (table.config.parsers[c].type == "text") ? ((order == 0)
598
- // ? makeSortText(c) : makeSortTextDesc(c)) : ((order == 0) ?
599
- // makeSortNumeric(c) : makeSortNumericDesc(c));
600
- var s = (table.config.parsers[c].type == "text") ? ((order == 0) ? makeSortFunction("text", "asc", c) : makeSortFunction("text", "desc", c)) : ((order == 0) ? makeSortFunction("numeric", "asc", c) : makeSortFunction("numeric", "desc", c));
601
- var e = "e" + i;
602
-
603
- dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c
604
- // + "]); ";
605
- dynamicExp += "if(" + e + ") { return " + e + "; } ";
606
- dynamicExp += "else { ";
607
-
608
- }
609
-
610
- // if value is the same keep orignal order
611
- var orgOrderCol = cache.normalized[0].length - 1;
612
- dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
613
-
614
- for (var i = 0; i < l; i++) {
615
- dynamicExp += "}; ";
616
- }
617
-
618
- dynamicExp += "return 0; ";
619
- dynamicExp += "}; ";
620
-
621
- if (table.config.debug) {
622
- benchmark("Evaling expression:" + dynamicExp, new Date());
623
- }
624
-
625
- eval(dynamicExp);
626
-
627
- cache.normalized.sort(sortWrapper);
628
-
629
- if (table.config.debug) {
630
- benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime);
631
- }
632
-
633
- return cache;
634
- };
635
-
636
- function makeSortFunction(type, direction, index) {
637
- var a = "a[" + index + "]",
638
- b = "b[" + index + "]";
639
- if (type == 'text' && direction == 'asc') {
640
- return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
641
- } else if (type == 'text' && direction == 'desc') {
642
- return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
643
- } else if (type == 'numeric' && direction == 'asc') {
644
- return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
645
- } else if (type == 'numeric' && direction == 'desc') {
646
- return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
647
- }
648
- };
649
-
650
- function makeSortText(i) {
651
- return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
652
- };
653
-
654
- function makeSortTextDesc(i) {
655
- return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
656
- };
657
-
658
- function makeSortNumeric(i) {
659
- return "a[" + i + "]-b[" + i + "];";
660
- };
661
-
662
- function makeSortNumericDesc(i) {
663
- return "b[" + i + "]-a[" + i + "];";
664
- };
665
-
666
- function sortText(a, b) {
667
- if (table.config.sortLocaleCompare) return a.localeCompare(b);
668
- return ((a < b) ? -1 : ((a > b) ? 1 : 0));
669
- };
670
-
671
- function sortTextDesc(a, b) {
672
- if (table.config.sortLocaleCompare) return b.localeCompare(a);
673
- return ((b < a) ? -1 : ((b > a) ? 1 : 0));
674
- };
675
-
676
- function sortNumeric(a, b) {
677
- return a - b;
678
- };
679
-
680
- function sortNumericDesc(a, b) {
681
- return b - a;
682
- };
683
-
684
- function getCachedSortType(parsers, i) {
685
- return parsers[i].type;
686
- }; /* public methods */
687
- this.construct = function (settings) {
688
- return this.each(function () {
689
- // if no thead or tbody quit.
690
- if (!this.tHead || !this.tBodies) return;
691
- // declare
692
- var $this, $document, $headers, cache, config, shiftDown = 0,
693
- sortOrder;
694
- // new blank config object
695
- this.config = {};
696
- // merge and extend.
697
- config = $.extend(this.config, $.tablesorter.defaults, settings);
698
- // store common expression for speed
699
- $this = $(this);
700
- // save the settings where they read
701
- $.data(this, "tablesorter", config);
702
- // build headers
703
- $headers = buildHeaders(this);
704
- // try to auto detect column type, and store in tables config
705
- this.config.parsers = buildParserCache(this, $headers);
706
- // build the cache for the tbody cells
707
- cache = buildCache(this);
708
- // get the css class names, could be done else where.
709
- var sortCSS = [config.cssDesc, config.cssAsc];
710
- // fixate columns if the users supplies the fixedWidth option
711
- fixColumnWidth(this);
712
- // apply event handling to headers
713
- // this is to big, perhaps break it out?
714
- $headers.click(
715
-
716
- function (e) {
717
- var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
718
- if (!this.sortDisabled && totalRows > 0) {
719
- // Only call sortStart if sorting is
720
- // enabled.
721
- $this.trigger("sortStart");
722
- // store exp, for speed
723
- var $cell = $(this);
724
- // get current column index
725
- var i = this.column;
726
- // get current column sort order
727
- this.order = this.count++ % 2;
728
- // always sort on the locked order.
729
- if(this.lockedOrder) this.order = this.lockedOrder;
730
-
731
- // user only whants to sort on one
732
- // column
733
- if (!e[config.sortMultiSortKey]) {
734
- // flush the sort list
735
- config.sortList = [];
736
- if (config.sortForce != null) {
737
- var a = config.sortForce;
738
- for (var j = 0; j < a.length; j++) {
739
- if (a[j][0] != i) {
740
- config.sortList.push(a[j]);
741
- }
742
- }
743
- }
744
- // add column to sort list
745
- config.sortList.push([i, this.order]);
746
- // multi column sorting
747
- } else {
748
- // the user has clicked on an all
749
- // ready sortet column.
750
- if (isValueInArray(i, config.sortList)) {
751
- // revers the sorting direction
752
- // for all tables.
753
- for (var j = 0; j < config.sortList.length; j++) {
754
- var s = config.sortList[j],
755
- o = config.headerList[s[0]];
756
- if (s[0] == i) {
757
- o.count = s[1];
758
- o.count++;
759
- s[1] = o.count % 2;
760
- }
761
- }
762
- } else {
763
- // add column to sort list array
764
- config.sortList.push([i, this.order]);
765
- }
766
- };
767
- setTimeout(function () {
768
- // set css for headers
769
- setHeadersCss($this[0], $headers, config.sortList, sortCSS);
770
- appendToTable(
771
- $this[0], multisort(
772
- $this[0], config.sortList, cache)
773
- );
774
- }, 1);
775
- // stop normal event by returning false
776
- return false;
777
- }
778
- // cancel selection
779
- }).mousedown(function () {
780
- if (config.cancelSelection) {
781
- this.onselectstart = function () {
782
- return false
783
- };
784
- return false;
785
- }
786
- });
787
- // apply easy methods that trigger binded events
788
- $this.bind("update", function () {
789
- var me = this;
790
- setTimeout(function () {
791
- // rebuild parsers.
792
- me.config.parsers = buildParserCache(
793
- me, $headers);
794
- // rebuild the cache map
795
- cache = buildCache(me);
796
- }, 1);
797
- }).bind("updateCell", function (e, cell) {
798
- var config = this.config;
799
- // get position from the dom.
800
- var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
801
- // update cache
802
- cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
803
- getElementText(config, cell), cell);
804
- }).bind("sorton", function (e, list) {
805
- $(this).trigger("sortStart");
806
- config.sortList = list;
807
- // update and store the sortlist
808
- var sortList = config.sortList;
809
- // update header count index
810
- updateHeaderSortCount(this, sortList);
811
- // set css for headers
812
- setHeadersCss(this, $headers, sortList, sortCSS);
813
- // sort the table and append it to the dom
814
- appendToTable(this, multisort(this, sortList, cache));
815
- }).bind("appendCache", function () {
816
- appendToTable(this, cache);
817
- }).bind("applyWidgetId", function (e, id) {
818
- getWidgetById(id).format(this);
819
- }).bind("applyWidgets", function () {
820
- // apply widgets
821
- applyWidget(this);
822
- });
823
- if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
824
- config.sortList = $(this).metadata().sortlist;
825
- }
826
- // if user has supplied a sort list to constructor.
827
- if (config.sortList.length > 0) {
828
- $this.trigger("sorton", [config.sortList]);
829
- }
830
- // apply widgets
831
- applyWidget(this);
832
- });
833
- };
834
- this.addParser = function (parser) {
835
- var l = parsers.length,
836
- a = true;
837
- for (var i = 0; i < l; i++) {
838
- if (parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
839
- a = false;
840
- }
841
- }
842
- if (a) {
843
- parsers.push(parser);
844
- };
845
- };
846
- this.addWidget = function (widget) {
847
- widgets.push(widget);
848
- };
849
- this.formatFloat = function (s) {
850
- var i = parseFloat(s);
851
- return (isNaN(i)) ? 0 : i;
852
- };
853
- this.formatInt = function (s) {
854
- var i = parseInt(s);
855
- return (isNaN(i)) ? 0 : i;
856
- };
857
- this.isDigit = function (s, config) {
858
- // replace all an wanted chars and match.
859
- return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, '')));
860
- };
861
- this.clearTableBody = function (table) {
862
- if ($.browser.msie) {
863
- function empty() {
864
- while (this.firstChild)
865
- this.removeChild(this.firstChild);
866
- }
867
- empty.apply(table.tBodies[0]);
868
- } else {
869
- table.tBodies[0].innerHTML = "";
870
- }
871
- };
872
- }
873
- });
874
-
875
- // extend plugin scope
876
- $.fn.extend({
877
- tablesorter: $.tablesorter.construct
878
- });
879
-
880
- // make shortcut
881
- var ts = $.tablesorter;
882
-
883
- // add default parsers
884
- ts.addParser({
885
- id: "text",
886
- is: function (s) {
887
- return true;
888
- }, format: function (s) {
889
- return $.trim(s.toLocaleLowerCase());
890
- }, type: "text"
891
- });
892
-
893
- ts.addParser({
894
- id: "digit",
895
- is: function (s, table) {
896
- var c = table.config;
897
- return $.tablesorter.isDigit(s, c);
898
- }, format: function (s) {
899
- return $.tablesorter.formatFloat(s);
900
- }, type: "numeric"
901
- });
902
-
903
- ts.addParser({
904
- id: "currency",
905
- is: function (s) {
906
- return /^[£$€?.]/.test(s);
907
- }, format: function (s) {
908
- return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), ""));
909
- }, type: "numeric"
910
- });
911
-
912
- ts.addParser({
913
- id: "ipAddress",
914
- is: function (s) {
915
- return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
916
- }, format: function (s) {
917
- var a = s.split("."),
918
- r = "",
919
- l = a.length;
920
- for (var i = 0; i < l; i++) {
921
- var item = a[i];
922
- if (item.length == 2) {
923
- r += "0" + item;
924
- } else {
925
- r += item;
926
- }
927
- }
928
- return $.tablesorter.formatFloat(r);
929
- }, type: "numeric"
930
- });
931
-
932
- ts.addParser({
933
- id: "url",
934
- is: function (s) {
935
- return /^(https?|ftp|file):\/\/$/.test(s);
936
- }, format: function (s) {
937
- return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), ''));
938
- }, type: "text"
939
- });
940
-
941
- ts.addParser({
942
- id: "isoDate",
943
- is: function (s) {
944
- return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
945
- }, format: function (s) {
946
- return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
947
- new RegExp(/-/g), "/")).getTime() : "0");
948
- }, type: "numeric"
949
- });
950
-
951
- ts.addParser({
952
- id: "percent",
953
- is: function (s) {
954
- return /\%$/.test($.trim(s));
955
- }, format: function (s) {
956
- return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), ""));
957
- }, type: "numeric"
958
- });
959
-
960
- ts.addParser({
961
- id: "usLongDate",
962
- is: function (s) {
963
- return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
964
- }, format: function (s) {
965
- return $.tablesorter.formatFloat(new Date(s).getTime());
966
- }, type: "numeric"
967
- });
968
-
969
- ts.addParser({
970
- id: "shortDate",
971
- is: function (s) {
972
- return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
973
- }, format: function (s, table) {
974
- var c = table.config;
975
- s = s.replace(/\-/g, "/");
976
- if (c.dateFormat == "us") {
977
- // reformat the string in ISO format
978
- s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
979
- } else if (c.dateFormat == "uk") {
980
- // reformat the string in ISO format
981
- s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
982
- } else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
983
- s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
984
- }
985
- return $.tablesorter.formatFloat(new Date(s).getTime());
986
- }, type: "numeric"
987
- });
988
- ts.addParser({
989
- id: "time",
990
- is: function (s) {
991
- return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
992
- }, format: function (s) {
993
- return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
994
- }, type: "numeric"
995
- });
996
- ts.addParser({
997
- id: "metadata",
998
- is: function (s) {
999
- return false;
1000
- }, format: function (s, table, cell) {
1001
- var c = table.config,
1002
- p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
1003
- return $(cell).metadata()[p];
1004
- }, type: "numeric"
1005
- });
1006
- // add default widgets
1007
- ts.addWidget({
1008
- id: "zebra",
1009
- format: function (table) {
1010
- if (table.config.debug) {
1011
- var time = new Date();
1012
- }
1013
- var $tr, row = -1,
1014
- odd;
1015
- // loop through the visible rows
1016
- $("tr:visible", table.tBodies[0]).each(function (i) {
1017
- $tr = $(this);
1018
- // style children rows the same way the parent
1019
- // row was styled
1020
- if (!$tr.hasClass(table.config.cssChildRow)) row++;
1021
- odd = (row % 2 == 0);
1022
- $tr.removeClass(
1023
- table.config.widgetZebra.css[odd ? 0 : 1]).addClass(
1024
- table.config.widgetZebra.css[odd ? 1 : 0])
1025
- });
1026
- if (table.config.debug) {
1027
- $.tablesorter.benchmark("Applying Zebra widget", time);
1028
- }
1029
- }
1030
- });
1031
  })(jQuery);
1
+ /*
2
+ *
3
+ * TableSorter 2.0 - Client-side table sorting with ease!
4
+ * Version 2.0.5b
5
+ * @requires jQuery v1.2.3
6
+ *
7
+ * Copyright (c) 2007 Christian Bach
8
+ * Examples and docs at: http://tablesorter.com
9
+ * Dual licensed under the MIT and GPL licenses:
10
+ * http://www.opensource.org/licenses/mit-license.php
11
+ * http://www.gnu.org/licenses/gpl.html
12
+ *
13
+ */
14
+ /**
15
+ *
16
+ * @description Create a sortable table with multi-column sorting capabilitys
17
+ *
18
+ * @example $('table').tablesorter();
19
+ * @desc Create a simple tablesorter interface.
20
+ *
21
+ * @example $('table').tablesorter({ sortList:[[0,0],[1,0]] });
22
+ * @desc Create a tablesorter interface and sort on the first and secound column column headers.
23
+ *
24
+ * @example $('table').tablesorter({ headers: { 0: { sorter: false}, 1: {sorter: false} } });
25
+ *
26
+ * @desc Create a tablesorter interface and disableing the first and second column headers.
27
+ *
28
+ *
29
+ * @example $('table').tablesorter({ headers: { 0: {sorter:"integer"}, 1: {sorter:"currency"} } });
30
+ *
31
+ * @desc Create a tablesorter interface and set a column parser for the first
32
+ * and second column.
33
+ *
34
+ *
35
+ * @param Object
36
+ * settings An object literal containing key/value pairs to provide
37
+ * optional settings.
38
+ *
39
+ *
40
+ * @option String cssHeader (optional) A string of the class name to be appended
41
+ * to sortable tr elements in the thead of the table. Default value:
42
+ * "header"
43
+ *
44
+ * @option String cssAsc (optional) A string of the class name to be appended to
45
+ * sortable tr elements in the thead on a ascending sort. Default value:
46
+ * "headerSortUp"
47
+ *
48
+ * @option String cssDesc (optional) A string of the class name to be appended
49
+ * to sortable tr elements in the thead on a descending sort. Default
50
+ * value: "headerSortDown"
51
+ *
52
+ * @option String sortInitialOrder (optional) A string of the inital sorting
53
+ * order can be asc or desc. Default value: "asc"
54
+ *
55
+ * @option String sortMultisortKey (optional) A string of the multi-column sort
56
+ * key. Default value: "shiftKey"
57
+ *
58
+ * @option String textExtraction (optional) A string of the text-extraction
59
+ * method to use. For complex html structures inside td cell set this
60
+ * option to "complex", on large tables the complex option can be slow.
61
+ * Default value: "simple"
62
+ *
63
+ * @option Object headers (optional) An array containing the forces sorting
64
+ * rules. This option let's you specify a default sorting rule. Default
65
+ * value: null
66
+ *
67
+ * @option Array sortList (optional) An array containing the forces sorting
68
+ * rules. This option let's you specify a default sorting rule. Default
69
+ * value: null
70
+ *
71
+ * @option Array sortForce (optional) An array containing forced sorting rules.
72
+ * This option let's you specify a default sorting rule, which is
73
+ * prepended to user-selected rules. Default value: null
74
+ *
75
+ * @option Boolean sortLocaleCompare (optional) Boolean flag indicating whatever
76
+ * to use String.localeCampare method or not. Default set to true.
77
+ *
78
+ *
79
+ * @option Array sortAppend (optional) An array containing forced sorting rules.
80
+ * This option let's you specify a default sorting rule, which is
81
+ * appended to user-selected rules. Default value: null
82
+ *
83
+ * @option Boolean widthFixed (optional) Boolean flag indicating if tablesorter
84
+ * should apply fixed widths to the table columns. This is usefull when
85
+ * using the pager companion plugin. This options requires the dimension
86
+ * jquery plugin. Default value: false
87
+ *
88
+ * @option Boolean cancelSelection (optional) Boolean flag indicating if
89
+ * tablesorter should cancel selection of the table headers text.
90
+ * Default value: true
91
+ *
92
+ * @option Boolean debug (optional) Boolean flag indicating if tablesorter
93
+ * should display debuging information usefull for development.
94
+ *
95
+ * @type jQuery
96
+ *
97
+ * @name tablesorter
98
+ *
99
+ * @cat Plugins/Tablesorter
100
+ *
101
+ * @author Christian Bach/christian.bach@polyester.se
102
+ */
103
+
104
+ (function ($) {
105
+ $.extend({
106
+ tablesorter: new
107
+ function () {
108
+
109
+ var parsers = [],
110
+ widgets = [];
111
+
112
+ this.defaults = {
113
+ cssHeader: "header",
114
+ cssAsc: "headerSortUp",
115
+ cssDesc: "headerSortDown",
116
+ cssChildRow: "expand-child",
117
+ sortInitialOrder: "asc",
118
+ sortMultiSortKey: "shiftKey",
119
+ sortForce: null,
120
+ sortAppend: null,
121
+ sortLocaleCompare: true,
122
+ textExtraction: "simple",
123
+ parsers: {}, widgets: [],
124
+ widgetZebra: {
125
+ css: ["even", "odd"]
126
+ }, headers: {}, widthFixed: false,
127
+ cancelSelection: true,
128
+ sortList: [],
129
+ headerList: [],
130
+ dateFormat: "us",
131
+ decimal: '/\.|\,/g',
132
+ onRenderHeader: null,
133
+ selectorHeaders: 'thead th',
134
+ debug: false
135
+ };
136
+
137
+ /* debuging utils */
138
+
139
+ function benchmark(s, d) {
140
+ log(s + "," + (new Date().getTime() - d.getTime()) + "ms");
141
+ }
142
+
143
+ this.benchmark = benchmark;
144
+
145
+ function log(s) {
146
+ if (typeof console != "undefined" && typeof console.debug != "undefined") {
147
+ console.log(s);
148
+ } else {
149
+ alert(s);
150
+ }
151
+ }
152
+
153
+ /* parsers utils */
154
+
155
+ function buildParserCache(table, $headers) {
156
+
157
+ if (table.config.debug) {
158
+ var parsersDebug = "";
159
+ }
160
+
161
+ if (table.tBodies.length == 0) return; // In the case of empty tables
162
+ var rows = table.tBodies[0].rows;
163
+
164
+ if (rows[0]) {
165
+
166
+ var list = [],
167
+ cells = rows[0].cells,
168
+ l = cells.length;
169
+
170
+ for (var i = 0; i < l; i++) {
171
+
172
+ var p = false;
173
+
174
+ if ($.metadata && ($($headers[i]).metadata() && $($headers[i]).metadata().sorter)) {
175
+
176
+ p = getParserById($($headers[i]).metadata().sorter);
177
+
178
+ } else if ((table.config.headers[i] && table.config.headers[i].sorter)) {
179
+
180
+ p = getParserById(table.config.headers[i].sorter);
181
+ }
182
+ if (!p) {
183
+
184
+ p = detectParserForColumn(table, rows, -1, i);
185
+ }
186
+
187
+ if (table.config.debug) {
188
+ parsersDebug += "column:" + i + " parser:" + p.id + "\n";
189
+ }
190
+
191
+ list.push(p);
192
+ }
193
+ }
194
+
195
+ if (table.config.debug) {
196
+ log(parsersDebug);
197
+ }
198
+
199
+ return list;
200
+ };
201
+
202
+ function detectParserForColumn(table, rows, rowIndex, cellIndex) {
203
+ var l = parsers.length,
204
+ node = false,
205
+ nodeValue = false,
206
+ keepLooking = true;
207
+ while (nodeValue == '' && keepLooking) {
208
+ rowIndex++;
209
+ if (rows[rowIndex]) {
210
+ node = getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex);
211
+ nodeValue = trimAndGetNodeText(table.config, node);
212
+ if (table.config.debug) {
213
+ log('Checking if value was empty on row:' + rowIndex);
214
+ }
215
+ } else {
216
+ keepLooking = false;
217
+ }
218
+ }
219
+ for (var i = 1; i < l; i++) {
220
+ if (parsers[i].is(nodeValue, table, node)) {
221
+ return parsers[i];
222
+ }
223
+ }
224
+ // 0 is always the generic parser (text)
225
+ return parsers[0];
226
+ }
227
+
228
+ function getNodeFromRowAndCellIndex(rows, rowIndex, cellIndex) {
229
+ return rows[rowIndex].cells[cellIndex];
230
+ }
231
+
232
+ function trimAndGetNodeText(config, node) {
233
+ return $.trim(getElementText(config, node));
234
+ }
235
+
236
+ function getParserById(name) {
237
+ var l = parsers.length;
238
+ for (var i = 0; i < l; i++) {
239
+ if (parsers[i].id.toLowerCase() == name.toLowerCase()) {
240
+ return parsers[i];
241
+ }
242
+ }
243
+ return false;
244
+ }
245
+
246
+ /* utils */
247
+
248
+ function buildCache(table) {
249
+
250
+ if (table.config.debug) {
251
+ var cacheTime = new Date();
252
+ }
253
+
254
+ var totalRows = (table.tBodies[0] && table.tBodies[0].rows.length) || 0,
255
+ totalCells = (table.tBodies[0].rows[0] && table.tBodies[0].rows[0].cells.length) || 0,
256
+ parsers = table.config.parsers,
257
+ cache = {
258
+ row: [],
259
+ normalized: []
260
+ };
261
+
262
+ for (var i = 0; i < totalRows; ++i) {
263
+
264
+ /** Add the table data to main data array */
265
+ var c = $(table.tBodies[0].rows[i]),
266
+ cols = [];
267
+
268
+ // if this is a child row, add it to the last row's children and
269
+ // continue to the next row
270
+ if (c.hasClass(table.config.cssChildRow)) {
271
+ cache.row[cache.row.length - 1] = cache.row[cache.row.length - 1].add(c);
272
+ // go to the next for loop
273
+ continue;
274
+ }
275
+
276
+ cache.row.push(c);
277
+
278
+ for (var j = 0; j < totalCells; ++j) {
279
+ cols.push(parsers[j].format(getElementText(table.config, c[0].cells[j]), table, c[0].cells[j]));
280
+ }
281
+
282
+ cols.push(cache.normalized.length); // add position for rowCache
283
+ cache.normalized.push(cols);
284
+ cols = null;
285
+ };
286
+
287
+ if (table.config.debug) {
288
+ benchmark("Building cache for " + totalRows + " rows:", cacheTime);
289
+ }
290
+
291
+ return cache;
292
+ };
293
+
294
+ function getElementText(config, node) {
295
+
296
+ var text = "";
297
+
298
+ if (!node) return "";
299
+
300
+ if (!config.supportsTextContent) config.supportsTextContent = node.textContent || false;
301
+
302
+ if (config.textExtraction == "simple") {
303
+ if (config.supportsTextContent) {
304
+ text = node.textContent;
305
+ } else {
306
+ if (node.childNodes[0] && node.childNodes[0].hasChildNodes()) {
307
+ text = node.childNodes[0].innerHTML;
308
+ } else {
309
+ text = node.innerHTML;
310
+ }
311
+ }
312
+ } else {
313
+ if (typeof(config.textExtraction) == "function") {
314
+ text = config.textExtraction(node);
315
+ } else {
316
+ text = $(node).text();
317
+ }
318
+ }
319
+ return text;
320
+ }
321
+
322
+ function appendToTable(table, cache) {
323
+
324
+ if (table.config.debug) {
325
+ var appendTime = new Date()
326
+ }
327
+
328
+ var c = cache,
329
+ r = c.row,
330
+ n = c.normalized,
331
+ totalRows = n.length,
332
+ checkCell = (n[0].length - 1),
333
+ tableBody = $(table.tBodies[0]),
334
+ rows = [];
335
+
336
+
337
+ for (var i = 0; i < totalRows; i++) {
338
+ var pos = n[i][checkCell];
339
+
340
+ rows.push(r[pos]);
341
+
342
+ if (!table.config.appender) {
343
+
344
+ //var o = ;
345
+ var l = r[pos].length;
346
+ for (var j = 0; j < l; j++) {
347
+ tableBody[0].appendChild(r[pos][j]);
348
+ }
349
+
350
+ //
351
+ }
352
+ }
353
+
354
+
355
+
356
+ if (table.config.appender) {
357
+
358
+ table.config.appender(table, rows);
359
+ }
360
+
361
+ rows = null;
362
+
363
+ if (table.config.debug) {
364
+ benchmark("Rebuilt table:", appendTime);
365
+ }
366
+
367
+ // apply table widgets
368
+ applyWidget(table);
369
+
370
+ // trigger sortend
371
+ setTimeout(function () {
372
+ $(table).trigger("sortEnd");
373
+ }, 0);
374
+
375
+ };
376
+
377
+ function buildHeaders(table) {
378
+
379
+ if (table.config.debug) {
380
+ var time = new Date();
381
+ }
382
+
383
+ var meta = ($.metadata) ? true : false;
384
+
385
+ var header_index = computeTableHeaderCellIndexes(table);
386
+
387
+ $tableHeaders = $(table.config.selectorHeaders, table).each(function (index) {
388
+
389
+ this.column = header_index[this.parentNode.rowIndex + "-" + this.cellIndex];
390
+ // this.column = index;
391
+ this.order = formatSortingOrder(table.config.sortInitialOrder);
392
+
393
+
394
+ this.count = this.order;
395
+
396
+ if (checkHeaderMetadata(this) || checkHeaderOptions(table, index)) this.sortDisabled = true;
397
+ if (checkHeaderOptionsSortingLocked(table, index)) this.order = this.lockedOrder = checkHeaderOptionsSortingLocked(table, index);
398
+
399
+ if (!this.sortDisabled) {
400
+ var $th = $(this).addClass(table.config.cssHeader);
401
+ if (table.config.onRenderHeader) table.config.onRenderHeader.apply($th);
402
+ }
403
+
404
+ // add cell to headerList
405
+ table.config.headerList[index] = this;
406
+ });
407
+
408
+ if (table.config.debug) {
409
+ benchmark("Built headers:", time);
410
+ log($tableHeaders);
411
+ }
412
+
413
+ return $tableHeaders;
414
+
415
+ };
416
+
417
+ // from:
418
+ // http://www.javascripttoolbox.com/lib/table/examples.php
419
+ // http://www.javascripttoolbox.com/temp/table_cellindex.html
420
+
421
+
422
+ function computeTableHeaderCellIndexes(t) {
423
+ var matrix = [];
424
+ var lookup = {};
425
+ var thead = t.getElementsByTagName('THEAD')[0];
426
+ var trs = thead.getElementsByTagName('TR');
427
+
428
+ for (var i = 0; i < trs.length; i++) {
429
+ var cells = trs[i].cells;
430
+ for (var j = 0; j < cells.length; j++) {
431
+ var c = cells[j];
432
+
433
+ var rowIndex = c.parentNode.rowIndex;
434
+ var cellId = rowIndex + "-" + c.cellIndex;
435
+ var rowSpan = c.rowSpan || 1;
436
+ var colSpan = c.colSpan || 1
437
+ var firstAvailCol;
438
+ if (typeof(matrix[rowIndex]) == "undefined") {
439
+ matrix[rowIndex] = [];
440
+ }
441
+ // Find first available column in the first row
442
+ for (var k = 0; k < matrix[rowIndex].length + 1; k++) {
443
+ if (typeof(matrix[rowIndex][k]) == "undefined") {
444
+ firstAvailCol = k;
445
+ break;
446
+ }
447
+ }
448
+ lookup[cellId] = firstAvailCol;
449
+ for (var k = rowIndex; k < rowIndex + rowSpan; k++) {
450
+ if (typeof(matrix[k]) == "undefined") {
451
+ matrix[k] = [];
452
+ }
453
+ var matrixrow = matrix[k];
454
+ for (var l = firstAvailCol; l < firstAvailCol + colSpan; l++) {
455
+ matrixrow[l] = "x";
456
+ }
457
+ }
458
+ }
459
+ }
460
+ return lookup;
461
+ }
462
+
463
+ function checkCellColSpan(table, rows, row) {
464
+ var arr = [],
465
+ r = table.tHead.rows,
466
+ c = r[row].cells;
467
+
468
+ for (var i = 0; i < c.length; i++) {
469
+ var cell = c[i];
470
+
471
+ if (cell.colSpan > 1) {
472
+ arr = arr.concat(checkCellColSpan(table, headerArr, row++));
473
+ } else {
474
+ if (table.tHead.length == 1 || (cell.rowSpan > 1 || !r[row + 1])) {
475
+ arr.push(cell);
476
+ }
477
+ // headerArr[row] = (i+row);
478
+ }
479
+ }
480
+ return arr;
481
+ };
482
+
483
+ function checkHeaderMetadata(cell) {
484
+ if (($.metadata) && ($(cell).metadata().sorter === false)) {
485
+ return true;
486
+ };
487
+ return false;
488
+ }
489
+
490
+ function checkHeaderOptions(table, i) {
491
+ if ((table.config.headers[i]) && (table.config.headers[i].sorter === false)) {
492
+ return true;
493
+ };
494
+ return false;
495
+ }
496
+
497
+ function checkHeaderOptionsSortingLocked(table, i) {
498
+ if ((table.config.headers[i]) && (table.config.headers[i].lockedOrder)) return table.config.headers[i].lockedOrder;
499
+ return false;
500
+ }
501
+
502
+ function applyWidget(table) {
503
+ var c = table.config.widgets;
504
+ var l = c.length;
505
+ for (var i = 0; i < l; i++) {
506
+
507
+ getWidgetById(c[i]).format(table);
508
+ }
509
+
510
+ }
511
+
512
+ function getWidgetById(name) {
513
+ var l = widgets.length;
514
+ for (var i = 0; i < l; i++) {
515
+ if (widgets[i].id.toLowerCase() == name.toLowerCase()) {
516
+ return widgets[i];
517
+ }
518
+ }
519
+ };
520
+
521
+ function formatSortingOrder(v) {
522
+ if (typeof(v) != "Number") {
523
+ return (v.toLowerCase() == "desc") ? 1 : 0;
524
+ } else {
525
+ return (v == 1) ? 1 : 0;
526
+ }
527
+ }
528
+
529
+ function isValueInArray(v, a) {
530
+ var l = a.length;
531
+ for (var i = 0; i < l; i++) {
532
+ if (a[i][0] == v) {
533
+ return true;
534
+ }
535
+ }
536
+ return false;
537
+ }
538
+
539
+ function setHeadersCss(table, $headers, list, css) {
540
+ // remove all header information
541
+ $headers.removeClass(css[0]).removeClass(css[1]);
542
+
543
+ var h = [];
544
+ $headers.each(function (offset) {
545
+ if (!this.sortDisabled) {
546
+ h[this.column] = $(this);
547
+ }
548
+ });
549
+
550
+ var l = list.length;
551
+ for (var i = 0; i < l; i++) {
552
+ h[list[i][0]].addClass(css[list[i][1]]);
553
+ }
554
+ }
555
+
556
+ function fixColumnWidth(table, $headers) {
557
+ var c = table.config;
558
+ if (c.widthFixed) {
559
+ var colgroup = $('<colgroup>');
560
+ $("tr:first td", table.tBodies[0]).each(function () {
561
+ colgroup.append($('<col>').css('width', $(this).width()));
562
+ });
563
+ $(table).prepend(colgroup);
564
+ };
565
+ }
566
+
567
+ function updateHeaderSortCount(table, sortList) {
568
+ var c = table.config,
569
+ l = sortList.length;
570
+ for (var i = 0; i < l; i++) {
571
+ var s = sortList[i],
572
+ o = c.headerList[s[0]];
573
+ o.count = s[1];
574
+ o.count++;
575
+ }
576
+ }
577
+
578
+ /* sorting methods */
579
+
580
+ function multisort(table, sortList, cache) {
581
+
582
+ if (table.config.debug) {
583
+ var sortTime = new Date();
584
+ }
585
+
586
+ var dynamicExp = "var sortWrapper = function(a,b) {",
587
+ l = sortList.length;
588
+
589
+ // TODO: inline functions.
590
+ for (var i = 0; i < l; i++) {
591
+
592
+ var c = sortList[i][0];
593
+ var order = sortList[i][1];
594
+ // var s = (getCachedSortType(table.config.parsers,c) == "text") ?
595
+ // ((order == 0) ? "sortText" : "sortTextDesc") : ((order == 0) ?
596
+ // "sortNumeric" : "sortNumericDesc");
597
+ // var s = (table.config.parsers[c].type == "text") ? ((order == 0)
598
+ // ? makeSortText(c) : makeSortTextDesc(c)) : ((order == 0) ?
599
+ // makeSortNumeric(c) : makeSortNumericDesc(c));
600
+ var s = (table.config.parsers[c].type == "text") ? ((order == 0) ? makeSortFunction("text", "asc", c) : makeSortFunction("text", "desc", c)) : ((order == 0) ? makeSortFunction("numeric", "asc", c) : makeSortFunction("numeric", "desc", c));
601
+ var e = "e" + i;
602
+
603
+ dynamicExp += "var " + e + " = " + s; // + "(a[" + c + "],b[" + c
604
+ // + "]); ";
605
+ dynamicExp += "if(" + e + ") { return " + e + "; } ";
606
+ dynamicExp += "else { ";
607
+
608
+ }
609
+
610
+ // if value is the same keep orignal order
611
+ var orgOrderCol = cache.normalized[0].length - 1;
612
+ dynamicExp += "return a[" + orgOrderCol + "]-b[" + orgOrderCol + "];";
613
+
614
+ for (var i = 0; i < l; i++) {
615
+ dynamicExp += "}; ";
616
+ }
617
+
618
+ dynamicExp += "return 0; ";
619
+ dynamicExp += "}; ";
620
+
621
+ if (table.config.debug) {
622
+ benchmark("Evaling expression:" + dynamicExp, new Date());
623
+ }
624
+
625
+ eval(dynamicExp);
626
+
627
+ cache.normalized.sort(sortWrapper);
628
+
629
+ if (table.config.debug) {
630
+ benchmark("Sorting on " + sortList.toString() + " and dir " + order + " time:", sortTime);
631
+ }
632
+
633
+ return cache;
634
+ };
635
+
636
+ function makeSortFunction(type, direction, index) {
637
+ var a = "a[" + index + "]",
638
+ b = "b[" + index + "]";
639
+ if (type == 'text' && direction == 'asc') {
640
+ return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + a + " < " + b + ") ? -1 : 1 )));";
641
+ } else if (type == 'text' && direction == 'desc') {
642
+ return "(" + a + " == " + b + " ? 0 : (" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : (" + b + " < " + a + ") ? -1 : 1 )));";
643
+ } else if (type == 'numeric' && direction == 'asc') {
644
+ return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + a + " - " + b + "));";
645
+ } else if (type == 'numeric' && direction == 'desc') {
646
+ return "(" + a + " === null && " + b + " === null) ? 0 :(" + a + " === null ? Number.POSITIVE_INFINITY : (" + b + " === null ? Number.NEGATIVE_INFINITY : " + b + " - " + a + "));";
647
+ }
648
+ };
649
+
650
+ function makeSortText(i) {
651
+ return "((a[" + i + "] < b[" + i + "]) ? -1 : ((a[" + i + "] > b[" + i + "]) ? 1 : 0));";
652
+ };
653
+
654
+ function makeSortTextDesc(i) {
655
+ return "((b[" + i + "] < a[" + i + "]) ? -1 : ((b[" + i + "] > a[" + i + "]) ? 1 : 0));";
656
+ };
657
+
658
+ function makeSortNumeric(i) {
659
+ return "a[" + i + "]-b[" + i + "];";
660
+ };
661
+
662
+ function makeSortNumericDesc(i) {
663
+ return "b[" + i + "]-a[" + i + "];";
664
+ };
665
+
666
+ function sortText(a, b) {
667
+ if (table.config.sortLocaleCompare) return a.localeCompare(b);
668
+ return ((a < b) ? -1 : ((a > b) ? 1 : 0));
669
+ };
670
+
671
+ function sortTextDesc(a, b) {
672
+ if (table.config.sortLocaleCompare) return b.localeCompare(a);
673
+ return ((b < a) ? -1 : ((b > a) ? 1 : 0));
674
+ };
675
+
676
+ function sortNumeric(a, b) {
677
+ return a - b;
678
+ };
679
+
680
+ function sortNumericDesc(a, b) {
681
+ return b - a;
682
+ };
683
+
684
+ function getCachedSortType(parsers, i) {
685
+ return parsers[i].type;
686
+ }; /* public methods */
687
+ this.construct = function (settings) {
688
+ return this.each(function () {
689
+ // if no thead or tbody quit.
690
+ if (!this.tHead || !this.tBodies) return;
691
+ // declare
692
+ var $this, $document, $headers, cache, config, shiftDown = 0,
693
+ sortOrder;
694
+ // new blank config object
695
+ this.config = {};
696
+ // merge and extend.
697
+ config = $.extend(this.config, $.tablesorter.defaults, settings);
698
+ // store common expression for speed
699
+ $this = $(this);
700
+ // save the settings where they read
701
+ $.data(this, "tablesorter", config);
702
+ // build headers
703
+ $headers = buildHeaders(this);
704
+ // try to auto detect column type, and store in tables config
705
+ this.config.parsers = buildParserCache(this, $headers);
706
+ // build the cache for the tbody cells
707
+ cache = buildCache(this);
708
+ // get the css class names, could be done else where.
709
+ var sortCSS = [config.cssDesc, config.cssAsc];
710
+ // fixate columns if the users supplies the fixedWidth option
711
+ fixColumnWidth(this);
712
+ // apply event handling to headers
713
+ // this is to big, perhaps break it out?
714
+ $headers.click(
715
+
716
+ function (e) {
717
+ var totalRows = ($this[0].tBodies[0] && $this[0].tBodies[0].rows.length) || 0;
718
+ if (!this.sortDisabled && totalRows > 0) {
719
+ // Only call sortStart if sorting is
720
+ // enabled.
721
+ $this.trigger("sortStart");
722
+ // store exp, for speed
723
+ var $cell = $(this);
724
+ // get current column index
725
+ var i = this.column;
726
+ // get current column sort order
727
+ this.order = this.count++ % 2;
728
+ // always sort on the locked order.
729
+ if(this.lockedOrder) this.order = this.lockedOrder;
730
+
731
+ // user only whants to sort on one
732
+ // column
733
+ if (!e[config.sortMultiSortKey]) {
734
+ // flush the sort list
735
+ config.sortList = [];
736
+ if (config.sortForce != null) {
737
+ var a = config.sortForce;
738
+ for (var j = 0; j < a.length; j++) {
739
+ if (a[j][0] != i) {
740
+ config.sortList.push(a[j]);
741
+ }
742
+ }
743
+ }
744
+ // add column to sort list
745
+ config.sortList.push([i, this.order]);
746
+ // multi column sorting
747
+ } else {
748
+ // the user has clicked on an all
749
+ // ready sortet column.
750
+ if (isValueInArray(i, config.sortList)) {
751
+ // revers the sorting direction
752
+ // for all tables.
753
+ for (var j = 0; j < config.sortList.length; j++) {
754
+ var s = config.sortList[j],
755
+ o = config.headerList[s[0]];
756
+ if (s[0] == i) {
757
+ o.count = s[1];
758
+ o.count++;
759
+ s[1] = o.count % 2;
760
+ }
761
+ }
762
+ } else {
763
+ // add column to sort list array
764
+ config.sortList.push([i, this.order]);
765
+ }
766
+ };
767
+ setTimeout(function () {
768
+ // set css for headers
769
+ setHeadersCss($this[0], $headers, config.sortList, sortCSS);
770
+ appendToTable(
771
+ $this[0], multisort(
772
+ $this[0], config.sortList, cache)
773
+ );
774
+ }, 1);
775
+ // stop normal event by returning false
776
+ return false;
777
+ }
778
+ // cancel selection
779
+ }).mousedown(function () {
780
+ if (config.cancelSelection) {
781
+ this.onselectstart = function () {
782
+ return false
783
+ };
784
+ return false;
785
+ }
786
+ });
787
+ // apply easy methods that trigger binded events
788
+ $this.bind("update", function () {
789
+ var me = this;
790
+ setTimeout(function () {
791
+ // rebuild parsers.
792
+ me.config.parsers = buildParserCache(
793
+ me, $headers);
794
+ // rebuild the cache map
795
+ cache = buildCache(me);
796
+ }, 1);
797
+ }).bind("updateCell", function (e, cell) {
798
+ var config = this.config;
799
+ // get position from the dom.
800
+ var pos = [(cell.parentNode.rowIndex - 1), cell.cellIndex];
801
+ // update cache
802
+ cache.normalized[pos[0]][pos[1]] = config.parsers[pos[1]].format(
803
+ getElementText(config, cell), cell);
804
+ }).bind("sorton", function (e, list) {
805
+ $(this).trigger("sortStart");
806
+ config.sortList = list;
807
+ // update and store the sortlist
808
+ var sortList = config.sortList;
809
+ // update header count index
810
+ updateHeaderSortCount(this, sortList);
811
+ // set css for headers
812
+ setHeadersCss(this, $headers, sortList, sortCSS);
813
+ // sort the table and append it to the dom
814
+ appendToTable(this, multisort(this, sortList, cache));
815
+ }).bind("appendCache", function () {
816
+ appendToTable(this, cache);
817
+ }).bind("applyWidgetId", function (e, id) {
818
+ getWidgetById(id).format(this);
819
+ }).bind("applyWidgets", function () {
820
+ // apply widgets
821
+ applyWidget(this);
822
+ });
823
+ if ($.metadata && ($(this).metadata() && $(this).metadata().sortlist)) {
824
+ config.sortList = $(this).metadata().sortlist;
825
+ }
826
+ // if user has supplied a sort list to constructor.
827
+ if (config.sortList.length > 0) {
828
+ $this.trigger("sorton", [config.sortList]);
829
+ }
830
+ // apply widgets
831
+ applyWidget(this);
832
+ });
833
+ };
834
+ this.addParser = function (parser) {
835
+ var l = parsers.length,
836
+ a = true;
837
+ for (var i = 0; i < l; i++) {
838
+ if (parsers[i].id.toLowerCase() == parser.id.toLowerCase()) {
839
+ a = false;
840
+ }
841
+ }
842
+ if (a) {
843
+ parsers.push(parser);
844
+ };
845
+ };
846
+ this.addWidget = function (widget) {
847
+ widgets.push(widget);
848
+ };
849
+ this.formatFloat = function (s) {
850
+ var i = parseFloat(s);
851
+ return (isNaN(i)) ? 0 : i;
852
+ };
853
+ this.formatInt = function (s) {
854
+ var i = parseInt(s);
855
+ return (isNaN(i)) ? 0 : i;
856
+ };
857
+ this.isDigit = function (s, config) {
858
+ // replace all an wanted chars and match.
859
+ return /^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g, '')));
860
+ };
861
+ this.clearTableBody = function (table) {
862
+ if ($.browser.msie) {
863
+ function empty() {
864
+ while (this.firstChild)
865
+ this.removeChild(this.firstChild);
866
+ }
867
+ empty.apply(table.tBodies[0]);
868
+ } else {
869
+ table.tBodies[0].innerHTML = "";
870
+ }
871
+ };
872
+ }
873
+ });
874
+
875
+ // extend plugin scope
876
+ $.fn.extend({
877
+ tablesorter: $.tablesorter.construct
878
+ });
879
+
880
+ // make shortcut
881
+ var ts = $.tablesorter;
882
+
883
+ // add default parsers
884
+ ts.addParser({
885
+ id: "text",
886
+ is: function (s) {
887
+ return true;
888
+ }, format: function (s) {
889
+ return $.trim(s.toLocaleLowerCase());
890
+ }, type: "text"
891
+ });
892
+
893
+ ts.addParser({
894
+ id: "digit",
895
+ is: function (s, table) {
896
+ var c = table.config;
897
+ return $.tablesorter.isDigit(s, c);
898
+ }, format: function (s) {
899
+ return $.tablesorter.formatFloat(s);
900
+ }, type: "numeric"
901
+ });
902
+
903
+ ts.addParser({
904
+ id: "currency",
905
+ is: function (s) {
906
+ return /^[£$€?.]/.test(s);
907
+ }, format: function (s) {
908
+ return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g), ""));
909
+ }, type: "numeric"
910
+ });
911
+
912
+ ts.addParser({
913
+ id: "ipAddress",
914
+ is: function (s) {
915
+ return /^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);
916
+ }, format: function (s) {
917
+ var a = s.split("."),
918
+ r = "",
919
+ l = a.length;
920
+ for (var i = 0; i < l; i++) {
921
+ var item = a[i];
922
+ if (item.length == 2) {
923
+ r += "0" + item;
924
+ } else {
925
+ r += item;
926
+ }
927
+ }
928
+ return $.tablesorter.formatFloat(r);
929
+ }, type: "numeric"
930
+ });
931
+
932
+ ts.addParser({
933
+ id: "url",
934
+ is: function (s) {
935
+ return /^(https?|ftp|file):\/\/$/.test(s);
936
+ }, format: function (s) {
937
+ return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//), ''));
938
+ }, type: "text"
939
+ });
940
+
941
+ ts.addParser({
942
+ id: "isoDate",
943
+ is: function (s) {
944
+ return /^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);
945
+ }, format: function (s) {
946
+ return $.tablesorter.formatFloat((s != "") ? new Date(s.replace(
947
+ new RegExp(/-/g), "/")).getTime() : "0");
948
+ }, type: "numeric"
949
+ });
950
+
951
+ ts.addParser({
952
+ id: "percent",
953
+ is: function (s) {
954
+ return /\%$/.test($.trim(s));
955
+ }, format: function (s) {
956
+ return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g), ""));
957
+ }, type: "numeric"
958
+ });
959
+
960
+ ts.addParser({
961
+ id: "usLongDate",
962
+ is: function (s) {
963
+ return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));
964
+ }, format: function (s) {
965
+ return $.tablesorter.formatFloat(new Date(s).getTime());
966
+ }, type: "numeric"
967
+ });
968
+
969
+ ts.addParser({
970
+ id: "shortDate",
971
+ is: function (s) {
972
+ return /\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);
973
+ }, format: function (s, table) {
974
+ var c = table.config;
975
+ s = s.replace(/\-/g, "/");
976
+ if (c.dateFormat == "us") {
977
+ // reformat the string in ISO format
978
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$1/$2");
979
+ } else if (c.dateFormat == "uk") {
980
+ // reformat the string in ISO format
981
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/, "$3/$2/$1");
982
+ } else if (c.dateFormat == "dd/mm/yy" || c.dateFormat == "dd-mm-yy") {
983
+ s = s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/, "$1/$2/$3");
984
+ }
985
+ return $.tablesorter.formatFloat(new Date(s).getTime());
986
+ }, type: "numeric"
987
+ });
988
+ ts.addParser({
989
+ id: "time",
990
+ is: function (s) {
991
+ return /^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);
992
+ }, format: function (s) {
993
+ return $.tablesorter.formatFloat(new Date("2000/01/01 " + s).getTime());
994
+ }, type: "numeric"
995
+ });
996
+ ts.addParser({
997
+ id: "metadata",
998
+ is: function (s) {
999
+ return false;
1000
+ }, format: function (s, table, cell) {
1001
+ var c = table.config,
1002
+ p = (!c.parserMetadataName) ? 'sortValue' : c.parserMetadataName;
1003
+ return $(cell).metadata()[p];
1004
+ }, type: "numeric"
1005
+ });
1006
+ // add default widgets
1007
+ ts.addWidget({
1008
+ id: "zebra",
1009
+ format: function (table) {
1010
+ if (table.config.debug) {
1011
+ var time = new Date();
1012
+ }
1013
+ var $tr, row = -1,
1014
+ odd;
1015
+ // loop through the visible rows
1016
+ $("tr:visible", table.tBodies[0]).each(function (i) {
1017
+ $tr = $(this);
1018
+ // style children rows the same way the parent
1019
+ // row was styled
1020
+ if (!$tr.hasClass(table.config.cssChildRow)) row++;
1021
+ odd = (row % 2 == 0);
1022
+ $tr.removeClass(
1023
+ table.config.widgetZebra.css[odd ? 0 : 1]).addClass(
1024
+ table.config.widgetZebra.css[odd ? 1 : 0])
1025
+ });
1026
+ if (table.config.debug) {
1027
+ $.tablesorter.benchmark("Applying Zebra widget", time);
1028
+ }
1029
+ }
1030
+ });
1031
  })(jQuery);
assets/js/jquery.total-storage.min.js CHANGED
@@ -1,22 +1,22 @@
1
- /*
2
- * TotalStorage
3
- *
4
- * Copyright (c) 2012 Jared Novack & Upstatement (upstatement.com)
5
- * Dual licensed under the MIT and GPL licenses:
6
- * http://www.opensource.org/licenses/mit-license.php
7
- * http://www.gnu.org/licenses/gpl.html
8
- *
9
- * Total Storage is the conceptual the love child of jStorage by Andris Reinman,
10
- * and Cookie by Klaus Hartl -- though this is not connected to either project.
11
- *
12
- * @name $.totalStorage
13
- * @cat Plugins/Cookie
14
- * @author Jared Novack/jared@upstatement.com
15
- * @version 1.1.2
16
- * @url http://upstatement.com/blog/2012/01/jquery-local-storage-done-right-and-easy/
17
- */
18
-
19
- (function(c,h){var e,d;if("localStorage"in window)try{d="undefined"===typeof window.localStorage?h:window.localStorage,e="undefined"==typeof d||"undefined"==typeof window.JSON?!1:!0}catch(j){e=!1}c.totalStorage=function(b,a){return c.totalStorage.impl.init(b,a)};c.totalStorage.setItem=function(b,a){return c.totalStorage.impl.setItem(b,a)};c.totalStorage.getItem=function(b){return c.totalStorage.impl.getItem(b)};c.totalStorage.getAll=function(){return c.totalStorage.impl.getAll()};c.totalStorage.deleteItem=
20
- function(b){return c.totalStorage.impl.deleteItem(b)};c.totalStorage.impl={init:function(b,a){return"undefined"!=typeof a?this.setItem(b,a):this.getItem(b)},setItem:function(b,a){if(!e)try{return c.cookie(b,a),a}catch(g){console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie")}var f=JSON.stringify(a);d.setItem(b,f);return this.parseResult(f)},getItem:function(b){if(!e)try{return this.parseResult(c.cookie(b))}catch(a){return null}b=
21
- d.getItem(b);return this.parseResult(b)},deleteItem:function(b){if(!e)try{return c.cookie(b,null),!0}catch(a){return!1}d.removeItem(b);return!0},getAll:function(){var b=[];if(e)for(var a in d)a.length&&b.push({key:a,value:this.parseResult(d.getItem(a))});else try{var g=document.cookie.split(";");for(a=0;a<g.length;a++){var f=g[a].split("=")[0];b.push({key:f,value:this.parseResult(c.cookie(f))})}}catch(h){return null}return b},parseResult:function(b){var a;try{a=JSON.parse(b),"undefined"==typeof a&&
22
  (a=b),"true"==a&&(a=!0),"false"==a&&(a=!1),parseFloat(a)==a&&"object"!=typeof a&&(a=parseFloat(a))}catch(c){a=b}return a}}})(jQuery);
1
+ /*
2
+ * TotalStorage
3
+ *
4
+ * Copyright (c) 2012 Jared Novack & Upstatement (upstatement.com)
5
+ * Dual licensed under the MIT and GPL licenses:
6
+ * http://www.opensource.org/licenses/mit-license.php
7
+ * http://www.gnu.org/licenses/gpl.html
8
+ *
9
+ * Total Storage is the conceptual the love child of jStorage by Andris Reinman,
10
+ * and Cookie by Klaus Hartl -- though this is not connected to either project.
11
+ *
12
+ * @name $.totalStorage
13
+ * @cat Plugins/Cookie
14
+ * @author Jared Novack/jared@upstatement.com
15
+ * @version 1.1.2
16
+ * @url http://upstatement.com/blog/2012/01/jquery-local-storage-done-right-and-easy/
17
+ */
18
+
19
+ (function(c,h){var e,d;if("localStorage"in window)try{d="undefined"===typeof window.localStorage?h:window.localStorage,e="undefined"==typeof d||"undefined"==typeof window.JSON?!1:!0}catch(j){e=!1}c.totalStorage=function(b,a){return c.totalStorage.impl.init(b,a)};c.totalStorage.setItem=function(b,a){return c.totalStorage.impl.setItem(b,a)};c.totalStorage.getItem=function(b){return c.totalStorage.impl.getItem(b)};c.totalStorage.getAll=function(){return c.totalStorage.impl.getAll()};c.totalStorage.deleteItem=
20
+ function(b){return c.totalStorage.impl.deleteItem(b)};c.totalStorage.impl={init:function(b,a){return"undefined"!=typeof a?this.setItem(b,a):this.getItem(b)},setItem:function(b,a){if(!e)try{return c.cookie(b,a),a}catch(g){console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie")}var f=JSON.stringify(a);d.setItem(b,f);return this.parseResult(f)},getItem:function(b){if(!e)try{return this.parseResult(c.cookie(b))}catch(a){return null}b=
21
+ d.getItem(b);return this.parseResult(b)},deleteItem:function(b){if(!e)try{return c.cookie(b,null),!0}catch(a){return!1}d.removeItem(b);return!0},getAll:function(){var b=[];if(e)for(var a in d)a.length&&b.push({key:a,value:this.parseResult(d.getItem(a))});else try{var g=document.cookie.split(";");for(a=0;a<g.length;a++){var f=g[a].split("=")[0];b.push({key:f,value:this.parseResult(c.cookie(f))})}}catch(h){return null}return b},parseResult:function(b){var a;try{a=JSON.parse(b),"undefined"==typeof a&&
22
  (a=b),"true"==a&&(a=!0),"false"==a&&(a=!1),parseFloat(a)==a&&"object"!=typeof a&&(a=parseFloat(a))}catch(c){a=b}return a}}})(jQuery);
assets/js/wordpress/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/lang/.tx/config CHANGED
@@ -1,7 +1,7 @@
1
- [main]
2
- host = https://www.transifex.com
3
-
4
- [landing-pages.landing-pages]
5
- file_filter = <lang>.po
6
- source_lang = en_US
7
-
1
+ [main]
2
+ host = https://www.transifex.com
3
+
4
+ [landing-pages.landing-pages]
5
+ file_filter = <lang>.po
6
+ source_lang = en_US
7
+
assets/lang/landing-pages.po CHANGED
@@ -1,243 +1,243 @@
1
- msgid ""
2
- msgstr ""
3
- "Project-Id-Version: Landing Pages\n"
4
- "POT-Creation-Date: 2015-09-29 16:45-0500\n"
5
- "PO-Revision-Date: 2015-09-29 16:45-0500\n"
6
- "Last-Translator: \n"
7
- "Language-Team: InboundNow <support@inboundnow.com>\n"
8
- "Language: en_US\n"
9
- "MIME-Version: 1.0\n"
10
- "Content-Type: text/plain; charset=UTF-8\n"
11
- "Content-Transfer-Encoding: 8bit\n"
12
- "X-Generator: Poedit 1.8.5\n"
13
- "X-Poedit-KeywordsList: __;_e\n"
14
- "X-Poedit-Basepath: .\n"
15
- "Plural-Forms: nplurals=2; plural=(n != 1);\n"
16
- "X-Poedit-SearchPath-0: ..\n"
17
- "X-Poedit-SearchPathExcluded-0: ../js\n"
18
- "X-Poedit-SearchPathExcluded-1: ../shared/docs\n"
19
- "X-Poedit-SearchPathExcluded-2: ../templates/*.js\n"
20
-
21
- #: ../libraries/class-tgm-plugin-activation.php:301
22
- msgid "Install Required Plugins"
23
- msgstr ""
24
-
25
- #: ../libraries/class-tgm-plugin-activation.php:302
26
- msgid "Install Plugins"
27
- msgstr ""
28
-
29
- #: ../libraries/class-tgm-plugin-activation.php:303
30
- #, php-format
31
- msgid "Installing Plugin: %s"
32
- msgstr ""
33
-
34
- #: ../libraries/class-tgm-plugin-activation.php:304
35
- msgid "Something went wrong with the plugin API."
36
- msgstr ""
37
-
38
- #: ../libraries/class-tgm-plugin-activation.php:365
39
- msgid "Return to Required Plugins Installer"
40
- msgstr ""
41
-
42
- #: ../libraries/class-tgm-plugin-activation.php:366
43
- msgid "Return to the dashboard"
44
- msgstr ""
45
-
46
- #: ../libraries/class-tgm-plugin-activation.php:367
47
- #: ../libraries/class-tgm-plugin-activation.php:2958
48
- msgid "Plugin activated successfully."
49
- msgstr ""
50
-
51
- #: ../libraries/class-tgm-plugin-activation.php:368
52
- msgid "The following plugin was activated successfully:"
53
- msgstr ""
54
-
55
- #: ../libraries/class-tgm-plugin-activation.php:369
56
- #, php-format
57
- msgid "No action taken. Plugin %1$s was already active."
58
- msgstr ""
59
-
60
- #: ../libraries/class-tgm-plugin-activation.php:370
61
- #, php-format
62
- msgid ""
63
- "Plugin not activated. A higher version of %s is needed for this theme. "
64
- "Please update the plugin."
65
- msgstr ""
66
-
67
- #: ../libraries/class-tgm-plugin-activation.php:371
68
- #, php-format
69
- msgid "All plugins installed and activated successfully. %1$s"
70
- msgstr ""
71
-
72
- #: ../libraries/class-tgm-plugin-activation.php:372
73
- msgid "Dismiss this notice"
74
- msgstr ""
75
-
76
- #: ../libraries/class-tgm-plugin-activation.php:373
77
- msgid "Please contact the administrator of this site for help."
78
- msgstr ""
79
-
80
- #: ../libraries/class-tgm-plugin-activation.php:2121
81
- msgid "Required"
82
- msgstr ""
83
-
84
- #: ../libraries/class-tgm-plugin-activation.php:2124
85
- msgid "Recommended"
86
- msgstr ""
87
-
88
- #: ../libraries/class-tgm-plugin-activation.php:2140
89
- msgid "WordPress Repository"
90
- msgstr ""
91
-
92
- #: ../libraries/class-tgm-plugin-activation.php:2143
93
- msgid "External Source"
94
- msgstr ""
95
-
96
- #: ../libraries/class-tgm-plugin-activation.php:2146
97
- msgid "Pre-Packaged"
98
- msgstr ""
99
-
100
- #: ../libraries/class-tgm-plugin-activation.php:2163
101
- msgid "Not Installed"
102
- msgstr ""
103
-
104
- #: ../libraries/class-tgm-plugin-activation.php:2167
105
- msgid "Installed But Not Activated"
106
- msgstr ""
107
-
108
- #: ../libraries/class-tgm-plugin-activation.php:2169
109
- msgid "Active"
110
- msgstr ""
111
-
112
- #: ../libraries/class-tgm-plugin-activation.php:2175
113
- msgid "Required Update not Available"
114
- msgstr ""
115
-
116
- #: ../libraries/class-tgm-plugin-activation.php:2178
117
- msgid "Requires Update"
118
- msgstr ""
119
-
120
- #: ../libraries/class-tgm-plugin-activation.php:2181
121
- msgid "Update recommended"
122
- msgstr ""
123
-
124
- #: ../libraries/class-tgm-plugin-activation.php:2333
125
- msgid "Installed version:"
126
- msgstr ""
127
-
128
- #: ../libraries/class-tgm-plugin-activation.php:2341
129
- msgid "Minimum required version:"
130
- msgstr ""
131
-
132
- #: ../libraries/class-tgm-plugin-activation.php:2353
133
- msgid "Available version:"
134
- msgstr ""
135
-
136
- #: ../libraries/class-tgm-plugin-activation.php:2376
137
- #, php-format
138
- msgid ""
139
- "No plugins to install, update or activate. <a href=\"%1$s\">Return to the "
140
- "Dashboard</a>"
141
- msgstr ""
142
-
143
- #: ../libraries/class-tgm-plugin-activation.php:2390
144
- msgid "Plugin"
145
- msgstr ""
146
-
147
- #: ../libraries/class-tgm-plugin-activation.php:2391
148
- msgid "Source"
149
- msgstr ""
150
-
151
- #: ../libraries/class-tgm-plugin-activation.php:2392
152
- msgid "Type"
153
- msgstr ""
154
-
155
- #: ../libraries/class-tgm-plugin-activation.php:2396
156
- msgid "Version"
157
- msgstr ""
158
-
159
- #: ../libraries/class-tgm-plugin-activation.php:2397
160
- msgid "Status"
161
- msgstr ""
162
-
163
- #: ../libraries/class-tgm-plugin-activation.php:2557
164
- msgid "Install"
165
- msgstr ""
166
-
167
- #: ../libraries/class-tgm-plugin-activation.php:2563
168
- msgid "Update"
169
- msgstr ""
170
-
171
- #: ../libraries/class-tgm-plugin-activation.php:2566
172
- msgid "Activate"
173
- msgstr ""
174
-
175
- #: ../libraries/class-tgm-plugin-activation.php:2597
176
- msgid "No plugins were selected to be installed. No action taken."
177
- msgstr ""
178
-
179
- #: ../libraries/class-tgm-plugin-activation.php:2599
180
- msgid "No plugins were selected to be updated. No action taken."
181
- msgstr ""
182
-
183
- #: ../libraries/class-tgm-plugin-activation.php:2635
184
- msgid "No plugins are available to be installed at this time."
185
- msgstr ""
186
-
187
- #: ../libraries/class-tgm-plugin-activation.php:2637
188
- msgid "No plugins are available to be updated at this time."
189
- msgstr ""
190
-
191
- #: ../libraries/class-tgm-plugin-activation.php:2957
192
- msgid "Plugin activation failed."
193
- msgstr ""
194
-
195
- #: ../libraries/class-tgm-plugin-activation.php:3287
196
- #, php-format
197
- msgid "Updating Plugin %1$s (%2$d/%3$d)"
198
- msgstr ""
199
-
200
- #: ../libraries/class-tgm-plugin-activation.php:3289
201
- #, php-format
202
- msgid "An error occurred while installing %1$s: <strong>%2$s</strong>."
203
- msgstr ""
204
-
205
- #: ../libraries/class-tgm-plugin-activation.php:3290
206
- #, php-format
207
- msgid "The installation of %1$s failed."
208
- msgstr ""
209
-
210
- #: ../libraries/class-tgm-plugin-activation.php:3294
211
- msgid ""
212
- "The installation and activation process is starting. This process may take a "
213
- "while on some hosts, so please be patient."
214
- msgstr ""
215
-
216
- #: ../libraries/class-tgm-plugin-activation.php:3295
217
- #, php-format
218
- msgid "%1$s installed and activated successfully."
219
- msgstr ""
220
-
221
- #: ../libraries/class-tgm-plugin-activation.php:3296
222
- msgid "All installations and activations have been completed."
223
- msgstr ""
224
-
225
- #: ../libraries/class-tgm-plugin-activation.php:3297
226
- #, php-format
227
- msgid "Installing and Activating Plugin %1$s (%2$d/%3$d)"
228
- msgstr ""
229
-
230
- #: ../libraries/class-tgm-plugin-activation.php:3300
231
- msgid ""
232
- "The installation process is starting. This process may take a while on some "
233
- "hosts, so please be patient."
234
- msgstr ""
235
-
236
- #: ../libraries/class-tgm-plugin-activation.php:3302
237
- msgid "All installations have been completed."
238
- msgstr ""
239
-
240
- #: ../libraries/class-tgm-plugin-activation.php:3303
241
- #, php-format
242
- msgid "Installing Plugin %1$s (%2$d/%3$d)"
243
- msgstr ""
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Landing Pages\n"
4
+ "POT-Creation-Date: 2015-09-29 16:45-0500\n"
5
+ "PO-Revision-Date: 2015-09-29 16:45-0500\n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: InboundNow <support@inboundnow.com>\n"
8
+ "Language: en_US\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "X-Generator: Poedit 1.8.5\n"
13
+ "X-Poedit-KeywordsList: __;_e\n"
14
+ "X-Poedit-Basepath: .\n"
15
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
16
+ "X-Poedit-SearchPath-0: ..\n"
17
+ "X-Poedit-SearchPathExcluded-0: ../js\n"
18
+ "X-Poedit-SearchPathExcluded-1: ../shared/docs\n"
19
+ "X-Poedit-SearchPathExcluded-2: ../templates/*.js\n"
20
+
21
+ #: ../libraries/class-tgm-plugin-activation.php:301
22
+ msgid "Install Required Plugins"
23
+ msgstr ""
24
+
25
+ #: ../libraries/class-tgm-plugin-activation.php:302
26
+ msgid "Install Plugins"
27
+ msgstr ""
28
+
29
+ #: ../libraries/class-tgm-plugin-activation.php:303
30
+ #, php-format
31
+ msgid "Installing Plugin: %s"
32
+ msgstr ""
33
+
34
+ #: ../libraries/class-tgm-plugin-activation.php:304
35
+ msgid "Something went wrong with the plugin API."
36
+ msgstr ""
37
+
38
+ #: ../libraries/class-tgm-plugin-activation.php:365
39
+ msgid "Return to Required Plugins Installer"
40
+ msgstr ""
41
+
42
+ #: ../libraries/class-tgm-plugin-activation.php:366
43
+ msgid "Return to the dashboard"
44
+ msgstr ""
45
+
46
+ #: ../libraries/class-tgm-plugin-activation.php:367
47
+ #: ../libraries/class-tgm-plugin-activation.php:2958
48
+ msgid "Plugin activated successfully."
49
+ msgstr ""
50
+
51
+ #: ../libraries/class-tgm-plugin-activation.php:368
52
+ msgid "The following plugin was activated successfully:"
53
+ msgstr ""
54
+
55
+ #: ../libraries/class-tgm-plugin-activation.php:369
56
+ #, php-format
57
+ msgid "No action taken. Plugin %1$s was already active."
58
+ msgstr ""
59
+
60
+ #: ../libraries/class-tgm-plugin-activation.php:370
61
+ #, php-format
62
+ msgid ""
63
+ "Plugin not activated. A higher version of %s is needed for this theme. "
64
+ "Please update the plugin."
65
+ msgstr ""
66
+
67
+ #: ../libraries/class-tgm-plugin-activation.php:371
68
+ #, php-format
69
+ msgid "All plugins installed and activated successfully. %1$s"
70
+ msgstr ""
71
+
72
+ #: ../libraries/class-tgm-plugin-activation.php:372
73
+ msgid "Dismiss this notice"
74
+ msgstr ""
75
+
76
+ #: ../libraries/class-tgm-plugin-activation.php:373
77
+ msgid "Please contact the administrator of this site for help."
78
+ msgstr ""
79
+
80
+ #: ../libraries/class-tgm-plugin-activation.php:2121
81
+ msgid "Required"
82
+ msgstr ""
83
+
84
+ #: ../libraries/class-tgm-plugin-activation.php:2124
85
+ msgid "Recommended"
86
+ msgstr ""
87
+
88
+ #: ../libraries/class-tgm-plugin-activation.php:2140
89
+ msgid "WordPress Repository"
90
+ msgstr ""
91
+
92
+ #: ../libraries/class-tgm-plugin-activation.php:2143
93
+ msgid "External Source"
94
+ msgstr ""
95
+
96
+ #: ../libraries/class-tgm-plugin-activation.php:2146
97
+ msgid "Pre-Packaged"
98
+ msgstr ""
99
+
100
+ #: ../libraries/class-tgm-plugin-activation.php:2163
101
+ msgid "Not Installed"
102
+ msgstr ""
103
+
104
+ #: ../libraries/class-tgm-plugin-activation.php:2167
105
+ msgid "Installed But Not Activated"
106
+ msgstr ""
107
+
108
+ #: ../libraries/class-tgm-plugin-activation.php:2169
109
+ msgid "Active"
110
+ msgstr ""
111
+
112
+ #: ../libraries/class-tgm-plugin-activation.php:2175
113
+ msgid "Required Update not Available"
114
+ msgstr ""
115
+
116
+ #: ../libraries/class-tgm-plugin-activation.php:2178
117
+ msgid "Requires Update"
118
+ msgstr ""
119
+
120
+ #: ../libraries/class-tgm-plugin-activation.php:2181
121
+ msgid "Update recommended"
122
+ msgstr ""
123
+
124
+ #: ../libraries/class-tgm-plugin-activation.php:2333
125
+ msgid "Installed version:"
126
+ msgstr ""
127
+
128
+ #: ../libraries/class-tgm-plugin-activation.php:2341
129
+ msgid "Minimum required version:"
130
+ msgstr ""
131
+
132
+ #: ../libraries/class-tgm-plugin-activation.php:2353
133
+ msgid "Available version:"
134
+ msgstr ""
135
+
136
+ #: ../libraries/class-tgm-plugin-activation.php:2376
137
+ #, php-format
138
+ msgid ""
139
+ "No plugins to install, update or activate. <a href=\"%1$s\">Return to the "
140
+ "Dashboard</a>"
141
+ msgstr ""
142
+
143
+ #: ../libraries/class-tgm-plugin-activation.php:2390
144
+ msgid "Plugin"
145
+ msgstr ""
146
+
147
+ #: ../libraries/class-tgm-plugin-activation.php:2391
148
+ msgid "Source"
149
+ msgstr ""
150
+
151
+ #: ../libraries/class-tgm-plugin-activation.php:2392
152
+ msgid "Type"
153
+ msgstr ""
154
+
155
+ #: ../libraries/class-tgm-plugin-activation.php:2396
156
+ msgid "Version"
157
+ msgstr ""
158
+
159
+ #: ../libraries/class-tgm-plugin-activation.php:2397
160
+ msgid "Status"
161
+ msgstr ""
162
+
163
+ #: ../libraries/class-tgm-plugin-activation.php:2557
164
+ msgid "Install"
165
+ msgstr ""
166
+
167
+ #: ../libraries/class-tgm-plugin-activation.php:2563
168
+ msgid "Update"
169
+ msgstr ""
170
+
171
+ #: ../libraries/class-tgm-plugin-activation.php:2566
172
+ msgid "Activate"
173
+ msgstr ""
174
+
175
+ #: ../libraries/class-tgm-plugin-activation.php:2597
176
+ msgid "No plugins were selected to be installed. No action taken."
177
+ msgstr ""
178
+
179
+ #: ../libraries/class-tgm-plugin-activation.php:2599
180
+ msgid "No plugins were selected to be updated. No action taken."
181
+ msgstr ""
182
+
183
+ #: ../libraries/class-tgm-plugin-activation.php:2635
184
+ msgid "No plugins are available to be installed at this time."
185
+ msgstr ""
186
+
187
+ #: ../libraries/class-tgm-plugin-activation.php:2637
188
+ msgid "No plugins are available to be updated at this time."
189
+ msgstr ""
190
+
191
+ #: ../libraries/class-tgm-plugin-activation.php:2957
192
+ msgid "Plugin activation failed."
193
+ msgstr ""
194
+
195
+ #: ../libraries/class-tgm-plugin-activation.php:3287
196
+ #, php-format
197
+ msgid "Updating Plugin %1$s (%2$d/%3$d)"
198
+ msgstr ""
199
+
200
+ #: ../libraries/class-tgm-plugin-activation.php:3289
201
+ #, php-format
202
+ msgid "An error occurred while installing %1$s: <strong>%2$s</strong>."
203
+ msgstr ""
204
+
205
+ #: ../libraries/class-tgm-plugin-activation.php:3290
206
+ #, php-format
207
+ msgid "The installation of %1$s failed."
208
+ msgstr ""
209
+
210
+ #: ../libraries/class-tgm-plugin-activation.php:3294
211
+ msgid ""
212
+ "The installation and activation process is starting. This process may take a "
213
+ "while on some hosts, so please be patient."
214
+ msgstr ""
215
+
216
+ #: ../libraries/class-tgm-plugin-activation.php:3295
217
+ #, php-format
218
+ msgid "%1$s installed and activated successfully."
219
+ msgstr ""
220
+
221
+ #: ../libraries/class-tgm-plugin-activation.php:3296
222
+ msgid "All installations and activations have been completed."
223
+ msgstr ""
224
+
225
+ #: ../libraries/class-tgm-plugin-activation.php:3297
226
+ #, php-format
227
+ msgid "Installing and Activating Plugin %1$s (%2$d/%3$d)"
228
+ msgstr ""
229
+
230
+ #: ../libraries/class-tgm-plugin-activation.php:3300
231
+ msgid ""
232
+ "The installation process is starting. This process may take a while on some "
233
+ "hosts, so please be patient."
234
+ msgstr ""
235
+
236
+ #: ../libraries/class-tgm-plugin-activation.php:3302
237
+ msgid "All installations have been completed."
238
+ msgstr ""
239
+
240
+ #: ../libraries/class-tgm-plugin-activation.php:3303
241
+ #, php-format
242
+ msgid "Installing Plugin %1$s (%2$d/%3$d)"
243
+ msgstr ""
assets/libraries/class-tgm-plugin-activation.php CHANGED
@@ -1,3541 +1,3541 @@
1
- <?php
2
- /**
3
- * Plugin installation and activation for WordPress themes.
4
- *
5
- * Please note that this is a drop-in library for a theme or plugin.
6
- * The authors of this library (Thomas, Gary and Juliette) are NOT responsible
7
- * for the support of your plugin or theme. Please contact the plugin
8
- * or theme author for support.
9
- *
10
- * @package TGM-Plugin-Activation
11
- * @version 2.5.0
12
- * @link http://tgmpluginactivation.com/
13
- * @author Thomas Griffin, Gary Jones, Juliette Reinders Folmer
14
- * @copyright Copyright (c) 2011, Thomas Griffin
15
- * @license GPL-2.0+
16
- *
17
- * @wordpress-plugin
18
- * Plugin Name: TGM Plugin Activation
19
- * Plugin URI:
20
- * Description: Plugin installation and activation for WordPress themes.
21
- * Version: 2.5.0
22
- * Author: Thomas Griffin, Gary Jones, Juliette Reinders Folmer
23
- * Author URI: http://tgmpluginactivation.com/
24
- * Text Domain: tgmpa
25
- * Domain Path: /languages/
26
- * Copyright: 2011, Thomas Griffin
27
- */
28
-
29
- /*
30
- Copyright 2011 Thomas Griffin (thomasgriffinmedia.com)
31
-
32
- This program is free software; you can redistribute it and/or modify
33
- it under the terms of the GNU General Public License, version 2, as
34
- published by the Free Software Foundation.
35
-
36
- This program is distributed in the hope that it will be useful,
37
- but WITHOUT ANY WARRANTY; without even the implied warranty of
38
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39
- GNU General Public License for more details.
40
-
41
- You should have received a copy of the GNU General Public License
42
- along with this program; if not, write to the Free Software
43
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
44
- */
45
-
46
- if ( ! class_exists( 'INBOUND_Plugin_Activation' ) ) {
47
-
48
- /**
49
- * Automatic plugin installation and activation library.
50
- *
51
- * Creates a way to automatically install and activate plugins from within themes.
52
- * The plugins can be either bundled, downloaded from the WordPress
53
- * Plugin Repository or downloaded from another external source.
54
- *
55
- * @since 1.0.0
56
- *
57
- * @package TGM-Plugin-Activation
58
- * @author Thomas Griffin
59
- * @author Gary Jones
60
- */
61
- class INBOUND_Plugin_Activation {
62
- /**
63
- * TGMPA version number.
64
- *
65
- * @since 2.5.0
66
- *
67
- * @const string Version number.
68
- */
69
- const TGMPA_VERSION = '2.5.0';
70
-
71
- /**
72
- * Regular expression to test if a URL is a WP plugin repo URL.
73
- *
74
- * @const string Regex.
75
- *
76
- * @since 2.5.0
77
- */
78
- const WP_REPO_REGEX = '|^http[s]?://wordpress\.org/(?:extend/)?plugins/|';
79
-
80
- /**
81
- * Arbitrary regular expression to test if a string starts with a URL.
82
- *
83
- * @const string Regex.
84
- *
85
- * @since 2.5.0
86
- */
87
- const IS_URL_REGEX = '|^http[s]?://|';
88
-
89
- /**
90
- * Holds a copy of itself, so it can be referenced by the class name.
91
- *
92
- * @since 1.0.0
93
- *
94
- * @var INBOUND_Plugin_Activation
95
- */
96
- public static $instance;
97
-
98
- /**
99
- * Holds arrays of plugin details.
100
- *
101
- * @since 1.0.0
102
- *
103
- * @since 2.5.0 the array has the plugin slug as an associative key.
104
- *
105
- * @var array
106
- */
107
- public $plugins = array();
108
-
109
- /**
110
- * Holds arrays of plugin names to use to sort the plugins array.
111
- *
112
- * @since 2.5.0
113
- *
114
- * @var array
115
- */
116
- protected $sort_order = array();
117
-
118
- /**
119
- * Whether any plugins have the 'force_activation' setting set to true.
120
- *
121
- * @since 2.5.0
122
- *
123
- * @var bool
124
- */
125
- protected $has_forced_activation = false;
126
-
127
- /**
128
- * Whether any plugins have the 'force_deactivation' setting set to true.
129
- *
130
- * @since 2.5.0
131
- *
132
- * @var bool
133
- */
134
- protected $has_forced_deactivation = false;
135
-
136
- /**
137
- * Name of the unique ID to hash notices.
138
- *
139
- * @since 2.4.0
140
- *
141
- * @var string
142
- */
143
- public $id = 'tgmpa';
144
-
145
- /**
146
- * Name of the query-string argument for the admin page.
147
- *
148
- * @since 1.0.0
149
- *
150
- * @var string
151
- */
152
- public $menu = 'tgmpa-install-plugins';
153
-
154
- /**
155
- * Parent menu file slug.
156
- *
157
- * @since 2.5.0
158
- *
159
- * @var string
160
- */
161
- public $parent_slug = 'themes.php';
162
-
163
- /**
164
- * Capability needed to view the plugin installation menu item.
165
- *
166
- * @since 2.5.0
167
- *
168
- * @var string
169
- */
170
- public $capability = 'edit_theme_options';
171
-
172
- /**
173
- * Default absolute path to folder containing bundled plugin zip files.
174
- *
175
- * @since 2.0.0
176
- *
177
- * @var string Absolute path prefix to zip file location for bundled plugins. Default is empty string.
178
- */
179
- public $default_path = '';
180
-
181
- /**
182
- * Flag to show admin notices or not.
183
- *
184
- * @since 2.1.0
185
- *
186
- * @var boolean
187
- */
188
- public $has_notices = true;
189
-
190
- /**
191
- * Flag to determine if the user can dismiss the notice nag.
192
- *
193
- * @since 2.4.0
194
- *
195
- * @var boolean
196
- */
197
- public $dismissable = true;
198
-
199
- /**
200
- * Message to be output above nag notice if dismissable is false.
201
- *
202
- * @since 2.4.0
203
- *
204
- * @var string
205
- */
206
- public $dismiss_msg = '';
207
-
208
- /**
209
- * Flag to set automatic activation of plugins. Off by default.
210
- *
211
- * @since 2.2.0
212
- *
213
- * @var boolean
214
- */
215
- public $is_automatic = false;
216
-
217
- /**
218
- * Optional message to display before the plugins table.
219
- *
220
- * @since 2.2.0
221
- *
222
- * @var string Message filtered by wp_kses_post(). Default is empty string.
223
- */
224
- public $message = '';
225
-
226
- /**
227
- * Holds configurable array of strings.
228
- *
229
- * Default values are added in the constructor.
230
- *
231
- * @since 2.0.0
232
- *
233
- * @var array
234
- */
235
- public $strings = array();
236
-
237
- /**
238
- * Holds the version of WordPress.
239
- *
240
- * @since 2.4.0
241
- *
242
- * @var int
243
- */
244
- public $wp_version;
245
-
246
- /**
247
- * Holds the hook name for the admin page.
248
- *
249
- * @since 2.5.0
250
- *
251
- * @var string
252
- */
253
- public $page_hook;
254
-
255
- /**
256
- * Adds a reference of this object to $instance, populates default strings,
257
- * does the tgmpa_init action hook, and hooks in the interactions to init.
258
- *
259
- * @since 1.0.0
260
- *
261
- * @see INBOUND_Plugin_Activation::init()
262
- */
263
- protected function __construct() {
264
- // Set the current WordPress version.
265
- $this->wp_version = $GLOBALS['wp_version'];
266
-
267
- // Announce that the class is ready, and pass the object (for advanced use).
268
- do_action_ref_array( 'tgmpa_init', array( $this ) );
269
-
270
- // When the rest of WP has loaded, kick-start the rest of the class.
271
- add_action( 'init', array( $this, 'init' ) );
272
- }
273
-
274
- /**
275
- * Initialise the interactions between this class and WordPress.
276
- *
277
- * Hooks in three new methods for the class: admin_menu, notices and styles.
278
- *
279
- * @since 2.0.0
280
- *
281
- * @see INBOUND_Plugin_Activation::admin_menu()
282
- * @see INBOUND_Plugin_Activation::notices()
283
- * @see INBOUND_Plugin_Activation::styles()
284
- */
285
- public function init() {
286
- /**
287
- * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter
288
- * you can overrule that behaviour.
289
- *
290
- * @since 2.5.0
291
- *
292
- * @param bool $load Whether or not TGMPA should load.
293
- * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`.
294
- */
295
- if ( true !== apply_filters( 'tgmpa_load', ( is_admin() && ! defined( 'DOING_AJAX' ) ) ) ) {
296
- return;
297
- }
298
-
299
- // Load class strings.
300
- $this->strings = array(
301
- 'page_title' => __( 'Install Required Plugins', 'tgmpa' ),
302
- 'menu_title' => __( 'Install Plugins', 'tgmpa' ),
303
- 'installing' => __( 'Installing Plugin: %s', 'tgmpa' ),
304
- 'oops' => __( 'Something went wrong with the plugin API.', 'tgmpa' ),
305
- 'notice_can_install_required' => _n_noop(
306
- 'This theme requires the following plugin: %1$s.',
307
- 'This theme requires the following plugins: %1$s.',
308
- 'tgmpa'
309
- ),
310
- 'notice_can_install_recommended' => _n_noop(
311
- 'This theme recommends the following plugin: %1$s.',
312
- 'This theme recommends the following plugins: %1$s.',
313
- 'tgmpa'
314
- ),
315
- 'notice_cannot_install' => _n_noop(
316
- 'Sorry, but you do not have the correct permissions to install the %1$s plugin.',
317
- 'Sorry, but you do not have the correct permissions to install the %1$s plugins.',
318
- 'tgmpa'
319
- ),
320
- 'notice_ask_to_update' => _n_noop(
321
- 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.',
322
- 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.',
323
- 'tgmpa'
324
- ),
325
- 'notice_ask_to_update_maybe' => _n_noop(
326
- 'There is an update available for: %1$s.',
327
- 'There are updates available for the following plugins: %1$s.',
328
- 'tgmpa'
329
- ),
330
- 'notice_cannot_update' => _n_noop(
331
- 'Sorry, but you do not have the correct permissions to update the %1$s plugin.',
332
- 'Sorry, but you do not have the correct permissions to update the %1$s plugins.',
333
- 'tgmpa'
334
- ),
335
- 'notice_can_activate_required' => _n_noop(
336
- 'The following required plugin is currently inactive: %1$s.',
337
- 'The following required plugins are currently inactive: %1$s.',
338
- 'tgmpa'
339
- ),
340
- 'notice_can_activate_recommended' => _n_noop(
341
- 'The following recommended plugin is currently inactive: %1$s.',
342
- 'The following recommended plugins are currently inactive: %1$s.',
343
- 'tgmpa'
344
- ),
345
- 'notice_cannot_activate' => _n_noop(
346
- 'Sorry, but you do not have the correct permissions to activate the %1$s plugin.',
347
- 'Sorry, but you do not have the correct permissions to activate the %1$s plugins.',
348
- 'tgmpa'
349
- ),
350
- 'install_link' => _n_noop(
351
- 'Begin installing plugin',
352
- 'Begin installing plugins',
353
- 'tgmpa'
354
- ),
355
- 'update_link' => _n_noop(
356
- 'Begin updating plugin',
357
- 'Begin updating plugins',
358
- 'tgmpa'
359
- ),
360
- 'activate_link' => _n_noop(
361
- 'Begin activating plugin',
362
- 'Begin activating plugins',
363
- 'tgmpa'
364
- ),
365
- 'return' => __( 'Return to Required Plugins Installer', 'tgmpa' ),
366
- 'dashboard' => __( 'Return to the dashboard', 'tgmpa' ),
367
- 'plugin_activated' => __( 'Plugin activated successfully.', 'tgmpa' ),
368
- 'activated_successfully' => __( 'The following plugin was activated successfully:', 'tgmpa' ),
369
- 'plugin_already_active' => __( 'No action taken. Plugin %1$s was already active.', 'tgmpa' ),
370
- 'plugin_needs_higher_version' => __( 'Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa' ),
371
- 'complete' => __( 'All plugins installed and activated successfully. %1$s', 'tgmpa' ),
372
- 'dismiss' => __( 'Dismiss this notice', 'tgmpa' ),
373
- 'contact_admin' => __( 'Please contact the administrator of this site for help.', 'tgmpa' ),
374
- );
375
-
376
- do_action( 'tgmpa_register' );
377
-
378
- /* After this point, the plugins should be registered and the configuration set. */
379
-
380
- // Proceed only if we have plugins to handle.
381
- if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
382
- return;
383
- }
384
-
385
- // Set up the menu and notices if we still have outstanding actions.
386
- if ( true !== $this->is_tgmpa_complete() ) {
387
- // Sort the plugins.
388
- array_multisort( $this->sort_order, SORT_ASC, $this->plugins );
389
-
390
- add_action( 'admin_menu', array( $this, 'admin_menu' ) );
391
- add_action( 'admin_head', array( $this, 'dismiss' ) );
392
-
393
- // Prevent the normal links from showing underneath a single install/update page.
394
- add_filter( 'install_plugin_complete_actions', array( $this, 'actions' ) );
395
- add_filter( 'update_plugin_complete_actions', array( $this, 'actions' ) );
396
-
397
- if ( $this->has_notices ) {
398
- add_action( 'admin_notices', array( $this, 'notices' ) );
399
- add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
400
- add_action( 'admin_enqueue_scripts', array( $this, 'thickbox' ) );
401
- }
402
-
403
- add_action( 'load-plugins.php', array( $this, 'add_plugin_action_link_filters' ), 1 );
404
- }
405
-
406
- // Make sure things get reset on switch theme.
407
- add_action( 'switch_theme', array( $this, 'flush_plugins_cache' ) );
408
-
409
- if ( $this->has_notices ) {
410
- add_action( 'switch_theme', array( $this, 'update_dismiss' ) );
411
- }
412
-
413
- // Setup the force activation hook.
414
- if ( true === $this->has_forced_activation ) {
415
- add_action( 'admin_init', array( $this, 'force_activation' ) );
416
- }
417
-
418
- // Setup the force deactivation hook.
419
- if ( true === $this->has_forced_deactivation ) {
420
- add_action( 'switch_theme', array( $this, 'force_deactivation' ) );
421
- }
422
- }
423
-
424
- /**
425
- * Prevent activation of plugins which don't meet the minimum version requirement from the
426
- * WP native plugins page.
427
- *
428
- * @since 2.5.0
429
- */
430
- public function add_plugin_action_link_filters() {
431
- foreach ( $this->plugins as $slug => $plugin ) {
432
- if ( false === $this->can_plugin_activate( $slug ) ) {
433
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_activate' ), 20 );
434
- }
435
-
436
- if ( true === $plugin['force_activation'] ) {
437
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_deactivate' ), 20 );
438
- }
439
-
440
- if ( false !== $this->does_plugin_require_update( $slug ) ) {
441
- add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_update' ), 20 );
442
- }
443
- }
444
- }
445
-
446
- /**
447
- * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the
448
- * minimum version requirements.
449
- *
450
- * @since 2.5.0
451
- *
452
- * @param array $actions Action links.
453
- * @return array
454
- */
455
- public function filter_plugin_action_links_activate( $actions ) {
456
- unset( $actions['activate'] );
457
-
458
- return $actions;
459
- }
460
-
461
- /**
462
- * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate.
463
- *
464
- * @since 2.5.0
465
- *
466
- * @param array $actions Action links.
467
- * @return array
468
- */
469
- public function filter_plugin_action_links_deactivate( $actions ) {
470
- unset( $actions['deactivate'] );
471
-
472
- return $actions;
473
- }
474
-
475
- /**
476
- * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the
477
- * minimum version requirements.
478
- *
479
- * @since 2.5.0
480
- *
481
- * @param array $actions Action links.
482
- * @return array
483
- */
484
- public function filter_plugin_action_links_update( $actions ) {
485
- $actions['update'] = sprintf(
486
- '<a href="%1$s" title="%2$s" class="edit">%3$s</a>',
487
- esc_url( $this->get_tgmpa_status_url( 'update' ) ),
488
- esc_attr__( 'This plugin needs to be updated to be compatible with your theme.', 'tgmpa' ),
489
- esc_html__( 'Update Required', 'tgmpa' )
490
- );
491
-
492
- return $actions;
493
- }
494
-
495
- /**
496
- * Handles calls to show plugin information via links in the notices.
497
- *
498
- * We get the links in the admin notices to point to the TGMPA page, rather
499
- * than the typical plugin-install.php file, so we can prepare everything
500
- * beforehand.
501
- *
502
- * WP does not make it easy to show the plugin information in the thickbox -
503
- * here we have to require a file that includes a function that does the
504
- * main work of displaying it, enqueue some styles, set up some globals and
505
- * finally call that function before exiting.
506
- *
507
- * Down right easy once you know how...
508
- *
509
- * Returns early if not the TGMPA page.
510
- *
511
- * @since 2.1.0
512
- *
513
- * @global string $tab Used as iframe div class names, helps with styling
514
- * @global string $body_id Used as the iframe body ID, helps with styling
515
- *
516
- * @return null Returns early if not the TGMPA page.
517
- */
518
- public function admin_init() {
519
- if ( ! $this->is_tgmpa_page() ) {
520
- return;
521
- }
522
-
523
- if ( isset( $_REQUEST['tab'] ) && 'plugin-information' === $_REQUEST['tab'] ) {
524
- // Needed for install_plugin_information().
525
- require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
526
-
527
- wp_enqueue_style( 'plugin-install' );
528
-
529
- global $tab, $body_id;
530
- $body_id = 'plugin-information';
531
- // @codingStandardsIgnoreStart
532
- $tab = 'plugin-information';
533
- // @codingStandardsIgnoreEnd
534
-
535
- install_plugin_information();
536
-
537
- exit;
538
- }
539
- }
540
-
541
- /**
542
- * Enqueue thickbox scripts/styles for plugin info.
543
- *
544
- * Thickbox is not automatically included on all admin pages, so we must
545
- * manually enqueue it for those pages.
546
- *
547
- * Thickbox is only loaded if the user has not dismissed the admin
548
- * notice or if there are any plugins left to install and activate.
549
- *
550
- * @since 2.1.0
551
- */
552
- public function thickbox() {
553
- if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
554
- add_thickbox();
555
- }
556
- }
557
-
558
- /**
559
- * Adds submenu page if there are plugin actions to take.
560
- *
561
- * This method adds the submenu page letting users know that a required
562
- * plugin needs to be installed.
563
- *
564
- * This page disappears once the plugin has been installed and activated.
565
- *
566
- * @since 1.0.0
567
- *
568
- * @see INBOUND_Plugin_Activation::init()
569
- * @see INBOUND_Plugin_Activation::install_plugins_page()
570
- *
571
- * @return null Return early if user lacks capability to install a plugin.
572
- */
573
- public function admin_menu() {
574
- // Make sure privileges are correct to see the page.
575
- if ( ! current_user_can( 'install_plugins' ) ) {
576
- return;
577
- }
578
-
579
- $args = apply_filters(
580
- 'tgmpa_admin_menu_args',
581
- array(
582
- 'parent_slug' => $this->parent_slug, // Parent Menu slug.
583
- 'page_title' => $this->strings['page_title'], // Page title.
584
- 'menu_title' => $this->strings['menu_title'], // Menu title.
585
- 'capability' => $this->capability, // Capability.
586
- 'menu_slug' => $this->menu, // Menu slug.
587
- 'function' => array( $this, 'install_plugins_page' ), // Callback.
588
- )
589
- );
590
-
591
- $this->add_admin_menu( $args );
592
- }
593
-
594
- /**
595
- * Add the menu item.
596
- *
597
- * @since 2.5.0
598
- *
599
- * @param array $args Menu item configuration.
600
- */
601
- protected function add_admin_menu( array $args ) {
602
- if ( has_filter( 'tgmpa_admin_menu_use_add_theme_page' ) ) {
603
- _deprecated_function( 'The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__( 'Set the parent_slug config variable instead.', 'tgmpa' ) );
604
- }
605
-
606
- if ( 'themes.php' === $this->parent_slug ) {
607
- $this->page_hook = call_user_func( 'add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
608
- } else {
609
- $this->page_hook = call_user_func( 'add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
610
- }
611
- }
612
-
613
- /**
614
- * Echoes plugin installation form.
615
- *
616
- * This method is the callback for the admin_menu method function.
617
- * This displays the admin page and form area where the user can select to install and activate the plugin.
618
- * Aborts early if we're processing a plugin installation action.
619
- *
620
- * @since 1.0.0
621
- *
622
- * @return null Aborts early if we're processing a plugin installation action.
623
- */
624
- public function install_plugins_page() {
625
- // Store new instance of plugin table in object.
626
- $plugin_table = new INBOUND_TGMPA_List_Table;
627
-
628
- // Return early if processing a plugin installation action.
629
- if ( ( ( 'tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action() ) && $plugin_table->process_bulk_actions() ) || $this->do_plugin_install() ) {
630
- return;
631
- }
632
-
633
- // Force refresh of available plugin information so we'll know about manual updates/deletes.
634
- wp_clean_plugins_cache( false );
635
-
636
- ?>
637
- <div class="tgmpa wrap">
638
- <h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
639
- <?php $plugin_table->prepare_items(); ?>
640
-
641
- <?php
642
- if ( ! empty( $this->message ) && is_string( $this->message ) ) {
643
- echo wp_kses_post( $this->message );
644
- }
645
- ?>
646
- <?php $plugin_table->views(); ?>
647
-
648
- <form id="tgmpa-plugins" action="" method="post">
649
- <input type="hidden" name="tgmpa-page" value="<?php echo esc_attr( $this->menu ); ?>" />
650
- <input type="hidden" name="plugin_status" value="<?php echo esc_attr( $plugin_table->view_context ); ?>" />
651
- <?php $plugin_table->display(); ?>
652
- </form>
653
- </div>
654
- <?php
655
- }
656
-
657
- /**
658
- * Installs, updates or activates a plugin depending on the action link clicked by the user.
659
- *
660
- * Checks the $_GET variable to see which actions have been
661
- * passed and responds with the appropriate method.
662
- *
663
- * Uses WP_Filesystem to process and handle the plugin installation
664
- * method.
665
- *
666
- * @since 1.0.0
667
- *
668
- * @uses WP_Filesystem
669
- * @uses WP_Error
670
- * @uses WP_Upgrader
671
- * @uses Plugin_Upgrader
672
- * @uses Plugin_Installer_Skin
673
- * @uses Plugin_Upgrader_Skin
674
- *
675
- * @return boolean True on success, false on failure.
676
- */
677
- protected function do_plugin_install() {
678
- if ( empty( $_GET['plugin'] ) ) {
679
- return false;
680
- }
681
-
682
- // All plugin information will be stored in an array for processing.
683
- $slug = $this->sanitize_key( urldecode( $_GET['plugin'] ) );
684
-
685
- if ( ! isset( $this->plugins[ $slug ] ) ) {
686
- return false;
687
- }
688
-
689
- // Was an install or upgrade action link clicked?
690
- if ( ( isset( $_GET['tgmpa-install'] ) && 'install-plugin' === $_GET['tgmpa-install'] ) || ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) ) {
691
-
692
- $install_type = 'install';
693
- if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
694
- $install_type = 'update';
695
- }
696
-
697
- check_admin_referer( 'tgmpa-' . $install_type, 'tgmpa-nonce' );
698
-
699
- // Pass necessary information via URL if WP_Filesystem is needed.
700
- $url = wp_nonce_url(
701
- add_query_arg(
702
- array(
703
- 'plugin' => urlencode( $slug ),
704
- 'tgmpa-' . $install_type => $install_type . '-plugin',
705
- ),
706
- $this->get_tgmpa_url()
707
- ),
708
- 'tgmpa-' . $install_type,
709
- 'tgmpa-nonce'
710
- );
711
-
712
- $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
713
-
714
- if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, array() ) ) ) {
715
- return true;
716
- }
717
-
718
- if ( ! WP_Filesystem( $creds ) ) {
719
- request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, array() ); // Setup WP_Filesystem.
720
- return true;
721
- }
722
-
723
- /* If we arrive here, we have the filesystem. */
724
-
725
- // Prep variables for Plugin_Installer_Skin class.
726
- $extra = array();
727
- $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
728
- $source = $this->get_download_url( $slug );
729
- $api = ( 'repo' === $this->plugins[ $slug ]['source_type'] ) ? $this->get_plugins_api( $slug ) : null;
730
- $api = ( false !== $api ) ? $api : null;
731
-
732
- $url = add_query_arg(
733
- array(
734
- 'action' => $install_type . '-plugin',
735
- 'plugin' => urlencode( $slug ),
736
- ),
737
- 'update.php'
738
- );
739
-
740
- if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
741
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
742
- }
743
-
744
- $skin_args = array(
745
- 'type' => ( 'bundled' !== $this->plugins[ $slug ]['source_type'] ) ? 'web' : 'upload',
746
- 'title' => sprintf( $this->strings['installing'], $this->plugins[ $slug ]['name'] ),
747
- 'url' => esc_url_raw( $url ),
748
- 'nonce' => $install_type . '-plugin_' . $slug,
749
- 'plugin' => '',
750
- 'api' => $api,
751
- 'extra' => $extra,
752
- );
753
-
754
- if ( 'update' === $install_type ) {
755
- $skin_args['plugin'] = $this->plugins[ $slug ]['file_path'];
756
- $skin = new Plugin_Upgrader_Skin( $skin_args );
757
- } else {
758
- $skin = new Plugin_Installer_Skin( $skin_args );
759
- }
760
-
761
- // Create a new instance of Plugin_Upgrader.
762
- $upgrader = new Plugin_Upgrader( $skin );
763
-
764
- // Perform the action and install the plugin from the $source urldecode().
765
- add_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
766
-
767
- if ( 'update' === $install_type ) {
768
- // Inject our info into the update transient.
769
- $to_inject = array( $slug => $this->plugins[ $slug ] );
770
- $to_inject[ $slug ]['source'] = $source;
771
- $this->inject_update_info( $to_inject );
772
-
773
- $upgrader->upgrade( $this->plugins[ $slug ]['file_path'] );
774
- } else {
775
- $upgrader->install( $source );
776
- }
777
-
778
- remove_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
779
-
780
- // Make sure we have the correct file path now the plugin is installed/updated.
781
- $this->populate_file_path( $slug );
782
-
783
- // Only activate plugins if the config option is set to true and the plugin isn't
784
- // already active (upgrade).
785
- if ( $this->is_automatic && ! $this->is_plugin_active( $slug ) ) {
786
- $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method.
787
- if ( false === $this->activate_single_plugin( $plugin_activate, $slug, true ) ) {
788
- return true; // Finish execution of the function early as we encountered an error.
789
- }
790
- }
791
-
792
- $this->show_tgmpa_version();
793
-
794
- // Display message based on if all plugins are now active or not.
795
- if ( $this->is_tgmpa_complete() ) {
796
- echo '<p>', sprintf( esc_html( $this->strings['complete'] ), '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>' ), '</p>';
797
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
798
- } else {
799
- echo '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
800
- }
801
-
802
- return true;
803
- } elseif ( isset( $this->plugins[ $slug ]['file_path'], $_GET['tgmpa-activate'] ) && 'activate-plugin' === $_GET['tgmpa-activate'] ) {
804
- // Activate action link was clicked.
805
- check_admin_referer( 'tgmpa-activate', 'tgmpa-nonce' );
806
-
807
- if ( false === $this->activate_single_plugin( $this->plugins[ $slug ]['file_path'], $slug ) ) {
808
- return true; // Finish execution of the function early as we encountered an error.
809
- }
810
- }
811
-
812
- return false;
813
- }
814
-
815
- /**
816
- * Inject information into the 'update_plugins' site transient as WP checks that before running an update.
817
- *
818
- * @since 2.5.0
819
- *
820
- * @param array $plugins The plugin information for the plugins which are to be updated.
821
- */
822
- public function inject_update_info( $plugins ) {
823
- $repo_updates = get_site_transient( 'update_plugins' );
824
-
825
- if ( ! is_object( $repo_updates ) ) {
826
- $repo_updates = new stdClass;
827
- }
828
-
829
- foreach ( $plugins as $slug => $plugin ) {
830
- $file_path = $plugin['file_path'];
831
-
832
- if ( empty( $repo_updates->response[ $file_path ] ) ) {
833
- $repo_updates->response[ $file_path ] = new stdClass;
834
- }
835
-
836
- // We only really need to set package, but let's do all we can in case WP changes something.
837
- $repo_updates->response[ $file_path ]->slug = $slug;
838
- $repo_updates->response[ $file_path ]->plugin = $file_path;
839
- $repo_updates->response[ $file_path ]->new_version = $plugin['version'];
840
- $repo_updates->response[ $file_path ]->package = $plugin['source'];
841
- if ( empty( $repo_updates->response[ $file_path ]->url ) && ! empty( $plugin['external_url'] ) ) {
842
- $repo_updates->response[ $file_path ]->url = $plugin['external_url'];
843
- }
844
- }
845
-
846
- set_site_transient( 'update_plugins', $repo_updates );
847
- }
848
-
849
- /**
850
- * Adjust the plugin directory name if necessary.
851
- *
852
- * The final destination directory of a plugin is based on the subdirectory name found in the
853
- * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, this
854
- * subdirectory name is not the same as the expected slug and the plugin will not be recognized
855
- * as installed. This is fixed by adjusting the temporary unzipped source subdirectory name to
856
- * the expected plugin slug.
857
- *
858
- * @since 2.5.0
859
- *
860
- * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
861
- * @param string $remote_source Path to upgrade/zip-file-name.tmp.
862
- * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
863
- * @return string $source
864
- */
865
- public function maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
866
- if ( ! $this->is_tgmpa_page() || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
867
- return $source;
868
- }
869
-
870
- // Check for single file plugins.
871
- $source_files = array_keys( $GLOBALS['wp_filesystem']->dirlist( $remote_source ) );
872
- if ( 1 === count( $source_files ) && false === $GLOBALS['wp_filesystem']->is_dir( $source ) ) {
873
- return $source;
874
- }
875
-
876
- // Multi-file plugin, let's see if the directory is correctly named.
877
- $desired_slug = '';
878
-
879
- // Figure out what the slug is supposed to be.
880
- if ( false === $upgrader->bulk && ! empty( $upgrader->skin->options['extra']['slug'] ) ) {
881
- $desired_slug = $upgrader->skin->options['extra']['slug'];
882
- } else {
883
- // Bulk installer contains less info, so fall back on the info registered here.
884
- foreach ( $this->plugins as $slug => $plugin ) {
885
- if ( ! empty( $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) && $plugin['name'] === $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) {
886
- $desired_slug = $slug;
887
- break;
888
- }
889
- }
890
- unset( $slug, $plugin );
891
- }
892
-
893
- if ( ! empty( $desired_slug ) ) {
894
- $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
895
-
896
- if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
897
- $from = untrailingslashit( $source );
898
- $to = trailingslashit( $remote_source ) . $desired_slug;
899
-
900
- if ( true === $GLOBALS['wp_filesystem']->move( $from, $to ) ) {
901
- return trailingslashit( $to );
902
- } else {
903
- return new WP_Error( 'rename_failed', esc_html__( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
904
- }
905
- } elseif ( empty( $subdir_name ) ) {
906
- return new WP_Error( 'packaged_wrong', esc_html__( 'The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
907
- }
908
- }
909
-
910
- return $source;
911
- }
912
-
913
- /**
914
- * Activate a single plugin and send feedback about the result to the screen.
915
- *
916
- * @since 2.5.0
917
- *
918
- * @param string $file_path Path within wp-plugins/ to main plugin file.
919
- * @param string $slug Plugin slug.
920
- * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false.
921
- * This determines the styling of the output messages.
922
- * @return bool False if an error was encountered, true otherwise.
923
- */
924
- protected function activate_single_plugin( $file_path, $slug, $automatic = false ) {
925
- if ( $this->can_plugin_activate( $slug ) ) {
926
- $activate = activate_plugin( $file_path );
927
-
928
- if ( is_wp_error( $activate ) ) {
929
- echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>',
930
- '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
931
-
932
- return false; // End it here if there is an error with activation.
933
- } else {
934
- if ( ! $automatic ) {
935
- // Make sure message doesn't display again if bulk activation is performed
936
- // immediately after a single activation.
937
- if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
938
- echo '<div id="message" class="updated"><p>', esc_html( $this->strings['activated_successfully'] ), ' <strong>', esc_html( $this->plugins[ $slug ]['name'] ), '.</strong></p></div>';
939
- }
940
- } else {
941
- // Simpler message layout for use on the plugin install page.
942
- echo '<p>', esc_html( $this->strings['plugin_activated'] ), '</p>';
943
- }
944
- }
945
- } elseif ( $this->is_plugin_active( $slug ) ) {
946
- // No simpler message format provided as this message should never be encountered
947
- // on the plugin install page.
948
- echo '<div id="message" class="error"><p>',
949
- sprintf(
950
- esc_html( $this->strings['plugin_already_active'] ),
951
- '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
952
- ),
953
- '</p></div>';
954
- } elseif ( $this->does_plugin_require_update( $slug ) ) {
955
- if ( ! $automatic ) {
956
- // Make sure message doesn't display again if bulk activation is performed
957
- // immediately after a single activation.
958
- if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
959
- echo '<div id="message" class="error"><p>',
960
- sprintf(
961
- esc_html( $this->strings['plugin_needs_higher_version'] ),
962
- '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
963
- ),
964
- '</p></div>';
965
- }
966
- } else {
967
- // Simpler message layout for use on the plugin install page.
968
- echo '<p>', sprintf( esc_html( $this->strings['plugin_needs_higher_version'] ), esc_html( $this->plugins[ $slug ]['name'] ) ), '</p>';
969
- }
970
- }
971
-
972
- return true;
973
- }
974
-
975
- /**
976
- * Echoes required plugin notice.
977
- *
978
- * Outputs a message telling users that a specific plugin is required for
979
- * their theme. If appropriate, it includes a link to the form page where
980
- * users can install and activate the plugin.
981
- *
982
- * Returns early if we're on the Install page.
983
- *
984
- * @since 1.0.0
985
- *
986
- * @global object $current_screen
987
- *
988
- * @return null Returns early if we're on the Install page.
989
- */
990
- public function notices() {
991
- // Remove nag on the install page / Return early if the nag message has been dismissed.
992
- if ( $this->is_tgmpa_page() || get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
993
- return;
994
- }
995
-
996
- // Store for the plugin slugs by message type.
997
- $message = array();
998
-
999
- // Initialize counters used to determine plurality of action link texts.
1000
- $install_link_count = 0;
1001
- $update_link_count = 0;
1002
- $activate_link_count = 0;
1003
-
1004
- foreach ( $this->plugins as $slug => $plugin ) {
1005
- if ( $this->is_plugin_active( $slug ) && false === $this->does_plugin_have_update( $slug ) ) {
1006
- continue;
1007
- }
1008
-
1009
- if ( ! $this->is_plugin_installed( $slug ) ) {
1010
- if ( current_user_can( 'install_plugins' ) ) {
1011
- $install_link_count++;
1012
-
1013
- if ( true === $plugin['required'] ) {
1014
- $message['notice_can_install_required'][] = $slug;
1015
- } else {
1016
- $message['notice_can_install_recommended'][] = $slug;
1017
- }
1018
- } else {
1019
- // Need higher privileges to install the plugin.
1020
- $message['notice_cannot_install'][] = $slug;
1021
- }
1022
- } else {
1023
- if ( ! $this->is_plugin_active( $slug ) && $this->can_plugin_activate( $slug ) ) {
1024
- if ( current_user_can( 'activate_plugins' ) ) {
1025
- $activate_link_count++;
1026
-
1027
- if ( true === $plugin['required'] ) {
1028
- $message['notice_can_activate_required'][] = $slug;
1029
- } else {
1030
- $message['notice_can_activate_recommended'][] = $slug;
1031
- }
1032
- } else {
1033
- // Need higher privileges to activate the plugin.
1034
- $message['notice_cannot_activate'][] = $slug;
1035
- }
1036
- }
1037
-
1038
- if ( $this->does_plugin_require_update( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1039
-
1040
- if ( current_user_can( 'install_plugins' ) ) {
1041
- $update_link_count++;
1042
-
1043
- if ( $this->does_plugin_require_update( $slug ) ) {
1044
- $message['notice_ask_to_update'][] = $slug;
1045
- } elseif ( false !== $this->does_plugin_have_update( $slug ) ) {
1046
- $message['notice_ask_to_update_maybe'][] = $slug;
1047
- }
1048
- } else {
1049
- // Need higher privileges to update the plugin.
1050
- $message['notice_cannot_update'][] = $slug;
1051
- }
1052
- }
1053
- }
1054
- }
1055
- unset( $slug, $plugin );
1056
-
1057
- // If we have notices to display, we move forward.
1058
- if ( ! empty( $message ) ) {
1059
- krsort( $message ); // Sort messages.
1060
- $rendered = '';
1061
-
1062
- // As add_settings_error() wraps the final message in a <p> and as the final message can't be
1063
- // filtered, using <p>'s in our html would render invalid html output.
1064
- $line_template = '<span style="display: block; margin: 0.5em 0.5em 0 0; clear: both;">%s</span>' . "\n";
1065
-
1066
- // If dismissable is false and a message is set, output it now.
1067
- if ( ! $this->dismissable && ! empty( $this->dismiss_msg ) ) {
1068
- $rendered .= sprintf( $line_template, wp_kses_post( $this->dismiss_msg ) );
1069
- }
1070
-
1071
- // Render the individual message lines for the notice.
1072
- foreach ( $message as $type => $plugin_group ) {
1073
- $linked_plugins = array();
1074
-
1075
- // Get the external info link for a plugin if one is available.
1076
- foreach ( $plugin_group as $plugin_slug ) {
1077
- $linked_plugins[] = $this->get_info_link( $plugin_slug );
1078
- }
1079
- unset( $plugin_slug );
1080
-
1081
- $count = count( $plugin_group );
1082
- $linked_plugins = array_map( array( 'INBOUND_TGM_Utils', 'wrap_in_em' ), $linked_plugins );
1083
- $last_plugin = array_pop( $linked_plugins ); // Pop off last name to prep for readability.
1084
-
1085
- $imploded = empty( $linked_plugins ) ? $last_plugin : ( implode( ', ', $linked_plugins ) . ' <span class="inbound-and">and</span>' . ' ' . $last_plugin );
1086
-
1087
- $rendered .= sprintf(
1088
- $line_template,
1089
- sprintf(
1090
- translate_nooped_plural( $this->strings[ $type ], $count, 'tgmpa' ),
1091
- $imploded,
1092
- $count
1093
- )
1094
- );
1095
-
1096
- if ( 0 === strpos( $type, 'notice_cannot' ) ) {
1097
- $rendered .= $this->strings['contact_admin'];
1098
- }
1099
- }
1100
- unset( $type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded );
1101
-
1102
- // Setup action links.
1103
- $action_links = array(
1104
- 'install' => '',
1105
- 'update' => '',
1106
- 'activate' => '',
1107
- 'dismiss' => $this->dismissable ? '<a href="' . esc_url( add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ) ) . '" class="dismiss-notice" target="_parent">' . esc_html( $this->strings['dismiss'] ) . '</a>' : '',
1108
- );
1109
-
1110
- $link_template = '<a href="%2$s">%1$s</a>';
1111
-
1112
- if ( current_user_can( 'install_plugins' ) ) {
1113
- if ( $install_link_count > 0 ) {
1114
- $action_links['install'] = sprintf(
1115
- $link_template,
1116
- translate_nooped_plural( $this->strings['install_link'], $install_link_count, 'tgmpa' ),
1117
- esc_url( $this->get_tgmpa_status_url( 'install' ) )
1118
- );
1119
- }
1120
- if ( $update_link_count > 0 ) {
1121
- $action_links['update'] = sprintf(
1122
- $link_template,
1123
- translate_nooped_plural( $this->strings['update_link'], $update_link_count, 'tgmpa' ),
1124
- esc_url( $this->get_tgmpa_status_url( 'update' ) )
1125
- );
1126
- }
1127
- }
1128
-
1129
- if ( current_user_can( 'activate_plugins' ) && $activate_link_count > 0 ) {
1130
- $action_links['activate'] = sprintf(
1131
- $link_template,
1132
- translate_nooped_plural( $this->strings['activate_link'], $activate_link_count, 'tgmpa' ),
1133
- esc_url( $this->get_tgmpa_status_url( 'activate' ) )
1134
- );
1135
- }
1136
-
1137
- $action_links = apply_filters( 'tgmpa_notice_action_links', $action_links );
1138
-
1139
- $action_links = array_filter( (array) $action_links ); // Remove any empty array items.
1140
-
1141
- if ( ! empty( $action_links ) && is_array( $action_links ) ) {
1142
- $action_links = sprintf( $line_template, implode( ' | ', $action_links ) );
1143
- $rendered .= apply_filters( 'tgmpa_notice_rendered_action_links', $action_links );
1144
- }
1145
-
1146
- // Register the nag messages and prepare them to be processed.
1147
- if ( ! empty( $this->strings['nag_type'] ) ) {
1148
- add_settings_error( 'tgmpa', 'tgmpa', $rendered, sanitize_html_class( strtolower( $this->strings['nag_type'] ) ) );
1149
- } else {
1150
- $nag_class = version_compare( $this->wp_version, '3.8', '<' ) ? 'updated' : 'update-nag';
1151
- add_settings_error( 'tgmpa', 'tgmpa', $rendered, $nag_class );
1152
- }
1153
- }
1154
-
1155
- // Admin options pages already output settings_errors, so this is to avoid duplication.
1156
- if ( 'options-general' !== $GLOBALS['current_screen']->parent_base ) {
1157
- $this->display_settings_errors();
1158
- }
1159
- }
1160
-
1161
- /**
1162
- * Display settings errors and remove those which have been displayed to avoid duplicate messages showing
1163
- *
1164
- * @since 2.5.0
1165
- */
1166
- protected function display_settings_errors() {
1167
- global $wp_settings_errors;
1168
-
1169
- settings_errors( 'tgmpa' );
1170
-
1171
- foreach ( (array) $wp_settings_errors as $key => $details ) {
1172
- if ( 'tgmpa' === $details['setting'] ) {
1173
- unset( $wp_settings_errors[ $key ] );
1174
- break;
1175
- }
1176
- }
1177
- }
1178
-
1179
- /**
1180
- * Add dismissable admin notices.
1181
- *
1182
- * Appends a link to the admin nag messages. If clicked, the admin notice disappears and no longer is visible to users.
1183
- *
1184
- * @since 2.1.0
1185
- */
1186
- public function dismiss() {
1187
- if ( isset( $_GET['tgmpa-dismiss'] ) ) {
1188
- update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1 );
1189
- }
1190
- }
1191
-
1192
- /**
1193
- * Add individual plugin to our collection of plugins.
1194
- *
1195
- * If the required keys are not set or the plugin has already
1196
- * been registered, the plugin is not added.
1197
- *
1198
- * @since 2.0.0
1199
- *
1200
- * @param array|null $plugin Array of plugin arguments or null if invalid argument.
1201
- * @return null Return early if incorrect argument.
1202
- */
1203
- public function register( $plugin ) {
1204
- if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
1205
- return;
1206
- }
1207
-
1208
- if ( empty( $plugin['slug'] ) || ! is_string( $plugin['slug'] ) || isset( $this->plugins[ $plugin['slug'] ] ) ) {
1209
- return;
1210
- }
1211
-
1212
- $defaults = array(
1213
- 'name' => '', // String
1214
- 'slug' => '', // String
1215
- 'source' => 'repo', // String
1216
- 'required' => false, // Boolean
1217
- 'version' => '', // String
1218
- 'force_activation' => false, // Boolean
1219
- 'force_deactivation' => false, // Boolean
1220
- 'external_url' => '', // String
1221
- 'is_callable' => '', // String|Array.
1222
- );
1223
-
1224
- // Prepare the received data.
1225
- $plugin = wp_parse_args( $plugin, $defaults );
1226
-
1227
- // Standardize the received slug.
1228
- $plugin['slug'] = $this->sanitize_key( $plugin['slug'] );
1229
-
1230
- // Forgive users for using string versions of booleans or floats for version number.
1231
- $plugin['version'] = (string) $plugin['version'];
1232
- $plugin['source'] = empty( $plugin['source'] ) ? 'repo' : $plugin['source'];
1233
- $plugin['required'] = INBOUND_TGM_Utils::validate_bool( $plugin['required'] );
1234
- $plugin['force_activation'] = INBOUND_TGM_Utils::validate_bool( $plugin['force_activation'] );
1235
- $plugin['force_deactivation'] = INBOUND_TGM_Utils::validate_bool( $plugin['force_deactivation'] );
1236
-
1237
- // Enrich the received data.
1238
- $plugin['file_path'] = $this->_get_plugin_basename_from_slug( $plugin['slug'] );
1239
- $plugin['source_type'] = $this->get_plugin_source_type( $plugin['source'] );
1240
-
1241
- // Set the class properties.
1242
- $this->plugins[ $plugin['slug'] ] = $plugin;
1243
- $this->sort_order[ $plugin['slug'] ] = $plugin['name'];
1244
-
1245
- // Should we add the force activation hook ?
1246
- if ( true === $plugin['force_activation'] ) {
1247
- $this->has_forced_activation = true;
1248
- }
1249
-
1250
- // Should we add the force deactivation hook ?
1251
- if ( true === $plugin['force_deactivation'] ) {
1252
- $this->has_forced_deactivation = true;
1253
- }
1254
- }
1255
-
1256
- /**
1257
- * Determine what type of source the plugin comes from.
1258
- *
1259
- * @since 2.5.0
1260
- *
1261
- * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path
1262
- * (= bundled) or an external URL.
1263
- * @return string 'repo', 'external', or 'bundled'
1264
- */
1265
- protected function get_plugin_source_type( $source ) {
1266
- if ( 'repo' === $source || preg_match( self::WP_REPO_REGEX, $source ) ) {
1267
- return 'repo';
1268
- } elseif ( preg_match( self::IS_URL_REGEX, $source ) ) {
1269
- return 'external';
1270
- } else {
1271
- return 'bundled';
1272
- }
1273
- }
1274
-
1275
- /**
1276
- * Sanitizes a string key.
1277
- *
1278
- * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are*
1279
- * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase
1280
- * characters in the plugin directory path/slug. Silly them.
1281
- *
1282
- * @see https://developer.wordpress.org/reference/hooks/sanitize_key/
1283
- *
1284
- * @since 2.5.0
1285
- *
1286
- * @param string $key String key.
1287
- * @return string Sanitized key
1288
- */
1289
- public function sanitize_key( $key ) {
1290
- $raw_key = $key;
1291
- $key = preg_replace( '`[^A-Za-z0-9_-]`', '', $key );
1292
-
1293
- /**
1294
- * Filter a sanitized key string.
1295
- *
1296
- * @since 3.0.0
1297
- *
1298
- * @param string $key Sanitized key.
1299
- * @param string $raw_key The key prior to sanitization.
1300
- */
1301
- return apply_filters( 'tgmpa_sanitize_key', $key, $raw_key );
1302
- }
1303
-
1304
- /**
1305
- * Amend default configuration settings.
1306
- *
1307
- * @since 2.0.0
1308
- *
1309
- * @param array $config Array of config options to pass as class properties.
1310
- */
1311
- public function config( $config ) {
1312
- $keys = array(
1313
- 'id',
1314
- 'default_path',
1315
- 'has_notices',
1316
- 'dismissable',
1317
- 'dismiss_msg',
1318
- 'menu',
1319
- 'parent_slug',
1320
- 'capability',
1321
- 'is_automatic',
1322
- 'message',
1323
- 'strings',
1324
- );
1325
-
1326
- foreach ( $keys as $key ) {
1327
- if ( isset( $config[ $key ] ) ) {
1328
- if ( is_array( $config[ $key ] ) ) {
1329
- $this->$key = array_merge( $this->$key, $config[ $key ] );
1330
- } else {
1331
- $this->$key = $config[ $key ];
1332
- }
1333
- }
1334
- }
1335
- }
1336
-
1337
- /**
1338
- * Amend action link after plugin installation.
1339
- *
1340
- * @since 2.0.0
1341
- *
1342
- * @param array $install_actions Existing array of actions.
1343
- * @return array Amended array of actions.
1344
- */
1345
- public function actions( $install_actions ) {
1346
- // Remove action links on the TGMPA install page.
1347
- if ( $this->is_tgmpa_page() ) {
1348
- return false;
1349
- }
1350
-
1351
- return $install_actions;
1352
- }
1353
-
1354
- /**
1355
- * Flushes the plugins cache on theme switch to prevent stale entries
1356
- * from remaining in the plugin table.
1357
- *
1358
- * @since 2.4.0
1359
- *
1360
- * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache.
1361
- * Parameter added in v2.5.0.
1362
- */
1363
- public function flush_plugins_cache( $clear_update_cache = true ) {
1364
- wp_clean_plugins_cache( $clear_update_cache );
1365
- }
1366
-
1367
- /**
1368
- * Set file_path key for each installed plugin.
1369
- *
1370
- * @since 2.1.0
1371
- *
1372
- * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin.
1373
- * Parameter added in v2.5.0.
1374
- */
1375
- public function populate_file_path( $plugin_slug = '' ) {
1376
- if ( ! empty( $plugin_slug ) && is_string( $plugin_slug ) && isset( $this->plugins[ $plugin_slug ] ) ) {
1377
- $this->plugins[ $plugin_slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $plugin_slug );
1378
- } else {
1379
- // Add file_path key for all plugins.
1380
- foreach ( $this->plugins as $slug => $values ) {
1381
- $this->plugins[ $slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $slug );
1382
- }
1383
- }
1384
- }
1385
-
1386
- /**
1387
- * Helper function to extract the file path of the plugin file from the
1388
- * plugin slug, if the plugin is installed.
1389
- *
1390
- * @since 2.0.0
1391
- *
1392
- * @param string $slug Plugin slug (typically folder name) as provided by the developer.
1393
- * @return string Either file path for plugin if installed, or just the plugin slug.
1394
- */
1395
- protected function _get_plugin_basename_from_slug( $slug ) {
1396
- $keys = array_keys( $this->get_plugins() );
1397
-
1398
- foreach ( $keys as $key ) {
1399
- if ( preg_match( '|^' . $slug . '/|', $key ) ) {
1400
- return $key;
1401
- }
1402
- }
1403
-
1404
- return $slug;
1405
- }
1406
-
1407
- /**
1408
- * Retrieve plugin data, given the plugin name.
1409
- *
1410
- * Loops through the registered plugins looking for $name. If it finds it,
1411
- * it returns the $data from that plugin. Otherwise, returns false.
1412
- *
1413
- * @since 2.1.0
1414
- *
1415
- * @param string $name Name of the plugin, as it was registered.
1416
- * @param string $data Optional. Array key of plugin data to return. Default is slug.
1417
- * @return string|boolean Plugin slug if found, false otherwise.
1418
- */
1419
- public function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1420
- foreach ( $this->plugins as $values ) {
1421
- if ( $name === $values['name'] && isset( $values[ $data ] ) ) {
1422
- return $values[ $data ];
1423
- }
1424
- }
1425
-
1426
- return false;
1427
- }
1428
-
1429
- /**
1430
- * Retrieve the download URL for a package.
1431
- *
1432
- * @since 2.5.0
1433
- *
1434
- * @param string $slug Plugin slug.
1435
- * @return string Plugin download URL or path to local file or empty string if undetermined.
1436
- */
1437
- public function get_download_url( $slug ) {
1438
- $dl_source = '';
1439
-
1440
- switch ( $this->plugins[ $slug ]['source_type'] ) {
1441
- case 'repo':
1442
- return $this->get_wp_repo_download_url( $slug );
1443
- case 'external':
1444
- return $this->plugins[ $slug ]['source'];
1445
- case 'bundled':
1446
- return $this->default_path . $this->plugins[ $slug ]['source'];
1447
- }
1448
-
1449
- return $dl_source; // Should never happen.
1450
- }
1451
-
1452
- /**
1453
- * Retrieve the download URL for a WP repo package.
1454
- *
1455
- * @since 2.5.0
1456
- *
1457
- * @param string $slug Plugin slug.
1458
- * @return string Plugin download URL.
1459
- */
1460
- protected function get_wp_repo_download_url( $slug ) {
1461
- $source = '';
1462
- $api = $this->get_plugins_api( $slug );
1463
-
1464
- if ( false !== $api && isset( $api->download_link ) ) {
1465
- $source = $api->download_link;
1466
- }
1467
-
1468
- return $source;
1469
- }
1470
-
1471
- /**
1472
- * Try to grab information from WordPress API.
1473
- *
1474
- * @since 2.5.0
1475
- *
1476
- * @param string $slug Plugin slug.
1477
- * @return object Plugins_api response object on success, WP_Error on failure.
1478
- */
1479
- protected function get_plugins_api( $slug ) {
1480
- static $api = array(); // Cache received responses.
1481
-
1482
- if ( ! isset( $api[ $slug ] ) ) {
1483
- if ( ! function_exists( 'plugins_api' ) ) {
1484
- require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1485
- }
1486
-
1487
- $response = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
1488
-
1489
- $api[ $slug ] = false;
1490
-
1491
- if ( is_wp_error( $response ) ) {
1492
- wp_die( esc_html( $this->strings['oops'] ) );
1493
- } else {
1494
- $api[ $slug ] = $response;
1495
- }
1496
- }
1497
-
1498
- return $api[ $slug ];
1499
- }
1500
-
1501
- /**
1502
- * Retrieve a link to a plugin information page.
1503
- *
1504
- * @since 2.5.0
1505
- *
1506
- * @param string $slug Plugin slug.
1507
- * @return string Fully formed html link to a plugin information page if available
1508
- * or the plugin name if not.
1509
- */
1510
- public function get_info_link( $slug ) {
1511
- if ( ! empty( $this->plugins[ $slug ]['external_url'] ) && preg_match( self::IS_URL_REGEX, $this->plugins[ $slug ]['external_url'] ) ) {
1512
- $link = sprintf(
1513
- '<a href="%1$s" target="_blank">%2$s</a>',
1514
- esc_url( $this->plugins[ $slug ]['external_url'] ),
1515
- esc_html( $this->plugins[ $slug ]['name'] )
1516
- );
1517
- } elseif ( 'repo' === $this->plugins[ $slug ]['source_type'] ) {
1518
- $url = add_query_arg(
1519
- array(
1520
- 'tab' => 'plugin-information',
1521
- 'plugin' => urlencode( $slug ),
1522
- 'TB_iframe' => 'true',
1523
- 'width' => '640',
1524
- 'height' => '500',
1525
- ),
1526
- self_admin_url( 'plugin-install.php' )
1527
- );
1528
-
1529
- $link = sprintf(
1530
- '<a href="%1$s" class="thickbox">%2$s</a>',
1531
- esc_url( $url ),
1532
- $this->plugins[ $slug ]['name']
1533
- );
1534
- } else {
1535
- $link = esc_html( $this->plugins[ $slug ]['name'] ); // No hyperlink.
1536
- }
1537
-
1538
- return $link;
1539
- }
1540
-
1541
- /**
1542
- * Determine if we're on the TGMPA Install page.
1543
- *
1544
- * @since 2.1.0
1545
- *
1546
- * @return boolean True when on the TGMPA page, false otherwise.
1547
- */
1548
- protected function is_tgmpa_page() {
1549
- return isset( $_GET['page'] ) && $this->menu === $_GET['page'];
1550
- }
1551
-
1552
- /**
1553
- * Retrieve the URL to the TGMPA Install page.
1554
- *
1555
- * I.e. depending on the config settings passed something along the lines of:
1556
- * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins
1557
- *
1558
- * @since 2.5.0
1559
- *
1560
- * @return string Properly encoded URL (not escaped).
1561
- */
1562
- public function get_tgmpa_url() {
1563
- static $url;
1564
-
1565
- if ( ! isset( $url ) ) {
1566
- $parent = $this->parent_slug;
1567
- if ( false === strpos( $parent, '.php' ) ) {
1568
- $parent = 'admin.php';
1569
- }
1570
- $url = add_query_arg(
1571
- array(
1572
- 'page' => urlencode( $this->menu ),
1573
- ),
1574
- self_admin_url( $parent )
1575
- );
1576
- }
1577
-
1578
- return $url;
1579
- }
1580
-
1581
- /**
1582
- * Retrieve the URL to the TGMPA Install page for a specific plugin status (view).
1583
- *
1584
- * I.e. depending on the config settings passed something along the lines of:
1585
- * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install
1586
- *
1587
- * @since 2.5.0
1588
- *
1589
- * @param string $status Plugin status - either 'install', 'update' or 'activate'.
1590
- * @return string Properly encoded URL (not escaped).
1591
- */
1592
- public function get_tgmpa_status_url( $status ) {
1593
- return add_query_arg(
1594
- array(
1595
- 'plugin_status' => urlencode( $status ),
1596
- ),
1597
- $this->get_tgmpa_url()
1598
- );
1599
- }
1600
-
1601
- /**
1602
- * Determine whether there are open actions for plugins registered with TGMPA.
1603
- *
1604
- * @since 2.5.0
1605
- *
1606
- * @return bool True if complete, i.e. no outstanding actions. False otherwise.
1607
- */
1608
- public function is_tgmpa_complete() {
1609
- $complete = true;
1610
- foreach ( $this->plugins as $slug => $plugin ) {
1611
- if ( ! $this->is_plugin_active( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1612
- $complete = false;
1613
- break;
1614
- }
1615
- }
1616
-
1617
- return $complete;
1618
- }
1619
-
1620
- /**
1621
- * Check if a plugin is installed. Does not take must-use plugins into account.
1622
- *
1623
- * @since 2.5.0
1624
- *
1625
- * @param string $slug Plugin slug.
1626
- * @return bool True if installed, false otherwise.
1627
- */
1628
- public function is_plugin_installed( $slug ) {
1629
- $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1630
-
1631
- return ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ] ) );
1632
- }
1633
-
1634
- /**
1635
- * Check if a plugin is active.
1636
- *
1637
- * @since 2.5.0
1638
- *
1639
- * @param string $slug Plugin slug.
1640
- * @return bool True if active, false otherwise.
1641
- */
1642
- public function is_plugin_active( $slug ) {
1643
- return ( ( ! empty( $this->plugins[ $slug ]['is_callable'] ) && is_callable( $this->plugins[ $slug ]['is_callable'] ) ) || is_plugin_active( $this->plugins[ $slug ]['file_path'] ) );
1644
- }
1645
-
1646
- /**
1647
- * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required
1648
- * available, check whether the current install meets them.
1649
- *
1650
- * @since 2.5.0
1651
- *
1652
- * @param string $slug Plugin slug.
1653
- * @return bool True if OK to update, false otherwise.
1654
- */
1655
- public function can_plugin_update( $slug ) {
1656
- // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1657
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1658
- return true;
1659
- }
1660
-
1661
- $api = $this->get_plugins_api( $slug );
1662
-
1663
- if ( false !== $api && isset( $api->requires ) ) {
1664
- return version_compare( $GLOBALS['wp_version'], $api->requires, '>=' );
1665
- }
1666
-
1667
- // No usable info received from the plugins API, presume we can update.
1668
- return true;
1669
- }
1670
-
1671
- /**
1672
- * Check if a plugin can be activated, i.e. is not currently active and meets the minimum
1673
- * plugin version requirements set in TGMPA (if any).
1674
- *
1675
- * @since 2.5.0
1676
- *
1677
- * @param string $slug Plugin slug.
1678
- * @return bool True if OK to activate, false otherwise.
1679
- */
1680
- public function can_plugin_activate( $slug ) {
1681
- return ( ! $this->is_plugin_active( $slug ) && ! $this->does_plugin_require_update( $slug ) );
1682
- }
1683
-
1684
- /**
1685
- * Retrieve the version number of an installed plugin.
1686
- *
1687
- * @since 2.5.0
1688
- *
1689
- * @param string $slug Plugin slug.
1690
- * @return string Version number as string or an empty string if the plugin is not installed
1691
- * or version unknown (plugins which don't comply with the plugin header standard).
1692
- */
1693
- public function get_installed_version( $slug ) {
1694
- $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1695
-
1696
- if ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'] ) ) {
1697
- return $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'];
1698
- }
1699
-
1700
- return '';
1701
- }
1702
-
1703
- /**
1704
- * Check whether a plugin complies with the minimum version requirements.
1705
- *
1706
- * @since 2.5.0
1707
- *
1708
- * @param string $slug Plugin slug.
1709
- * @return bool True when a plugin needs to be updated, otherwise false.
1710
- */
1711
- public function does_plugin_require_update( $slug ) {
1712
- $installed_version = $this->get_installed_version( $slug );
1713
- $minimum_version = $this->plugins[ $slug ]['version'];
1714
-
1715
- return version_compare( $minimum_version, $installed_version, '>' );
1716
- }
1717
-
1718
- /**
1719
- * Check whether there is an update available for a plugin.
1720
- *
1721
- * @since 2.5.0
1722
- *
1723
- * @param string $slug Plugin slug.
1724
- * @return false|string Version number string of the available update or false if no update available.
1725
- */
1726
- public function does_plugin_have_update( $slug ) {
1727
- // Presume bundled and external plugins will point to a package which meets the minimum required version.
1728
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1729
- if ( $this->does_plugin_require_update( $slug ) ) {
1730
- return $this->plugins[ $slug ]['version'];
1731
- }
1732
-
1733
- return false;
1734
- }
1735
-
1736
- $repo_updates = get_site_transient( 'update_plugins' );
1737
-
1738
- if ( isset( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version ) ) {
1739
- return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version;
1740
- }
1741
-
1742
- return false;
1743
- }
1744
-
1745
- /**
1746
- * Retrieve potential upgrade notice for a plugin.
1747
- *
1748
- * @since 2.5.0
1749
- *
1750
- * @param string $slug Plugin slug.
1751
- * @return string The upgrade notice or an empty string if no message was available or provided.
1752
- */
1753
- public function get_upgrade_notice( $slug ) {
1754
- // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1755
- if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1756
- return '';
1757
- }
1758
-
1759
- $repo_updates = get_site_transient( 'update_plugins' );
1760
-
1761
- if ( ! empty( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice ) ) {
1762
- return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice;
1763
- }
1764
-
1765
- return '';
1766
- }
1767
-
1768
- /**
1769
- * Wrapper around the core WP get_plugins function, making sure it's actually available.
1770
- *
1771
- * @since 2.5.0
1772
- *
1773
- * @param string $plugin_folder Optional. Relative path to single plugin folder.
1774
- * @return array Array of installed plugins with plugin information.
1775
- */
1776
- public function get_plugins( $plugin_folder = '' ) {
1777
- if ( ! function_exists( 'get_plugins' ) ) {
1778
- require_once ABSPATH . 'wp-admin/includes/plugin.php';
1779
- }
1780
-
1781
- return get_plugins( $plugin_folder );
1782
- }
1783
-
1784
- /**
1785
- * Delete dismissable nag option when theme is switched.
1786
- *
1787
- * This ensures that the user(s) is/are again reminded via nag of required
1788
- * and/or recommended plugins if they re-activate the theme.
1789
- *
1790
- * @since 2.1.1
1791
- */
1792
- public function update_dismiss() {
1793
- delete_metadata( 'user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true );
1794
- }
1795
-
1796
- /**
1797
- * Forces plugin activation if the parameter 'force_activation' is
1798
- * set to true.
1799
- *
1800
- * This allows theme authors to specify certain plugins that must be
1801
- * active at all times while using the current theme.
1802
- *
1803
- * Please take special care when using this parameter as it has the
1804
- * potential to be harmful if not used correctly. Setting this parameter
1805
- * to true will not allow the specified plugin to be deactivated unless
1806
- * the user switches themes.
1807
- *
1808
- * @since 2.2.0
1809
- */
1810
- public function force_activation() {
1811
- foreach ( $this->plugins as $slug => $plugin ) {
1812
- if ( true === $plugin['force_activation'] ) {
1813
- if ( ! $this->is_plugin_installed( $slug ) ) {
1814
- // Oops, plugin isn't there so iterate to next condition.
1815
- continue;
1816
- } elseif ( $this->can_plugin_activate( $slug ) ) {
1817
- // There we go, activate the plugin.
1818
- activate_plugin( $plugin['file_path'] );
1819
- }
1820
- }
1821
- }
1822
- }
1823
-
1824
- /**
1825
- * Forces plugin deactivation if the parameter 'force_deactivation'
1826
- * is set to true.
1827
- *
1828
- * This allows theme authors to specify certain plugins that must be
1829
- * deactivated upon switching from the current theme to another.
1830
- *
1831
- * Please take special care when using this parameter as it has the
1832
- * potential to be harmful if not used correctly.
1833
- *
1834
- * @since 2.2.0
1835
- */
1836
- public function force_deactivation() {
1837
- foreach ( $this->plugins as $slug => $plugin ) {
1838
- // Only proceed forward if the parameter is set to true and plugin is active.
1839
- if ( true === $plugin['force_deactivation'] && $this->is_plugin_active( $slug ) ) {
1840
- deactivate_plugins( $plugin['file_path'] );
1841
- }
1842
- }
1843
- }
1844
-
1845
- /**
1846
- * Echo the current TGMPA version number to the page.
1847
- */
1848
- public function show_tgmpa_version() {
1849
- echo '<p style="float: right; padding: 0em 1.5em 0.5em 0;"><strong><small>',
1850
- esc_html( sprintf( _x( 'TGMPA v%s', '%s = version number', 'tgmpa' ), self::TGMPA_VERSION ) ),
1851
- '</small></strong></p>';
1852
- }
1853
-
1854
- /**
1855
- * Returns the singleton instance of the class.
1856
- *
1857
- * @since 2.4.0
1858
- *
1859
- * @return object The INBOUND_Plugin_Activation object.
1860
- */
1861
- public static function get_instance() {
1862
- if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
1863
- self::$instance = new self();
1864
- }
1865
-
1866
- return self::$instance;
1867
- }
1868
- }
1869
-
1870
- if ( ! function_exists( 'load_tgm_plugin_activation' ) ) {
1871
- /**
1872
- * Ensure only one instance of the class is ever invoked.
1873
- */
1874
- function load_tgm_plugin_activation() {
1875
- $GLOBALS['tgmpa'] = INBOUND_Plugin_Activation::get_instance();
1876
- }
1877
- }
1878
-
1879
- if ( did_action( 'plugins_loaded' ) ) {
1880
- load_tgm_plugin_activation();
1881
- } else {
1882
- add_action( 'plugins_loaded', 'load_tgm_plugin_activation' );
1883
- }
1884
- }
1885
-
1886
- if ( ! function_exists( 'inbound_activate' ) ) {
1887
- /**
1888
- * Helper function to register a collection of required plugins.
1889
- *
1890
- * @since 2.0.0
1891
- * @api
1892
- *
1893
- * @param array $plugins An array of plugin arrays.
1894
- * @param array $config Optional. An array of configuration values.
1895
- */
1896
- function inbound_activate( $plugins, $config = array() ) {
1897
- $instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
1898
-
1899
- foreach ( $plugins as $plugin ) {
1900
- call_user_func( array( $instance, 'register' ), $plugin );
1901
- }
1902
-
1903
- if ( ! empty( $config ) && is_array( $config ) ) {
1904
- call_user_func( array( $instance, 'config' ), $config );
1905
- }
1906
- }
1907
- }
1908
-
1909
- /**
1910
- * WP_List_Table isn't always available. If it isn't available,
1911
- * we load it here.
1912
- *
1913
- * @since 2.2.0
1914
- */
1915
- if ( ! class_exists( 'WP_List_Table' ) ) {
1916
- require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
1917
- }
1918
-
1919
- if ( ! class_exists( 'INBOUND_TGMPA_List_Table' ) ) {
1920
-
1921
- /**
1922
- * List table class for handling plugins.
1923
- *
1924
- * Extends the WP_List_Table class to provide a future-compatible
1925
- * way of listing out all required/recommended plugins.
1926
- *
1927
- * Gives users an interface similar to the Plugin Administration
1928
- * area with similar (albeit stripped down) capabilities.
1929
- *
1930
- * This class also allows for the bulk install of plugins.
1931
- *
1932
- * @since 2.2.0
1933
- *
1934
- * @package TGM-Plugin-Activation
1935
- * @author Thomas Griffin
1936
- * @author Gary Jones
1937
- */
1938
- class INBOUND_TGMPA_List_Table extends WP_List_Table {
1939
- /**
1940
- * TGMPA instance.
1941
- *
1942
- * @since 2.5.0
1943
- *
1944
- * @var object
1945
- */
1946
- protected $tgmpa;
1947
-
1948
- /**
1949
- * The currently chosen view.
1950
- *
1951
- * @since 2.5.0
1952
- *
1953
- * @var string One of: 'all', 'install', 'update', 'activate'
1954
- */
1955
- public $view_context = 'all';
1956
-
1957
- /**
1958
- * The plugin counts for the various views.
1959
- *
1960
- * @since 2.5.0
1961
- *
1962
- * @var array
1963
- */
1964
- protected $view_totals = array(
1965
- 'all' => 0,
1966
- 'install' => 0,
1967
- 'update' => 0,
1968
- 'activate' => 0,
1969
- );
1970
-
1971
- /**
1972
- * References parent constructor and sets defaults for class.
1973
- *
1974
- * @since 2.2.0
1975
- */
1976
- public function __construct() {
1977
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
1978
-
1979
- parent::__construct(
1980
- array(
1981
- 'singular' => 'plugin',
1982
- 'plural' => 'plugins',
1983
- 'ajax' => false,
1984
- )
1985
- );
1986
-
1987
- if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) {
1988
- $this->view_context = sanitize_key( $_REQUEST['plugin_status'] );
1989
- }
1990
-
1991
- add_filter( 'tgmpa_table_data_items', array( $this, 'sort_table_items' ) );
1992
- }
1993
-
1994
- /**
1995
- * Get a list of CSS classes for the <table> tag.
1996
- *
1997
- * Overruled to prevent the 'plural' argument from being added.
1998
- *
1999
- * @since 2.5.0
2000
- *
2001
- * @return array CSS classnames.
2002
- */
2003
- public function get_table_classes() {
2004
- return array( 'widefat', 'fixed' );
2005
- }
2006
-
2007
- /**
2008
- * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table.
2009
- *
2010
- * @since 2.2.0
2011
- *
2012
- * @return array $table_data Information for use in table.
2013
- */
2014
- protected function _gather_plugin_data() {
2015
- // Load thickbox for plugin links.
2016
- $this->tgmpa->admin_init();
2017
- $this->tgmpa->thickbox();
2018
-
2019
- // Categorize the plugins which have open actions.
2020
- $plugins = $this->categorize_plugins_to_views();
2021
-
2022
- // Set the counts for the view links.
2023
- $this->set_view_totals( $plugins );
2024
-
2025
- // Prep variables for use and grab list of all installed plugins.
2026
- $table_data = array();
2027
- $i = 0;
2028
-
2029
- // Redirect to the 'all' view if no plugins where found for the selected view context.
2030
- if ( empty( $plugins[ $this->view_context ] ) ) {
2031
- $this->view_context = 'all';
2032
- }
2033
-
2034
- foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) {
2035
- $table_data[ $i ]['sanitized_plugin'] = $plugin['name'];
2036
- $table_data[ $i ]['slug'] = $slug;
2037
- $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>';
2038
- $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] );
2039
- $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] );
2040
- $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug );
2041
- $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug );
2042
- $table_data[ $i ]['minimum_version'] = $plugin['version'];
2043
- $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug );
2044
-
2045
- // Prep the upgrade notice info.
2046
- $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug );
2047
- if ( ! empty( $upgrade_notice ) ) {
2048
- $table_data[ $i ]['upgrade_notice'] = $upgrade_notice;
2049
-
2050
- add_action( "tgmpa_after_plugin_row_$slug", array( $this, 'wp_plugin_update_row' ), 10, 2 );
2051
- }
2052
-
2053
- $table_data[ $i ] = apply_filters( 'tgmpa_table_data_item', $table_data[ $i ], $plugin );
2054
-
2055
- $i++;
2056
- }
2057
-
2058
- return $table_data;
2059
- }
2060
-
2061
- /**
2062
- * Categorize the plugins which have open actions into views for the TGMPA page.
2063
- *
2064
- * @since 2.5.0
2065
- */
2066
- protected function categorize_plugins_to_views() {
2067
- $plugins = array(
2068
- 'all' => array(), // Meaning: all plugins which still have open actions.
2069
- 'install' => array(),
2070
- 'update' => array(),
2071
- 'activate' => array(),
2072
- );
2073
-
2074
- foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
2075
- if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2076
- // No need to display plugins if they are installed, up-to-date and active.
2077
- continue;
2078
- } else {
2079
- $plugins['all'][ $slug ] = $plugin;
2080
-
2081
- if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2082
- $plugins['install'][ $slug ] = $plugin;
2083
- } else {
2084
- if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2085
- $plugins['update'][ $slug ] = $plugin;
2086
- }
2087
-
2088
- if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2089
- $plugins['activate'][ $slug ] = $plugin;
2090
- }
2091
- }
2092
- }
2093
- }
2094
-
2095
- return $plugins;
2096
- }
2097
-
2098
- /**
2099
- * Set the counts for the view links.
2100
- *
2101
- * @since 2.5.0
2102
- *
2103
- * @param array $plugins Plugins order by view.
2104
- */
2105
- protected function set_view_totals( $plugins ) {
2106
- foreach ( $plugins as $type => $list ) {
2107
- $this->view_totals[ $type ] = count( $list );
2108
- }
2109
- }
2110
-
2111
- /**
2112
- * Get the plugin required/recommended text string.
2113
- *
2114
- * @since 2.5.0
2115
- *
2116
- * @param string $required Plugin required setting.
2117
- * @return string
2118
- */
2119
- protected function get_plugin_advise_type_text( $required ) {
2120
- if ( true === $required ) {
2121
- return __( 'Required', 'tgmpa' );
2122
- }
2123
-
2124
- return __( 'Recommended', 'tgmpa' );
2125
- }
2126
-
2127
- /**
2128
- * Get the plugin source type text string.
2129
- *
2130
- * @since 2.5.0
2131
- *
2132
- * @param string $type Plugin type.
2133
- * @return string
2134
- */
2135
- protected function get_plugin_source_type_text( $type ) {
2136
- $string = '';
2137
-
2138
- switch ( $type ) {
2139
- case 'repo':
2140
- $string = __( 'WordPress Repository', 'tgmpa' );
2141
- break;
2142
- case 'external':
2143
- $string = __( 'External Source', 'tgmpa' );
2144
- break;
2145
- case 'bundled':
2146
- $string = __( 'Pre-Packaged', 'tgmpa' );
2147
- break;
2148
- }
2149
-
2150
- return $string;
2151
- }
2152
-
2153
- /**
2154
- * Determine the plugin status message.
2155
- *
2156
- * @since 2.5.0
2157
- *
2158
- * @param string $slug Plugin slug.
2159
- * @return string
2160
- */
2161
- protected function get_plugin_status_text( $slug ) {
2162
- if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2163
- return __( 'Not Installed', 'tgmpa' );
2164
- }
2165
-
2166
- if ( ! $this->tgmpa->is_plugin_active( $slug ) ) {
2167
- $install_status = __( 'Installed But Not Activated', 'tgmpa' );
2168
- } else {
2169
- $install_status = __( 'Active', 'tgmpa' );
2170
- }
2171
-
2172
- $update_status = '';
2173
-
2174
- if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2175
- $update_status = __( 'Required Update not Available', 'tgmpa' );
2176
-
2177
- } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) {
2178
- $update_status = __( 'Requires Update', 'tgmpa' );
2179
-
2180
- } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2181
- $update_status = __( 'Update recommended', 'tgmpa' );
2182
- }
2183
-
2184
- if ( '' === $update_status ) {
2185
- return $install_status;
2186
- }
2187
-
2188
- return sprintf(
2189
- _x( '%1$s, %2$s', '%1$s = install status, %2$s = update status', 'tgmpa' ),
2190
- $install_status,
2191
- $update_status
2192
- );
2193
- }
2194
-
2195
- /**
2196
- * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type.
2197
- *
2198
- * @since 2.5.0
2199
- *
2200
- * @param array $items Prepared table items.
2201
- * @return array Sorted table items.
2202
- */
2203
- public function sort_table_items( $items ) {
2204
- $type = array();
2205
- $name = array();
2206
-
2207
- foreach ( $items as $i => $plugin ) {
2208
- $type[ $i ] = $plugin['type']; // Required / recommended.
2209
- $name[ $i ] = $plugin['sanitized_plugin'];
2210
- }
2211
-
2212
- array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items );
2213
-
2214
- return $items;
2215
- }
2216
-
2217
- /**
2218
- * Get an associative array ( id => link ) of the views available on this table.
2219
- *
2220
- * @since 2.5.0
2221
- *
2222
- * @return array
2223
- */
2224
- public function get_views() {
2225
- $status_links = array();
2226
-
2227
- foreach ( $this->view_totals as $type => $count ) {
2228
- if ( $count < 1 ) {
2229
- continue;
2230
- }
2231
-
2232
- switch ( $type ) {
2233
- case 'all':
2234
- $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'tgmpa' );
2235
- break;
2236
- case 'install':
2237
- $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'tgmpa' );
2238
- break;
2239
- case 'update':
2240
- $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'tgmpa' );
2241
- break;
2242
- case 'activate':
2243
- $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'tgmpa' );
2244
- break;
2245
- default:
2246
- $text = '';
2247
- break;
2248
- }
2249
-
2250
- if ( ! empty( $text ) ) {
2251
-
2252
- $status_links[ $type ] = sprintf(
2253
- '<a href="%s"%s>%s</a>',
2254
- esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),
2255
- ( $type === $this->view_context ) ? ' class="current"' : '',
2256
- sprintf( $text, number_format_i18n( $count ) )
2257
- );
2258
- }
2259
- }
2260
-
2261
- return $status_links;
2262
- }
2263
-
2264
- /**
2265
- * Create default columns to display important plugin information
2266
- * like type, action and status.
2267
- *
2268
- * @since 2.2.0
2269
- *
2270
- * @param array $item Array of item data.
2271
- * @param string $column_name The name of the column.
2272
- * @return string
2273
- */
2274
- public function column_default( $item, $column_name ) {
2275
- return $item[ $column_name ];
2276
- }
2277
-
2278
- /**
2279
- * Required for bulk installing.
2280
- *
2281
- * Adds a checkbox for each plugin.
2282
- *
2283
- * @since 2.2.0
2284
- *
2285
- * @param array $item Array of item data.
2286
- * @return string The input checkbox with all necessary info.
2287
- */
2288
- public function column_cb( $item ) {
2289
- return sprintf(
2290
- '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',
2291
- esc_attr( $this->_args['singular'] ),
2292
- esc_attr( $item['slug'] ),
2293
- esc_attr( $item['sanitized_plugin'] )
2294
- );
2295
- }
2296
-
2297
- /**
2298
- * Create default title column along with the action links.
2299
- *
2300
- * @since 2.2.0
2301
- *
2302
- * @param array $item Array of item data.
2303
- * @return string The plugin name and action links.
2304
- */
2305
- public function column_plugin( $item ) {
2306
- return sprintf(
2307
- '%1$s %2$s',
2308
- $item['plugin'],
2309
- $this->row_actions( $this->get_row_actions( $item ), true )
2310
- );
2311
- }
2312
-
2313
- /**
2314
- * Create version information column.
2315
- *
2316
- * @since 2.5.0
2317
- *
2318
- * @param array $item Array of item data.
2319
- * @return string HTML-formatted version information.
2320
- */
2321
- public function column_version( $item ) {
2322
- $output = array();
2323
-
2324
- if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2325
- $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'tgmpa' );
2326
-
2327
- $color = '';
2328
- if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) {
2329
- $color = ' color: #ff0000; font-weight: bold;';
2330
- }
2331
-
2332
- $output[] = sprintf(
2333
- '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'tgmpa' ) . '</p>',
2334
- $color,
2335
- $installed
2336
- );
2337
- }
2338
-
2339
- if ( ! empty( $item['minimum_version'] ) ) {
2340
- $output[] = sprintf(
2341
- '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'tgmpa' ) . '</p>',
2342
- $item['minimum_version']
2343
- );
2344
- }
2345
-
2346
- if ( ! empty( $item['available_version'] ) ) {
2347
- $color = '';
2348
- if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) {
2349
- $color = ' color: #71C671; font-weight: bold;';
2350
- }
2351
-
2352
- $output[] = sprintf(
2353
- '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'tgmpa' ) . '</p>',
2354
- $color,
2355
- $item['available_version']
2356
- );
2357
- }
2358
-
2359
- if ( empty( $output ) ) {
2360
- return '&nbsp;'; // Let's not break the table layout.
2361
- } else {
2362
- return implode( "\n", $output );
2363
- }
2364
- }
2365
-
2366
- /**
2367
- * Sets default message within the plugins table if no plugins
2368
- * are left for interaction.
2369
- *
2370
- * Hides the menu item to prevent the user from clicking and
2371
- * getting a permissions error.
2372
- *
2373
- * @since 2.2.0
2374
- */
2375
- public function no_items() {
2376
- printf( wp_kses_post( __( 'No plugins to install, update or activate. <a href="%1$s">Return to the Dashboard</a>', 'tgmpa' ) ), esc_url( self_admin_url() ) );
2377
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2378
- }
2379
-
2380
- /**
2381
- * Output all the column information within the table.
2382
- *
2383
- * @since 2.2.0
2384
- *
2385
- * @return array $columns The column names.
2386
- */
2387
- public function get_columns() {
2388
- $columns = array(
2389
- 'cb' => '<input type="checkbox" />',
2390
- 'plugin' => __( 'Plugin', 'tgmpa' ),
2391
- 'source' => __( 'Source', 'tgmpa' ),
2392
- 'type' => __( 'Type', 'tgmpa' ),
2393
- );
2394
-
2395
- if ( 'all' === $this->view_context || 'update' === $this->view_context ) {
2396
- $columns['version'] = __( 'Version', 'tgmpa' );
2397
- $columns['status'] = __( 'Status', 'tgmpa' );
2398
- }
2399
-
2400
- return apply_filters( 'tgmpa_table_columns', $columns );
2401
- }
2402
-
2403
- /**
2404
- * Get name of default primary column
2405
- *
2406
- * @since 2.5.0 / WP 4.3+ compatibility
2407
- * @access protected
2408
- *
2409
- * @return string
2410
- */
2411
- protected function get_default_primary_column_name() {
2412
- return 'plugin';
2413
- }
2414
-
2415
- /**
2416
- * Get the name of the primary column.
2417
- *
2418
- * @since 2.5.0 / WP 4.3+ compatibility
2419
- * @access protected
2420
- *
2421
- * @return string The name of the primary column.
2422
- */
2423
- protected function get_primary_column_name() {
2424
- if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) {
2425
- return parent::get_primary_column_name();
2426
- } else {
2427
- return $this->get_default_primary_column_name();
2428
- }
2429
- }
2430
-
2431
- /**
2432
- * Get the actions which are relevant for a specific plugin row.
2433
- *
2434
- * @since 2.5.0
2435
- *
2436
- * @param array $item Array of item data.
2437
- * @return array Array with relevant action links.
2438
- */
2439
- protected function get_row_actions( $item ) {
2440
- $actions = array();
2441
- $action_links = array();
2442
-
2443
- // Display the 'Install' action link if the plugin is not yet available.
2444
- if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2445
- $actions['install'] = _x( 'Install %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2446
- } else {
2447
- // Display the 'Update' action link if an update is available and WP complies with plugin minimum.
2448
- if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) {
2449
- $actions['update'] = _x( 'Update %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2450
- }
2451
-
2452
- // Display the 'Activate' action link, but only if the plugin meets the minimum version.
2453
- if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) {
2454
- $actions['activate'] = _x( 'Activate %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2455
- }
2456
- }
2457
-
2458
- // Create the actual links.
2459
- foreach ( $actions as $action => $text ) {
2460
- $nonce_url = wp_nonce_url(
2461
- add_query_arg(
2462
- array(
2463
- 'plugin' => urlencode( $item['slug'] ),
2464
- 'tgmpa-' . $action => $action . '-plugin',
2465
- ),
2466
- $this->tgmpa->get_tgmpa_url()
2467
- ),
2468
- 'tgmpa-' . $action,
2469
- 'tgmpa-nonce'
2470
- );
2471
-
2472
- $action_links[ $action ] = sprintf(
2473
- '<a href="%1$s">' . esc_html( $text ) . '</a>',
2474
- esc_url( $nonce_url ),
2475
- '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>'
2476
- );
2477
- }
2478
-
2479
- $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : '';
2480
- return apply_filters( "tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context );
2481
- }
2482
-
2483
- /**
2484
- * Generates content for a single row of the table.
2485
- *
2486
- * @since 2.5.0
2487
- *
2488
- * @param object $item The current item.
2489
- */
2490
- public function single_row( $item ) {
2491
- parent::single_row( $item );
2492
-
2493
- /**
2494
- * Fires after each specific row in the TGMPA Plugins list table.
2495
- *
2496
- * The dynamic portion of the hook name, `$item['slug']`, refers to the slug
2497
- * for the plugin.
2498
- *
2499
- * @since 2.5.0
2500
- */
2501
- do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context );
2502
- }
2503
-
2504
- /**
2505
- * Show the upgrade notice below a plugin row if there is one.
2506
- *
2507
- * @since 2.5.0
2508
- *
2509
- * @see /wp-admin/includes/update.php
2510
- *
2511
- * @param string $slug Plugin slug.
2512
- * @param array $item The information available in this table row.
2513
- * @return null Return early if upgrade notice is empty.
2514
- */
2515
- public function wp_plugin_update_row( $slug, $item ) {
2516
- if ( empty( $item['upgrade_notice'] ) ) {
2517
- return;
2518
- }
2519
-
2520
- echo '
2521
- <tr class="plugin-update-tr">
2522
- <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange">
2523
- <div class="update-message">',
2524
- esc_html__( 'Upgrade message from the plugin author:', 'tgmpa' ),
2525
- ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong>
2526
- </div>
2527
- </td>
2528
- </tr>';
2529
- }
2530
-
2531
- /**
2532
- * Extra controls to be displayed between bulk actions and pagination.
2533
- *
2534
- * @since 2.5.0
2535
- *
2536
- * @param string $which 'top' or 'bottom' table navigation.
2537
- */
2538
- public function extra_tablenav( $which ) {
2539
- if ( 'bottom' === $which ) {
2540
- $this->tgmpa->show_tgmpa_version();
2541
- }
2542
- }
2543
-
2544
- /**
2545
- * Defines the bulk actions for handling registered plugins.
2546
- *
2547
- * @since 2.2.0
2548
- *
2549
- * @return array $actions The bulk actions for the plugin install table.
2550
- */
2551
- public function get_bulk_actions() {
2552
-
2553
- $actions = array();
2554
-
2555
- if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) {
2556
- if ( current_user_can( 'install_plugins' ) ) {
2557
- $actions['tgmpa-bulk-install'] = __( 'Install', 'tgmpa' );
2558
- }
2559
- }
2560
-
2561
- if ( 'install' !== $this->view_context ) {
2562
- if ( current_user_can( 'update_plugins' ) ) {
2563
- $actions['tgmpa-bulk-update'] = __( 'Update', 'tgmpa' );
2564
- }
2565
- if ( current_user_can( 'activate_plugins' ) ) {
2566
- $actions['tgmpa-bulk-activate'] = __( 'Activate', 'tgmpa' );
2567
- }
2568
- }
2569
-
2570
- return $actions;
2571
- }
2572
-
2573
- /**
2574
- * Processes bulk installation and activation actions.
2575
- *
2576
- * The bulk installation process looks for the $_POST information and passes that
2577
- * through if a user has to use WP_Filesystem to enter their credentials.
2578
- *
2579
- * @since 2.2.0
2580
- */
2581
- public function process_bulk_actions() {
2582
- // Bulk installation process.
2583
- if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) {
2584
-
2585
- check_admin_referer( 'bulk-' . $this->_args['plural'] );
2586
-
2587
- $install_type = 'install';
2588
- if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2589
- $install_type = 'update';
2590
- }
2591
-
2592
- $plugins_to_install = array();
2593
-
2594
- // Did user actually select any plugins to install/update ?
2595
- if ( empty( $_POST['plugin'] ) ) {
2596
- if ( 'install' === $install_type ) {
2597
- $message = __( 'No plugins were selected to be installed. No action taken.', 'tgmpa' );
2598
- } else {
2599
- $message = __( 'No plugins were selected to be updated. No action taken.', 'tgmpa' );
2600
- }
2601
-
2602
- echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2603
-
2604
- return false;
2605
- }
2606
-
2607
- if ( is_array( $_POST['plugin'] ) ) {
2608
- $plugins_to_install = (array) $_POST['plugin'];
2609
- } elseif ( is_string( $_POST['plugin'] ) ) {
2610
- // Received via Filesystem page - un-flatten array (WP bug #19643).
2611
- $plugins_to_install = explode( ',', $_POST['plugin'] );
2612
- }
2613
-
2614
- // Sanitize the received input.
2615
- $plugins_to_install = array_map( 'urldecode', $plugins_to_install );
2616
- $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install );
2617
-
2618
- // Validate the received input.
2619
- foreach ( $plugins_to_install as $key => $slug ) {
2620
- // Check if the plugin was registered with TGMPA and remove if not.
2621
- if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) {
2622
- unset( $plugins_to_install[ $key ] );
2623
- continue;
2624
- }
2625
-
2626
- // For updates: make sure this is a plugin we *can* update (update available and WP version ok).
2627
- if ( 'update' === $install_type && ( $this->tgmpa->is_plugin_installed( $slug ) && ( false === $this->tgmpa->does_plugin_have_update( $slug ) || ! $this->tgmpa->can_plugin_update( $slug ) ) ) ) {
2628
- unset( $plugins_to_install[ $key ] );
2629
- }
2630
- }
2631
-
2632
- // No need to proceed further if we have no plugins to handle.
2633
- if ( empty( $plugins_to_install ) ) {
2634
- if ( 'install' === $install_type ) {
2635
- $message = __( 'No plugins are available to be installed at this time.', 'tgmpa' );
2636
- } else {
2637
- $message = __( 'No plugins are available to be updated at this time.', 'tgmpa' );
2638
- }
2639
-
2640
- echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2641
-
2642
- return false;
2643
- }
2644
-
2645
- // Pass all necessary information if WP_Filesystem is needed.
2646
- $url = wp_nonce_url(
2647
- $this->tgmpa->get_tgmpa_url(),
2648
- 'bulk-' . $this->_args['plural']
2649
- );
2650
-
2651
- // Give validated data back to $_POST which is the only place the filesystem looks for extra fields.
2652
- $_POST['plugin'] = implode( ',', $plugins_to_install ); // Work around for WP bug #19643.
2653
-
2654
- $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
2655
- $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem.
2656
-
2657
- if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) {
2658
- return true; // Stop the normal page form from displaying, credential request form will be shown.
2659
- }
2660
-
2661
- // Now we have some credentials, setup WP_Filesystem.
2662
- if ( ! WP_Filesystem( $creds ) ) {
2663
- // Our credentials were no good, ask the user for them again.
2664
- request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields );
2665
-
2666
- return true;
2667
- }
2668
-
2669
- /* If we arrive here, we have the filesystem */
2670
-
2671
- // Store all information in arrays since we are processing a bulk installation.
2672
- $names = array();
2673
- $sources = array(); // Needed for installs.
2674
- $file_paths = array(); // Needed for upgrades.
2675
- $to_inject = array(); // Information to inject into the update_plugins transient.
2676
-
2677
- // Prepare the data for validated plugins for the install/upgrade.
2678
- foreach ( $plugins_to_install as $slug ) {
2679
- $name = $this->tgmpa->plugins[ $slug ]['name'];
2680
- $source = $this->tgmpa->get_download_url( $slug );
2681
-
2682
- if ( ! empty( $name ) && ! empty( $source ) ) {
2683
- $names[] = $name;
2684
-
2685
- switch ( $install_type ) {
2686
-
2687
- case 'install':
2688
- $sources[] = $source;
2689
- break;
2690
-
2691
- case 'update':
2692
- $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2693
- $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ];
2694
- $to_inject[ $slug ]['source'] = $source;
2695
- break;
2696
- }
2697
- }
2698
- }
2699
- unset( $slug, $name, $source );
2700
-
2701
- // Create a new instance of INBOUND_TGM_Bulk_Installer.
2702
- $installer = new INBOUND_TGM_Bulk_Installer(
2703
- new INBOUND_TGM_Bulk_Installer_Skin(
2704
- array(
2705
- 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),
2706
- 'nonce' => 'bulk-' . $this->_args['plural'],
2707
- 'names' => $names,
2708
- 'install_type' => $install_type,
2709
- )
2710
- )
2711
- );
2712
-
2713
- // Wrap the install process with the appropriate HTML.
2714
- echo '<div class="tgmpa wrap">',
2715
- '<h2>', esc_html( get_admin_page_title() ), '</h2>';
2716
-
2717
- // Process the bulk installation submissions.
2718
- add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2719
-
2720
- if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2721
- // Inject our info into the update transient.
2722
- $this->tgmpa->inject_update_info( $to_inject );
2723
-
2724
- $installer->bulk_upgrade( $file_paths );
2725
- } else {
2726
- $installer->bulk_install( $sources );
2727
- }
2728
-
2729
- remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2730
-
2731
- echo '</div>';
2732
-
2733
- return true;
2734
- }
2735
-
2736
- // Bulk activation process.
2737
- if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2738
- check_admin_referer( 'bulk-' . $this->_args['plural'] );
2739
-
2740
- // Did user actually select any plugins to activate ?
2741
- if ( empty( $_POST['plugin'] ) ) {
2742
- echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'tgmpa' ), '</p></div>';
2743
-
2744
- return false;
2745
- }
2746
-
2747
- // Grab plugin data from $_POST.
2748
- $plugins = array();
2749
- if ( isset( $_POST['plugin'] ) ) {
2750
- $plugins = array_map( 'urldecode', (array) $_POST['plugin'] );
2751
- $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins );
2752
- }
2753
-
2754
- $plugins_to_activate = array();
2755
- $plugin_names = array();
2756
-
2757
- // Grab the file paths for the selected & inactive plugins from the registration array.
2758
- foreach ( $plugins as $slug ) {
2759
- if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2760
- $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2761
- $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name'];
2762
- }
2763
- }
2764
- unset( $slug );
2765
-
2766
- // Return early if there are no plugins to activate.
2767
- if ( empty( $plugins_to_activate ) ) {
2768
- echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'tgmpa' ), '</p></div>';
2769
-
2770
- return false;
2771
- }
2772
-
2773
- // Now we are good to go - let's start activating plugins.
2774
- $activate = activate_plugins( $plugins_to_activate );
2775
-
2776
- if ( is_wp_error( $activate ) ) {
2777
- echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>';
2778
- } else {
2779
- $count = count( $plugin_names ); // Count so we can use _n function.
2780
- $plugin_names = array_map( array( 'INBOUND_TGM_Utils', 'wrap_in_strong' ), $plugin_names );
2781
- $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability.
2782
- $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
2783
-
2784
- printf( // WPCS: xss ok.
2785
- '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',
2786
- esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa' ) ),
2787
- $imploded
2788
- );
2789
-
2790
- // Update recently activated plugins option.
2791
- $recent = (array) get_option( 'recently_activated' );
2792
- foreach ( $plugins_to_activate as $plugin => $time ) {
2793
- if ( isset( $recent[ $plugin ] ) ) {
2794
- unset( $recent[ $plugin ] );
2795
- }
2796
- }
2797
- update_option( 'recently_activated', $recent );
2798
- }
2799
-
2800
- unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another.
2801
-
2802
- return true;
2803
- }
2804
-
2805
- return false;
2806
- }
2807
-
2808
- /**
2809
- * Prepares all of our information to be outputted into a usable table.
2810
- *
2811
- * @since 2.2.0
2812
- */
2813
- public function prepare_items() {
2814
- $columns = $this->get_columns(); // Get all necessary column information.
2815
- $hidden = array(); // No columns to hide, but we must set as an array.
2816
- $sortable = array(); // No reason to make sortable columns.
2817
- $primary = $this->get_primary_column_name(); // Column which has the row actions.
2818
- $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers.
2819
-
2820
- // Process our bulk activations here.
2821
- if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2822
- $this->process_bulk_actions();
2823
- }
2824
-
2825
- // Store all of our plugin data into $items array so WP_List_Table can use it.
2826
- $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() );
2827
- }
2828
-
2829
- /* *********** DEPRECATED METHODS *********** */
2830
-
2831
- /**
2832
- * Retrieve plugin data, given the plugin name.
2833
- *
2834
- * @since 2.2.0
2835
- * @deprecated 2.5.0 use {@see INBOUND_Plugin_Activation::_get_plugin_data_from_name()} instead.
2836
- * @see INBOUND_Plugin_Activation::_get_plugin_data_from_name()
2837
- *
2838
- * @param string $name Name of the plugin, as it was registered.
2839
- * @param string $data Optional. Array key of plugin data to return. Default is slug.
2840
- * @return string|boolean Plugin slug if found, false otherwise.
2841
- */
2842
- protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
2843
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'INBOUND_Plugin_Activation::_get_plugin_data_from_name()' );
2844
-
2845
- return $this->tgmpa->_get_plugin_data_from_name( $name, $data );
2846
- }
2847
- }
2848
- }
2849
-
2850
- /**
2851
- * The WP_Upgrader file isn't always available. If it isn't available,
2852
- * we load it here.
2853
- *
2854
- * We check to make sure no action or activation keys are set so that WordPress
2855
- * does not try to re-include the class when processing upgrades or installs outside
2856
- * of the class.
2857
- *
2858
- * @since 2.2.0
2859
- */
2860
- add_action( 'admin_init', 'tgmpa_load_bulk_installer' );
2861
- if ( ! function_exists( 'tgmpa_load_bulk_installer' ) ) {
2862
- /**
2863
- * Load bulk installer
2864
- */
2865
- function tgmpa_load_bulk_installer() {
2866
- // Get TGMPA class instance.
2867
- $tgmpa_instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2868
-
2869
- if ( isset( $_GET['page'] ) && $tgmpa_instance->menu === $_GET['page'] ) {
2870
- if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
2871
- require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
2872
- }
2873
-
2874
- if ( ! class_exists( 'INBOUND_TGM_Bulk_Installer' ) ) {
2875
-
2876
- /**
2877
- * Installer class to handle bulk plugin installations.
2878
- *
2879
- * Extends WP_Upgrader and customizes to suit the installation of multiple
2880
- * plugins.
2881
- *
2882
- * @since 2.2.0
2883
- *
2884
- * @internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader
2885
- *
2886
- * @package TGM-Plugin-Activation
2887
- * @author Thomas Griffin
2888
- * @author Gary Jones
2889
- */
2890
- class INBOUND_TGM_Bulk_Installer extends Plugin_Upgrader {
2891
- /**
2892
- * Holds result of bulk plugin installation.
2893
- *
2894
- * @since 2.2.0
2895
- *
2896
- * @var string
2897
- */
2898
- public $result;
2899
-
2900
- /**
2901
- * Flag to check if bulk installation is occurring or not.
2902
- *
2903
- * @since 2.2.0
2904
- *
2905
- * @var boolean
2906
- */
2907
- public $bulk = false;
2908
-
2909
- /**
2910
- * TGMPA instance
2911
- *
2912
- * @since 2.5.0
2913
- *
2914
- * @var object
2915
- */
2916
- protected $tgmpa;
2917
-
2918
- /**
2919
- * Whether or not the destination directory needs to be cleared ( = on update).
2920
- *
2921
- * @since 2.5.0
2922
- *
2923
- * @var bool
2924
- */
2925
- protected $clear_destination = false;
2926
-
2927
- /**
2928
- * References parent constructor and sets defaults for class.
2929
- *
2930
- * @since 2.2.0
2931
- *
2932
- * @param \Bulk_Upgrader_Skin|null $skin Installer skin.
2933
- */
2934
- public function __construct( $skin = null ) {
2935
- // Get TGMPA class instance.
2936
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2937
-
2938
- parent::__construct( $skin );
2939
-
2940
- if ( isset( $this->skin->options['install_type'] ) && 'update' === $this->skin->options['install_type'] ) {
2941
- $this->clear_destination = true;
2942
- }
2943
-
2944
- if ( $this->tgmpa->is_automatic ) {
2945
- $this->activate_strings();
2946
- }
2947
-
2948
- add_action( 'upgrader_process_complete', array( $this->tgmpa, 'populate_file_path' ) );
2949
- }
2950
-
2951
- /**
2952
- * Sets the correct activation strings for the installer skin to use.
2953
- *
2954
- * @since 2.2.0
2955
- */
2956
- public function activate_strings() {
2957
- $this->strings['activation_failed'] = __( 'Plugin activation failed.', 'tgmpa' );
2958
- $this->strings['activation_success'] = __( 'Plugin activated successfully.', 'tgmpa' );
2959
- }
2960
-
2961
- /**
2962
- * Performs the actual installation of each plugin.
2963
- *
2964
- * @since 2.2.0
2965
- *
2966
- * @see WP_Upgrader::run()
2967
- *
2968
- * @param array $options The installation config options.
2969
- * @return null|array Return early if error, array of installation data on success.
2970
- */
2971
- public function run( $options ) {
2972
- $result = parent::run( $options );
2973
-
2974
- // Reset the strings in case we changed one during automatic activation.
2975
- if ( $this->tgmpa->is_automatic ) {
2976
- if ( 'update' === $this->skin->options['install_type'] ) {
2977
- $this->upgrade_strings();
2978
- } else {
2979
- $this->install_strings();
2980
- }
2981
- }
2982
-
2983
- return $result;
2984
- }
2985
-
2986
- /**
2987
- * Processes the bulk installation of plugins.
2988
- *
2989
- * @since 2.2.0
2990
- *
2991
- * @internal This is basically a near identical copy of the WP Core Plugin_Upgrader::bulk_upgrade()
2992
- * method, with minor adjustments to deal with new installs instead of upgrades.
2993
- * For ease of future synchronizations, the adjustments are clearly commented, but no other
2994
- * comments are added. Code style has been made to comply.
2995
- *
2996
- * @see Plugin_Upgrader::bulk_upgrade()
2997
- * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838
2998
- *
2999
- * @param array $plugins The plugin sources needed for installation.
3000
- * @param array $args Arbitrary passed extra arguments.
3001
- * @return string|bool Install confirmation messages on success, false on failure.
3002
- */
3003
- public function bulk_install( $plugins, $args = array() ) {
3004
- // [TGMPA + ] Hook auto-activation in.
3005
- add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3006
-
3007
- $defaults = array(
3008
- 'clear_update_cache' => true,
3009
- );
3010
- $parsed_args = wp_parse_args( $args, $defaults );
3011
-
3012
- $this->init();
3013
- $this->bulk = true;
3014
-
3015
- $this->install_strings(); // [TGMPA + ] adjusted.
3016
-
3017
- /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */
3018
-
3019
- /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */
3020
-
3021
- $this->skin->header();
3022
-
3023
- // Connect to the Filesystem first.
3024
- $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
3025
- if ( ! $res ) {
3026
- $this->skin->footer();
3027
-
3028
- return false;
3029
- }
3030
-
3031
- $this->skin->bulk_header();
3032
-
3033
- // Only start maintenance mode if:
3034
- // - running Multisite and there are one or more plugins specified, OR
3035
- // - a plugin with an update available is currently active.
3036
- // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.
3037
- $maintenance = ( is_multisite() && ! empty( $plugins ) );
3038
-
3039
- /*
3040
- [TGMPA - ]
3041
- foreach ( $plugins as $plugin )
3042
- $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );
3043
- */
3044
- if ( $maintenance ) {
3045
- $this->maintenance_mode( true );
3046
- }
3047
-
3048
- $results = array();
3049
-
3050
- $this->update_count = count( $plugins );
3051
- $this->update_current = 0;
3052
- foreach ( $plugins as $plugin ) {
3053
- $this->update_current++;
3054
-
3055
- /*
3056
- [TGMPA - ]
3057
- $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
3058
-
3059
- if ( !isset( $current->response[ $plugin ] ) ) {
3060
- $this->skin->set_result('up_to_date');
3061
- $this->skin->before();
3062
- $this->skin->feedback('up_to_date');
3063
- $this->skin->after();
3064
- $results[$plugin] = true;
3065
- continue;
3066
- }
3067
-
3068
- // Get the URL to the zip file
3069
- $r = $current->response[ $plugin ];
3070
-
3071
- $this->skin->plugin_active = is_plugin_active($plugin);
3072
- */
3073
-
3074
- $result = $this->run( array(
3075
- 'package' => $plugin, // [TGMPA + ] adjusted.
3076
- 'destination' => WP_PLUGIN_DIR,
3077
- 'clear_destination' => false, // [TGMPA + ] adjusted.
3078
- 'clear_working' => true,
3079
- 'is_multi' => true,
3080
- 'hook_extra' => array(
3081
- 'plugin' => $plugin,
3082
- ),
3083
- ) );
3084
-
3085
- $results[ $plugin ] = $this->result;
3086
-
3087
- // Prevent credentials auth screen from displaying multiple times.
3088
- if ( false === $result ) {
3089
- break;
3090
- }
3091
- } //end foreach $plugins
3092
-
3093
- $this->maintenance_mode( false );
3094
-
3095
- /**
3096
- * Fires when the bulk upgrader process is complete.
3097
- *
3098
- * @since WP 3.6.0 / TGMPA 2.5.0
3099
- *
3100
- * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
3101
- * be a Theme_Upgrader or Core_Upgrade instance.
3102
- * @param array $data {
3103
- * Array of bulk item update data.
3104
- *
3105
- * @type string $action Type of action. Default 'update'.
3106
- * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
3107
- * @type bool $bulk Whether the update process is a bulk update. Default true.
3108
- * @type array $packages Array of plugin, theme, or core packages to update.
3109
- * }
3110
- */
3111
- do_action( 'upgrader_process_complete', $this, array(
3112
- 'action' => 'install', // [TGMPA + ] adjusted.
3113
- 'type' => 'plugin',
3114
- 'bulk' => true,
3115
- 'plugins' => $plugins,
3116
- ) );
3117
-
3118
- $this->skin->bulk_footer();
3119
-
3120
- $this->skin->footer();
3121
-
3122
- // Cleanup our hooks, in case something else does a upgrade on this connection.
3123
- /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */
3124
-
3125
- // [TGMPA + ] Remove our auto-activation hook.
3126
- remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3127
-
3128
- // Force refresh of plugin update information.
3129
- wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
3130
-
3131
- return $results;
3132
- }
3133
-
3134
- /**
3135
- * Handle a bulk upgrade request.
3136
- *
3137
- * @since 2.5.0
3138
- *
3139
- * @see Plugin_Upgrader::bulk_upgrade()
3140
- *
3141
- * @param array $plugins The local WP file_path's of the plugins which should be upgraded.
3142
- * @param array $args Arbitrary passed extra arguments.
3143
- * @return string|bool Install confirmation messages on success, false on failure.
3144
- */
3145
- public function bulk_upgrade( $plugins, $args = array() ) {
3146
-
3147
- add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3148
-
3149
- $result = parent::bulk_upgrade( $plugins, $args );
3150
-
3151
- remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3152
-
3153
- return $result;
3154
- }
3155
-
3156
- /**
3157
- * Abuse a filter to auto-activate plugins after installation.
3158
- *
3159
- * Hooked into the 'upgrader_post_install' filter hook.
3160
- *
3161
- * @since 2.5.0
3162
- *
3163
- * @param bool $bool The value we need to give back (true).
3164
- * @return bool
3165
- */
3166
- public function auto_activate( $bool ) {
3167
- // Only process the activation of installed plugins if the automatic flag is set to true.
3168
- if ( $this->tgmpa->is_automatic ) {
3169
- // Flush plugins cache so the headers of the newly installed plugins will be read correctly.
3170
- wp_clean_plugins_cache();
3171
-
3172
- // Get the installed plugin file.
3173
- $plugin_info = $this->plugin_info();
3174
-
3175
- // Don't try to activate on upgrade of active plugin as WP will do this already.
3176
- if ( ! is_plugin_active( $plugin_info ) ) {
3177
- $activate = activate_plugin( $plugin_info );
3178
-
3179
- // Adjust the success string based on the activation result.
3180
- $this->strings['process_success'] = $this->strings['process_success'] . "<br />\n";
3181
-
3182
- if ( is_wp_error( $activate ) ) {
3183
- $this->skin->error( $activate );
3184
- $this->strings['process_success'] .= $this->strings['activation_failed'];
3185
- } else {
3186
- $this->strings['process_success'] .= $this->strings['activation_success'];
3187
- }
3188
- }
3189
- }
3190
-
3191
- return $bool;
3192
- }
3193
- }
3194
- }
3195
-
3196
- if ( ! class_exists( 'INBOUND_TGM_Bulk_Installer_Skin' ) ) {
3197
-
3198
- /**
3199
- * Installer skin to set strings for the bulk plugin installations..
3200
- *
3201
- * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
3202
- * plugins.
3203
- *
3204
- * @since 2.2.0
3205
- *
3206
- * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
3207
- *
3208
- * @package TGM-Plugin-Activation
3209
- * @author Thomas Griffin
3210
- * @author Gary Jones
3211
- */
3212
- class INBOUND_TGM_Bulk_Installer_Skin extends Bulk_Upgrader_Skin {
3213
- /**
3214
- * Holds plugin info for each individual plugin installation.
3215
- *
3216
- * @since 2.2.0
3217
- *
3218
- * @var array
3219
- */
3220
- public $plugin_info = array();
3221
-
3222
- /**
3223
- * Holds names of plugins that are undergoing bulk installations.
3224
- *
3225
- * @since 2.2.0
3226
- *
3227
- * @var array
3228
- */
3229
- public $plugin_names = array();
3230
-
3231
- /**
3232
- * Integer to use for iteration through each plugin installation.
3233
- *
3234
- * @since 2.2.0
3235
- *
3236
- * @var integer
3237
- */
3238
- public $i = 0;
3239
-
3240
- /**
3241
- * TGMPA instance
3242
- *
3243
- * @since 2.5.0
3244
- *
3245
- * @var object
3246
- */
3247
- protected $tgmpa;
3248
-
3249
- /**
3250
- * Constructor. Parses default args with new ones and extracts them for use.
3251
- *
3252
- * @since 2.2.0
3253
- *
3254
- * @param array $args Arguments to pass for use within the class.
3255
- */
3256
- public function __construct( $args = array() ) {
3257
- // Get TGMPA class instance.
3258
- $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
3259
-
3260
- // Parse default and new args.
3261
- $defaults = array(
3262
- 'url' => '',
3263
- 'nonce' => '',
3264
- 'names' => array(),
3265
- 'install_type' => 'install',
3266
- );
3267
- $args = wp_parse_args( $args, $defaults );
3268
-
3269
- // Set plugin names to $this->plugin_names property.
3270
- $this->plugin_names = $args['names'];
3271
-
3272
- // Extract the new args.
3273
- parent::__construct( $args );
3274
- }
3275
-
3276
- /**
3277
- * Sets install skin strings for each individual plugin.
3278
- *
3279
- * Checks to see if the automatic activation flag is set and uses the
3280
- * the proper strings accordingly.
3281
- *
3282
- * @since 2.2.0
3283
- */
3284
- public function add_strings() {
3285
- if ( 'update' === $this->options['install_type'] ) {
3286
- parent::add_strings();
3287
- $this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3288
- } else {
3289
- $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', 'tgmpa' );
3290
- $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', 'tgmpa' );
3291
-
3292
- if ( $this->tgmpa->is_automatic ) {
3293
- // Automatic activation strings.
3294
- $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3295
- $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3296
- $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', 'tgmpa' );
3297
- $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3298
- } else {
3299
- // Default installation strings.
3300
- $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3301
- $this->upgrader->strings['skin_update_successful'] = esc_html__( '%1$s installed successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3302
- $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', 'tgmpa' );
3303
- $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3304
- }
3305
- }
3306
- }
3307
-
3308
- /**
3309
- * Outputs the header strings and necessary JS before each plugin installation.
3310
- *
3311
- * @since 2.2.0
3312
- *
3313
- * @param string $title Unused in this implementation.
3314
- */
3315
- public function before( $title = '' ) {
3316
- if ( empty( $title ) ) {
3317
- $title = esc_html( $this->plugin_names[ $this->i ] );
3318
- }
3319
- parent::before( $title );
3320
- }
3321
-
3322
- /**
3323
- * Outputs the footer strings and necessary JS after each plugin installation.
3324
- *
3325
- * Checks for any errors and outputs them if they exist, else output
3326
- * success strings.
3327
- *
3328
- * @since 2.2.0
3329
- *
3330
- * @param string $title Unused in this implementation.
3331
- */
3332
- public function after( $title = '' ) {
3333
- if ( empty( $title ) ) {
3334
- $title = esc_html( $this->plugin_names[ $this->i ] );
3335
- }
3336
- parent::after( $title );
3337
-
3338
- $this->i++;
3339
- }
3340
-
3341
- /**
3342
- * Outputs links after bulk plugin installation is complete.
3343
- *
3344
- * @since 2.2.0
3345
- */
3346
- public function bulk_footer() {
3347
- // Serve up the string to say installations (and possibly activations) are complete.
3348
- parent::bulk_footer();
3349
-
3350
- // Flush plugins cache so we can make sure that the installed plugins list is always up to date.
3351
- wp_clean_plugins_cache();
3352
-
3353
- $this->tgmpa->show_tgmpa_version();
3354
-
3355
- // Display message based on if all plugins are now active or not.
3356
- $update_actions = array();
3357
-
3358
- if ( $this->tgmpa->is_tgmpa_complete() ) {
3359
- // All plugins are active, so we display the complete string and hide the menu to protect users.
3360
- echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
3361
- $update_actions['dashboard'] = sprintf(
3362
- esc_html( $this->tgmpa->strings['complete'] ),
3363
- '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>'
3364
- );
3365
- } else {
3366
- $update_actions['tgmpa_page'] = '<a href="' . esc_url( $this->tgmpa->get_tgmpa_url() ) . '" target="_parent">' . esc_html( $this->tgmpa->strings['return'] ) . '</a>';
3367
- }
3368
-
3369
- /**
3370
- * Filter the list of action links available following bulk plugin installs/updates.
3371
- *
3372
- * @since 2.5.0
3373
- *
3374
- * @param array $update_actions Array of plugin action links.
3375
- * @param array $plugin_info Array of information for the last-handled plugin.
3376
- */
3377
- $update_actions = apply_filters( 'tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
3378
-
3379
- if ( ! empty( $update_actions ) ) {
3380
- $this->feedback( implode( ' | ', (array) $update_actions ) );
3381
- }
3382
- }
3383
-
3384
- /* *********** DEPRECATED METHODS *********** */
3385
-
3386
- /**
3387
- * Flush header output buffer.
3388
- *
3389
- * @since 2.2.0
3390
- * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3391
- * @see Bulk_Upgrader_Skin::flush_output()
3392
- */
3393
- public function before_flush_output() {
3394
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3395
- $this->flush_output();
3396
- }
3397
-
3398
- /**
3399
- * Flush footer output buffer and iterate $this->i to make sure the
3400
- * installation strings reference the correct plugin.
3401
- *
3402
- * @since 2.2.0
3403
- * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3404
- * @see Bulk_Upgrader_Skin::flush_output()
3405
- */
3406
- public function after_flush_output() {
3407
- _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3408
- $this->flush_output();
3409
- $this->i++;
3410
- }
3411
- }
3412
- }
3413
- }
3414
- }
3415
- }
3416
-
3417
- if ( ! class_exists( 'INBOUND_TGM_Utils' ) ) {
3418
-
3419
- /**
3420
- * Generic utilities for TGMPA.
3421
- *
3422
- * All methods are static, poor-dev name-spacing class wrapper.
3423
- *
3424
- * @since 2.5.0
3425
- *
3426
- * @package TGM-Plugin-Activation
3427
- * @author Juliette Reinders Folmer
3428
- */
3429
- class INBOUND_TGM_Utils {
3430
- /**
3431
- * Whether the PHP filter extension is enabled.
3432
- *
3433
- * @see http://php.net/book.filter
3434
- *
3435
- * @since 2.5.0
3436
- *
3437
- * @static
3438
- *
3439
- * @var bool $has_filters True is the extension is enabled.
3440
- */
3441
- public static $has_filters;
3442
-
3443
- /**
3444
- * Wrap an arbitrary string in <em> tags. Meant to be used in combination with array_map().
3445
- *
3446
- * @since 2.5.0
3447
- *
3448
- * @static
3449
- *
3450
- * @param string $string Text to be wrapped.
3451
- * @return string
3452
- */
3453
- public static function wrap_in_em( $string ) {
3454
- return '<em>' . $string . '</em>';
3455
- }
3456
-
3457
- /**
3458
- * Wrap an arbitrary string in <strong> tags. Meant to be used in combination with array_map().
3459
- *
3460
- * @since 2.5.0
3461
- *
3462
- * @static
3463
- *
3464
- * @param string $string Text to be wrapped.
3465
- * @return string
3466
- */
3467
- public static function wrap_in_strong( $string ) {
3468
- return '<strong>' . wp_kses_post( $string ) . '</strong>';
3469
- }
3470
-
3471
- /**
3472
- * Helper function: Validate a value as boolean
3473
- *
3474
- * @since 2.5.0
3475
- *
3476
- * @static
3477
- *
3478
- * @param mixed $value Arbitrary value.
3479
- * @return bool
3480
- */
3481
- public static function validate_bool( $value ) {
3482
- if ( ! isset( self::$has_filters ) ) {
3483
- self::$has_filters = extension_loaded( 'filter' );
3484
- }
3485
-
3486
- if ( self::$has_filters ) {
3487
- return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
3488
- } else {
3489
- return self::emulate_filter_bool( $value );
3490
- }
3491
- }
3492
-
3493
- /**
3494
- * Helper function: Cast a value to bool
3495
- *
3496
- * @since 2.5.0
3497
- *
3498
- * @static
3499
- *
3500
- * @param mixed $value Value to cast.
3501
- * @return bool
3502
- */
3503
- protected static function emulate_filter_bool( $value ) {
3504
- // @codingStandardsIgnoreStart
3505
- static $true = array(
3506
- '1',
3507
- 'true', 'True', 'TRUE',
3508
- 'y', 'Y',
3509
- 'yes', 'Yes', 'YES',
3510
- 'on', 'On', 'ON',
3511
- );
3512
- static $false = array(
3513
- '0',
3514
- 'false', 'False', 'FALSE',
3515
- 'n', 'N',
3516
- 'no', 'No', 'NO',
3517
- 'off', 'Off', 'OFF',
3518
- );
3519
- // @codingStandardsIgnoreEnd
3520
-
3521
- if ( is_bool( $value ) ) {
3522
- return $value;
3523
- } else if ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
3524
- return (bool) $value;
3525
- } else if ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
3526
- return (bool) $value;
3527
- } else if ( is_string( $value ) ) {
3528
- $value = trim( $value );
3529
- if ( in_array( $value, $true, true ) ) {
3530
- return true;
3531
- } else if ( in_array( $value, $false, true ) ) {
3532
- return false;
3533
- } else {
3534
- return false;
3535
- }
3536
- }
3537
-
3538
- return false;
3539
- }
3540
- } // End of class INBOUND_TGM_Utils
3541
  } // End of class_exists wrapper
1
+ <?php
2
+ /**
3
+ * Plugin installation and activation for WordPress themes.
4
+ *
5
+ * Please note that this is a drop-in library for a theme or plugin.
6
+ * The authors of this library (Thomas, Gary and Juliette) are NOT responsible
7
+ * for the support of your plugin or theme. Please contact the plugin
8
+ * or theme author for support.
9
+ *
10
+ * @package TGM-Plugin-Activation
11
+ * @version 2.5.0
12
+ * @link http://tgmpluginactivation.com/
13
+ * @author Thomas Griffin, Gary Jones, Juliette Reinders Folmer
14
+ * @copyright Copyright (c) 2011, Thomas Griffin
15
+ * @license GPL-2.0+
16
+ *
17
+ * @wordpress-plugin
18
+ * Plugin Name: TGM Plugin Activation
19
+ * Plugin URI:
20
+ * Description: Plugin installation and activation for WordPress themes.
21
+ * Version: 2.5.0
22
+ * Author: Thomas Griffin, Gary Jones, Juliette Reinders Folmer
23
+ * Author URI: http://tgmpluginactivation.com/
24
+ * Text Domain: tgmpa
25
+ * Domain Path: /languages/
26
+ * Copyright: 2011, Thomas Griffin
27
+ */
28
+
29
+ /*
30
+ Copyright 2011 Thomas Griffin (thomasgriffinmedia.com)
31
+
32
+ This program is free software; you can redistribute it and/or modify
33
+ it under the terms of the GNU General Public License, version 2, as
34
+ published by the Free Software Foundation.
35
+
36
+ This program is distributed in the hope that it will be useful,
37
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
38
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
39
+ GNU General Public License for more details.
40
+
41
+ You should have received a copy of the GNU General Public License
42
+ along with this program; if not, write to the Free Software
43
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
44
+ */
45
+
46
+ if ( ! class_exists( 'INBOUND_Plugin_Activation' ) ) {
47
+
48
+ /**
49
+ * Automatic plugin installation and activation library.
50
+ *
51
+ * Creates a way to automatically install and activate plugins from within themes.
52
+ * The plugins can be either bundled, downloaded from the WordPress
53
+ * Plugin Repository or downloaded from another external source.
54
+ *
55
+ * @since 1.0.0
56
+ *
57
+ * @package TGM-Plugin-Activation
58
+ * @author Thomas Griffin
59
+ * @author Gary Jones
60
+ */
61
+ class INBOUND_Plugin_Activation {
62
+ /**
63
+ * TGMPA version number.
64
+ *
65
+ * @since 2.5.0
66
+ *
67
+ * @const string Version number.
68
+ */
69
+ const TGMPA_VERSION = '2.5.0';
70
+
71
+ /**
72
+ * Regular expression to test if a URL is a WP plugin repo URL.
73
+ *
74
+ * @const string Regex.
75
+ *
76
+ * @since 2.5.0
77
+ */
78
+ const WP_REPO_REGEX = '|^http[s]?://wordpress\.org/(?:extend/)?plugins/|';
79
+
80
+ /**
81
+ * Arbitrary regular expression to test if a string starts with a URL.
82
+ *
83
+ * @const string Regex.
84
+ *
85
+ * @since 2.5.0
86
+ */
87
+ const IS_URL_REGEX = '|^http[s]?://|';
88
+
89
+ /**
90
+ * Holds a copy of itself, so it can be referenced by the class name.
91
+ *
92
+ * @since 1.0.0
93
+ *
94
+ * @var INBOUND_Plugin_Activation
95
+ */
96
+ public static $instance;
97
+
98
+ /**
99
+ * Holds arrays of plugin details.
100
+ *
101
+ * @since 1.0.0
102
+ *
103
+ * @since 2.5.0 the array has the plugin slug as an associative key.
104
+ *
105
+ * @var array
106
+ */
107
+ public $plugins = array();
108
+
109
+ /**
110
+ * Holds arrays of plugin names to use to sort the plugins array.
111
+ *
112
+ * @since 2.5.0
113
+ *
114
+ * @var array
115
+ */
116
+ protected $sort_order = array();
117
+
118
+ /**
119
+ * Whether any plugins have the 'force_activation' setting set to true.
120
+ *
121
+ * @since 2.5.0
122
+ *
123
+ * @var bool
124
+ */
125
+ protected $has_forced_activation = false;
126
+
127
+ /**
128
+ * Whether any plugins have the 'force_deactivation' setting set to true.
129
+ *
130
+ * @since 2.5.0
131
+ *
132
+ * @var bool
133
+ */
134
+ protected $has_forced_deactivation = false;
135
+
136
+ /**
137
+ * Name of the unique ID to hash notices.
138
+ *
139
+ * @since 2.4.0
140
+ *
141
+ * @var string
142
+ */
143
+ public $id = 'tgmpa';
144
+
145
+ /**
146
+ * Name of the query-string argument for the admin page.
147
+ *
148
+ * @since 1.0.0
149
+ *
150
+ * @var string
151
+ */
152
+ public $menu = 'tgmpa-install-plugins';
153
+
154
+ /**
155
+ * Parent menu file slug.
156
+ *
157
+ * @since 2.5.0
158
+ *
159
+ * @var string
160
+ */
161
+ public $parent_slug = 'themes.php';
162
+
163
+ /**
164
+ * Capability needed to view the plugin installation menu item.
165
+ *
166
+ * @since 2.5.0
167
+ *
168
+ * @var string
169
+ */
170
+ public $capability = 'edit_theme_options';
171
+
172
+ /**
173
+ * Default absolute path to folder containing bundled plugin zip files.
174
+ *
175
+ * @since 2.0.0
176
+ *
177
+ * @var string Absolute path prefix to zip file location for bundled plugins. Default is empty string.
178
+ */
179
+ public $default_path = '';
180
+
181
+ /**
182
+ * Flag to show admin notices or not.
183
+ *
184
+ * @since 2.1.0
185
+ *
186
+ * @var boolean
187
+ */
188
+ public $has_notices = true;
189
+
190
+ /**
191
+ * Flag to determine if the user can dismiss the notice nag.
192
+ *
193
+ * @since 2.4.0
194
+ *
195
+ * @var boolean
196
+ */
197
+ public $dismissable = true;
198
+
199
+ /**
200
+ * Message to be output above nag notice if dismissable is false.
201
+ *
202
+ * @since 2.4.0
203
+ *
204
+ * @var string
205
+ */
206
+ public $dismiss_msg = '';
207
+
208
+ /**
209
+ * Flag to set automatic activation of plugins. Off by default.
210
+ *
211
+ * @since 2.2.0
212
+ *
213
+ * @var boolean
214
+ */
215
+ public $is_automatic = false;
216
+
217
+ /**
218
+ * Optional message to display before the plugins table.
219
+ *
220
+ * @since 2.2.0
221
+ *
222
+ * @var string Message filtered by wp_kses_post(). Default is empty string.
223
+ */
224
+ public $message = '';
225
+
226
+ /**
227
+ * Holds configurable array of strings.
228
+ *
229
+ * Default values are added in the constructor.
230
+ *
231
+ * @since 2.0.0
232
+ *
233
+ * @var array
234
+ */
235
+ public $strings = array();
236
+
237
+ /**
238
+ * Holds the version of WordPress.
239
+ *
240
+ * @since 2.4.0
241
+ *
242
+ * @var int
243
+ */
244
+ public $wp_version;
245
+
246
+ /**
247
+ * Holds the hook name for the admin page.
248
+ *
249
+ * @since 2.5.0
250
+ *
251
+ * @var string
252
+ */
253
+ public $page_hook;
254
+
255
+ /**
256
+ * Adds a reference of this object to $instance, populates default strings,
257
+ * does the tgmpa_init action hook, and hooks in the interactions to init.
258
+ *
259
+ * @since 1.0.0
260
+ *
261
+ * @see INBOUND_Plugin_Activation::init()
262
+ */
263
+ protected function __construct() {
264
+ // Set the current WordPress version.
265
+ $this->wp_version = $GLOBALS['wp_version'];
266
+
267
+ // Announce that the class is ready, and pass the object (for advanced use).
268
+ do_action_ref_array( 'tgmpa_init', array( $this ) );
269
+
270
+ // When the rest of WP has loaded, kick-start the rest of the class.
271
+ add_action( 'init', array( $this, 'init' ) );
272
+ }
273
+
274
+ /**
275
+ * Initialise the interactions between this class and WordPress.
276
+ *
277
+ * Hooks in three new methods for the class: admin_menu, notices and styles.
278
+ *
279
+ * @since 2.0.0
280
+ *
281
+ * @see INBOUND_Plugin_Activation::admin_menu()
282
+ * @see INBOUND_Plugin_Activation::notices()
283
+ * @see INBOUND_Plugin_Activation::styles()
284
+ */
285
+ public function init() {
286
+ /**
287
+ * By default TGMPA only loads on the WP back-end and not in an Ajax call. Using this filter
288
+ * you can overrule that behaviour.
289
+ *
290
+ * @since 2.5.0
291
+ *
292
+ * @param bool $load Whether or not TGMPA should load.
293
+ * Defaults to the return of `is_admin() && ! defined( 'DOING_AJAX' )`.
294
+ */
295
+ if ( true !== apply_filters( 'tgmpa_load', ( is_admin() && ! defined( 'DOING_AJAX' ) ) ) ) {
296
+ return;
297
+ }
298
+
299
+ // Load class strings.
300
+ $this->strings = array(
301
+ 'page_title' => __( 'Install Required Plugins', 'tgmpa' ),
302
+ 'menu_title' => __( 'Install Plugins', 'tgmpa' ),
303
+ 'installing' => __( 'Installing Plugin: %s', 'tgmpa' ),
304
+ 'oops' => __( 'Something went wrong with the plugin API.', 'tgmpa' ),
305
+ 'notice_can_install_required' => _n_noop(
306
+ 'This theme requires the following plugin: %1$s.',
307
+ 'This theme requires the following plugins: %1$s.',
308
+ 'tgmpa'
309
+ ),
310
+ 'notice_can_install_recommended' => _n_noop(
311
+ 'This theme recommends the following plugin: %1$s.',
312
+ 'This theme recommends the following plugins: %1$s.',
313
+ 'tgmpa'
314
+ ),
315
+ 'notice_cannot_install' => _n_noop(
316
+ 'Sorry, but you do not have the correct permissions to install the %1$s plugin.',
317
+ 'Sorry, but you do not have the correct permissions to install the %1$s plugins.',
318
+ 'tgmpa'
319
+ ),
320
+ 'notice_ask_to_update' => _n_noop(
321
+ 'The following plugin needs to be updated to its latest version to ensure maximum compatibility with this theme: %1$s.',
322
+ 'The following plugins need to be updated to their latest version to ensure maximum compatibility with this theme: %1$s.',
323
+ 'tgmpa'
324
+ ),
325
+ 'notice_ask_to_update_maybe' => _n_noop(
326
+ 'There is an update available for: %1$s.',
327
+ 'There are updates available for the following plugins: %1$s.',
328
+ 'tgmpa'
329
+ ),
330
+ 'notice_cannot_update' => _n_noop(
331
+ 'Sorry, but you do not have the correct permissions to update the %1$s plugin.',
332
+ 'Sorry, but you do not have the correct permissions to update the %1$s plugins.',
333
+ 'tgmpa'
334
+ ),
335
+ 'notice_can_activate_required' => _n_noop(
336
+ 'The following required plugin is currently inactive: %1$s.',
337
+ 'The following required plugins are currently inactive: %1$s.',
338
+ 'tgmpa'
339
+ ),
340
+ 'notice_can_activate_recommended' => _n_noop(
341
+ 'The following recommended plugin is currently inactive: %1$s.',
342
+ 'The following recommended plugins are currently inactive: %1$s.',
343
+ 'tgmpa'
344
+ ),
345
+ 'notice_cannot_activate' => _n_noop(
346
+ 'Sorry, but you do not have the correct permissions to activate the %1$s plugin.',
347
+ 'Sorry, but you do not have the correct permissions to activate the %1$s plugins.',
348
+ 'tgmpa'
349
+ ),
350
+ 'install_link' => _n_noop(
351
+ 'Begin installing plugin',
352
+ 'Begin installing plugins',
353
+ 'tgmpa'
354
+ ),
355
+ 'update_link' => _n_noop(
356
+ 'Begin updating plugin',
357
+ 'Begin updating plugins',
358
+ 'tgmpa'
359
+ ),
360
+ 'activate_link' => _n_noop(
361
+ 'Begin activating plugin',
362
+ 'Begin activating plugins',
363
+ 'tgmpa'
364
+ ),
365
+ 'return' => __( 'Return to Required Plugins Installer', 'tgmpa' ),
366
+ 'dashboard' => __( 'Return to the dashboard', 'tgmpa' ),
367
+ 'plugin_activated' => __( 'Plugin activated successfully.', 'tgmpa' ),
368
+ 'activated_successfully' => __( 'The following plugin was activated successfully:', 'tgmpa' ),
369
+ 'plugin_already_active' => __( 'No action taken. Plugin %1$s was already active.', 'tgmpa' ),
370
+ 'plugin_needs_higher_version' => __( 'Plugin not activated. A higher version of %s is needed for this theme. Please update the plugin.', 'tgmpa' ),
371
+ 'complete' => __( 'All plugins installed and activated successfully. %1$s', 'tgmpa' ),
372
+ 'dismiss' => __( 'Dismiss this notice', 'tgmpa' ),
373
+ 'contact_admin' => __( 'Please contact the administrator of this site for help.', 'tgmpa' ),
374
+ );
375
+
376
+ do_action( 'tgmpa_register' );
377
+
378
+ /* After this point, the plugins should be registered and the configuration set. */
379
+
380
+ // Proceed only if we have plugins to handle.
381
+ if ( empty( $this->plugins ) || ! is_array( $this->plugins ) ) {
382
+ return;
383
+ }
384
+
385
+ // Set up the menu and notices if we still have outstanding actions.
386
+ if ( true !== $this->is_tgmpa_complete() ) {
387
+ // Sort the plugins.
388
+ array_multisort( $this->sort_order, SORT_ASC, $this->plugins );
389
+
390
+ add_action( 'admin_menu', array( $this, 'admin_menu' ) );
391
+ add_action( 'admin_head', array( $this, 'dismiss' ) );
392
+
393
+ // Prevent the normal links from showing underneath a single install/update page.
394
+ add_filter( 'install_plugin_complete_actions', array( $this, 'actions' ) );
395
+ add_filter( 'update_plugin_complete_actions', array( $this, 'actions' ) );
396
+
397
+ if ( $this->has_notices ) {
398
+ add_action( 'admin_notices', array( $this, 'notices' ) );
399
+ add_action( 'admin_init', array( $this, 'admin_init' ), 1 );
400
+ add_action( 'admin_enqueue_scripts', array( $this, 'thickbox' ) );
401
+ }
402
+
403
+ add_action( 'load-plugins.php', array( $this, 'add_plugin_action_link_filters' ), 1 );
404
+ }
405
+
406
+ // Make sure things get reset on switch theme.
407
+ add_action( 'switch_theme', array( $this, 'flush_plugins_cache' ) );
408
+
409
+ if ( $this->has_notices ) {
410
+ add_action( 'switch_theme', array( $this, 'update_dismiss' ) );
411
+ }
412
+
413
+ // Setup the force activation hook.
414
+ if ( true === $this->has_forced_activation ) {
415
+ add_action( 'admin_init', array( $this, 'force_activation' ) );
416
+ }
417
+
418
+ // Setup the force deactivation hook.
419
+ if ( true === $this->has_forced_deactivation ) {
420
+ add_action( 'switch_theme', array( $this, 'force_deactivation' ) );
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Prevent activation of plugins which don't meet the minimum version requirement from the
426
+ * WP native plugins page.
427
+ *
428
+ * @since 2.5.0
429
+ */
430
+ public function add_plugin_action_link_filters() {
431
+ foreach ( $this->plugins as $slug => $plugin ) {
432
+ if ( false === $this->can_plugin_activate( $slug ) ) {
433
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_activate' ), 20 );
434
+ }
435
+
436
+ if ( true === $plugin['force_activation'] ) {
437
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_deactivate' ), 20 );
438
+ }
439
+
440
+ if ( false !== $this->does_plugin_require_update( $slug ) ) {
441
+ add_filter( 'plugin_action_links_' . $plugin['file_path'], array( $this, 'filter_plugin_action_links_update' ), 20 );
442
+ }
443
+ }
444
+ }
445
+
446
+ /**
447
+ * Remove the 'Activate' link on the WP native plugins page if the plugin does not meet the
448
+ * minimum version requirements.
449
+ *
450
+ * @since 2.5.0
451
+ *
452
+ * @param array $actions Action links.
453
+ * @return array
454
+ */
455
+ public function filter_plugin_action_links_activate( $actions ) {
456
+ unset( $actions['activate'] );
457
+
458
+ return $actions;
459
+ }
460
+
461
+ /**
462
+ * Remove the 'Deactivate' link on the WP native plugins page if the plugin has been set to force activate.
463
+ *
464
+ * @since 2.5.0
465
+ *
466
+ * @param array $actions Action links.
467
+ * @return array
468
+ */
469
+ public function filter_plugin_action_links_deactivate( $actions ) {
470
+ unset( $actions['deactivate'] );
471
+
472
+ return $actions;
473
+ }
474
+
475
+ /**
476
+ * Add a 'Requires update' link on the WP native plugins page if the plugin does not meet the
477
+ * minimum version requirements.
478
+ *
479
+ * @since 2.5.0
480
+ *
481
+ * @param array $actions Action links.
482
+ * @return array
483
+ */
484
+ public function filter_plugin_action_links_update( $actions ) {
485
+ $actions['update'] = sprintf(
486
+ '<a href="%1$s" title="%2$s" class="edit">%3$s</a>',
487
+ esc_url( $this->get_tgmpa_status_url( 'update' ) ),
488
+ esc_attr__( 'This plugin needs to be updated to be compatible with your theme.', 'tgmpa' ),
489
+ esc_html__( 'Update Required', 'tgmpa' )
490
+ );
491
+
492
+ return $actions;
493
+ }
494
+
495
+ /**
496
+ * Handles calls to show plugin information via links in the notices.
497
+ *
498
+ * We get the links in the admin notices to point to the TGMPA page, rather
499
+ * than the typical plugin-install.php file, so we can prepare everything
500
+ * beforehand.
501
+ *
502
+ * WP does not make it easy to show the plugin information in the thickbox -
503
+ * here we have to require a file that includes a function that does the
504
+ * main work of displaying it, enqueue some styles, set up some globals and
505
+ * finally call that function before exiting.
506
+ *
507
+ * Down right easy once you know how...
508
+ *
509
+ * Returns early if not the TGMPA page.
510
+ *
511
+ * @since 2.1.0
512
+ *
513
+ * @global string $tab Used as iframe div class names, helps with styling
514
+ * @global string $body_id Used as the iframe body ID, helps with styling
515
+ *
516
+ * @return null Returns early if not the TGMPA page.
517
+ */
518
+ public function admin_init() {
519
+ if ( ! $this->is_tgmpa_page() ) {
520
+ return;
521
+ }
522
+
523
+ if ( isset( $_REQUEST['tab'] ) && 'plugin-information' === $_REQUEST['tab'] ) {
524
+ // Needed for install_plugin_information().
525
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
526
+
527
+ wp_enqueue_style( 'plugin-install' );
528
+
529
+ global $tab, $body_id;
530
+ $body_id = 'plugin-information';
531
+ // @codingStandardsIgnoreStart
532
+ $tab = 'plugin-information';
533
+ // @codingStandardsIgnoreEnd
534
+
535
+ install_plugin_information();
536
+
537
+ exit;
538
+ }
539
+ }
540
+
541
+ /**
542
+ * Enqueue thickbox scripts/styles for plugin info.
543
+ *
544
+ * Thickbox is not automatically included on all admin pages, so we must
545
+ * manually enqueue it for those pages.
546
+ *
547
+ * Thickbox is only loaded if the user has not dismissed the admin
548
+ * notice or if there are any plugins left to install and activate.
549
+ *
550
+ * @since 2.1.0
551
+ */
552
+ public function thickbox() {
553
+ if ( ! get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
554
+ add_thickbox();
555
+ }
556
+ }
557
+
558
+ /**
559
+ * Adds submenu page if there are plugin actions to take.
560
+ *
561
+ * This method adds the submenu page letting users know that a required
562
+ * plugin needs to be installed.
563
+ *
564
+ * This page disappears once the plugin has been installed and activated.
565
+ *
566
+ * @since 1.0.0
567
+ *
568
+ * @see INBOUND_Plugin_Activation::init()
569
+ * @see INBOUND_Plugin_Activation::install_plugins_page()
570
+ *
571
+ * @return null Return early if user lacks capability to install a plugin.
572
+ */
573
+ public function admin_menu() {
574
+ // Make sure privileges are correct to see the page.
575
+ if ( ! current_user_can( 'install_plugins' ) ) {
576
+ return;
577
+ }
578
+
579
+ $args = apply_filters(
580
+ 'tgmpa_admin_menu_args',
581
+ array(
582
+ 'parent_slug' => $this->parent_slug, // Parent Menu slug.
583
+ 'page_title' => $this->strings['page_title'], // Page title.
584
+ 'menu_title' => $this->strings['menu_title'], // Menu title.
585
+ 'capability' => $this->capability, // Capability.
586
+ 'menu_slug' => $this->menu, // Menu slug.
587
+ 'function' => array( $this, 'install_plugins_page' ), // Callback.
588
+ )
589
+ );
590
+
591
+ $this->add_admin_menu( $args );
592
+ }
593
+
594
+ /**
595
+ * Add the menu item.
596
+ *
597
+ * @since 2.5.0
598
+ *
599
+ * @param array $args Menu item configuration.
600
+ */
601
+ protected function add_admin_menu( array $args ) {
602
+ if ( has_filter( 'tgmpa_admin_menu_use_add_theme_page' ) ) {
603
+ _deprecated_function( 'The "tgmpa_admin_menu_use_add_theme_page" filter', '2.5.0', esc_html__( 'Set the parent_slug config variable instead.', 'tgmpa' ) );
604
+ }
605
+
606
+ if ( 'themes.php' === $this->parent_slug ) {
607
+ $this->page_hook = call_user_func( 'add_theme_page', $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
608
+ } else {
609
+ $this->page_hook = call_user_func( 'add_submenu_page', $args['parent_slug'], $args['page_title'], $args['menu_title'], $args['capability'], $args['menu_slug'], $args['function'] );
610
+ }
611
+ }
612
+
613
+ /**
614
+ * Echoes plugin installation form.
615
+ *
616
+ * This method is the callback for the admin_menu method function.
617
+ * This displays the admin page and form area where the user can select to install and activate the plugin.
618
+ * Aborts early if we're processing a plugin installation action.
619
+ *
620
+ * @since 1.0.0
621
+ *
622
+ * @return null Aborts early if we're processing a plugin installation action.
623
+ */
624
+ public function install_plugins_page() {
625
+ // Store new instance of plugin table in object.
626
+ $plugin_table = new INBOUND_TGMPA_List_Table;
627
+
628
+ // Return early if processing a plugin installation action.
629
+ if ( ( ( 'tgmpa-bulk-install' === $plugin_table->current_action() || 'tgmpa-bulk-update' === $plugin_table->current_action() ) && $plugin_table->process_bulk_actions() ) || $this->do_plugin_install() ) {
630
+ return;
631
+ }
632
+
633
+ // Force refresh of available plugin information so we'll know about manual updates/deletes.
634
+ wp_clean_plugins_cache( false );
635
+
636
+ ?>
637
+ <div class="tgmpa wrap">
638
+ <h2><?php echo esc_html( get_admin_page_title() ); ?></h2>
639
+ <?php $plugin_table->prepare_items(); ?>
640
+
641
+ <?php
642
+ if ( ! empty( $this->message ) && is_string( $this->message ) ) {
643
+ echo wp_kses_post( $this->message );
644
+ }
645
+ ?>
646
+ <?php $plugin_table->views(); ?>
647
+
648
+ <form id="tgmpa-plugins" action="" method="post">
649
+ <input type="hidden" name="tgmpa-page" value="<?php echo esc_attr( $this->menu ); ?>" />
650
+ <input type="hidden" name="plugin_status" value="<?php echo esc_attr( $plugin_table->view_context ); ?>" />
651
+ <?php $plugin_table->display(); ?>
652
+ </form>
653
+ </div>
654
+ <?php
655
+ }
656
+
657
+ /**
658
+ * Installs, updates or activates a plugin depending on the action link clicked by the user.
659
+ *
660
+ * Checks the $_GET variable to see which actions have been
661
+ * passed and responds with the appropriate method.
662
+ *
663
+ * Uses WP_Filesystem to process and handle the plugin installation
664
+ * method.
665
+ *
666
+ * @since 1.0.0
667
+ *
668
+ * @uses WP_Filesystem
669
+ * @uses WP_Error
670
+ * @uses WP_Upgrader
671
+ * @uses Plugin_Upgrader
672
+ * @uses Plugin_Installer_Skin
673
+ * @uses Plugin_Upgrader_Skin
674
+ *
675
+ * @return boolean True on success, false on failure.
676
+ */
677
+ protected function do_plugin_install() {
678
+ if ( empty( $_GET['plugin'] ) ) {
679
+ return false;
680
+ }
681
+
682
+ // All plugin information will be stored in an array for processing.
683
+ $slug = $this->sanitize_key( urldecode( $_GET['plugin'] ) );
684
+
685
+ if ( ! isset( $this->plugins[ $slug ] ) ) {
686
+ return false;
687
+ }
688
+
689
+ // Was an install or upgrade action link clicked?
690
+ if ( ( isset( $_GET['tgmpa-install'] ) && 'install-plugin' === $_GET['tgmpa-install'] ) || ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) ) {
691
+
692
+ $install_type = 'install';
693
+ if ( isset( $_GET['tgmpa-update'] ) && 'update-plugin' === $_GET['tgmpa-update'] ) {
694
+ $install_type = 'update';
695
+ }
696
+
697
+ check_admin_referer( 'tgmpa-' . $install_type, 'tgmpa-nonce' );
698
+
699
+ // Pass necessary information via URL if WP_Filesystem is needed.
700
+ $url = wp_nonce_url(
701
+ add_query_arg(
702
+ array(
703
+ 'plugin' => urlencode( $slug ),
704
+ 'tgmpa-' . $install_type => $install_type . '-plugin',
705
+ ),
706
+ $this->get_tgmpa_url()
707
+ ),
708
+ 'tgmpa-' . $install_type,
709
+ 'tgmpa-nonce'
710
+ );
711
+
712
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
713
+
714
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, array() ) ) ) {
715
+ return true;
716
+ }
717
+
718
+ if ( ! WP_Filesystem( $creds ) ) {
719
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, array() ); // Setup WP_Filesystem.
720
+ return true;
721
+ }
722
+
723
+ /* If we arrive here, we have the filesystem. */
724
+
725
+ // Prep variables for Plugin_Installer_Skin class.
726
+ $extra = array();
727
+ $extra['slug'] = $slug; // Needed for potentially renaming of directory name.
728
+ $source = $this->get_download_url( $slug );
729
+ $api = ( 'repo' === $this->plugins[ $slug ]['source_type'] ) ? $this->get_plugins_api( $slug ) : null;
730
+ $api = ( false !== $api ) ? $api : null;
731
+
732
+ $url = add_query_arg(
733
+ array(
734
+ 'action' => $install_type . '-plugin',
735
+ 'plugin' => urlencode( $slug ),
736
+ ),
737
+ 'update.php'
738
+ );
739
+
740
+ if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
741
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
742
+ }
743
+
744
+ $skin_args = array(
745
+ 'type' => ( 'bundled' !== $this->plugins[ $slug ]['source_type'] ) ? 'web' : 'upload',
746
+ 'title' => sprintf( $this->strings['installing'], $this->plugins[ $slug ]['name'] ),
747
+ 'url' => esc_url_raw( $url ),
748
+ 'nonce' => $install_type . '-plugin_' . $slug,
749
+ 'plugin' => '',
750
+ 'api' => $api,
751
+ 'extra' => $extra,
752
+ );
753
+
754
+ if ( 'update' === $install_type ) {
755
+ $skin_args['plugin'] = $this->plugins[ $slug ]['file_path'];
756
+ $skin = new Plugin_Upgrader_Skin( $skin_args );
757
+ } else {
758
+ $skin = new Plugin_Installer_Skin( $skin_args );
759
+ }
760
+
761
+ // Create a new instance of Plugin_Upgrader.
762
+ $upgrader = new Plugin_Upgrader( $skin );
763
+
764
+ // Perform the action and install the plugin from the $source urldecode().
765
+ add_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
766
+
767
+ if ( 'update' === $install_type ) {
768
+ // Inject our info into the update transient.
769
+ $to_inject = array( $slug => $this->plugins[ $slug ] );
770
+ $to_inject[ $slug ]['source'] = $source;
771
+ $this->inject_update_info( $to_inject );
772
+
773
+ $upgrader->upgrade( $this->plugins[ $slug ]['file_path'] );
774
+ } else {
775
+ $upgrader->install( $source );
776
+ }
777
+
778
+ remove_filter( 'upgrader_source_selection', array( $this, 'maybe_adjust_source_dir' ), 1, 3 );
779
+
780
+ // Make sure we have the correct file path now the plugin is installed/updated.
781
+ $this->populate_file_path( $slug );
782
+
783
+ // Only activate plugins if the config option is set to true and the plugin isn't
784
+ // already active (upgrade).
785
+ if ( $this->is_automatic && ! $this->is_plugin_active( $slug ) ) {
786
+ $plugin_activate = $upgrader->plugin_info(); // Grab the plugin info from the Plugin_Upgrader method.
787
+ if ( false === $this->activate_single_plugin( $plugin_activate, $slug, true ) ) {
788
+ return true; // Finish execution of the function early as we encountered an error.
789
+ }
790
+ }
791
+
792
+ $this->show_tgmpa_version();
793
+
794
+ // Display message based on if all plugins are now active or not.
795
+ if ( $this->is_tgmpa_complete() ) {
796
+ echo '<p>', sprintf( esc_html( $this->strings['complete'] ), '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>' ), '</p>';
797
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
798
+ } else {
799
+ echo '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
800
+ }
801
+
802
+ return true;
803
+ } elseif ( isset( $this->plugins[ $slug ]['file_path'], $_GET['tgmpa-activate'] ) && 'activate-plugin' === $_GET['tgmpa-activate'] ) {
804
+ // Activate action link was clicked.
805
+ check_admin_referer( 'tgmpa-activate', 'tgmpa-nonce' );
806
+
807
+ if ( false === $this->activate_single_plugin( $this->plugins[ $slug ]['file_path'], $slug ) ) {
808
+ return true; // Finish execution of the function early as we encountered an error.
809
+ }
810
+ }
811
+
812
+ return false;
813
+ }
814
+
815
+ /**
816
+ * Inject information into the 'update_plugins' site transient as WP checks that before running an update.
817
+ *
818
+ * @since 2.5.0
819
+ *
820
+ * @param array $plugins The plugin information for the plugins which are to be updated.
821
+ */
822
+ public function inject_update_info( $plugins ) {
823
+ $repo_updates = get_site_transient( 'update_plugins' );
824
+
825
+ if ( ! is_object( $repo_updates ) ) {
826
+ $repo_updates = new stdClass;
827
+ }
828
+
829
+ foreach ( $plugins as $slug => $plugin ) {
830
+ $file_path = $plugin['file_path'];
831
+
832
+ if ( empty( $repo_updates->response[ $file_path ] ) ) {
833
+ $repo_updates->response[ $file_path ] = new stdClass;
834
+ }
835
+
836
+ // We only really need to set package, but let's do all we can in case WP changes something.
837
+ $repo_updates->response[ $file_path ]->slug = $slug;
838
+ $repo_updates->response[ $file_path ]->plugin = $file_path;
839
+ $repo_updates->response[ $file_path ]->new_version = $plugin['version'];
840
+ $repo_updates->response[ $file_path ]->package = $plugin['source'];
841
+ if ( empty( $repo_updates->response[ $file_path ]->url ) && ! empty( $plugin['external_url'] ) ) {
842
+ $repo_updates->response[ $file_path ]->url = $plugin['external_url'];
843
+ }
844
+ }
845
+
846
+ set_site_transient( 'update_plugins', $repo_updates );
847
+ }
848
+
849
+ /**
850
+ * Adjust the plugin directory name if necessary.
851
+ *
852
+ * The final destination directory of a plugin is based on the subdirectory name found in the
853
+ * (un)zipped source. In some cases - most notably GitHub repository plugin downloads -, this
854
+ * subdirectory name is not the same as the expected slug and the plugin will not be recognized
855
+ * as installed. This is fixed by adjusting the temporary unzipped source subdirectory name to
856
+ * the expected plugin slug.
857
+ *
858
+ * @since 2.5.0
859
+ *
860
+ * @param string $source Path to upgrade/zip-file-name.tmp/subdirectory/.
861
+ * @param string $remote_source Path to upgrade/zip-file-name.tmp.
862
+ * @param \WP_Upgrader $upgrader Instance of the upgrader which installs the plugin.
863
+ * @return string $source
864
+ */
865
+ public function maybe_adjust_source_dir( $source, $remote_source, $upgrader ) {
866
+ if ( ! $this->is_tgmpa_page() || ! is_object( $GLOBALS['wp_filesystem'] ) ) {
867
+ return $source;
868
+ }
869
+
870
+ // Check for single file plugins.
871
+ $source_files = array_keys( $GLOBALS['wp_filesystem']->dirlist( $remote_source ) );
872
+ if ( 1 === count( $source_files ) && false === $GLOBALS['wp_filesystem']->is_dir( $source ) ) {
873
+ return $source;
874
+ }
875
+
876
+ // Multi-file plugin, let's see if the directory is correctly named.
877
+ $desired_slug = '';
878
+
879
+ // Figure out what the slug is supposed to be.
880
+ if ( false === $upgrader->bulk && ! empty( $upgrader->skin->options['extra']['slug'] ) ) {
881
+ $desired_slug = $upgrader->skin->options['extra']['slug'];
882
+ } else {
883
+ // Bulk installer contains less info, so fall back on the info registered here.
884
+ foreach ( $this->plugins as $slug => $plugin ) {
885
+ if ( ! empty( $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) && $plugin['name'] === $upgrader->skin->plugin_names[ $upgrader->skin->i ] ) {
886
+ $desired_slug = $slug;
887
+ break;
888
+ }
889
+ }
890
+ unset( $slug, $plugin );
891
+ }
892
+
893
+ if ( ! empty( $desired_slug ) ) {
894
+ $subdir_name = untrailingslashit( str_replace( trailingslashit( $remote_source ), '', $source ) );
895
+
896
+ if ( ! empty( $subdir_name ) && $subdir_name !== $desired_slug ) {
897
+ $from = untrailingslashit( $source );
898
+ $to = trailingslashit( $remote_source ) . $desired_slug;
899
+
900
+ if ( true === $GLOBALS['wp_filesystem']->move( $from, $to ) ) {
901
+ return trailingslashit( $to );
902
+ } else {
903
+ return new WP_Error( 'rename_failed', esc_html__( 'The remote plugin package does not contain a folder with the desired slug and renaming did not work.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
904
+ }
905
+ } elseif ( empty( $subdir_name ) ) {
906
+ return new WP_Error( 'packaged_wrong', esc_html__( 'The remote plugin package consists of more than one file, but the files are not packaged in a folder.', 'tgmpa' ) . ' ' . esc_html__( 'Please contact the plugin provider and ask them to package their plugin according to the WordPress guidelines.', 'tgmpa' ), array( 'found' => $subdir_name, 'expected' => $desired_slug ) );
907
+ }
908
+ }
909
+
910
+ return $source;
911
+ }
912
+
913
+ /**
914
+ * Activate a single plugin and send feedback about the result to the screen.
915
+ *
916
+ * @since 2.5.0
917
+ *
918
+ * @param string $file_path Path within wp-plugins/ to main plugin file.
919
+ * @param string $slug Plugin slug.
920
+ * @param bool $automatic Whether this is an automatic activation after an install. Defaults to false.
921
+ * This determines the styling of the output messages.
922
+ * @return bool False if an error was encountered, true otherwise.
923
+ */
924
+ protected function activate_single_plugin( $file_path, $slug, $automatic = false ) {
925
+ if ( $this->can_plugin_activate( $slug ) ) {
926
+ $activate = activate_plugin( $file_path );
927
+
928
+ if ( is_wp_error( $activate ) ) {
929
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>',
930
+ '<p><a href="', esc_url( $this->get_tgmpa_url() ), '" target="_parent">', esc_html( $this->strings['return'] ), '</a></p>';
931
+
932
+ return false; // End it here if there is an error with activation.
933
+ } else {
934
+ if ( ! $automatic ) {
935
+ // Make sure message doesn't display again if bulk activation is performed
936
+ // immediately after a single activation.
937
+ if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
938
+ echo '<div id="message" class="updated"><p>', esc_html( $this->strings['activated_successfully'] ), ' <strong>', esc_html( $this->plugins[ $slug ]['name'] ), '.</strong></p></div>';
939
+ }
940
+ } else {
941
+ // Simpler message layout for use on the plugin install page.
942
+ echo '<p>', esc_html( $this->strings['plugin_activated'] ), '</p>';
943
+ }
944
+ }
945
+ } elseif ( $this->is_plugin_active( $slug ) ) {
946
+ // No simpler message format provided as this message should never be encountered
947
+ // on the plugin install page.
948
+ echo '<div id="message" class="error"><p>',
949
+ sprintf(
950
+ esc_html( $this->strings['plugin_already_active'] ),
951
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
952
+ ),
953
+ '</p></div>';
954
+ } elseif ( $this->does_plugin_require_update( $slug ) ) {
955
+ if ( ! $automatic ) {
956
+ // Make sure message doesn't display again if bulk activation is performed
957
+ // immediately after a single activation.
958
+ if ( ! isset( $_POST['action'] ) ) { // WPCS: CSRF OK.
959
+ echo '<div id="message" class="error"><p>',
960
+ sprintf(
961
+ esc_html( $this->strings['plugin_needs_higher_version'] ),
962
+ '<strong>' . esc_html( $this->plugins[ $slug ]['name'] ) . '</strong>'
963
+ ),
964
+ '</p></div>';
965
+ }
966
+ } else {
967
+ // Simpler message layout for use on the plugin install page.
968
+ echo '<p>', sprintf( esc_html( $this->strings['plugin_needs_higher_version'] ), esc_html( $this->plugins[ $slug ]['name'] ) ), '</p>';
969
+ }
970
+ }
971
+
972
+ return true;
973
+ }
974
+
975
+ /**
976
+ * Echoes required plugin notice.
977
+ *
978
+ * Outputs a message telling users that a specific plugin is required for
979
+ * their theme. If appropriate, it includes a link to the form page where
980
+ * users can install and activate the plugin.
981
+ *
982
+ * Returns early if we're on the Install page.
983
+ *
984
+ * @since 1.0.0
985
+ *
986
+ * @global object $current_screen
987
+ *
988
+ * @return null Returns early if we're on the Install page.
989
+ */
990
+ public function notices() {
991
+ // Remove nag on the install page / Return early if the nag message has been dismissed.
992
+ if ( $this->is_tgmpa_page() || get_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, true ) ) {
993
+ return;
994
+ }
995
+
996
+ // Store for the plugin slugs by message type.
997
+ $message = array();
998
+
999
+ // Initialize counters used to determine plurality of action link texts.
1000
+ $install_link_count = 0;
1001
+ $update_link_count = 0;
1002
+ $activate_link_count = 0;
1003
+
1004
+ foreach ( $this->plugins as $slug => $plugin ) {
1005
+ if ( $this->is_plugin_active( $slug ) && false === $this->does_plugin_have_update( $slug ) ) {
1006
+ continue;
1007
+ }
1008
+
1009
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1010
+ if ( current_user_can( 'install_plugins' ) ) {
1011
+ $install_link_count++;
1012
+
1013
+ if ( true === $plugin['required'] ) {
1014
+ $message['notice_can_install_required'][] = $slug;
1015
+ } else {
1016
+ $message['notice_can_install_recommended'][] = $slug;
1017
+ }
1018
+ } else {
1019
+ // Need higher privileges to install the plugin.
1020
+ $message['notice_cannot_install'][] = $slug;
1021
+ }
1022
+ } else {
1023
+ if ( ! $this->is_plugin_active( $slug ) && $this->can_plugin_activate( $slug ) ) {
1024
+ if ( current_user_can( 'activate_plugins' ) ) {
1025
+ $activate_link_count++;
1026
+
1027
+ if ( true === $plugin['required'] ) {
1028
+ $message['notice_can_activate_required'][] = $slug;
1029
+ } else {
1030
+ $message['notice_can_activate_recommended'][] = $slug;
1031
+ }
1032
+ } else {
1033
+ // Need higher privileges to activate the plugin.
1034
+ $message['notice_cannot_activate'][] = $slug;
1035
+ }
1036
+ }
1037
+
1038
+ if ( $this->does_plugin_require_update( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1039
+
1040
+ if ( current_user_can( 'install_plugins' ) ) {
1041
+ $update_link_count++;
1042
+
1043
+ if ( $this->does_plugin_require_update( $slug ) ) {
1044
+ $message['notice_ask_to_update'][] = $slug;
1045
+ } elseif ( false !== $this->does_plugin_have_update( $slug ) ) {
1046
+ $message['notice_ask_to_update_maybe'][] = $slug;
1047
+ }
1048
+ } else {
1049
+ // Need higher privileges to update the plugin.
1050
+ $message['notice_cannot_update'][] = $slug;
1051
+ }
1052
+ }
1053
+ }
1054
+ }
1055
+ unset( $slug, $plugin );
1056
+
1057
+ // If we have notices to display, we move forward.
1058
+ if ( ! empty( $message ) ) {
1059
+ krsort( $message ); // Sort messages.
1060
+ $rendered = '';
1061
+
1062
+ // As add_settings_error() wraps the final message in a <p> and as the final message can't be
1063
+ // filtered, using <p>'s in our html would render invalid html output.
1064
+ $line_template = '<span style="display: block; margin: 0.5em 0.5em 0 0; clear: both;">%s</span>' . "\n";
1065
+
1066
+ // If dismissable is false and a message is set, output it now.
1067
+ if ( ! $this->dismissable && ! empty( $this->dismiss_msg ) ) {
1068
+ $rendered .= sprintf( $line_template, wp_kses_post( $this->dismiss_msg ) );
1069
+ }
1070
+
1071
+ // Render the individual message lines for the notice.
1072
+ foreach ( $message as $type => $plugin_group ) {
1073
+ $linked_plugins = array();
1074
+
1075
+ // Get the external info link for a plugin if one is available.
1076
+ foreach ( $plugin_group as $plugin_slug ) {
1077
+ $linked_plugins[] = $this->get_info_link( $plugin_slug );
1078
+ }
1079
+ unset( $plugin_slug );
1080
+
1081
+ $count = count( $plugin_group );
1082
+ $linked_plugins = array_map( array( 'INBOUND_TGM_Utils', 'wrap_in_em' ), $linked_plugins );
1083
+ $last_plugin = array_pop( $linked_plugins ); // Pop off last name to prep for readability.
1084
+
1085
+ $imploded = empty( $linked_plugins ) ? $last_plugin : ( implode( ', ', $linked_plugins ) . ' <span class="inbound-and">and</span>' . ' ' . $last_plugin );
1086
+
1087
+ $rendered .= sprintf(
1088
+ $line_template,
1089
+ sprintf(
1090
+ translate_nooped_plural( $this->strings[ $type ], $count, 'tgmpa' ),
1091
+ $imploded,
1092
+ $count
1093
+ )
1094
+ );
1095
+
1096
+ if ( 0 === strpos( $type, 'notice_cannot' ) ) {
1097
+ $rendered .= $this->strings['contact_admin'];
1098
+ }
1099
+ }
1100
+ unset( $type, $plugin_group, $linked_plugins, $count, $last_plugin, $imploded );
1101
+
1102
+ // Setup action links.
1103
+ $action_links = array(
1104
+ 'install' => '',
1105
+ 'update' => '',
1106
+ 'activate' => '',
1107
+ 'dismiss' => $this->dismissable ? '<a href="' . esc_url( add_query_arg( 'tgmpa-dismiss', 'dismiss_admin_notices' ) ) . '" class="dismiss-notice" target="_parent">' . esc_html( $this->strings['dismiss'] ) . '</a>' : '',
1108
+ );
1109
+
1110
+ $link_template = '<a href="%2$s">%1$s</a>';
1111
+
1112
+ if ( current_user_can( 'install_plugins' ) ) {
1113
+ if ( $install_link_count > 0 ) {
1114
+ $action_links['install'] = sprintf(
1115
+ $link_template,
1116
+ translate_nooped_plural( $this->strings['install_link'], $install_link_count, 'tgmpa' ),
1117
+ esc_url( $this->get_tgmpa_status_url( 'install' ) )
1118
+ );
1119
+ }
1120
+ if ( $update_link_count > 0 ) {
1121
+ $action_links['update'] = sprintf(
1122
+ $link_template,
1123
+ translate_nooped_plural( $this->strings['update_link'], $update_link_count, 'tgmpa' ),
1124
+ esc_url( $this->get_tgmpa_status_url( 'update' ) )
1125
+ );
1126
+ }
1127
+ }
1128
+
1129
+ if ( current_user_can( 'activate_plugins' ) && $activate_link_count > 0 ) {
1130
+ $action_links['activate'] = sprintf(
1131
+ $link_template,
1132
+ translate_nooped_plural( $this->strings['activate_link'], $activate_link_count, 'tgmpa' ),
1133
+ esc_url( $this->get_tgmpa_status_url( 'activate' ) )
1134
+ );
1135
+ }
1136
+
1137
+ $action_links = apply_filters( 'tgmpa_notice_action_links', $action_links );
1138
+
1139
+ $action_links = array_filter( (array) $action_links ); // Remove any empty array items.
1140
+
1141
+ if ( ! empty( $action_links ) && is_array( $action_links ) ) {
1142
+ $action_links = sprintf( $line_template, implode( ' | ', $action_links ) );
1143
+ $rendered .= apply_filters( 'tgmpa_notice_rendered_action_links', $action_links );
1144
+ }
1145
+
1146
+ // Register the nag messages and prepare them to be processed.
1147
+ if ( ! empty( $this->strings['nag_type'] ) ) {
1148
+ add_settings_error( 'tgmpa', 'tgmpa', $rendered, sanitize_html_class( strtolower( $this->strings['nag_type'] ) ) );
1149
+ } else {
1150
+ $nag_class = version_compare( $this->wp_version, '3.8', '<' ) ? 'updated' : 'update-nag';
1151
+ add_settings_error( 'tgmpa', 'tgmpa', $rendered, $nag_class );
1152
+ }
1153
+ }
1154
+
1155
+ // Admin options pages already output settings_errors, so this is to avoid duplication.
1156
+ if ( 'options-general' !== $GLOBALS['current_screen']->parent_base ) {
1157
+ $this->display_settings_errors();
1158
+ }
1159
+ }
1160
+
1161
+ /**
1162
+ * Display settings errors and remove those which have been displayed to avoid duplicate messages showing
1163
+ *
1164
+ * @since 2.5.0
1165
+ */
1166
+ protected function display_settings_errors() {
1167
+ global $wp_settings_errors;
1168
+
1169
+ settings_errors( 'tgmpa' );
1170
+
1171
+ foreach ( (array) $wp_settings_errors as $key => $details ) {
1172
+ if ( 'tgmpa' === $details['setting'] ) {
1173
+ unset( $wp_settings_errors[ $key ] );
1174
+ break;
1175
+ }
1176
+ }
1177
+ }
1178
+
1179
+ /**
1180
+ * Add dismissable admin notices.
1181
+ *
1182
+ * Appends a link to the admin nag messages. If clicked, the admin notice disappears and no longer is visible to users.
1183
+ *
1184
+ * @since 2.1.0
1185
+ */
1186
+ public function dismiss() {
1187
+ if ( isset( $_GET['tgmpa-dismiss'] ) ) {
1188
+ update_user_meta( get_current_user_id(), 'tgmpa_dismissed_notice_' . $this->id, 1 );
1189
+ }
1190
+ }
1191
+
1192
+ /**
1193
+ * Add individual plugin to our collection of plugins.
1194
+ *
1195
+ * If the required keys are not set or the plugin has already
1196
+ * been registered, the plugin is not added.
1197
+ *
1198
+ * @since 2.0.0
1199
+ *
1200
+ * @param array|null $plugin Array of plugin arguments or null if invalid argument.
1201
+ * @return null Return early if incorrect argument.
1202
+ */
1203
+ public function register( $plugin ) {
1204
+ if ( empty( $plugin['slug'] ) || empty( $plugin['name'] ) ) {
1205
+ return;
1206
+ }
1207
+
1208
+ if ( empty( $plugin['slug'] ) || ! is_string( $plugin['slug'] ) || isset( $this->plugins[ $plugin['slug'] ] ) ) {
1209
+ return;
1210
+ }
1211
+
1212
+ $defaults = array(
1213
+ 'name' => '', // String
1214
+ 'slug' => '', // String
1215
+ 'source' => 'repo', // String
1216
+ 'required' => false, // Boolean
1217
+ 'version' => '', // String
1218
+ 'force_activation' => false, // Boolean
1219
+ 'force_deactivation' => false, // Boolean
1220
+ 'external_url' => '', // String
1221
+ 'is_callable' => '', // String|Array.
1222
+ );
1223
+
1224
+ // Prepare the received data.
1225
+ $plugin = wp_parse_args( $plugin, $defaults );
1226
+
1227
+ // Standardize the received slug.
1228
+ $plugin['slug'] = $this->sanitize_key( $plugin['slug'] );
1229
+
1230
+ // Forgive users for using string versions of booleans or floats for version number.
1231
+ $plugin['version'] = (string) $plugin['version'];
1232
+ $plugin['source'] = empty( $plugin['source'] ) ? 'repo' : $plugin['source'];
1233
+ $plugin['required'] = INBOUND_TGM_Utils::validate_bool( $plugin['required'] );
1234
+ $plugin['force_activation'] = INBOUND_TGM_Utils::validate_bool( $plugin['force_activation'] );
1235
+ $plugin['force_deactivation'] = INBOUND_TGM_Utils::validate_bool( $plugin['force_deactivation'] );
1236
+
1237
+ // Enrich the received data.
1238
+ $plugin['file_path'] = $this->_get_plugin_basename_from_slug( $plugin['slug'] );
1239
+ $plugin['source_type'] = $this->get_plugin_source_type( $plugin['source'] );
1240
+
1241
+ // Set the class properties.
1242
+ $this->plugins[ $plugin['slug'] ] = $plugin;
1243
+ $this->sort_order[ $plugin['slug'] ] = $plugin['name'];
1244
+
1245
+ // Should we add the force activation hook ?
1246
+ if ( true === $plugin['force_activation'] ) {
1247
+ $this->has_forced_activation = true;
1248
+ }
1249
+
1250
+ // Should we add the force deactivation hook ?
1251
+ if ( true === $plugin['force_deactivation'] ) {
1252
+ $this->has_forced_deactivation = true;
1253
+ }
1254
+ }
1255
+
1256
+ /**
1257
+ * Determine what type of source the plugin comes from.
1258
+ *
1259
+ * @since 2.5.0
1260
+ *
1261
+ * @param string $source The source of the plugin as provided, either empty (= WP repo), a file path
1262
+ * (= bundled) or an external URL.
1263
+ * @return string 'repo', 'external', or 'bundled'
1264
+ */
1265
+ protected function get_plugin_source_type( $source ) {
1266
+ if ( 'repo' === $source || preg_match( self::WP_REPO_REGEX, $source ) ) {
1267
+ return 'repo';
1268
+ } elseif ( preg_match( self::IS_URL_REGEX, $source ) ) {
1269
+ return 'external';
1270
+ } else {
1271
+ return 'bundled';
1272
+ }
1273
+ }
1274
+
1275
+ /**
1276
+ * Sanitizes a string key.
1277
+ *
1278
+ * Near duplicate of WP Core `sanitize_key()`. The difference is that uppercase characters *are*
1279
+ * allowed, so as not to break upgrade paths from non-standard bundled plugins using uppercase
1280
+ * characters in the plugin directory path/slug. Silly them.
1281
+ *
1282
+ * @see https://developer.wordpress.org/reference/hooks/sanitize_key/
1283
+ *
1284
+ * @since 2.5.0
1285
+ *
1286
+ * @param string $key String key.
1287
+ * @return string Sanitized key
1288
+ */
1289
+ public function sanitize_key( $key ) {
1290
+ $raw_key = $key;
1291
+ $key = preg_replace( '`[^A-Za-z0-9_-]`', '', $key );
1292
+
1293
+ /**
1294
+ * Filter a sanitized key string.
1295
+ *
1296
+ * @since 3.0.0
1297
+ *
1298
+ * @param string $key Sanitized key.
1299
+ * @param string $raw_key The key prior to sanitization.
1300
+ */
1301
+ return apply_filters( 'tgmpa_sanitize_key', $key, $raw_key );
1302
+ }
1303
+
1304
+ /**
1305
+ * Amend default configuration settings.
1306
+ *
1307
+ * @since 2.0.0
1308
+ *
1309
+ * @param array $config Array of config options to pass as class properties.
1310
+ */
1311
+ public function config( $config ) {
1312
+ $keys = array(
1313
+ 'id',
1314
+ 'default_path',
1315
+ 'has_notices',
1316
+ 'dismissable',
1317
+ 'dismiss_msg',
1318
+ 'menu',
1319
+ 'parent_slug',
1320
+ 'capability',
1321
+ 'is_automatic',
1322
+ 'message',
1323
+ 'strings',
1324
+ );
1325
+
1326
+ foreach ( $keys as $key ) {
1327
+ if ( isset( $config[ $key ] ) ) {
1328
+ if ( is_array( $config[ $key ] ) ) {
1329
+ $this->$key = array_merge( $this->$key, $config[ $key ] );
1330
+ } else {
1331
+ $this->$key = $config[ $key ];
1332
+ }
1333
+ }
1334
+ }
1335
+ }
1336
+
1337
+ /**
1338
+ * Amend action link after plugin installation.
1339
+ *
1340
+ * @since 2.0.0
1341
+ *
1342
+ * @param array $install_actions Existing array of actions.
1343
+ * @return array Amended array of actions.
1344
+ */
1345
+ public function actions( $install_actions ) {
1346
+ // Remove action links on the TGMPA install page.
1347
+ if ( $this->is_tgmpa_page() ) {
1348
+ return false;
1349
+ }
1350
+
1351
+ return $install_actions;
1352
+ }
1353
+
1354
+ /**
1355
+ * Flushes the plugins cache on theme switch to prevent stale entries
1356
+ * from remaining in the plugin table.
1357
+ *
1358
+ * @since 2.4.0
1359
+ *
1360
+ * @param bool $clear_update_cache Optional. Whether to clear the Plugin updates cache.
1361
+ * Parameter added in v2.5.0.
1362
+ */
1363
+ public function flush_plugins_cache( $clear_update_cache = true ) {
1364
+ wp_clean_plugins_cache( $clear_update_cache );
1365
+ }
1366
+
1367
+ /**
1368
+ * Set file_path key for each installed plugin.
1369
+ *
1370
+ * @since 2.1.0
1371
+ *
1372
+ * @param string $plugin_slug Optional. If set, only (re-)populates the file path for that specific plugin.
1373
+ * Parameter added in v2.5.0.
1374
+ */
1375
+ public function populate_file_path( $plugin_slug = '' ) {
1376
+ if ( ! empty( $plugin_slug ) && is_string( $plugin_slug ) && isset( $this->plugins[ $plugin_slug ] ) ) {
1377
+ $this->plugins[ $plugin_slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $plugin_slug );
1378
+ } else {
1379
+ // Add file_path key for all plugins.
1380
+ foreach ( $this->plugins as $slug => $values ) {
1381
+ $this->plugins[ $slug ]['file_path'] = $this->_get_plugin_basename_from_slug( $slug );
1382
+ }
1383
+ }
1384
+ }
1385
+
1386
+ /**
1387
+ * Helper function to extract the file path of the plugin file from the
1388
+ * plugin slug, if the plugin is installed.
1389
+ *
1390
+ * @since 2.0.0
1391
+ *
1392
+ * @param string $slug Plugin slug (typically folder name) as provided by the developer.
1393
+ * @return string Either file path for plugin if installed, or just the plugin slug.
1394
+ */
1395
+ protected function _get_plugin_basename_from_slug( $slug ) {
1396
+ $keys = array_keys( $this->get_plugins() );
1397
+
1398
+ foreach ( $keys as $key ) {
1399
+ if ( preg_match( '|^' . $slug . '/|', $key ) ) {
1400
+ return $key;
1401
+ }
1402
+ }
1403
+
1404
+ return $slug;
1405
+ }
1406
+
1407
+ /**
1408
+ * Retrieve plugin data, given the plugin name.
1409
+ *
1410
+ * Loops through the registered plugins looking for $name. If it finds it,
1411
+ * it returns the $data from that plugin. Otherwise, returns false.
1412
+ *
1413
+ * @since 2.1.0
1414
+ *
1415
+ * @param string $name Name of the plugin, as it was registered.
1416
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
1417
+ * @return string|boolean Plugin slug if found, false otherwise.
1418
+ */
1419
+ public function _get_plugin_data_from_name( $name, $data = 'slug' ) {
1420
+ foreach ( $this->plugins as $values ) {
1421
+ if ( $name === $values['name'] && isset( $values[ $data ] ) ) {
1422
+ return $values[ $data ];
1423
+ }
1424
+ }
1425
+
1426
+ return false;
1427
+ }
1428
+
1429
+ /**
1430
+ * Retrieve the download URL for a package.
1431
+ *
1432
+ * @since 2.5.0
1433
+ *
1434
+ * @param string $slug Plugin slug.
1435
+ * @return string Plugin download URL or path to local file or empty string if undetermined.
1436
+ */
1437
+ public function get_download_url( $slug ) {
1438
+ $dl_source = '';
1439
+
1440
+ switch ( $this->plugins[ $slug ]['source_type'] ) {
1441
+ case 'repo':
1442
+ return $this->get_wp_repo_download_url( $slug );
1443
+ case 'external':
1444
+ return $this->plugins[ $slug ]['source'];
1445
+ case 'bundled':
1446
+ return $this->default_path . $this->plugins[ $slug ]['source'];
1447
+ }
1448
+
1449
+ return $dl_source; // Should never happen.
1450
+ }
1451
+
1452
+ /**
1453
+ * Retrieve the download URL for a WP repo package.
1454
+ *
1455
+ * @since 2.5.0
1456
+ *
1457
+ * @param string $slug Plugin slug.
1458
+ * @return string Plugin download URL.
1459
+ */
1460
+ protected function get_wp_repo_download_url( $slug ) {
1461
+ $source = '';
1462
+ $api = $this->get_plugins_api( $slug );
1463
+
1464
+ if ( false !== $api && isset( $api->download_link ) ) {
1465
+ $source = $api->download_link;
1466
+ }
1467
+
1468
+ return $source;
1469
+ }
1470
+
1471
+ /**
1472
+ * Try to grab information from WordPress API.
1473
+ *
1474
+ * @since 2.5.0
1475
+ *
1476
+ * @param string $slug Plugin slug.
1477
+ * @return object Plugins_api response object on success, WP_Error on failure.
1478
+ */
1479
+ protected function get_plugins_api( $slug ) {
1480
+ static $api = array(); // Cache received responses.
1481
+
1482
+ if ( ! isset( $api[ $slug ] ) ) {
1483
+ if ( ! function_exists( 'plugins_api' ) ) {
1484
+ require_once ABSPATH . 'wp-admin/includes/plugin-install.php';
1485
+ }
1486
+
1487
+ $response = plugins_api( 'plugin_information', array( 'slug' => $slug, 'fields' => array( 'sections' => false ) ) );
1488
+
1489
+ $api[ $slug ] = false;
1490
+
1491
+ if ( is_wp_error( $response ) ) {
1492
+ wp_die( esc_html( $this->strings['oops'] ) );
1493
+ } else {
1494
+ $api[ $slug ] = $response;
1495
+ }
1496
+ }
1497
+
1498
+ return $api[ $slug ];
1499
+ }
1500
+
1501
+ /**
1502
+ * Retrieve a link to a plugin information page.
1503
+ *
1504
+ * @since 2.5.0
1505
+ *
1506
+ * @param string $slug Plugin slug.
1507
+ * @return string Fully formed html link to a plugin information page if available
1508
+ * or the plugin name if not.
1509
+ */
1510
+ public function get_info_link( $slug ) {
1511
+ if ( ! empty( $this->plugins[ $slug ]['external_url'] ) && preg_match( self::IS_URL_REGEX, $this->plugins[ $slug ]['external_url'] ) ) {
1512
+ $link = sprintf(
1513
+ '<a href="%1$s" target="_blank">%2$s</a>',
1514
+ esc_url( $this->plugins[ $slug ]['external_url'] ),
1515
+ esc_html( $this->plugins[ $slug ]['name'] )
1516
+ );
1517
+ } elseif ( 'repo' === $this->plugins[ $slug ]['source_type'] ) {
1518
+ $url = add_query_arg(
1519
+ array(
1520
+ 'tab' => 'plugin-information',
1521
+ 'plugin' => urlencode( $slug ),
1522
+ 'TB_iframe' => 'true',
1523
+ 'width' => '640',
1524
+ 'height' => '500',
1525
+ ),
1526
+ self_admin_url( 'plugin-install.php' )
1527
+ );
1528
+
1529
+ $link = sprintf(
1530
+ '<a href="%1$s" class="thickbox">%2$s</a>',
1531
+ esc_url( $url ),
1532
+ $this->plugins[ $slug ]['name']
1533
+ );
1534
+ } else {
1535
+ $link = esc_html( $this->plugins[ $slug ]['name'] ); // No hyperlink.
1536
+ }
1537
+
1538
+ return $link;
1539
+ }
1540
+
1541
+ /**
1542
+ * Determine if we're on the TGMPA Install page.
1543
+ *
1544
+ * @since 2.1.0
1545
+ *
1546
+ * @return boolean True when on the TGMPA page, false otherwise.
1547
+ */
1548
+ protected function is_tgmpa_page() {
1549
+ return isset( $_GET['page'] ) && $this->menu === $_GET['page'];
1550
+ }
1551
+
1552
+ /**
1553
+ * Retrieve the URL to the TGMPA Install page.
1554
+ *
1555
+ * I.e. depending on the config settings passed something along the lines of:
1556
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins
1557
+ *
1558
+ * @since 2.5.0
1559
+ *
1560
+ * @return string Properly encoded URL (not escaped).
1561
+ */
1562
+ public function get_tgmpa_url() {
1563
+ static $url;
1564
+
1565
+ if ( ! isset( $url ) ) {
1566
+ $parent = $this->parent_slug;
1567
+ if ( false === strpos( $parent, '.php' ) ) {
1568
+ $parent = 'admin.php';
1569
+ }
1570
+ $url = add_query_arg(
1571
+ array(
1572
+ 'page' => urlencode( $this->menu ),
1573
+ ),
1574
+ self_admin_url( $parent )
1575
+ );
1576
+ }
1577
+
1578
+ return $url;
1579
+ }
1580
+
1581
+ /**
1582
+ * Retrieve the URL to the TGMPA Install page for a specific plugin status (view).
1583
+ *
1584
+ * I.e. depending on the config settings passed something along the lines of:
1585
+ * http://example.com/wp-admin/themes.php?page=tgmpa-install-plugins&plugin_status=install
1586
+ *
1587
+ * @since 2.5.0
1588
+ *
1589
+ * @param string $status Plugin status - either 'install', 'update' or 'activate'.
1590
+ * @return string Properly encoded URL (not escaped).
1591
+ */
1592
+ public function get_tgmpa_status_url( $status ) {
1593
+ return add_query_arg(
1594
+ array(
1595
+ 'plugin_status' => urlencode( $status ),
1596
+ ),
1597
+ $this->get_tgmpa_url()
1598
+ );
1599
+ }
1600
+
1601
+ /**
1602
+ * Determine whether there are open actions for plugins registered with TGMPA.
1603
+ *
1604
+ * @since 2.5.0
1605
+ *
1606
+ * @return bool True if complete, i.e. no outstanding actions. False otherwise.
1607
+ */
1608
+ public function is_tgmpa_complete() {
1609
+ $complete = true;
1610
+ foreach ( $this->plugins as $slug => $plugin ) {
1611
+ if ( ! $this->is_plugin_active( $slug ) || false !== $this->does_plugin_have_update( $slug ) ) {
1612
+ $complete = false;
1613
+ break;
1614
+ }
1615
+ }
1616
+
1617
+ return $complete;
1618
+ }
1619
+
1620
+ /**
1621
+ * Check if a plugin is installed. Does not take must-use plugins into account.
1622
+ *
1623
+ * @since 2.5.0
1624
+ *
1625
+ * @param string $slug Plugin slug.
1626
+ * @return bool True if installed, false otherwise.
1627
+ */
1628
+ public function is_plugin_installed( $slug ) {
1629
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1630
+
1631
+ return ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ] ) );
1632
+ }
1633
+
1634
+ /**
1635
+ * Check if a plugin is active.
1636
+ *
1637
+ * @since 2.5.0
1638
+ *
1639
+ * @param string $slug Plugin slug.
1640
+ * @return bool True if active, false otherwise.
1641
+ */
1642
+ public function is_plugin_active( $slug ) {
1643
+ return ( ( ! empty( $this->plugins[ $slug ]['is_callable'] ) && is_callable( $this->plugins[ $slug ]['is_callable'] ) ) || is_plugin_active( $this->plugins[ $slug ]['file_path'] ) );
1644
+ }
1645
+
1646
+ /**
1647
+ * Check if a plugin can be updated, i.e. if we have information on the minimum WP version required
1648
+ * available, check whether the current install meets them.
1649
+ *
1650
+ * @since 2.5.0
1651
+ *
1652
+ * @param string $slug Plugin slug.
1653
+ * @return bool True if OK to update, false otherwise.
1654
+ */
1655
+ public function can_plugin_update( $slug ) {
1656
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1657
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1658
+ return true;
1659
+ }
1660
+
1661
+ $api = $this->get_plugins_api( $slug );
1662
+
1663
+ if ( false !== $api && isset( $api->requires ) ) {
1664
+ return version_compare( $GLOBALS['wp_version'], $api->requires, '>=' );
1665
+ }
1666
+
1667
+ // No usable info received from the plugins API, presume we can update.
1668
+ return true;
1669
+ }
1670
+
1671
+ /**
1672
+ * Check if a plugin can be activated, i.e. is not currently active and meets the minimum
1673
+ * plugin version requirements set in TGMPA (if any).
1674
+ *
1675
+ * @since 2.5.0
1676
+ *
1677
+ * @param string $slug Plugin slug.
1678
+ * @return bool True if OK to activate, false otherwise.
1679
+ */
1680
+ public function can_plugin_activate( $slug ) {
1681
+ return ( ! $this->is_plugin_active( $slug ) && ! $this->does_plugin_require_update( $slug ) );
1682
+ }
1683
+
1684
+ /**
1685
+ * Retrieve the version number of an installed plugin.
1686
+ *
1687
+ * @since 2.5.0
1688
+ *
1689
+ * @param string $slug Plugin slug.
1690
+ * @return string Version number as string or an empty string if the plugin is not installed
1691
+ * or version unknown (plugins which don't comply with the plugin header standard).
1692
+ */
1693
+ public function get_installed_version( $slug ) {
1694
+ $installed_plugins = $this->get_plugins(); // Retrieve a list of all installed plugins (WP cached).
1695
+
1696
+ if ( ! empty( $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'] ) ) {
1697
+ return $installed_plugins[ $this->plugins[ $slug ]['file_path'] ]['Version'];
1698
+ }
1699
+
1700
+ return '';
1701
+ }
1702
+
1703
+ /**
1704
+ * Check whether a plugin complies with the minimum version requirements.
1705
+ *
1706
+ * @since 2.5.0
1707
+ *
1708
+ * @param string $slug Plugin slug.
1709
+ * @return bool True when a plugin needs to be updated, otherwise false.
1710
+ */
1711
+ public function does_plugin_require_update( $slug ) {
1712
+ $installed_version = $this->get_installed_version( $slug );
1713
+ $minimum_version = $this->plugins[ $slug ]['version'];
1714
+
1715
+ return version_compare( $minimum_version, $installed_version, '>' );
1716
+ }
1717
+
1718
+ /**
1719
+ * Check whether there is an update available for a plugin.
1720
+ *
1721
+ * @since 2.5.0
1722
+ *
1723
+ * @param string $slug Plugin slug.
1724
+ * @return false|string Version number string of the available update or false if no update available.
1725
+ */
1726
+ public function does_plugin_have_update( $slug ) {
1727
+ // Presume bundled and external plugins will point to a package which meets the minimum required version.
1728
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1729
+ if ( $this->does_plugin_require_update( $slug ) ) {
1730
+ return $this->plugins[ $slug ]['version'];
1731
+ }
1732
+
1733
+ return false;
1734
+ }
1735
+
1736
+ $repo_updates = get_site_transient( 'update_plugins' );
1737
+
1738
+ if ( isset( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version ) ) {
1739
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->new_version;
1740
+ }
1741
+
1742
+ return false;
1743
+ }
1744
+
1745
+ /**
1746
+ * Retrieve potential upgrade notice for a plugin.
1747
+ *
1748
+ * @since 2.5.0
1749
+ *
1750
+ * @param string $slug Plugin slug.
1751
+ * @return string The upgrade notice or an empty string if no message was available or provided.
1752
+ */
1753
+ public function get_upgrade_notice( $slug ) {
1754
+ // We currently can't get reliable info on non-WP-repo plugins - issue #380.
1755
+ if ( 'repo' !== $this->plugins[ $slug ]['source_type'] ) {
1756
+ return '';
1757
+ }
1758
+
1759
+ $repo_updates = get_site_transient( 'update_plugins' );
1760
+
1761
+ if ( ! empty( $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice ) ) {
1762
+ return $repo_updates->response[ $this->plugins[ $slug ]['file_path'] ]->upgrade_notice;
1763
+ }
1764
+
1765
+ return '';
1766
+ }
1767
+
1768
+ /**
1769
+ * Wrapper around the core WP get_plugins function, making sure it's actually available.
1770
+ *
1771
+ * @since 2.5.0
1772
+ *
1773
+ * @param string $plugin_folder Optional. Relative path to single plugin folder.
1774
+ * @return array Array of installed plugins with plugin information.
1775
+ */
1776
+ public function get_plugins( $plugin_folder = '' ) {
1777
+ if ( ! function_exists( 'get_plugins' ) ) {
1778
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
1779
+ }
1780
+
1781
+ return get_plugins( $plugin_folder );
1782
+ }
1783
+
1784
+ /**
1785
+ * Delete dismissable nag option when theme is switched.
1786
+ *
1787
+ * This ensures that the user(s) is/are again reminded via nag of required
1788
+ * and/or recommended plugins if they re-activate the theme.
1789
+ *
1790
+ * @since 2.1.1
1791
+ */
1792
+ public function update_dismiss() {
1793
+ delete_metadata( 'user', null, 'tgmpa_dismissed_notice_' . $this->id, null, true );
1794
+ }
1795
+
1796
+ /**
1797
+ * Forces plugin activation if the parameter 'force_activation' is
1798
+ * set to true.
1799
+ *
1800
+ * This allows theme authors to specify certain plugins that must be
1801
+ * active at all times while using the current theme.
1802
+ *
1803
+ * Please take special care when using this parameter as it has the
1804
+ * potential to be harmful if not used correctly. Setting this parameter
1805
+ * to true will not allow the specified plugin to be deactivated unless
1806
+ * the user switches themes.
1807
+ *
1808
+ * @since 2.2.0
1809
+ */
1810
+ public function force_activation() {
1811
+ foreach ( $this->plugins as $slug => $plugin ) {
1812
+ if ( true === $plugin['force_activation'] ) {
1813
+ if ( ! $this->is_plugin_installed( $slug ) ) {
1814
+ // Oops, plugin isn't there so iterate to next condition.
1815
+ continue;
1816
+ } elseif ( $this->can_plugin_activate( $slug ) ) {
1817
+ // There we go, activate the plugin.
1818
+ activate_plugin( $plugin['file_path'] );
1819
+ }
1820
+ }
1821
+ }
1822
+ }
1823
+
1824
+ /**
1825
+ * Forces plugin deactivation if the parameter 'force_deactivation'
1826
+ * is set to true.
1827
+ *
1828
+ * This allows theme authors to specify certain plugins that must be
1829
+ * deactivated upon switching from the current theme to another.
1830
+ *
1831
+ * Please take special care when using this parameter as it has the
1832
+ * potential to be harmful if not used correctly.
1833
+ *
1834
+ * @since 2.2.0
1835
+ */
1836
+ public function force_deactivation() {
1837
+ foreach ( $this->plugins as $slug => $plugin ) {
1838
+ // Only proceed forward if the parameter is set to true and plugin is active.
1839
+ if ( true === $plugin['force_deactivation'] && $this->is_plugin_active( $slug ) ) {
1840
+ deactivate_plugins( $plugin['file_path'] );
1841
+ }
1842
+ }
1843
+ }
1844
+
1845
+ /**
1846
+ * Echo the current TGMPA version number to the page.
1847
+ */
1848
+ public function show_tgmpa_version() {
1849
+ echo '<p style="float: right; padding: 0em 1.5em 0.5em 0;"><strong><small>',
1850
+ esc_html( sprintf( _x( 'TGMPA v%s', '%s = version number', 'tgmpa' ), self::TGMPA_VERSION ) ),
1851
+ '</small></strong></p>';
1852
+ }
1853
+
1854
+ /**
1855
+ * Returns the singleton instance of the class.
1856
+ *
1857
+ * @since 2.4.0
1858
+ *
1859
+ * @return object The INBOUND_Plugin_Activation object.
1860
+ */
1861
+ public static function get_instance() {
1862
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof self ) ) {
1863
+ self::$instance = new self();
1864
+ }
1865
+
1866
+ return self::$instance;
1867
+ }
1868
+ }
1869
+
1870
+ if ( ! function_exists( 'load_tgm_plugin_activation' ) ) {
1871
+ /**
1872
+ * Ensure only one instance of the class is ever invoked.
1873
+ */
1874
+ function load_tgm_plugin_activation() {
1875
+ $GLOBALS['tgmpa'] = INBOUND_Plugin_Activation::get_instance();
1876
+ }
1877
+ }
1878
+
1879
+ if ( did_action( 'plugins_loaded' ) ) {
1880
+ load_tgm_plugin_activation();
1881
+ } else {
1882
+ add_action( 'plugins_loaded', 'load_tgm_plugin_activation' );
1883
+ }
1884
+ }
1885
+
1886
+ if ( ! function_exists( 'inbound_activate' ) ) {
1887
+ /**
1888
+ * Helper function to register a collection of required plugins.
1889
+ *
1890
+ * @since 2.0.0
1891
+ * @api
1892
+ *
1893
+ * @param array $plugins An array of plugin arrays.
1894
+ * @param array $config Optional. An array of configuration values.
1895
+ */
1896
+ function inbound_activate( $plugins, $config = array() ) {
1897
+ $instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
1898
+
1899
+ foreach ( $plugins as $plugin ) {
1900
+ call_user_func( array( $instance, 'register' ), $plugin );
1901
+ }
1902
+
1903
+ if ( ! empty( $config ) && is_array( $config ) ) {
1904
+ call_user_func( array( $instance, 'config' ), $config );
1905
+ }
1906
+ }
1907
+ }
1908
+
1909
+ /**
1910
+ * WP_List_Table isn't always available. If it isn't available,
1911
+ * we load it here.
1912
+ *
1913
+ * @since 2.2.0
1914
+ */
1915
+ if ( ! class_exists( 'WP_List_Table' ) ) {
1916
+ require_once ABSPATH . 'wp-admin/includes/class-wp-list-table.php';
1917
+ }
1918
+
1919
+ if ( ! class_exists( 'INBOUND_TGMPA_List_Table' ) ) {
1920
+
1921
+ /**
1922
+ * List table class for handling plugins.
1923
+ *
1924
+ * Extends the WP_List_Table class to provide a future-compatible
1925
+ * way of listing out all required/recommended plugins.
1926
+ *
1927
+ * Gives users an interface similar to the Plugin Administration
1928
+ * area with similar (albeit stripped down) capabilities.
1929
+ *
1930
+ * This class also allows for the bulk install of plugins.
1931
+ *
1932
+ * @since 2.2.0
1933
+ *
1934
+ * @package TGM-Plugin-Activation
1935
+ * @author Thomas Griffin
1936
+ * @author Gary Jones
1937
+ */
1938
+ class INBOUND_TGMPA_List_Table extends WP_List_Table {
1939
+ /**
1940
+ * TGMPA instance.
1941
+ *
1942
+ * @since 2.5.0
1943
+ *
1944
+ * @var object
1945
+ */
1946
+ protected $tgmpa;
1947
+
1948
+ /**
1949
+ * The currently chosen view.
1950
+ *
1951
+ * @since 2.5.0
1952
+ *
1953
+ * @var string One of: 'all', 'install', 'update', 'activate'
1954
+ */
1955
+ public $view_context = 'all';
1956
+
1957
+ /**
1958
+ * The plugin counts for the various views.
1959
+ *
1960
+ * @since 2.5.0
1961
+ *
1962
+ * @var array
1963
+ */
1964
+ protected $view_totals = array(
1965
+ 'all' => 0,
1966
+ 'install' => 0,
1967
+ 'update' => 0,
1968
+ 'activate' => 0,
1969
+ );
1970
+
1971
+ /**
1972
+ * References parent constructor and sets defaults for class.
1973
+ *
1974
+ * @since 2.2.0
1975
+ */
1976
+ public function __construct() {
1977
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
1978
+
1979
+ parent::__construct(
1980
+ array(
1981
+ 'singular' => 'plugin',
1982
+ 'plural' => 'plugins',
1983
+ 'ajax' => false,
1984
+ )
1985
+ );
1986
+
1987
+ if ( isset( $_REQUEST['plugin_status'] ) && in_array( $_REQUEST['plugin_status'], array( 'install', 'update', 'activate' ), true ) ) {
1988
+ $this->view_context = sanitize_key( $_REQUEST['plugin_status'] );
1989
+ }
1990
+
1991
+ add_filter( 'tgmpa_table_data_items', array( $this, 'sort_table_items' ) );
1992
+ }
1993
+
1994
+ /**
1995
+ * Get a list of CSS classes for the <table> tag.
1996
+ *
1997
+ * Overruled to prevent the 'plural' argument from being added.
1998
+ *
1999
+ * @since 2.5.0
2000
+ *
2001
+ * @return array CSS classnames.
2002
+ */
2003
+ public function get_table_classes() {
2004
+ return array( 'widefat', 'fixed' );
2005
+ }
2006
+
2007
+ /**
2008
+ * Gathers and renames all of our plugin information to be used by WP_List_Table to create our table.
2009
+ *
2010
+ * @since 2.2.0
2011
+ *
2012
+ * @return array $table_data Information for use in table.
2013
+ */
2014
+ protected function _gather_plugin_data() {
2015
+ // Load thickbox for plugin links.
2016
+ $this->tgmpa->admin_init();
2017
+ $this->tgmpa->thickbox();
2018
+
2019
+ // Categorize the plugins which have open actions.
2020
+ $plugins = $this->categorize_plugins_to_views();
2021
+
2022
+ // Set the counts for the view links.
2023
+ $this->set_view_totals( $plugins );
2024
+
2025
+ // Prep variables for use and grab list of all installed plugins.
2026
+ $table_data = array();
2027
+ $i = 0;
2028
+
2029
+ // Redirect to the 'all' view if no plugins where found for the selected view context.
2030
+ if ( empty( $plugins[ $this->view_context ] ) ) {
2031
+ $this->view_context = 'all';
2032
+ }
2033
+
2034
+ foreach ( $plugins[ $this->view_context ] as $slug => $plugin ) {
2035
+ $table_data[ $i ]['sanitized_plugin'] = $plugin['name'];
2036
+ $table_data[ $i ]['slug'] = $slug;
2037
+ $table_data[ $i ]['plugin'] = '<strong>' . $this->tgmpa->get_info_link( $slug ) . '</strong>';
2038
+ $table_data[ $i ]['source'] = $this->get_plugin_source_type_text( $plugin['source_type'] );
2039
+ $table_data[ $i ]['type'] = $this->get_plugin_advise_type_text( $plugin['required'] );
2040
+ $table_data[ $i ]['status'] = $this->get_plugin_status_text( $slug );
2041
+ $table_data[ $i ]['installed_version'] = $this->tgmpa->get_installed_version( $slug );
2042
+ $table_data[ $i ]['minimum_version'] = $plugin['version'];
2043
+ $table_data[ $i ]['available_version'] = $this->tgmpa->does_plugin_have_update( $slug );
2044
+
2045
+ // Prep the upgrade notice info.
2046
+ $upgrade_notice = $this->tgmpa->get_upgrade_notice( $slug );
2047
+ if ( ! empty( $upgrade_notice ) ) {
2048
+ $table_data[ $i ]['upgrade_notice'] = $upgrade_notice;
2049
+
2050
+ add_action( "tgmpa_after_plugin_row_$slug", array( $this, 'wp_plugin_update_row' ), 10, 2 );
2051
+ }
2052
+
2053
+ $table_data[ $i ] = apply_filters( 'tgmpa_table_data_item', $table_data[ $i ], $plugin );
2054
+
2055
+ $i++;
2056
+ }
2057
+
2058
+ return $table_data;
2059
+ }
2060
+
2061
+ /**
2062
+ * Categorize the plugins which have open actions into views for the TGMPA page.
2063
+ *
2064
+ * @since 2.5.0
2065
+ */
2066
+ protected function categorize_plugins_to_views() {
2067
+ $plugins = array(
2068
+ 'all' => array(), // Meaning: all plugins which still have open actions.
2069
+ 'install' => array(),
2070
+ 'update' => array(),
2071
+ 'activate' => array(),
2072
+ );
2073
+
2074
+ foreach ( $this->tgmpa->plugins as $slug => $plugin ) {
2075
+ if ( $this->tgmpa->is_plugin_active( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2076
+ // No need to display plugins if they are installed, up-to-date and active.
2077
+ continue;
2078
+ } else {
2079
+ $plugins['all'][ $slug ] = $plugin;
2080
+
2081
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2082
+ $plugins['install'][ $slug ] = $plugin;
2083
+ } else {
2084
+ if ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2085
+ $plugins['update'][ $slug ] = $plugin;
2086
+ }
2087
+
2088
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2089
+ $plugins['activate'][ $slug ] = $plugin;
2090
+ }
2091
+ }
2092
+ }
2093
+ }
2094
+
2095
+ return $plugins;
2096
+ }
2097
+
2098
+ /**
2099
+ * Set the counts for the view links.
2100
+ *
2101
+ * @since 2.5.0
2102
+ *
2103
+ * @param array $plugins Plugins order by view.
2104
+ */
2105
+ protected function set_view_totals( $plugins ) {
2106
+ foreach ( $plugins as $type => $list ) {
2107
+ $this->view_totals[ $type ] = count( $list );
2108
+ }
2109
+ }
2110
+
2111
+ /**
2112
+ * Get the plugin required/recommended text string.
2113
+ *
2114
+ * @since 2.5.0
2115
+ *
2116
+ * @param string $required Plugin required setting.
2117
+ * @return string
2118
+ */
2119
+ protected function get_plugin_advise_type_text( $required ) {
2120
+ if ( true === $required ) {
2121
+ return __( 'Required', 'tgmpa' );
2122
+ }
2123
+
2124
+ return __( 'Recommended', 'tgmpa' );
2125
+ }
2126
+
2127
+ /**
2128
+ * Get the plugin source type text string.
2129
+ *
2130
+ * @since 2.5.0
2131
+ *
2132
+ * @param string $type Plugin type.
2133
+ * @return string
2134
+ */
2135
+ protected function get_plugin_source_type_text( $type ) {
2136
+ $string = '';
2137
+
2138
+ switch ( $type ) {
2139
+ case 'repo':
2140
+ $string = __( 'WordPress Repository', 'tgmpa' );
2141
+ break;
2142
+ case 'external':
2143
+ $string = __( 'External Source', 'tgmpa' );
2144
+ break;
2145
+ case 'bundled':
2146
+ $string = __( 'Pre-Packaged', 'tgmpa' );
2147
+ break;
2148
+ }
2149
+
2150
+ return $string;
2151
+ }
2152
+
2153
+ /**
2154
+ * Determine the plugin status message.
2155
+ *
2156
+ * @since 2.5.0
2157
+ *
2158
+ * @param string $slug Plugin slug.
2159
+ * @return string
2160
+ */
2161
+ protected function get_plugin_status_text( $slug ) {
2162
+ if ( ! $this->tgmpa->is_plugin_installed( $slug ) ) {
2163
+ return __( 'Not Installed', 'tgmpa' );
2164
+ }
2165
+
2166
+ if ( ! $this->tgmpa->is_plugin_active( $slug ) ) {
2167
+ $install_status = __( 'Installed But Not Activated', 'tgmpa' );
2168
+ } else {
2169
+ $install_status = __( 'Active', 'tgmpa' );
2170
+ }
2171
+
2172
+ $update_status = '';
2173
+
2174
+ if ( $this->tgmpa->does_plugin_require_update( $slug ) && false === $this->tgmpa->does_plugin_have_update( $slug ) ) {
2175
+ $update_status = __( 'Required Update not Available', 'tgmpa' );
2176
+
2177
+ } elseif ( $this->tgmpa->does_plugin_require_update( $slug ) ) {
2178
+ $update_status = __( 'Requires Update', 'tgmpa' );
2179
+
2180
+ } elseif ( false !== $this->tgmpa->does_plugin_have_update( $slug ) ) {
2181
+ $update_status = __( 'Update recommended', 'tgmpa' );
2182
+ }
2183
+
2184
+ if ( '' === $update_status ) {
2185
+ return $install_status;
2186
+ }
2187
+
2188
+ return sprintf(
2189
+ _x( '%1$s, %2$s', '%1$s = install status, %2$s = update status', 'tgmpa' ),
2190
+ $install_status,
2191
+ $update_status
2192
+ );
2193
+ }
2194
+
2195
+ /**
2196
+ * Sort plugins by Required/Recommended type and by alphabetical plugin name within each type.
2197
+ *
2198
+ * @since 2.5.0
2199
+ *
2200
+ * @param array $items Prepared table items.
2201
+ * @return array Sorted table items.
2202
+ */
2203
+ public function sort_table_items( $items ) {
2204
+ $type = array();
2205
+ $name = array();
2206
+
2207
+ foreach ( $items as $i => $plugin ) {
2208
+ $type[ $i ] = $plugin['type']; // Required / recommended.
2209
+ $name[ $i ] = $plugin['sanitized_plugin'];
2210
+ }
2211
+
2212
+ array_multisort( $type, SORT_DESC, $name, SORT_ASC, $items );
2213
+
2214
+ return $items;
2215
+ }
2216
+
2217
+ /**
2218
+ * Get an associative array ( id => link ) of the views available on this table.
2219
+ *
2220
+ * @since 2.5.0
2221
+ *
2222
+ * @return array
2223
+ */
2224
+ public function get_views() {
2225
+ $status_links = array();
2226
+
2227
+ foreach ( $this->view_totals as $type => $count ) {
2228
+ if ( $count < 1 ) {
2229
+ continue;
2230
+ }
2231
+
2232
+ switch ( $type ) {
2233
+ case 'all':
2234
+ $text = _nx( 'All <span class="count">(%s)</span>', 'All <span class="count">(%s)</span>', $count, 'plugins', 'tgmpa' );
2235
+ break;
2236
+ case 'install':
2237
+ $text = _n( 'To Install <span class="count">(%s)</span>', 'To Install <span class="count">(%s)</span>', $count, 'tgmpa' );
2238
+ break;
2239
+ case 'update':
2240
+ $text = _n( 'Update Available <span class="count">(%s)</span>', 'Update Available <span class="count">(%s)</span>', $count, 'tgmpa' );
2241
+ break;
2242
+ case 'activate':
2243
+ $text = _n( 'To Activate <span class="count">(%s)</span>', 'To Activate <span class="count">(%s)</span>', $count, 'tgmpa' );
2244
+ break;
2245
+ default:
2246
+ $text = '';
2247
+ break;
2248
+ }
2249
+
2250
+ if ( ! empty( $text ) ) {
2251
+
2252
+ $status_links[ $type ] = sprintf(
2253
+ '<a href="%s"%s>%s</a>',
2254
+ esc_url( $this->tgmpa->get_tgmpa_status_url( $type ) ),
2255
+ ( $type === $this->view_context ) ? ' class="current"' : '',
2256
+ sprintf( $text, number_format_i18n( $count ) )
2257
+ );
2258
+ }
2259
+ }
2260
+
2261
+ return $status_links;
2262
+ }
2263
+
2264
+ /**
2265
+ * Create default columns to display important plugin information
2266
+ * like type, action and status.
2267
+ *
2268
+ * @since 2.2.0
2269
+ *
2270
+ * @param array $item Array of item data.
2271
+ * @param string $column_name The name of the column.
2272
+ * @return string
2273
+ */
2274
+ public function column_default( $item, $column_name ) {
2275
+ return $item[ $column_name ];
2276
+ }
2277
+
2278
+ /**
2279
+ * Required for bulk installing.
2280
+ *
2281
+ * Adds a checkbox for each plugin.
2282
+ *
2283
+ * @since 2.2.0
2284
+ *
2285
+ * @param array $item Array of item data.
2286
+ * @return string The input checkbox with all necessary info.
2287
+ */
2288
+ public function column_cb( $item ) {
2289
+ return sprintf(
2290
+ '<input type="checkbox" name="%1$s[]" value="%2$s" id="%3$s" />',
2291
+ esc_attr( $this->_args['singular'] ),
2292
+ esc_attr( $item['slug'] ),
2293
+ esc_attr( $item['sanitized_plugin'] )
2294
+ );
2295
+ }
2296
+
2297
+ /**
2298
+ * Create default title column along with the action links.
2299
+ *
2300
+ * @since 2.2.0
2301
+ *
2302
+ * @param array $item Array of item data.
2303
+ * @return string The plugin name and action links.
2304
+ */
2305
+ public function column_plugin( $item ) {
2306
+ return sprintf(
2307
+ '%1$s %2$s',
2308
+ $item['plugin'],
2309
+ $this->row_actions( $this->get_row_actions( $item ), true )
2310
+ );
2311
+ }
2312
+
2313
+ /**
2314
+ * Create version information column.
2315
+ *
2316
+ * @since 2.5.0
2317
+ *
2318
+ * @param array $item Array of item data.
2319
+ * @return string HTML-formatted version information.
2320
+ */
2321
+ public function column_version( $item ) {
2322
+ $output = array();
2323
+
2324
+ if ( $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2325
+ $installed = ! empty( $item['installed_version'] ) ? $item['installed_version'] : _x( 'unknown', 'as in: "version nr unknown"', 'tgmpa' );
2326
+
2327
+ $color = '';
2328
+ if ( ! empty( $item['minimum_version'] ) && $this->tgmpa->does_plugin_require_update( $item['slug'] ) ) {
2329
+ $color = ' color: #ff0000; font-weight: bold;';
2330
+ }
2331
+
2332
+ $output[] = sprintf(
2333
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Installed version:', 'tgmpa' ) . '</p>',
2334
+ $color,
2335
+ $installed
2336
+ );
2337
+ }
2338
+
2339
+ if ( ! empty( $item['minimum_version'] ) ) {
2340
+ $output[] = sprintf(
2341
+ '<p><span style="min-width: 32px; text-align: right; float: right;">%1$s</span>' . __( 'Minimum required version:', 'tgmpa' ) . '</p>',
2342
+ $item['minimum_version']
2343
+ );
2344
+ }
2345
+
2346
+ if ( ! empty( $item['available_version'] ) ) {
2347
+ $color = '';
2348
+ if ( ! empty( $item['minimum_version'] ) && version_compare( $item['available_version'], $item['minimum_version'], '>=' ) ) {
2349
+ $color = ' color: #71C671; font-weight: bold;';
2350
+ }
2351
+
2352
+ $output[] = sprintf(
2353
+ '<p><span style="min-width: 32px; text-align: right; float: right;%1$s">%2$s</span>' . __( 'Available version:', 'tgmpa' ) . '</p>',
2354
+ $color,
2355
+ $item['available_version']
2356
+ );
2357
+ }
2358
+
2359
+ if ( empty( $output ) ) {
2360
+ return '&nbsp;'; // Let's not break the table layout.
2361
+ } else {
2362
+ return implode( "\n", $output );
2363
+ }
2364
+ }
2365
+
2366
+ /**
2367
+ * Sets default message within the plugins table if no plugins
2368
+ * are left for interaction.
2369
+ *
2370
+ * Hides the menu item to prevent the user from clicking and
2371
+ * getting a permissions error.
2372
+ *
2373
+ * @since 2.2.0
2374
+ */
2375
+ public function no_items() {
2376
+ printf( wp_kses_post( __( 'No plugins to install, update or activate. <a href="%1$s">Return to the Dashboard</a>', 'tgmpa' ) ), esc_url( self_admin_url() ) );
2377
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
2378
+ }
2379
+
2380
+ /**
2381
+ * Output all the column information within the table.
2382
+ *
2383
+ * @since 2.2.0
2384
+ *
2385
+ * @return array $columns The column names.
2386
+ */
2387
+ public function get_columns() {
2388
+ $columns = array(
2389
+ 'cb' => '<input type="checkbox" />',
2390
+ 'plugin' => __( 'Plugin', 'tgmpa' ),
2391
+ 'source' => __( 'Source', 'tgmpa' ),
2392
+ 'type' => __( 'Type', 'tgmpa' ),
2393
+ );
2394
+
2395
+ if ( 'all' === $this->view_context || 'update' === $this->view_context ) {
2396
+ $columns['version'] = __( 'Version', 'tgmpa' );
2397
+ $columns['status'] = __( 'Status', 'tgmpa' );
2398
+ }
2399
+
2400
+ return apply_filters( 'tgmpa_table_columns', $columns );
2401
+ }
2402
+
2403
+ /**
2404
+ * Get name of default primary column
2405
+ *
2406
+ * @since 2.5.0 / WP 4.3+ compatibility
2407
+ * @access protected
2408
+ *
2409
+ * @return string
2410
+ */
2411
+ protected function get_default_primary_column_name() {
2412
+ return 'plugin';
2413
+ }
2414
+
2415
+ /**
2416
+ * Get the name of the primary column.
2417
+ *
2418
+ * @since 2.5.0 / WP 4.3+ compatibility
2419
+ * @access protected
2420
+ *
2421
+ * @return string The name of the primary column.
2422
+ */
2423
+ protected function get_primary_column_name() {
2424
+ if ( method_exists( 'WP_List_Table', 'get_primary_column_name' ) ) {
2425
+ return parent::get_primary_column_name();
2426
+ } else {
2427
+ return $this->get_default_primary_column_name();
2428
+ }
2429
+ }
2430
+
2431
+ /**
2432
+ * Get the actions which are relevant for a specific plugin row.
2433
+ *
2434
+ * @since 2.5.0
2435
+ *
2436
+ * @param array $item Array of item data.
2437
+ * @return array Array with relevant action links.
2438
+ */
2439
+ protected function get_row_actions( $item ) {
2440
+ $actions = array();
2441
+ $action_links = array();
2442
+
2443
+ // Display the 'Install' action link if the plugin is not yet available.
2444
+ if ( ! $this->tgmpa->is_plugin_installed( $item['slug'] ) ) {
2445
+ $actions['install'] = _x( 'Install %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2446
+ } else {
2447
+ // Display the 'Update' action link if an update is available and WP complies with plugin minimum.
2448
+ if ( false !== $this->tgmpa->does_plugin_have_update( $item['slug'] ) && $this->tgmpa->can_plugin_update( $item['slug'] ) ) {
2449
+ $actions['update'] = _x( 'Update %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2450
+ }
2451
+
2452
+ // Display the 'Activate' action link, but only if the plugin meets the minimum version.
2453
+ if ( $this->tgmpa->can_plugin_activate( $item['slug'] ) ) {
2454
+ $actions['activate'] = _x( 'Activate %2$s', '%2$s = plugin name in screen reader markup', 'tgmpa' );
2455
+ }
2456
+ }
2457
+
2458
+ // Create the actual links.
2459
+ foreach ( $actions as $action => $text ) {
2460
+ $nonce_url = wp_nonce_url(
2461
+ add_query_arg(
2462
+ array(
2463
+ 'plugin' => urlencode( $item['slug'] ),
2464
+ 'tgmpa-' . $action => $action . '-plugin',
2465
+ ),
2466
+ $this->tgmpa->get_tgmpa_url()
2467
+ ),
2468
+ 'tgmpa-' . $action,
2469
+ 'tgmpa-nonce'
2470
+ );
2471
+
2472
+ $action_links[ $action ] = sprintf(
2473
+ '<a href="%1$s">' . esc_html( $text ) . '</a>',
2474
+ esc_url( $nonce_url ),
2475
+ '<span class="screen-reader-text">' . esc_html( $item['sanitized_plugin'] ) . '</span>'
2476
+ );
2477
+ }
2478
+
2479
+ $prefix = ( defined( 'WP_NETWORK_ADMIN' ) && WP_NETWORK_ADMIN ) ? 'network_admin_' : '';
2480
+ return apply_filters( "tgmpa_{$prefix}plugin_action_links", array_filter( $action_links ), $item['slug'], $item, $this->view_context );
2481
+ }
2482
+
2483
+ /**
2484
+ * Generates content for a single row of the table.
2485
+ *
2486
+ * @since 2.5.0
2487
+ *
2488
+ * @param object $item The current item.
2489
+ */
2490
+ public function single_row( $item ) {
2491
+ parent::single_row( $item );
2492
+
2493
+ /**
2494
+ * Fires after each specific row in the TGMPA Plugins list table.
2495
+ *
2496
+ * The dynamic portion of the hook name, `$item['slug']`, refers to the slug
2497
+ * for the plugin.
2498
+ *
2499
+ * @since 2.5.0
2500
+ */
2501
+ do_action( "tgmpa_after_plugin_row_{$item['slug']}", $item['slug'], $item, $this->view_context );
2502
+ }
2503
+
2504
+ /**
2505
+ * Show the upgrade notice below a plugin row if there is one.
2506
+ *
2507
+ * @since 2.5.0
2508
+ *
2509
+ * @see /wp-admin/includes/update.php
2510
+ *
2511
+ * @param string $slug Plugin slug.
2512
+ * @param array $item The information available in this table row.
2513
+ * @return null Return early if upgrade notice is empty.
2514
+ */
2515
+ public function wp_plugin_update_row( $slug, $item ) {
2516
+ if ( empty( $item['upgrade_notice'] ) ) {
2517
+ return;
2518
+ }
2519
+
2520
+ echo '
2521
+ <tr class="plugin-update-tr">
2522
+ <td colspan="', absint( $this->get_column_count() ), '" class="plugin-update colspanchange">
2523
+ <div class="update-message">',
2524
+ esc_html__( 'Upgrade message from the plugin author:', 'tgmpa' ),
2525
+ ' <strong>', wp_kses_data( $item['upgrade_notice'] ), '</strong>
2526
+ </div>
2527
+ </td>
2528
+ </tr>';
2529
+ }
2530
+
2531
+ /**
2532
+ * Extra controls to be displayed between bulk actions and pagination.
2533
+ *
2534
+ * @since 2.5.0
2535
+ *
2536
+ * @param string $which 'top' or 'bottom' table navigation.
2537
+ */
2538
+ public function extra_tablenav( $which ) {
2539
+ if ( 'bottom' === $which ) {
2540
+ $this->tgmpa->show_tgmpa_version();
2541
+ }
2542
+ }
2543
+
2544
+ /**
2545
+ * Defines the bulk actions for handling registered plugins.
2546
+ *
2547
+ * @since 2.2.0
2548
+ *
2549
+ * @return array $actions The bulk actions for the plugin install table.
2550
+ */
2551
+ public function get_bulk_actions() {
2552
+
2553
+ $actions = array();
2554
+
2555
+ if ( 'update' !== $this->view_context && 'activate' !== $this->view_context ) {
2556
+ if ( current_user_can( 'install_plugins' ) ) {
2557
+ $actions['tgmpa-bulk-install'] = __( 'Install', 'tgmpa' );
2558
+ }
2559
+ }
2560
+
2561
+ if ( 'install' !== $this->view_context ) {
2562
+ if ( current_user_can( 'update_plugins' ) ) {
2563
+ $actions['tgmpa-bulk-update'] = __( 'Update', 'tgmpa' );
2564
+ }
2565
+ if ( current_user_can( 'activate_plugins' ) ) {
2566
+ $actions['tgmpa-bulk-activate'] = __( 'Activate', 'tgmpa' );
2567
+ }
2568
+ }
2569
+
2570
+ return $actions;
2571
+ }
2572
+
2573
+ /**
2574
+ * Processes bulk installation and activation actions.
2575
+ *
2576
+ * The bulk installation process looks for the $_POST information and passes that
2577
+ * through if a user has to use WP_Filesystem to enter their credentials.
2578
+ *
2579
+ * @since 2.2.0
2580
+ */
2581
+ public function process_bulk_actions() {
2582
+ // Bulk installation process.
2583
+ if ( 'tgmpa-bulk-install' === $this->current_action() || 'tgmpa-bulk-update' === $this->current_action() ) {
2584
+
2585
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
2586
+
2587
+ $install_type = 'install';
2588
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2589
+ $install_type = 'update';
2590
+ }
2591
+
2592
+ $plugins_to_install = array();
2593
+
2594
+ // Did user actually select any plugins to install/update ?
2595
+ if ( empty( $_POST['plugin'] ) ) {
2596
+ if ( 'install' === $install_type ) {
2597
+ $message = __( 'No plugins were selected to be installed. No action taken.', 'tgmpa' );
2598
+ } else {
2599
+ $message = __( 'No plugins were selected to be updated. No action taken.', 'tgmpa' );
2600
+ }
2601
+
2602
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2603
+
2604
+ return false;
2605
+ }
2606
+
2607
+ if ( is_array( $_POST['plugin'] ) ) {
2608
+ $plugins_to_install = (array) $_POST['plugin'];
2609
+ } elseif ( is_string( $_POST['plugin'] ) ) {
2610
+ // Received via Filesystem page - un-flatten array (WP bug #19643).
2611
+ $plugins_to_install = explode( ',', $_POST['plugin'] );
2612
+ }
2613
+
2614
+ // Sanitize the received input.
2615
+ $plugins_to_install = array_map( 'urldecode', $plugins_to_install );
2616
+ $plugins_to_install = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins_to_install );
2617
+
2618
+ // Validate the received input.
2619
+ foreach ( $plugins_to_install as $key => $slug ) {
2620
+ // Check if the plugin was registered with TGMPA and remove if not.
2621
+ if ( ! isset( $this->tgmpa->plugins[ $slug ] ) ) {
2622
+ unset( $plugins_to_install[ $key ] );
2623
+ continue;
2624
+ }
2625
+
2626
+ // For updates: make sure this is a plugin we *can* update (update available and WP version ok).
2627
+ if ( 'update' === $install_type && ( $this->tgmpa->is_plugin_installed( $slug ) && ( false === $this->tgmpa->does_plugin_have_update( $slug ) || ! $this->tgmpa->can_plugin_update( $slug ) ) ) ) {
2628
+ unset( $plugins_to_install[ $key ] );
2629
+ }
2630
+ }
2631
+
2632
+ // No need to proceed further if we have no plugins to handle.
2633
+ if ( empty( $plugins_to_install ) ) {
2634
+ if ( 'install' === $install_type ) {
2635
+ $message = __( 'No plugins are available to be installed at this time.', 'tgmpa' );
2636
+ } else {
2637
+ $message = __( 'No plugins are available to be updated at this time.', 'tgmpa' );
2638
+ }
2639
+
2640
+ echo '<div id="message" class="error"><p>', esc_html( $message ), '</p></div>';
2641
+
2642
+ return false;
2643
+ }
2644
+
2645
+ // Pass all necessary information if WP_Filesystem is needed.
2646
+ $url = wp_nonce_url(
2647
+ $this->tgmpa->get_tgmpa_url(),
2648
+ 'bulk-' . $this->_args['plural']
2649
+ );
2650
+
2651
+ // Give validated data back to $_POST which is the only place the filesystem looks for extra fields.
2652
+ $_POST['plugin'] = implode( ',', $plugins_to_install ); // Work around for WP bug #19643.
2653
+
2654
+ $method = ''; // Leave blank so WP_Filesystem can populate it as necessary.
2655
+ $fields = array_keys( $_POST ); // Extra fields to pass to WP_Filesystem.
2656
+
2657
+ if ( false === ( $creds = request_filesystem_credentials( esc_url_raw( $url ), $method, false, false, $fields ) ) ) {
2658
+ return true; // Stop the normal page form from displaying, credential request form will be shown.
2659
+ }
2660
+
2661
+ // Now we have some credentials, setup WP_Filesystem.
2662
+ if ( ! WP_Filesystem( $creds ) ) {
2663
+ // Our credentials were no good, ask the user for them again.
2664
+ request_filesystem_credentials( esc_url_raw( $url ), $method, true, false, $fields );
2665
+
2666
+ return true;
2667
+ }
2668
+
2669
+ /* If we arrive here, we have the filesystem */
2670
+
2671
+ // Store all information in arrays since we are processing a bulk installation.
2672
+ $names = array();
2673
+ $sources = array(); // Needed for installs.
2674
+ $file_paths = array(); // Needed for upgrades.
2675
+ $to_inject = array(); // Information to inject into the update_plugins transient.
2676
+
2677
+ // Prepare the data for validated plugins for the install/upgrade.
2678
+ foreach ( $plugins_to_install as $slug ) {
2679
+ $name = $this->tgmpa->plugins[ $slug ]['name'];
2680
+ $source = $this->tgmpa->get_download_url( $slug );
2681
+
2682
+ if ( ! empty( $name ) && ! empty( $source ) ) {
2683
+ $names[] = $name;
2684
+
2685
+ switch ( $install_type ) {
2686
+
2687
+ case 'install':
2688
+ $sources[] = $source;
2689
+ break;
2690
+
2691
+ case 'update':
2692
+ $file_paths[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2693
+ $to_inject[ $slug ] = $this->tgmpa->plugins[ $slug ];
2694
+ $to_inject[ $slug ]['source'] = $source;
2695
+ break;
2696
+ }
2697
+ }
2698
+ }
2699
+ unset( $slug, $name, $source );
2700
+
2701
+ // Create a new instance of INBOUND_TGM_Bulk_Installer.
2702
+ $installer = new INBOUND_TGM_Bulk_Installer(
2703
+ new INBOUND_TGM_Bulk_Installer_Skin(
2704
+ array(
2705
+ 'url' => esc_url_raw( $this->tgmpa->get_tgmpa_url() ),
2706
+ 'nonce' => 'bulk-' . $this->_args['plural'],
2707
+ 'names' => $names,
2708
+ 'install_type' => $install_type,
2709
+ )
2710
+ )
2711
+ );
2712
+
2713
+ // Wrap the install process with the appropriate HTML.
2714
+ echo '<div class="tgmpa wrap">',
2715
+ '<h2>', esc_html( get_admin_page_title() ), '</h2>';
2716
+
2717
+ // Process the bulk installation submissions.
2718
+ add_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2719
+
2720
+ if ( 'tgmpa-bulk-update' === $this->current_action() ) {
2721
+ // Inject our info into the update transient.
2722
+ $this->tgmpa->inject_update_info( $to_inject );
2723
+
2724
+ $installer->bulk_upgrade( $file_paths );
2725
+ } else {
2726
+ $installer->bulk_install( $sources );
2727
+ }
2728
+
2729
+ remove_filter( 'upgrader_source_selection', array( $this->tgmpa, 'maybe_adjust_source_dir' ), 1, 3 );
2730
+
2731
+ echo '</div>';
2732
+
2733
+ return true;
2734
+ }
2735
+
2736
+ // Bulk activation process.
2737
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2738
+ check_admin_referer( 'bulk-' . $this->_args['plural'] );
2739
+
2740
+ // Did user actually select any plugins to activate ?
2741
+ if ( empty( $_POST['plugin'] ) ) {
2742
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins were selected to be activated. No action taken.', 'tgmpa' ), '</p></div>';
2743
+
2744
+ return false;
2745
+ }
2746
+
2747
+ // Grab plugin data from $_POST.
2748
+ $plugins = array();
2749
+ if ( isset( $_POST['plugin'] ) ) {
2750
+ $plugins = array_map( 'urldecode', (array) $_POST['plugin'] );
2751
+ $plugins = array_map( array( $this->tgmpa, 'sanitize_key' ), $plugins );
2752
+ }
2753
+
2754
+ $plugins_to_activate = array();
2755
+ $plugin_names = array();
2756
+
2757
+ // Grab the file paths for the selected & inactive plugins from the registration array.
2758
+ foreach ( $plugins as $slug ) {
2759
+ if ( $this->tgmpa->can_plugin_activate( $slug ) ) {
2760
+ $plugins_to_activate[] = $this->tgmpa->plugins[ $slug ]['file_path'];
2761
+ $plugin_names[] = $this->tgmpa->plugins[ $slug ]['name'];
2762
+ }
2763
+ }
2764
+ unset( $slug );
2765
+
2766
+ // Return early if there are no plugins to activate.
2767
+ if ( empty( $plugins_to_activate ) ) {
2768
+ echo '<div id="message" class="error"><p>', esc_html__( 'No plugins are available to be activated at this time.', 'tgmpa' ), '</p></div>';
2769
+
2770
+ return false;
2771
+ }
2772
+
2773
+ // Now we are good to go - let's start activating plugins.
2774
+ $activate = activate_plugins( $plugins_to_activate );
2775
+
2776
+ if ( is_wp_error( $activate ) ) {
2777
+ echo '<div id="message" class="error"><p>', wp_kses_post( $activate->get_error_message() ), '</p></div>';
2778
+ } else {
2779
+ $count = count( $plugin_names ); // Count so we can use _n function.
2780
+ $plugin_names = array_map( array( 'INBOUND_TGM_Utils', 'wrap_in_strong' ), $plugin_names );
2781
+ $last_plugin = array_pop( $plugin_names ); // Pop off last name to prep for readability.
2782
+ $imploded = empty( $plugin_names ) ? $last_plugin : ( implode( ', ', $plugin_names ) . ' ' . esc_html_x( 'and', 'plugin A *and* plugin B', 'tgmpa' ) . ' ' . $last_plugin );
2783
+
2784
+ printf( // WPCS: xss ok.
2785
+ '<div id="message" class="updated"><p>%1$s %2$s.</p></div>',
2786
+ esc_html( _n( 'The following plugin was activated successfully:', 'The following plugins were activated successfully:', $count, 'tgmpa' ) ),
2787
+ $imploded
2788
+ );
2789
+
2790
+ // Update recently activated plugins option.
2791
+ $recent = (array) get_option( 'recently_activated' );
2792
+ foreach ( $plugins_to_activate as $plugin => $time ) {
2793
+ if ( isset( $recent[ $plugin ] ) ) {
2794
+ unset( $recent[ $plugin ] );
2795
+ }
2796
+ }
2797
+ update_option( 'recently_activated', $recent );
2798
+ }
2799
+
2800
+ unset( $_POST ); // Reset the $_POST variable in case user wants to perform one action after another.
2801
+
2802
+ return true;
2803
+ }
2804
+
2805
+ return false;
2806
+ }
2807
+
2808
+ /**
2809
+ * Prepares all of our information to be outputted into a usable table.
2810
+ *
2811
+ * @since 2.2.0
2812
+ */
2813
+ public function prepare_items() {
2814
+ $columns = $this->get_columns(); // Get all necessary column information.
2815
+ $hidden = array(); // No columns to hide, but we must set as an array.
2816
+ $sortable = array(); // No reason to make sortable columns.
2817
+ $primary = $this->get_primary_column_name(); // Column which has the row actions.
2818
+ $this->_column_headers = array( $columns, $hidden, $sortable, $primary ); // Get all necessary column headers.
2819
+
2820
+ // Process our bulk activations here.
2821
+ if ( 'tgmpa-bulk-activate' === $this->current_action() ) {
2822
+ $this->process_bulk_actions();
2823
+ }
2824
+
2825
+ // Store all of our plugin data into $items array so WP_List_Table can use it.
2826
+ $this->items = apply_filters( 'tgmpa_table_data_items', $this->_gather_plugin_data() );
2827
+ }
2828
+
2829
+ /* *********** DEPRECATED METHODS *********** */
2830
+
2831
+ /**
2832
+ * Retrieve plugin data, given the plugin name.
2833
+ *
2834
+ * @since 2.2.0
2835
+ * @deprecated 2.5.0 use {@see INBOUND_Plugin_Activation::_get_plugin_data_from_name()} instead.
2836
+ * @see INBOUND_Plugin_Activation::_get_plugin_data_from_name()
2837
+ *
2838
+ * @param string $name Name of the plugin, as it was registered.
2839
+ * @param string $data Optional. Array key of plugin data to return. Default is slug.
2840
+ * @return string|boolean Plugin slug if found, false otherwise.
2841
+ */
2842
+ protected function _get_plugin_data_from_name( $name, $data = 'slug' ) {
2843
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'INBOUND_Plugin_Activation::_get_plugin_data_from_name()' );
2844
+
2845
+ return $this->tgmpa->_get_plugin_data_from_name( $name, $data );
2846
+ }
2847
+ }
2848
+ }
2849
+
2850
+ /**
2851
+ * The WP_Upgrader file isn't always available. If it isn't available,
2852
+ * we load it here.
2853
+ *
2854
+ * We check to make sure no action or activation keys are set so that WordPress
2855
+ * does not try to re-include the class when processing upgrades or installs outside
2856
+ * of the class.
2857
+ *
2858
+ * @since 2.2.0
2859
+ */
2860
+ add_action( 'admin_init', 'tgmpa_load_bulk_installer' );
2861
+ if ( ! function_exists( 'tgmpa_load_bulk_installer' ) ) {
2862
+ /**
2863
+ * Load bulk installer
2864
+ */
2865
+ function tgmpa_load_bulk_installer() {
2866
+ // Get TGMPA class instance.
2867
+ $tgmpa_instance = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2868
+
2869
+ if ( isset( $_GET['page'] ) && $tgmpa_instance->menu === $_GET['page'] ) {
2870
+ if ( ! class_exists( 'Plugin_Upgrader', false ) ) {
2871
+ require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
2872
+ }
2873
+
2874
+ if ( ! class_exists( 'INBOUND_TGM_Bulk_Installer' ) ) {
2875
+
2876
+ /**
2877
+ * Installer class to handle bulk plugin installations.
2878
+ *
2879
+ * Extends WP_Upgrader and customizes to suit the installation of multiple
2880
+ * plugins.
2881
+ *
2882
+ * @since 2.2.0
2883
+ *
2884
+ * @internal Since 2.5.0 the class is an extension of Plugin_Upgrader rather than WP_Upgrader
2885
+ *
2886
+ * @package TGM-Plugin-Activation
2887
+ * @author Thomas Griffin
2888
+ * @author Gary Jones
2889
+ */
2890
+ class INBOUND_TGM_Bulk_Installer extends Plugin_Upgrader {
2891
+ /**
2892
+ * Holds result of bulk plugin installation.
2893
+ *
2894
+ * @since 2.2.0
2895
+ *
2896
+ * @var string
2897
+ */
2898
+ public $result;
2899
+
2900
+ /**
2901
+ * Flag to check if bulk installation is occurring or not.
2902
+ *
2903
+ * @since 2.2.0
2904
+ *
2905
+ * @var boolean
2906
+ */
2907
+ public $bulk = false;
2908
+
2909
+ /**
2910
+ * TGMPA instance
2911
+ *
2912
+ * @since 2.5.0
2913
+ *
2914
+ * @var object
2915
+ */
2916
+ protected $tgmpa;
2917
+
2918
+ /**
2919
+ * Whether or not the destination directory needs to be cleared ( = on update).
2920
+ *
2921
+ * @since 2.5.0
2922
+ *
2923
+ * @var bool
2924
+ */
2925
+ protected $clear_destination = false;
2926
+
2927
+ /**
2928
+ * References parent constructor and sets defaults for class.
2929
+ *
2930
+ * @since 2.2.0
2931
+ *
2932
+ * @param \Bulk_Upgrader_Skin|null $skin Installer skin.
2933
+ */
2934
+ public function __construct( $skin = null ) {
2935
+ // Get TGMPA class instance.
2936
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
2937
+
2938
+ parent::__construct( $skin );
2939
+
2940
+ if ( isset( $this->skin->options['install_type'] ) && 'update' === $this->skin->options['install_type'] ) {
2941
+ $this->clear_destination = true;
2942
+ }
2943
+
2944
+ if ( $this->tgmpa->is_automatic ) {
2945
+ $this->activate_strings();
2946
+ }
2947
+
2948
+ add_action( 'upgrader_process_complete', array( $this->tgmpa, 'populate_file_path' ) );
2949
+ }
2950
+
2951
+ /**
2952
+ * Sets the correct activation strings for the installer skin to use.
2953
+ *
2954
+ * @since 2.2.0
2955
+ */
2956
+ public function activate_strings() {
2957
+ $this->strings['activation_failed'] = __( 'Plugin activation failed.', 'tgmpa' );
2958
+ $this->strings['activation_success'] = __( 'Plugin activated successfully.', 'tgmpa' );
2959
+ }
2960
+
2961
+ /**
2962
+ * Performs the actual installation of each plugin.
2963
+ *
2964
+ * @since 2.2.0
2965
+ *
2966
+ * @see WP_Upgrader::run()
2967
+ *
2968
+ * @param array $options The installation config options.
2969
+ * @return null|array Return early if error, array of installation data on success.
2970
+ */
2971
+ public function run( $options ) {
2972
+ $result = parent::run( $options );
2973
+
2974
+ // Reset the strings in case we changed one during automatic activation.
2975
+ if ( $this->tgmpa->is_automatic ) {
2976
+ if ( 'update' === $this->skin->options['install_type'] ) {
2977
+ $this->upgrade_strings();
2978
+ } else {
2979
+ $this->install_strings();
2980
+ }
2981
+ }
2982
+
2983
+ return $result;
2984
+ }
2985
+
2986
+ /**
2987
+ * Processes the bulk installation of plugins.
2988
+ *
2989
+ * @since 2.2.0
2990
+ *
2991
+ * @internal This is basically a near identical copy of the WP Core Plugin_Upgrader::bulk_upgrade()
2992
+ * method, with minor adjustments to deal with new installs instead of upgrades.
2993
+ * For ease of future synchronizations, the adjustments are clearly commented, but no other
2994
+ * comments are added. Code style has been made to comply.
2995
+ *
2996
+ * @see Plugin_Upgrader::bulk_upgrade()
2997
+ * @see https://core.trac.wordpress.org/browser/tags/4.2.1/src/wp-admin/includes/class-wp-upgrader.php#L838
2998
+ *
2999
+ * @param array $plugins The plugin sources needed for installation.
3000
+ * @param array $args Arbitrary passed extra arguments.
3001
+ * @return string|bool Install confirmation messages on success, false on failure.
3002
+ */
3003
+ public function bulk_install( $plugins, $args = array() ) {
3004
+ // [TGMPA + ] Hook auto-activation in.
3005
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3006
+
3007
+ $defaults = array(
3008
+ 'clear_update_cache' => true,
3009
+ );
3010
+ $parsed_args = wp_parse_args( $args, $defaults );
3011
+
3012
+ $this->init();
3013
+ $this->bulk = true;
3014
+
3015
+ $this->install_strings(); // [TGMPA + ] adjusted.
3016
+
3017
+ /* [TGMPA - ] $current = get_site_transient( 'update_plugins' ); */
3018
+
3019
+ /* [TGMPA - ] add_filter('upgrader_clear_destination', array($this, 'delete_old_plugin'), 10, 4); */
3020
+
3021
+ $this->skin->header();
3022
+
3023
+ // Connect to the Filesystem first.
3024
+ $res = $this->fs_connect( array( WP_CONTENT_DIR, WP_PLUGIN_DIR ) );
3025
+ if ( ! $res ) {
3026
+ $this->skin->footer();
3027
+
3028
+ return false;
3029
+ }
3030
+
3031
+ $this->skin->bulk_header();
3032
+
3033
+ // Only start maintenance mode if:
3034
+ // - running Multisite and there are one or more plugins specified, OR
3035
+ // - a plugin with an update available is currently active.
3036
+ // @TODO: For multisite, maintenance mode should only kick in for individual sites if at all possible.
3037
+ $maintenance = ( is_multisite() && ! empty( $plugins ) );
3038
+
3039
+ /*
3040
+ [TGMPA - ]
3041
+ foreach ( $plugins as $plugin )
3042
+ $maintenance = $maintenance || ( is_plugin_active( $plugin ) && isset( $current->response[ $plugin] ) );
3043
+ */
3044
+ if ( $maintenance ) {
3045
+ $this->maintenance_mode( true );
3046
+ }
3047
+
3048
+ $results = array();
3049
+
3050
+ $this->update_count = count( $plugins );
3051
+ $this->update_current = 0;
3052
+ foreach ( $plugins as $plugin ) {
3053
+ $this->update_current++;
3054
+
3055
+ /*
3056
+ [TGMPA - ]
3057
+ $this->skin->plugin_info = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin, false, true);
3058
+
3059
+ if ( !isset( $current->response[ $plugin ] ) ) {
3060
+ $this->skin->set_result('up_to_date');
3061
+ $this->skin->before();
3062
+ $this->skin->feedback('up_to_date');
3063
+ $this->skin->after();
3064
+ $results[$plugin] = true;
3065
+ continue;
3066
+ }
3067
+
3068
+ // Get the URL to the zip file
3069
+ $r = $current->response[ $plugin ];
3070
+
3071
+ $this->skin->plugin_active = is_plugin_active($plugin);
3072
+ */
3073
+
3074
+ $result = $this->run( array(
3075
+ 'package' => $plugin, // [TGMPA + ] adjusted.
3076
+ 'destination' => WP_PLUGIN_DIR,
3077
+ 'clear_destination' => false, // [TGMPA + ] adjusted.
3078
+ 'clear_working' => true,
3079
+ 'is_multi' => true,
3080
+ 'hook_extra' => array(
3081
+ 'plugin' => $plugin,
3082
+ ),
3083
+ ) );
3084
+
3085
+ $results[ $plugin ] = $this->result;
3086
+
3087
+ // Prevent credentials auth screen from displaying multiple times.
3088
+ if ( false === $result ) {
3089
+ break;
3090
+ }
3091
+ } //end foreach $plugins
3092
+
3093
+ $this->maintenance_mode( false );
3094
+
3095
+ /**
3096
+ * Fires when the bulk upgrader process is complete.
3097
+ *
3098
+ * @since WP 3.6.0 / TGMPA 2.5.0
3099
+ *
3100
+ * @param Plugin_Upgrader $this Plugin_Upgrader instance. In other contexts, $this, might
3101
+ * be a Theme_Upgrader or Core_Upgrade instance.
3102
+ * @param array $data {
3103
+ * Array of bulk item update data.
3104
+ *
3105
+ * @type string $action Type of action. Default 'update'.
3106
+ * @type string $type Type of update process. Accepts 'plugin', 'theme', or 'core'.
3107
+ * @type bool $bulk Whether the update process is a bulk update. Default true.
3108
+ * @type array $packages Array of plugin, theme, or core packages to update.
3109
+ * }
3110
+ */
3111
+ do_action( 'upgrader_process_complete', $this, array(
3112
+ 'action' => 'install', // [TGMPA + ] adjusted.
3113
+ 'type' => 'plugin',
3114
+ 'bulk' => true,
3115
+ 'plugins' => $plugins,
3116
+ ) );
3117
+
3118
+ $this->skin->bulk_footer();
3119
+
3120
+ $this->skin->footer();
3121
+
3122
+ // Cleanup our hooks, in case something else does a upgrade on this connection.
3123
+ /* [TGMPA - ] remove_filter('upgrader_clear_destination', array($this, 'delete_old_plugin')); */
3124
+
3125
+ // [TGMPA + ] Remove our auto-activation hook.
3126
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3127
+
3128
+ // Force refresh of plugin update information.
3129
+ wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
3130
+
3131
+ return $results;
3132
+ }
3133
+
3134
+ /**
3135
+ * Handle a bulk upgrade request.
3136
+ *
3137
+ * @since 2.5.0
3138
+ *
3139
+ * @see Plugin_Upgrader::bulk_upgrade()
3140
+ *
3141
+ * @param array $plugins The local WP file_path's of the plugins which should be upgraded.
3142
+ * @param array $args Arbitrary passed extra arguments.
3143
+ * @return string|bool Install confirmation messages on success, false on failure.
3144
+ */
3145
+ public function bulk_upgrade( $plugins, $args = array() ) {
3146
+
3147
+ add_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3148
+
3149
+ $result = parent::bulk_upgrade( $plugins, $args );
3150
+
3151
+ remove_filter( 'upgrader_post_install', array( $this, 'auto_activate' ), 10 );
3152
+
3153
+ return $result;
3154
+ }
3155
+
3156
+ /**
3157
+ * Abuse a filter to auto-activate plugins after installation.
3158
+ *
3159
+ * Hooked into the 'upgrader_post_install' filter hook.
3160
+ *
3161
+ * @since 2.5.0
3162
+ *
3163
+ * @param bool $bool The value we need to give back (true).
3164
+ * @return bool
3165
+ */
3166
+ public function auto_activate( $bool ) {
3167
+ // Only process the activation of installed plugins if the automatic flag is set to true.
3168
+ if ( $this->tgmpa->is_automatic ) {
3169
+ // Flush plugins cache so the headers of the newly installed plugins will be read correctly.
3170
+ wp_clean_plugins_cache();
3171
+
3172
+ // Get the installed plugin file.
3173
+ $plugin_info = $this->plugin_info();
3174
+
3175
+ // Don't try to activate on upgrade of active plugin as WP will do this already.
3176
+ if ( ! is_plugin_active( $plugin_info ) ) {
3177
+ $activate = activate_plugin( $plugin_info );
3178
+
3179
+ // Adjust the success string based on the activation result.
3180
+ $this->strings['process_success'] = $this->strings['process_success'] . "<br />\n";
3181
+
3182
+ if ( is_wp_error( $activate ) ) {
3183
+ $this->skin->error( $activate );
3184
+ $this->strings['process_success'] .= $this->strings['activation_failed'];
3185
+ } else {
3186
+ $this->strings['process_success'] .= $this->strings['activation_success'];
3187
+ }
3188
+ }
3189
+ }
3190
+
3191
+ return $bool;
3192
+ }
3193
+ }
3194
+ }
3195
+
3196
+ if ( ! class_exists( 'INBOUND_TGM_Bulk_Installer_Skin' ) ) {
3197
+
3198
+ /**
3199
+ * Installer skin to set strings for the bulk plugin installations..
3200
+ *
3201
+ * Extends Bulk_Upgrader_Skin and customizes to suit the installation of multiple
3202
+ * plugins.
3203
+ *
3204
+ * @since 2.2.0
3205
+ *
3206
+ * @see https://core.trac.wordpress.org/browser/trunk/src/wp-admin/includes/class-wp-upgrader-skins.php
3207
+ *
3208
+ * @package TGM-Plugin-Activation
3209
+ * @author Thomas Griffin
3210
+ * @author Gary Jones
3211
+ */
3212
+ class INBOUND_TGM_Bulk_Installer_Skin extends Bulk_Upgrader_Skin {
3213
+ /**
3214
+ * Holds plugin info for each individual plugin installation.
3215
+ *
3216
+ * @since 2.2.0
3217
+ *
3218
+ * @var array
3219
+ */
3220
+ public $plugin_info = array();
3221
+
3222
+ /**
3223
+ * Holds names of plugins that are undergoing bulk installations.
3224
+ *
3225
+ * @since 2.2.0
3226
+ *
3227
+ * @var array
3228
+ */
3229
+ public $plugin_names = array();
3230
+
3231
+ /**
3232
+ * Integer to use for iteration through each plugin installation.
3233
+ *
3234
+ * @since 2.2.0
3235
+ *
3236
+ * @var integer
3237
+ */
3238
+ public $i = 0;
3239
+
3240
+ /**
3241
+ * TGMPA instance
3242
+ *
3243
+ * @since 2.5.0
3244
+ *
3245
+ * @var object
3246
+ */
3247
+ protected $tgmpa;
3248
+
3249
+ /**
3250
+ * Constructor. Parses default args with new ones and extracts them for use.
3251
+ *
3252
+ * @since 2.2.0
3253
+ *
3254
+ * @param array $args Arguments to pass for use within the class.
3255
+ */
3256
+ public function __construct( $args = array() ) {
3257
+ // Get TGMPA class instance.
3258
+ $this->tgmpa = call_user_func( array( get_class( $GLOBALS['tgmpa'] ), 'get_instance' ) );
3259
+
3260
+ // Parse default and new args.
3261
+ $defaults = array(
3262
+ 'url' => '',
3263
+ 'nonce' => '',
3264
+ 'names' => array(),
3265
+ 'install_type' => 'install',
3266
+ );
3267
+ $args = wp_parse_args( $args, $defaults );
3268
+
3269
+ // Set plugin names to $this->plugin_names property.
3270
+ $this->plugin_names = $args['names'];
3271
+
3272
+ // Extract the new args.
3273
+ parent::__construct( $args );
3274
+ }
3275
+
3276
+ /**
3277
+ * Sets install skin strings for each individual plugin.
3278
+ *
3279
+ * Checks to see if the automatic activation flag is set and uses the
3280
+ * the proper strings accordingly.
3281
+ *
3282
+ * @since 2.2.0
3283
+ */
3284
+ public function add_strings() {
3285
+ if ( 'update' === $this->options['install_type'] ) {
3286
+ parent::add_strings();
3287
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Updating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3288
+ } else {
3289
+ $this->upgrader->strings['skin_update_failed_error'] = __( 'An error occurred while installing %1$s: <strong>%2$s</strong>.', 'tgmpa' );
3290
+ $this->upgrader->strings['skin_update_failed'] = __( 'The installation of %1$s failed.', 'tgmpa' );
3291
+
3292
+ if ( $this->tgmpa->is_automatic ) {
3293
+ // Automatic activation strings.
3294
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation and activation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3295
+ $this->upgrader->strings['skin_update_successful'] = __( '%1$s installed and activated successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3296
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations and activations have been completed.', 'tgmpa' );
3297
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing and Activating Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3298
+ } else {
3299
+ // Default installation strings.
3300
+ $this->upgrader->strings['skin_upgrade_start'] = __( 'The installation process is starting. This process may take a while on some hosts, so please be patient.', 'tgmpa' );
3301
+ $this->upgrader->strings['skin_update_successful'] = esc_html__( '%1$s installed successfully.', 'tgmpa' ) . ' <a href="#" class="hide-if-no-js" onclick="%2$s"><span>' . esc_html__( 'Show Details', 'tgmpa' ) . '</span><span class="hidden">' . esc_html__( 'Hide Details', 'tgmpa' ) . '</span>.</a>';
3302
+ $this->upgrader->strings['skin_upgrade_end'] = __( 'All installations have been completed.', 'tgmpa' );
3303
+ $this->upgrader->strings['skin_before_update_header'] = __( 'Installing Plugin %1$s (%2$d/%3$d)', 'tgmpa' );
3304
+ }
3305
+ }
3306
+ }
3307
+
3308
+ /**
3309
+ * Outputs the header strings and necessary JS before each plugin installation.
3310
+ *
3311
+ * @since 2.2.0
3312
+ *
3313
+ * @param string $title Unused in this implementation.
3314
+ */
3315
+ public function before( $title = '' ) {
3316
+ if ( empty( $title ) ) {
3317
+ $title = esc_html( $this->plugin_names[ $this->i ] );
3318
+ }
3319
+ parent::before( $title );
3320
+ }
3321
+
3322
+ /**
3323
+ * Outputs the footer strings and necessary JS after each plugin installation.
3324
+ *
3325
+ * Checks for any errors and outputs them if they exist, else output
3326
+ * success strings.
3327
+ *
3328
+ * @since 2.2.0
3329
+ *
3330
+ * @param string $title Unused in this implementation.
3331
+ */
3332
+ public function after( $title = '' ) {
3333
+ if ( empty( $title ) ) {
3334
+ $title = esc_html( $this->plugin_names[ $this->i ] );
3335
+ }
3336
+ parent::after( $title );
3337
+
3338
+ $this->i++;
3339
+ }
3340
+
3341
+ /**
3342
+ * Outputs links after bulk plugin installation is complete.
3343
+ *
3344
+ * @since 2.2.0
3345
+ */
3346
+ public function bulk_footer() {
3347
+ // Serve up the string to say installations (and possibly activations) are complete.
3348
+ parent::bulk_footer();
3349
+
3350
+ // Flush plugins cache so we can make sure that the installed plugins list is always up to date.
3351
+ wp_clean_plugins_cache();
3352
+
3353
+ $this->tgmpa->show_tgmpa_version();
3354
+
3355
+ // Display message based on if all plugins are now active or not.
3356
+ $update_actions = array();
3357
+
3358
+ if ( $this->tgmpa->is_tgmpa_complete() ) {
3359
+ // All plugins are active, so we display the complete string and hide the menu to protect users.
3360
+ echo '<style type="text/css">#adminmenu .wp-submenu li.current { display: none !important; }</style>';
3361
+ $update_actions['dashboard'] = sprintf(
3362
+ esc_html( $this->tgmpa->strings['complete'] ),
3363
+ '<a href="' . esc_url( self_admin_url() ) . '">' . esc_html__( 'Return to the Dashboard', 'tgmpa' ) . '</a>'
3364
+ );
3365
+ } else {
3366
+ $update_actions['tgmpa_page'] = '<a href="' . esc_url( $this->tgmpa->get_tgmpa_url() ) . '" target="_parent">' . esc_html( $this->tgmpa->strings['return'] ) . '</a>';
3367
+ }
3368
+
3369
+ /**
3370
+ * Filter the list of action links available following bulk plugin installs/updates.
3371
+ *
3372
+ * @since 2.5.0
3373
+ *
3374
+ * @param array $update_actions Array of plugin action links.
3375
+ * @param array $plugin_info Array of information for the last-handled plugin.
3376
+ */
3377
+ $update_actions = apply_filters( 'tgmpa_update_bulk_plugins_complete_actions', $update_actions, $this->plugin_info );
3378
+
3379
+ if ( ! empty( $update_actions ) ) {
3380
+ $this->feedback( implode( ' | ', (array) $update_actions ) );
3381
+ }
3382
+ }
3383
+
3384
+ /* *********** DEPRECATED METHODS *********** */
3385
+
3386
+ /**
3387
+ * Flush header output buffer.
3388
+ *
3389
+ * @since 2.2.0
3390
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3391
+ * @see Bulk_Upgrader_Skin::flush_output()
3392
+ */
3393
+ public function before_flush_output() {
3394
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3395
+ $this->flush_output();
3396
+ }
3397
+
3398
+ /**
3399
+ * Flush footer output buffer and iterate $this->i to make sure the
3400
+ * installation strings reference the correct plugin.
3401
+ *
3402
+ * @since 2.2.0
3403
+ * @deprecated 2.5.0 use {@see Bulk_Upgrader_Skin::flush_output()} instead
3404
+ * @see Bulk_Upgrader_Skin::flush_output()
3405
+ */
3406
+ public function after_flush_output() {
3407
+ _deprecated_function( __FUNCTION__, 'TGMPA 2.5.0', 'Bulk_Upgrader_Skin::flush_output()' );
3408
+ $this->flush_output();
3409
+ $this->i++;
3410
+ }
3411
+ }
3412
+ }
3413
+ }
3414
+ }
3415
+ }
3416
+
3417
+ if ( ! class_exists( 'INBOUND_TGM_Utils' ) ) {
3418
+
3419
+ /**
3420
+ * Generic utilities for TGMPA.
3421
+ *
3422
+ * All methods are static, poor-dev name-spacing class wrapper.
3423
+ *
3424
+ * @since 2.5.0
3425
+ *
3426
+ * @package TGM-Plugin-Activation
3427
+ * @author Juliette Reinders Folmer
3428
+ */
3429
+ class INBOUND_TGM_Utils {
3430
+ /**
3431
+ * Whether the PHP filter extension is enabled.
3432
+ *
3433
+ * @see http://php.net/book.filter
3434
+ *
3435
+ * @since 2.5.0
3436
+ *
3437
+ * @static
3438
+ *
3439
+ * @var bool $has_filters True is the extension is enabled.
3440
+ */
3441
+ public static $has_filters;
3442
+
3443
+ /**
3444
+ * Wrap an arbitrary string in <em> tags. Meant to be used in combination with array_map().
3445
+ *
3446
+ * @since 2.5.0
3447
+ *
3448
+ * @static
3449
+ *
3450
+ * @param string $string Text to be wrapped.
3451
+ * @return string
3452
+ */
3453
+ public static function wrap_in_em( $string ) {
3454
+ return '<em>' . $string . '</em>';
3455
+ }
3456
+
3457
+ /**
3458
+ * Wrap an arbitrary string in <strong> tags. Meant to be used in combination with array_map().
3459
+ *
3460
+ * @since 2.5.0
3461
+ *
3462
+ * @static
3463
+ *
3464
+ * @param string $string Text to be wrapped.
3465
+ * @return string
3466
+ */
3467
+ public static function wrap_in_strong( $string ) {
3468
+ return '<strong>' . wp_kses_post( $string ) . '</strong>';
3469
+ }
3470
+
3471
+ /**
3472
+ * Helper function: Validate a value as boolean
3473
+ *
3474
+ * @since 2.5.0
3475
+ *
3476
+ * @static
3477
+ *
3478
+ * @param mixed $value Arbitrary value.
3479
+ * @return bool
3480
+ */
3481
+ public static function validate_bool( $value ) {
3482
+ if ( ! isset( self::$has_filters ) ) {
3483
+ self::$has_filters = extension_loaded( 'filter' );
3484
+ }
3485
+
3486
+ if ( self::$has_filters ) {
3487
+ return filter_var( $value, FILTER_VALIDATE_BOOLEAN );
3488
+ } else {
3489
+ return self::emulate_filter_bool( $value );
3490
+ }
3491
+ }
3492
+
3493
+ /**
3494
+ * Helper function: Cast a value to bool
3495
+ *
3496
+ * @since 2.5.0
3497
+ *
3498
+ * @static
3499
+ *
3500
+ * @param mixed $value Value to cast.
3501
+ * @return bool
3502
+ */
3503
+ protected static function emulate_filter_bool( $value ) {
3504
+ // @codingStandardsIgnoreStart
3505
+ static $true = array(
3506
+ '1',
3507
+ 'true', 'True', 'TRUE',
3508
+ 'y', 'Y',
3509
+ 'yes', 'Yes', 'YES',
3510
+ 'on', 'On', 'ON',
3511
+ );
3512
+ static $false = array(
3513
+ '0',
3514
+ 'false', 'False', 'FALSE',
3515
+ 'n', 'N',
3516
+ 'no', 'No', 'NO',
3517
+ 'off', 'Off', 'OFF',
3518
+ );
3519
+ // @codingStandardsIgnoreEnd
3520
+
3521
+ if ( is_bool( $value ) ) {
3522
+ return $value;
3523
+ } else if ( is_int( $value ) && ( 0 === $value || 1 === $value ) ) {
3524
+ return (bool) $value;
3525
+ } else if ( ( is_float( $value ) && ! is_nan( $value ) ) && ( (float) 0 === $value || (float) 1 === $value ) ) {
3526
+ return (bool) $value;
3527
+ } else if ( is_string( $value ) ) {
3528
+ $value = trim( $value );
3529
+ if ( in_array( $value, $true, true ) ) {
3530
+ return true;
3531
+ } else if ( in_array( $value, $false, true ) ) {
3532
+ return false;
3533
+ } else {
3534
+ return false;
3535
+ }
3536
+ }
3537
+
3538
+ return false;
3539
+ }
3540
+ } // End of class INBOUND_TGM_Utils
3541
  } // End of class_exists wrapper
assets/libraries/datetimepicker/MIT-LICENSE.txt CHANGED
@@ -1,19 +1,19 @@
1
- Copyright (c) 2013 http://xdsoft.net
2
-
3
- Permission is hereby granted, free of charge, to any person obtaining a copy
4
- of this software and associated documentation files (the "Software"), to deal
5
- in the Software without restriction, including without limitation the rights
6
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
- copies of the Software, and to permit persons to whom the Software is
8
- furnished to do so, subject to the following conditions:
9
-
10
- The above copyright notice and this permission notice shall be included in
11
- all copies or substantial portions of the Software.
12
-
13
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
  THE SOFTWARE.
1
+ Copyright (c) 2013 http://xdsoft.net
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
  THE SOFTWARE.
assets/libraries/datetimepicker/README.md CHANGED
@@ -1,28 +1,28 @@
1
- datetimepicker
2
- ==============
3
- [Documentation][doc]
4
-
5
-
6
- jQuery Plugin Date and Time Picker
7
-
8
- DateTimePicker
9
-
10
- ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/1.png)
11
-
12
- DatePicker
13
-
14
- ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/2.png)
15
-
16
- TimePicker
17
-
18
- ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/3.png)
19
-
20
- Options to highlight individual dates or periods
21
-
22
- ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/4.png)
23
-
24
- ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/5.png)
25
-
26
- ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/6.png)
27
-
28
- [doc]: http://xdsoft.net/jqplugins/datetimepicker/
1
+ datetimepicker
2
+ ==============
3
+ [Documentation][doc]
4
+
5
+
6
+ jQuery Plugin Date and Time Picker
7
+
8
+ DateTimePicker
9
+
10
+ ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/1.png)
11
+
12
+ DatePicker
13
+
14
+ ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/2.png)
15
+
16
+ TimePicker
17
+
18
+ ![ScreenShot](https://raw.github.com/xdan/datetimepicker/master/screen/3.png)
19
+
20
+ Options to highlight individual dates or periods
21
+
22
+ ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/4.png)
23
+
24
+ ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/5.png)
25
+
26
+ ![ScreenShot](https://raw.github.com/Mingpao/datetimepicker/master/screen/6.png)
27
+
28
+ [doc]: http://xdsoft.net/jqplugins/datetimepicker/
assets/libraries/datetimepicker/bower.json CHANGED
@@ -1,52 +1,52 @@
1
- {
2
- "name": "datetimepicker",
3
- "version": "2.4.5",
4
- "main": [
5
- "jquery.datetimepicker.js",
6
- "jquery.datetimepicker.css"
7
- ],
8
- "ignore": [
9
- "**/screen",
10
- "**/datetimepicker.jquery.json",
11
- "**/*.png",
12
- "**/*.txt",
13
- "**/*.md",
14
- "**/*.html",
15
- "**/*.tpl",
16
- "**/jquery.js"
17
- ],
18
- "keywords": [
19
- "calendar",
20
- "date",
21
- "time",
22
- "form",
23
- "datetime",
24
- "datepicker",
25
- "timepicker",
26
- "datetimepicker",
27
- "validation",
28
- "ui",
29
- "scroller",
30
- "picker",
31
- "i18n",
32
- "input",
33
- "jquery",
34
- "touch"
35
- ],
36
- "dependencies": {
37
- "jquery": ">= 1.7.2"
38
- },
39
- "authors": [
40
- {
41
- "name": "Chupurnov Valeriy",
42
- "email": "chupurnov@gmail.com",
43
- "homepage": "http://xdsoft.net/contacts.html"
44
- }
45
- ],
46
- "license": "MIT",
47
- "homepage": "http://xdsoft.net/jqplugins/datetimepicker/",
48
- "repository": {
49
- "type": "git",
50
- "url": "git://github.com:xdan/datetimepicker.git"
51
- }
52
- }
1
+ {
2
+ "name": "datetimepicker",
3
+ "version": "2.4.5",
4
+ "main": [
5
+ "jquery.datetimepicker.js",
6
+ "jquery.datetimepicker.css"
7
+ ],
8
+ "ignore": [
9
+ "**/screen",
10
+ "**/datetimepicker.jquery.json",
11
+ "**/*.png",
12
+ "**/*.txt",
13
+ "**/*.md",
14
+ "**/*.html",
15
+ "**/*.tpl",
16
+ "**/jquery.js"
17
+ ],
18
+ "keywords": [
19
+ "calendar",
20
+ "date",
21
+ "time",
22
+ "form",
23
+ "datetime",
24
+ "datepicker",
25
+ "timepicker",
26
+ "datetimepicker",
27
+ "validation",
28
+ "ui",
29
+ "scroller",
30
+ "picker",
31
+ "i18n",
32
+ "input",
33
+ "jquery",
34
+ "touch"
35
+ ],
36
+ "dependencies": {
37
+ "jquery": ">= 1.7.2"
38
+ },
39
+ "authors": [
40
+ {
41
+ "name": "Chupurnov Valeriy",
42
+ "email": "chupurnov@gmail.com",
43
+ "homepage": "http://xdsoft.net/contacts.html"
44
+ }
45
+ ],
46
+ "license": "MIT",
47
+ "homepage": "http://xdsoft.net/jqplugins/datetimepicker/",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git://github.com:xdan/datetimepicker.git"
51
+ }
52
+ }
assets/libraries/datetimepicker/datetimepicker.jquery.json CHANGED
@@ -1,47 +1,47 @@
1
- {
2
- "name": "datetimepicker",
3
- "version": "2.4.5",
4
- "title": "jQuery Date and Time picker",
5
- "description": "jQuery plugin for date, time, or datetime manipulation in form",
6
- "keywords": [
7
- "calendar",
8
- "date",
9
- "time",
10
- "form",
11
- "datetime",
12
- "datepicker",
13
- "timepicker",
14
- "datetimepicker",
15
- "validation",
16
- "ui",
17
- "scroller",
18
- "picker",
19
- "i18n",
20
- "input",
21
- "jquery",
22
- "touch"
23
- ],
24
- "author": {
25
- "name": "Chupurnov Valeriy",
26
- "email": "chupurnov@gmail.com",
27
- "url": "http://xdsoft.net/contacts.html"
28
- },
29
- "maintainers": [{
30
- "name": "Chupurnov Valeriy",
31
- "email": "chupurnov@gmail.com",
32
- "url": "http://xdsoft.net"
33
- }],
34
- "licenses": [
35
- {
36
- "type": "MIT",
37
- "url": "https://github.com/xdan/datetimepicker/blob/master/MIT-LICENSE.txt"
38
- }
39
- ],
40
- "bugs": "https://github.com/xdan/datetimepicker/issues",
41
- "homepage": "http://xdsoft.net/jqplugins/datetimepicker/",
42
- "docs": "http://xdsoft.net/jqplugins/datetimepicker/",
43
- "download": "https://github.com/xdan/datetimepicker/archive/master.zip",
44
- "dependencies": {
45
- "jquery": ">=1.7"
46
- }
47
  }
1
+ {
2
+ "name": "datetimepicker",
3
+ "version": "2.4.5",
4
+ "title": "jQuery Date and Time picker",
5
+ "description": "jQuery plugin for date, time, or datetime manipulation in form",
6
+ "keywords": [
7
+ "calendar",
8
+ "date",
9
+ "time",
10
+ "form",
11
+ "datetime",
12
+ "datepicker",
13
+ "timepicker",
14
+ "datetimepicker",
15
+ "validation",
16
+ "ui",
17
+ "scroller",
18
+ "picker",
19
+ "i18n",
20
+ "input",
21
+ "jquery",
22
+ "touch"
23
+ ],
24
+ "author": {
25
+ "name": "Chupurnov Valeriy",
26
+ "email": "chupurnov@gmail.com",
27
+ "url": "http://xdsoft.net/contacts.html"
28
+ },
29
+ "maintainers": [{
30
+ "name": "Chupurnov Valeriy",
31
+ "email": "chupurnov@gmail.com",
32
+ "url": "http://xdsoft.net"
33
+ }],
34
+ "licenses": [
35
+ {
36
+ "type": "MIT",
37
+ "url": "https://github.com/xdan/datetimepicker/blob/master/MIT-LICENSE.txt"
38
+ }
39
+ ],
40
+ "bugs": "https://github.com/xdan/datetimepicker/issues",
41
+ "homepage": "http://xdsoft.net/jqplugins/datetimepicker/",
42
+ "docs": "http://xdsoft.net/jqplugins/datetimepicker/",
43
+ "download": "https://github.com/xdan/datetimepicker/archive/master.zip",
44
+ "dependencies": {
45
+ "jquery": ">=1.7"
46
+ }
47
  }
assets/libraries/datetimepicker/jquery.datetimepicker.css CHANGED
@@ -1,545 +1,545 @@
1
- .xdsoft_datetimepicker {
2
- box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.506);
3
- background: #fff;
4
- border-bottom: 1px solid #bbb;
5
- border-left: 1px solid #ccc;
6
- border-right: 1px solid #ccc;
7
- border-top: 1px solid #ccc;
8
- color: #333;
9
- font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
10
- padding: 8px;
11
- padding-left: 0;
12
- padding-top: 2px;
13
- position: absolute;
14
- z-index: 9999;
15
- -moz-box-sizing: border-box;
16
- box-sizing: border-box;
17
- display: none;
18
- }
19
-
20
- .xdsoft_datetimepicker iframe {
21
- position: absolute;
22
- left: 0;
23
- top: 0;
24
- width: 75px;
25
- height: 210px;
26
- background: transparent;
27
- border: none;
28
- }
29
-
30
- /*For IE8 or lower*/
31
- .xdsoft_datetimepicker button {
32
- border: none !important;
33
- }
34
-
35
- .xdsoft_noselect {
36
- -webkit-touch-callout: none;
37
- -webkit-user-select: none;
38
- -khtml-user-select: none;
39
- -moz-user-select: none;
40
- -ms-user-select: none;
41
- -o-user-select: none;
42
- user-select: none;
43
- }
44
-
45
- .xdsoft_noselect::selection { background: transparent }
46
- .xdsoft_noselect::-moz-selection { background: transparent }
47
-
48
- .xdsoft_datetimepicker.xdsoft_inline {
49
- display: inline-block;
50
- position: static;
51
- box-shadow: none;
52
- }
53
-
54
- .xdsoft_datetimepicker * {
55
- -moz-box-sizing: border-box;
56
- box-sizing: border-box;
57
- padding: 0;
58
- margin: 0;
59
- }
60
-
61
- .xdsoft_datetimepicker .xdsoft_datepicker, .xdsoft_datetimepicker .xdsoft_timepicker {
62
- display: none;
63
- }
64
-
65
- .xdsoft_datetimepicker .xdsoft_datepicker.active, .xdsoft_datetimepicker .xdsoft_timepicker.active {
66
- display: block;
67
- }
68
-
69
- .xdsoft_datetimepicker .xdsoft_datepicker {
70
- width: 224px;
71
- float: left;
72
- margin-left: 8px;
73
- }
74
-
75
- .xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker {
76
- width: 256px;
77
- }
78
-
79
- .xdsoft_datetimepicker .xdsoft_timepicker {
80
- width: 58px;
81
- float: left;
82
- text-align: center;
83
- margin-left: 8px;
84
- margin-top: 0;
85
- }
86
-
87
- .xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker {
88
- margin-top: 8px;
89
- margin-bottom: 3px
90
- }
91
-
92
- .xdsoft_datetimepicker .xdsoft_mounthpicker {
93
- position: relative;
94
- text-align: center;
95
- }
96
-
97
- .xdsoft_datetimepicker .xdsoft_label i,
98
- .xdsoft_datetimepicker .xdsoft_prev,
99
- .xdsoft_datetimepicker .xdsoft_next,
100
- .xdsoft_datetimepicker .xdsoft_today_button {
101
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC);
102
- }
103
-
104
- .xdsoft_datetimepicker .xdsoft_label i {
105
- opacity: 0.5;
106
- background-position: -92px -19px;
107
- display: inline-block;
108
- width: 9px;
109
- height: 20px;
110
- vertical-align: middle;
111
- }
112
-
113
- .xdsoft_datetimepicker .xdsoft_prev {
114
- float: left;
115
- background-position: -20px 0;
116
- }
117
- .xdsoft_datetimepicker .xdsoft_today_button {
118
- float: left;
119
- background-position: -70px 0;
120
- margin-left: 5px;
121
- }
122
-
123
- .xdsoft_datetimepicker .xdsoft_next {
124
- float: right;
125
- background-position: 0 0;
126
- }
127
-
128
- .xdsoft_datetimepicker .xdsoft_next,
129
- .xdsoft_datetimepicker .xdsoft_prev ,
130
- .xdsoft_datetimepicker .xdsoft_today_button {
131
- background-color: transparent;
132
- background-repeat: no-repeat;
133
- border: 0 none;
134
- cursor: pointer;
135
- display: block;
136
- height: 30px;
137
- opacity: 0.5;
138
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
139
- outline: medium none;
140
- overflow: hidden;
141
- padding: 0;
142
- position: relative;
143
- text-indent: 100%;
144
- white-space: nowrap;
145
- width: 20px;
146
- min-width: 0;
147
- }
148
-
149
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,
150
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next {
151
- float: none;
152
- background-position: -40px -15px;
153
- height: 15px;
154
- width: 30px;
155
- display: block;
156
- margin-left: 14px;
157
- margin-top: 7px;
158
- }
159
-
160
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev {
161
- background-position: -40px 0;
162
- margin-bottom: 7px;
163
- margin-top: 0;
164
- }
165
-
166
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box {
167
- height: 151px;
168
- overflow: hidden;
169
- border-bottom: 1px solid #ddd;
170
- }
171
-
172
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div {
173
- background: #f5f5f5;
174
- border-top: 1px solid #ddd;
175
- color: #666;
176
- font-size: 12px;
177
- text-align: center;
178
- border-collapse: collapse;
179
- cursor: pointer;
180
- border-bottom-width: 0;
181
- height: 25px;
182
- line-height: 25px;
183
- }
184
-
185
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div > div:first-child {
186
- border-top-width: 0;
187
- }
188
-
189
- .xdsoft_datetimepicker .xdsoft_today_button:hover,
190
- .xdsoft_datetimepicker .xdsoft_next:hover,
191
- .xdsoft_datetimepicker .xdsoft_prev:hover {
192
- opacity: 1;
193
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
194
- }
195
-
196
- .xdsoft_datetimepicker .xdsoft_label {
197
- display: inline;
198
- position: relative;
199
- z-index: 9999;
200
- margin: 0;
201
- padding: 5px 3px;
202
- font-size: 14px;
203
- line-height: 20px;
204
- font-weight: bold;
205
- background-color: #fff;
206
- float: left;
207
- width: 182px;
208
- text-align: center;
209
- cursor: pointer;
210
- }
211
-
212
- .xdsoft_datetimepicker .xdsoft_label:hover>span {
213
- text-decoration: underline;
214
- }
215
-
216
- .xdsoft_datetimepicker .xdsoft_label:hover i {
217
- opacity: 1.0;
218
- }
219
-
220
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select {
221
- border: 1px solid #ccc;
222
- position: absolute;
223
- right: 0;
224
- top: 30px;
225
- z-index: 101;
226
- display: none;
227
- background: #fff;
228
- max-height: 160px;
229
- overflow-y: hidden;
230
- }
231
-
232
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_monthselect{ right: -7px }
233
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_yearselect{ right: 2px }
234
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
235
- color: #fff;
236
- background: #ff8000;
237
- }
238
-
239
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option {
240
- padding: 2px 10px 2px 5px;
241
- text-decoration: none !important;
242
- }
243
-
244
- .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
245
- background: #33aaff;
246
- box-shadow: #178fe5 0 1px 3px 0 inset;
247
- color: #fff;
248
- font-weight: 700;
249
- }
250
-
251
- .xdsoft_datetimepicker .xdsoft_month {
252
- width: 100px;
253
- text-align: right;
254
- }
255
-
256
- .xdsoft_datetimepicker .xdsoft_calendar {
257
- clear: both;
258
- }
259
-
260
- .xdsoft_datetimepicker .xdsoft_year{
261
- width: 48px;
262
- margin-left: 5px;
263
- }
264
-
265
- .xdsoft_datetimepicker .xdsoft_calendar table {
266
- border-collapse: collapse;
267
- width: 100%;
268
-
269
- }
270
-
271
- .xdsoft_datetimepicker .xdsoft_calendar td > div {
272
- padding-right: 5px;
273
- }
274
-
275
- .xdsoft_datetimepicker .xdsoft_calendar th {
276
- height: 25px;
277
- }
278
-
279
- .xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th {
280
- width: 14.2857142%;
281
- background: #f5f5f5;
282
- border: 1px solid #ddd;
283
- color: #666;
284
- font-size: 12px;
285
- text-align: right;
286
- vertical-align: middle;
287
- padding: 0;
288
- border-collapse: collapse;
289
- cursor: pointer;
290
- height: 25px;
291
- }
292
- .xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th {
293
- width: 12.5%;
294
- }
295
-
296
- .xdsoft_datetimepicker .xdsoft_calendar th {
297
- background: #f1f1f1;
298
- }
299
-
300
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
301
- color: #33aaff;
302
- }
303
-
304
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default {
305
- background: #ffe9d2;
306
- box-shadow: #ffb871 0 1px 4px 0 inset;
307
- color: #000;
308
- }
309
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint {
310
- background: #c1ffc9;
311
- box-shadow: #00dd1c 0 1px 4px 0 inset;
312
- color: #000;
313
- }
314
-
315
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
316
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
317
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
318
- background: #33aaff;
319
- box-shadow: #178fe5 0 1px 3px 0 inset;
320
- color: #fff;
321
- font-weight: 700;
322
- }
323
-
324
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,
325
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,
326
- .xdsoft_datetimepicker .xdsoft_time_box >div >div.xdsoft_disabled {
327
- opacity: 0.5;
328
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
329
- cursor: default;
330
- }
331
-
332
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
333
- opacity: 0.2;
334
- -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
335
- }
336
-
337
- .xdsoft_datetimepicker .xdsoft_calendar td:hover,
338
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
339
- color: #fff !important;
340
- background: #ff8000 !important;
341
- box-shadow: none !important;
342
- }
343
-
344
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,
345
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover {
346
- background: #33aaff !important;
347
- box-shadow: #178fe5 0 1px 3px 0 inset !important;
348
- color: #fff !important;
349
- }
350
-
351
- .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,
352
- .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_disabled:hover {
353
- color: inherit !important;
354
- background: inherit !important;
355
- box-shadow: inherit !important;
356
- }
357
-
358
- .xdsoft_datetimepicker .xdsoft_calendar th {
359
- font-weight: 700;
360
- text-align: center;
361
- color: #999;
362
- cursor: default;
363
- }
364
-
365
- .xdsoft_datetimepicker .xdsoft_copyright {
366
- color: #ccc !important;
367
- font-size: 10px;
368
- clear: both;
369
- float: none;
370
- margin-left: 8px;
371
- }
372
-
373
- .xdsoft_datetimepicker .xdsoft_copyright a { color: #eee !important }
374
- .xdsoft_datetimepicker .xdsoft_copyright a:hover { color: #aaa !important }
375
-
376
- .xdsoft_time_box {
377
- position: relative;
378
- border: 1px solid #ccc;
379
- }
380
- .xdsoft_scrollbar >.xdsoft_scroller {
381
- background: #ccc !important;
382
- height: 20px;
383
- border-radius: 3px;
384
- }
385
- .xdsoft_scrollbar {
386
- position: absolute;
387
- width: 7px;
388
- right: 0;
389
- top: 0;
390
- bottom: 0;
391
- cursor: pointer;
392
- }
393
- .xdsoft_scroller_box {
394
- position: relative;
395
- }
396
-
397
- .xdsoft_datetimepicker.xdsoft_dark {
398
- box-shadow: 0 5px 15px -5px rgba(255, 255, 255, 0.506);
399
- background: #000;
400
- border-bottom: 1px solid #444;
401
- border-left: 1px solid #333;
402
- border-right: 1px solid #333;
403
- border-top: 1px solid #333;
404
- color: #ccc;
405
- }
406
-
407
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box {
408
- border-bottom: 1px solid #222;
409
- }
410
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div {
411
- background: #0a0a0a;
412
- border-top: 1px solid #222;
413
- color: #999;
414
- }
415
-
416
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label {
417
- background-color: #000;
418
- }
419
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select {
420
- border: 1px solid #333;
421
- background: #000;
422
- }
423
-
424
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
425
- color: #000;
426
- background: #007fff;
427
- }
428
-
429
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
430
- background: #cc5500;
431
- box-shadow: #b03e00 0 1px 3px 0 inset;
432
- color: #000;
433
- }
434
-
435
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,
436
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,
437
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,
438
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button {
439
- background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==);
440
- }
441
-
442
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,
443
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
444
- background: #0a0a0a;
445
- border: 1px solid #222;
446
- color: #999;
447
- }
448
-
449
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
450
- background: #0e0e0e;
451
- }
452
-
453
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today {
454
- color: #cc5500;
455
- }
456
-
457
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default {
458
- background: #ffe9d2;
459
- box-shadow: #ffb871 0 1px 4px 0 inset;
460
- color:#000;
461
- }
462
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint {
463
- background: #c1ffc9;
464
- box-shadow: #00dd1c 0 1px 4px 0 inset;
465
- color:#000;
466
- }
467
-
468
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,
469
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,
470
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
471
- background: #cc5500;
472
- box-shadow: #b03e00 0 1px 3px 0 inset;
473
- color: #000;
474
- }
475
-
476
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,
477
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
478
- color: #000 !important;
479
- background: #007fff !important;
480
- }
481
-
482
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
483
- color: #666;
484
- }
485
-
486
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright { color: #333 !important }
487
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a { color: #111 !important }
488
- .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover { color: #555 !important }
489
-
490
- .xdsoft_dark .xdsoft_time_box {
491
- border: 1px solid #333;
492
- }
493
-
494
- .xdsoft_dark .xdsoft_scrollbar >.xdsoft_scroller {
495
- background: #333 !important;
496
- }
497
- .xdsoft_datetimepicker .xdsoft_save_selected {
498
- display: block;
499
- border: 1px solid #dddddd !important;
500
- margin-top: 5px;
501
- width: 100%;
502
- color: #454551;
503
- font-size: 13px;
504
- }
505
- .xdsoft_datetimepicker .blue-gradient-button {
506
- font-family: "museo-sans", "Book Antiqua", sans-serif;
507
- font-size: 12px;
508
- font-weight: 300;
509
- color: #82878c;
510
- height: 28px;
511
- position: relative;
512
- padding: 4px 17px 4px 33px;
513
- border: 1px solid #d7d8da;
514
- background: -moz-linear-gradient(top, #fff 0%, #f4f8fa 73%);
515
- /* FF3.6+ */
516
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(73%, #f4f8fa));
517
- /* Chrome,Safari4+ */
518
- background: -webkit-linear-gradient(top, #fff 0%, #f4f8fa 73%);
519
- /* Chrome10+,Safari5.1+ */
520
- background: -o-linear-gradient(top, #fff 0%, #f4f8fa 73%);
521
- /* Opera 11.10+ */
522
- background: -ms-linear-gradient(top, #fff 0%, #f4f8fa 73%);
523
- /* IE10+ */
524
- background: linear-gradient(to bottom, #fff 0%, #f4f8fa 73%);
525
- /* W3C */
526
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff', endColorstr='#f4f8fa',GradientType=0 );
527
- /* IE6-9 */
528
- }
529
- .xdsoft_datetimepicker .blue-gradient-button:hover, .xdsoft_datetimepicker .blue-gradient-button:focus, .xdsoft_datetimepicker .blue-gradient-button:hover span, .xdsoft_datetimepicker .blue-gradient-button:focus span {
530
- color: #454551;
531
- background: -moz-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
532
- /* FF3.6+ */
533
- background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f8fa), color-stop(73%, #FFF));
534
- /* Chrome,Safari4+ */
535
- background: -webkit-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
536
- /* Chrome10+,Safari5.1+ */
537
- background: -o-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
538
- /* Opera 11.10+ */
539
- background: -ms-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
540
- /* IE10+ */
541
- background: linear-gradient(to bottom, #f4f8fa 0%, #FFF 73%);
542
- /* W3C */
543
- filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f8fa', endColorstr='#FFF',GradientType=0 );
544
- /* IE6-9 */
545
- }
1
+ .xdsoft_datetimepicker {
2
+ box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.506);
3
+ background: #fff;
4
+ border-bottom: 1px solid #bbb;
5
+ border-left: 1px solid #ccc;
6
+ border-right: 1px solid #ccc;
7
+ border-top: 1px solid #ccc;
8
+ color: #333;
9
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
10
+ padding: 8px;
11
+ padding-left: 0;
12
+ padding-top: 2px;
13
+ position: absolute;
14
+ z-index: 9999;
15
+ -moz-box-sizing: border-box;
16
+ box-sizing: border-box;
17
+ display: none;
18
+ }
19
+
20
+ .xdsoft_datetimepicker iframe {
21
+ position: absolute;
22
+ left: 0;
23
+ top: 0;
24
+ width: 75px;
25
+ height: 210px;
26
+ background: transparent;
27
+ border: none;
28
+ }
29
+
30
+ /*For IE8 or lower*/
31
+ .xdsoft_datetimepicker button {
32
+ border: none !important;
33
+ }
34
+
35
+ .xdsoft_noselect {
36
+ -webkit-touch-callout: none;
37
+ -webkit-user-select: none;
38
+ -khtml-user-select: none;
39
+ -moz-user-select: none;
40
+ -ms-user-select: none;
41
+ -o-user-select: none;
42
+ user-select: none;
43
+ }
44
+
45
+ .xdsoft_noselect::selection { background: transparent }
46
+ .xdsoft_noselect::-moz-selection { background: transparent }
47
+
48
+ .xdsoft_datetimepicker.xdsoft_inline {
49
+ display: inline-block;
50
+ position: static;
51
+ box-shadow: none;
52
+ }
53
+
54
+ .xdsoft_datetimepicker * {
55
+ -moz-box-sizing: border-box;
56
+ box-sizing: border-box;
57
+ padding: 0;
58
+ margin: 0;
59
+ }
60
+
61
+ .xdsoft_datetimepicker .xdsoft_datepicker, .xdsoft_datetimepicker .xdsoft_timepicker {
62
+ display: none;
63
+ }
64
+
65
+ .xdsoft_datetimepicker .xdsoft_datepicker.active, .xdsoft_datetimepicker .xdsoft_timepicker.active {
66
+ display: block;
67
+ }
68
+
69
+ .xdsoft_datetimepicker .xdsoft_datepicker {
70
+ width: 224px;
71
+ float: left;
72
+ margin-left: 8px;
73
+ }
74
+
75
+ .xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_datepicker {
76
+ width: 256px;
77
+ }
78
+
79
+ .xdsoft_datetimepicker .xdsoft_timepicker {
80
+ width: 58px;
81
+ float: left;
82
+ text-align: center;
83
+ margin-left: 8px;
84
+ margin-top: 0;
85
+ }
86
+
87
+ .xdsoft_datetimepicker .xdsoft_datepicker.active+.xdsoft_timepicker {
88
+ margin-top: 8px;
89
+ margin-bottom: 3px
90
+ }
91
+
92
+ .xdsoft_datetimepicker .xdsoft_mounthpicker {
93
+ position: relative;
94
+ text-align: center;
95
+ }
96
+
97
+ .xdsoft_datetimepicker .xdsoft_label i,
98
+ .xdsoft_datetimepicker .xdsoft_prev,
99
+ .xdsoft_datetimepicker .xdsoft_next,
100
+ .xdsoft_datetimepicker .xdsoft_today_button {
101
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Q0NBRjI1NjM0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Q0NBRjI1NjQ0M0UwMTFFNDk4NkFGMzJFQkQzQjEwRUIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDQ0FGMjU2MTQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDQ0FGMjU2MjQzRTAxMUU0OTg2QUYzMkVCRDNCMTBFQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PoNEP54AAAIOSURBVHja7Jq9TsMwEMcxrZD4WpBYeKUCe+kTMCACHZh4BFfHO/AAIHZGFhYkBBsSEqxsLCAgXKhbXYOTxh9pfJVP+qutnZ5s/5Lz2Y5I03QhWji2GIcgAokWgfCxNvcOCCGKqiSqhUp0laHOne05vdEyGMfkdxJDVjgwDlEQgYQBgx+ULJaWSXXS6r/ER5FBVR8VfGftTKcITNs+a1XpcFoExREIDF14AVIFxgQUS+h520cdud6wNkC0UBw6BCO/HoCYwBhD8QCkQ/x1mwDyD4plh4D6DDV0TAGyo4HcawLIBBSLDkHeH0Mg2yVP3l4TQMZQDDsEOl/MgHQqhMNuE0D+oBh0CIr8MAKyazBH9WyBuKxDWgbXfjNf32TZ1KWm/Ap1oSk/R53UtQ5xTh3LUlMmT8gt6g51Q9p+SobxgJQ/qmsfZhWywGFSl0yBjCLJCMgXail3b7+rumdVJ2YRss4cN+r6qAHDkPWjPjdJCF4n9RmAD/V9A/Wp4NQassDjwlB6XBiCxcJQWmZZb8THFilfy/lfrTvLghq2TqTHrRMTKNJ0sIhdo15RT+RpyWwFdY96UZ/LdQKBGjcXpcc1AlSFEfLmouD+1knuxBDUVrvOBmoOC/rEcN7OQxKVeJTCiAdUzUJhA2Oez9QTkp72OTVcxDcXY8iKNkxGAJXmJCOQwOa6dhyXsOa6XwEGAKdeb5ET3rQdAAAAAElFTkSuQmCC);
102
+ }
103
+
104
+ .xdsoft_datetimepicker .xdsoft_label i {
105
+ opacity: 0.5;
106
+ background-position: -92px -19px;
107
+ display: inline-block;
108
+ width: 9px;
109
+ height: 20px;
110
+ vertical-align: middle;
111
+ }
112
+
113
+ .xdsoft_datetimepicker .xdsoft_prev {
114
+ float: left;
115
+ background-position: -20px 0;
116
+ }
117
+ .xdsoft_datetimepicker .xdsoft_today_button {
118
+ float: left;
119
+ background-position: -70px 0;
120
+ margin-left: 5px;
121
+ }
122
+
123
+ .xdsoft_datetimepicker .xdsoft_next {
124
+ float: right;
125
+ background-position: 0 0;
126
+ }
127
+
128
+ .xdsoft_datetimepicker .xdsoft_next,
129
+ .xdsoft_datetimepicker .xdsoft_prev ,
130
+ .xdsoft_datetimepicker .xdsoft_today_button {
131
+ background-color: transparent;
132
+ background-repeat: no-repeat;
133
+ border: 0 none;
134
+ cursor: pointer;
135
+ display: block;
136
+ height: 30px;
137
+ opacity: 0.5;
138
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
139
+ outline: medium none;
140
+ overflow: hidden;
141
+ padding: 0;
142
+ position: relative;
143
+ text-indent: 100%;
144
+ white-space: nowrap;
145
+ width: 20px;
146
+ min-width: 0;
147
+ }
148
+
149
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev,
150
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_next {
151
+ float: none;
152
+ background-position: -40px -15px;
153
+ height: 15px;
154
+ width: 30px;
155
+ display: block;
156
+ margin-left: 14px;
157
+ margin-top: 7px;
158
+ }
159
+
160
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_prev {
161
+ background-position: -40px 0;
162
+ margin-bottom: 7px;
163
+ margin-top: 0;
164
+ }
165
+
166
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box {
167
+ height: 151px;
168
+ overflow: hidden;
169
+ border-bottom: 1px solid #ddd;
170
+ }
171
+
172
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div {
173
+ background: #f5f5f5;
174
+ border-top: 1px solid #ddd;
175
+ color: #666;
176
+ font-size: 12px;
177
+ text-align: center;
178
+ border-collapse: collapse;
179
+ cursor: pointer;
180
+ border-bottom-width: 0;
181
+ height: 25px;
182
+ line-height: 25px;
183
+ }
184
+
185
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div > div:first-child {
186
+ border-top-width: 0;
187
+ }
188
+
189
+ .xdsoft_datetimepicker .xdsoft_today_button:hover,
190
+ .xdsoft_datetimepicker .xdsoft_next:hover,
191
+ .xdsoft_datetimepicker .xdsoft_prev:hover {
192
+ opacity: 1;
193
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=100)";
194
+ }
195
+
196
+ .xdsoft_datetimepicker .xdsoft_label {
197
+ display: inline;
198
+ position: relative;
199
+ z-index: 9999;
200
+ margin: 0;
201
+ padding: 5px 3px;
202
+ font-size: 14px;
203
+ line-height: 20px;
204
+ font-weight: bold;
205
+ background-color: #fff;
206
+ float: left;
207
+ width: 182px;
208
+ text-align: center;
209
+ cursor: pointer;
210
+ }
211
+
212
+ .xdsoft_datetimepicker .xdsoft_label:hover>span {
213
+ text-decoration: underline;
214
+ }
215
+
216
+ .xdsoft_datetimepicker .xdsoft_label:hover i {
217
+ opacity: 1.0;
218
+ }
219
+
220
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select {
221
+ border: 1px solid #ccc;
222
+ position: absolute;
223
+ right: 0;
224
+ top: 30px;
225
+ z-index: 101;
226
+ display: none;
227
+ background: #fff;
228
+ max-height: 160px;
229
+ overflow-y: hidden;
230
+ }
231
+
232
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_monthselect{ right: -7px }
233
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select.xdsoft_yearselect{ right: 2px }
234
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
235
+ color: #fff;
236
+ background: #ff8000;
237
+ }
238
+
239
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option {
240
+ padding: 2px 10px 2px 5px;
241
+ text-decoration: none !important;
242
+ }
243
+
244
+ .xdsoft_datetimepicker .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
245
+ background: #33aaff;
246
+ box-shadow: #178fe5 0 1px 3px 0 inset;
247
+ color: #fff;
248
+ font-weight: 700;
249
+ }
250
+
251
+ .xdsoft_datetimepicker .xdsoft_month {
252
+ width: 100px;
253
+ text-align: right;
254
+ }
255
+
256
+ .xdsoft_datetimepicker .xdsoft_calendar {
257
+ clear: both;
258
+ }
259
+
260
+ .xdsoft_datetimepicker .xdsoft_year{
261
+ width: 48px;
262
+ margin-left: 5px;
263
+ }
264
+
265
+ .xdsoft_datetimepicker .xdsoft_calendar table {
266
+ border-collapse: collapse;
267
+ width: 100%;
268
+
269
+ }
270
+
271
+ .xdsoft_datetimepicker .xdsoft_calendar td > div {
272
+ padding-right: 5px;
273
+ }
274
+
275
+ .xdsoft_datetimepicker .xdsoft_calendar th {
276
+ height: 25px;
277
+ }
278
+
279
+ .xdsoft_datetimepicker .xdsoft_calendar td,.xdsoft_datetimepicker .xdsoft_calendar th {
280
+ width: 14.2857142%;
281
+ background: #f5f5f5;
282
+ border: 1px solid #ddd;
283
+ color: #666;
284
+ font-size: 12px;
285
+ text-align: right;
286
+ vertical-align: middle;
287
+ padding: 0;
288
+ border-collapse: collapse;
289
+ cursor: pointer;
290
+ height: 25px;
291
+ }
292
+ .xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar td,.xdsoft_datetimepicker.xdsoft_showweeks .xdsoft_calendar th {
293
+ width: 12.5%;
294
+ }
295
+
296
+ .xdsoft_datetimepicker .xdsoft_calendar th {
297
+ background: #f1f1f1;
298
+ }
299
+
300
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_today {
301
+ color: #33aaff;
302
+ }
303
+
304
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_default {
305
+ background: #ffe9d2;
306
+ box-shadow: #ffb871 0 1px 4px 0 inset;
307
+ color: #000;
308
+ }
309
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_highlighted_mint {
310
+ background: #c1ffc9;
311
+ box-shadow: #00dd1c 0 1px 4px 0 inset;
312
+ color: #000;
313
+ }
314
+
315
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_default,
316
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current,
317
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
318
+ background: #33aaff;
319
+ box-shadow: #178fe5 0 1px 3px 0 inset;
320
+ color: #fff;
321
+ font-weight: 700;
322
+ }
323
+
324
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month,
325
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled,
326
+ .xdsoft_datetimepicker .xdsoft_time_box >div >div.xdsoft_disabled {
327
+ opacity: 0.5;
328
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=50)";
329
+ cursor: default;
330
+ }
331
+
332
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_other_month.xdsoft_disabled {
333
+ opacity: 0.2;
334
+ -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=20)";
335
+ }
336
+
337
+ .xdsoft_datetimepicker .xdsoft_calendar td:hover,
338
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
339
+ color: #fff !important;
340
+ background: #ff8000 !important;
341
+ box-shadow: none !important;
342
+ }
343
+
344
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_current.xdsoft_disabled:hover,
345
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box>div>div.xdsoft_current.xdsoft_disabled:hover {
346
+ background: #33aaff !important;
347
+ box-shadow: #178fe5 0 1px 3px 0 inset !important;
348
+ color: #fff !important;
349
+ }
350
+
351
+ .xdsoft_datetimepicker .xdsoft_calendar td.xdsoft_disabled:hover,
352
+ .xdsoft_datetimepicker .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_disabled:hover {
353
+ color: inherit !important;
354
+ background: inherit !important;
355
+ box-shadow: inherit !important;
356
+ }
357
+
358
+ .xdsoft_datetimepicker .xdsoft_calendar th {
359
+ font-weight: 700;
360
+ text-align: center;
361
+ color: #999;
362
+ cursor: default;
363
+ }
364
+
365
+ .xdsoft_datetimepicker .xdsoft_copyright {
366
+ color: #ccc !important;
367
+ font-size: 10px;
368
+ clear: both;
369
+ float: none;
370
+ margin-left: 8px;
371
+ }
372
+
373
+ .xdsoft_datetimepicker .xdsoft_copyright a { color: #eee !important }
374
+ .xdsoft_datetimepicker .xdsoft_copyright a:hover { color: #aaa !important }
375
+
376
+ .xdsoft_time_box {
377
+ position: relative;
378
+ border: 1px solid #ccc;
379
+ }
380
+ .xdsoft_scrollbar >.xdsoft_scroller {
381
+ background: #ccc !important;
382
+ height: 20px;
383
+ border-radius: 3px;
384
+ }
385
+ .xdsoft_scrollbar {
386
+ position: absolute;
387
+ width: 7px;
388
+ right: 0;
389
+ top: 0;
390
+ bottom: 0;
391
+ cursor: pointer;
392
+ }
393
+ .xdsoft_scroller_box {
394
+ position: relative;
395
+ }
396
+
397
+ .xdsoft_datetimepicker.xdsoft_dark {
398
+ box-shadow: 0 5px 15px -5px rgba(255, 255, 255, 0.506);
399
+ background: #000;
400
+ border-bottom: 1px solid #444;
401
+ border-left: 1px solid #333;
402
+ border-right: 1px solid #333;
403
+ border-top: 1px solid #333;
404
+ color: #ccc;
405
+ }
406
+
407
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box {
408
+ border-bottom: 1px solid #222;
409
+ }
410
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div {
411
+ background: #0a0a0a;
412
+ border-top: 1px solid #222;
413
+ color: #999;
414
+ }
415
+
416
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label {
417
+ background-color: #000;
418
+ }
419
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select {
420
+ border: 1px solid #333;
421
+ background: #000;
422
+ }
423
+
424
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option:hover {
425
+ color: #000;
426
+ background: #007fff;
427
+ }
428
+
429
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label > .xdsoft_select > div > .xdsoft_option.xdsoft_current {
430
+ background: #cc5500;
431
+ box-shadow: #b03e00 0 1px 3px 0 inset;
432
+ color: #000;
433
+ }
434
+
435
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_label i,
436
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_prev,
437
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_next,
438
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_today_button {
439
+ background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAeCAYAAADaW7vzAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUExQUUzOTA0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUExQUUzOTE0M0UyMTFFNDlBM0FFQTJENTExRDVBODYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQTFBRTM4RTQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQTFBRTM4RjQzRTIxMUU0OUEzQUVBMkQ1MTFENUE4NiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pp0VxGEAAAIASURBVHja7JrNSgMxEMebtgh+3MSLr1T1Xn2CHoSKB08+QmR8Bx9A8e7RixdB9CKCoNdexIugxFlJa7rNZneTbLIpM/CnNLsdMvNjM8l0mRCiQ9Ye61IKCAgZAUnH+mU3MMZaHYChBnJUDzWOFZdVfc5+ZFLbrWDeXPwbxIqrLLfaeS0hEBVGIRQCEiZoHQwtlGSByCCdYBl8g8egTTAWoKQMRBRBcZxYlhzhKegqMOageErsCHVkk3hXIFooDgHB1KkHIHVgzKB4ADJQ/A1jAFmAYhkQqA5TOBtocrKrgXwQA8gcFIuAIO8sQSA7hidvPwaQGZSaAYHOUWJABhWWw2EMIH9QagQERU4SArJXo0ZZL18uvaxejXt/Em8xjVBXmvFr1KVm/AJ10tRe2XnraNqaJvKE3KHuUbfK1E+VHB0q40/y3sdQSxY4FHWeKJCunP8UyDdqJZenT3ntVV5jIYCAh20vT7ioP8tpf6E2lfEMwERe+whV1MHjwZB7PBiCxcGQWwKZKD62lfGNnP/1poFAA60T7rF1UgcKd2id3KDeUS+oLWV8DfWAepOfq00CgQabi9zjcgJVYVD7PVzQUAUGAQkbNJTBICDhgwYTjDYD6XeW08ZKh+A4pYkzenOxXUbvZcWz7E8ykRMnIHGX1XPl+1m2vPYpL+2qdb8CDAARlKFEz/ZVkAAAAABJRU5ErkJggg==);
440
+ }
441
+
442
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td,
443
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
444
+ background: #0a0a0a;
445
+ border: 1px solid #222;
446
+ color: #999;
447
+ }
448
+
449
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
450
+ background: #0e0e0e;
451
+ }
452
+
453
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_today {
454
+ color: #cc5500;
455
+ }
456
+
457
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_default {
458
+ background: #ffe9d2;
459
+ box-shadow: #ffb871 0 1px 4px 0 inset;
460
+ color:#000;
461
+ }
462
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_highlighted_mint {
463
+ background: #c1ffc9;
464
+ box-shadow: #00dd1c 0 1px 4px 0 inset;
465
+ color:#000;
466
+ }
467
+
468
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_default,
469
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td.xdsoft_current,
470
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div.xdsoft_current {
471
+ background: #cc5500;
472
+ box-shadow: #b03e00 0 1px 3px 0 inset;
473
+ color: #000;
474
+ }
475
+
476
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar td:hover,
477
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_timepicker .xdsoft_time_box >div >div:hover {
478
+ color: #000 !important;
479
+ background: #007fff !important;
480
+ }
481
+
482
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_calendar th {
483
+ color: #666;
484
+ }
485
+
486
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright { color: #333 !important }
487
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a { color: #111 !important }
488
+ .xdsoft_datetimepicker.xdsoft_dark .xdsoft_copyright a:hover { color: #555 !important }
489
+
490
+ .xdsoft_dark .xdsoft_time_box {
491
+ border: 1px solid #333;
492
+ }
493
+
494
+ .xdsoft_dark .xdsoft_scrollbar >.xdsoft_scroller {
495
+ background: #333 !important;
496
+ }
497
+ .xdsoft_datetimepicker .xdsoft_save_selected {
498
+ display: block;
499
+ border: 1px solid #dddddd !important;
500
+ margin-top: 5px;
501
+ width: 100%;
502
+ color: #454551;
503
+ font-size: 13px;
504
+ }
505
+ .xdsoft_datetimepicker .blue-gradient-button {
506
+ font-family: "museo-sans", "Book Antiqua", sans-serif;
507
+ font-size: 12px;
508
+ font-weight: 300;
509
+ color: #82878c;
510
+ height: 28px;
511
+ position: relative;
512
+ padding: 4px 17px 4px 33px;
513
+ border: 1px solid #d7d8da;
514
+ background: -moz-linear-gradient(top, #fff 0%, #f4f8fa 73%);
515
+ /* FF3.6+ */
516
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #fff), color-stop(73%, #f4f8fa));
517
+ /* Chrome,Safari4+ */
518
+ background: -webkit-linear-gradient(top, #fff 0%, #f4f8fa 73%);
519
+ /* Chrome10+,Safari5.1+ */
520
+ background: -o-linear-gradient(top, #fff 0%, #f4f8fa 73%);
521
+ /* Opera 11.10+ */
522
+ background: -ms-linear-gradient(top, #fff 0%, #f4f8fa 73%);
523
+ /* IE10+ */
524
+ background: linear-gradient(to bottom, #fff 0%, #f4f8fa 73%);
525
+ /* W3C */
526
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fff', endColorstr='#f4f8fa',GradientType=0 );
527
+ /* IE6-9 */
528
+ }
529
+ .xdsoft_datetimepicker .blue-gradient-button:hover, .xdsoft_datetimepicker .blue-gradient-button:focus, .xdsoft_datetimepicker .blue-gradient-button:hover span, .xdsoft_datetimepicker .blue-gradient-button:focus span {
530
+ color: #454551;
531
+ background: -moz-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
532
+ /* FF3.6+ */
533
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #f4f8fa), color-stop(73%, #FFF));
534
+ /* Chrome,Safari4+ */
535
+ background: -webkit-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
536
+ /* Chrome10+,Safari5.1+ */
537
+ background: -o-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
538
+ /* Opera 11.10+ */
539
+ background: -ms-linear-gradient(top, #f4f8fa 0%, #FFF 73%);
540
+ /* IE10+ */
541
+ background: linear-gradient(to bottom, #f4f8fa 0%, #FFF 73%);
542
+ /* W3C */
543
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4f8fa', endColorstr='#FFF',GradientType=0 );
544
+ /* IE6-9 */
545
+ }
assets/libraries/datetimepicker/jquery.datetimepicker.js CHANGED
@@ -1,2073 +1,2073 @@
1
- /**
2
- * @preserve jQuery DateTimePicker plugin v2.4.5
3
- * @homepage http://xdsoft.net/jqplugins/datetimepicker/
4
- * (c) 2014, Chupurnov Valeriy.
5
- */
6
- /*global document,window,jQuery,setTimeout,clearTimeout,HighlightedDate,getCurrentValue*/
7
- (function ($) {
8
- 'use strict';
9
- var default_options = {
10
- i18n: {
11
- ar: { // Arabic
12
- months: [
13
- "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول"
14
- ],
15
- dayOfWeek: [
16
- "ن", "ث", "ع", "خ", "ج", "س", "ح"
17
- ]
18
- },
19
- ro: { // Romanian
20
- months: [
21
- "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie"
22
- ],
23
- dayOfWeek: [
24
- "l", "ma", "mi", "j", "v", "s", "d"
25
- ]
26
- },
27
- id: { // Indonesian
28
- months: [
29
- "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"
30
- ],
31
- dayOfWeek: [
32
- "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"
33
- ]
34
- },
35
- is: { // Icelandic
36
- months: [
37
- "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"
38
- ],
39
- dayOfWeek: [
40
- "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau"
41
- ]
42
- },
43
- bg: { // Bulgarian
44
- months: [
45
- "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"
46
- ],
47
- dayOfWeek: [
48
- "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
49
- ]
50
- },
51
- fa: { // Persian/Farsi
52
- months: [
53
- 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'
54
- ],
55
- dayOfWeek: [
56
- 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'
57
- ]
58
- },
59
- ru: { // Russian
60
- months: [
61
- 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
62
- ],
63
- dayOfWeek: [
64
- "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
65
- ]
66
- },
67
- uk: { // Ukrainian
68
- months: [
69
- 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'
70
- ],
71
- dayOfWeek: [
72
- "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт"
73
- ]
74
- },
75
- en: { // English
76
- months: [
77
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
78
- ],
79
- dayOfWeek: [
80
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
81
- ]
82
- },
83
- el: { // Ελληνικά
84
- months: [
85
- "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"
86
- ],
87
- dayOfWeek: [
88
- "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"
89
- ]
90
- },
91
- de: { // German
92
- months: [
93
- 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
94
- ],
95
- dayOfWeek: [
96
- "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"
97
- ]
98
- },
99
- nl: { // Dutch
100
- months: [
101
- "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"
102
- ],
103
- dayOfWeek: [
104
- "zo", "ma", "di", "wo", "do", "vr", "za"
105
- ]
106
- },
107
- tr: { // Turkish
108
- months: [
109
- "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"
110
- ],
111
- dayOfWeek: [
112
- "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts"
113
- ]
114
- },
115
- fr: { //French
116
- months: [
117
- "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
118
- ],
119
- dayOfWeek: [
120
- "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"
121
- ]
122
- },
123
- es: { // Spanish
124
- months: [
125
- "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
126
- ],
127
- dayOfWeek: [
128
- "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"
129
- ]
130
- },
131
- th: { // Thai
132
- months: [
133
- 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'
134
- ],
135
- dayOfWeek: [
136
- 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'
137
- ]
138
- },
139
- pl: { // Polish
140
- months: [
141
- "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień"
142
- ],
143
- dayOfWeek: [
144
- "nd", "pn", "wt", "śr", "cz", "pt", "sb"
145
- ]
146
- },
147
- pt: { // Portuguese
148
- months: [
149
- "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
150
- ],
151
- dayOfWeek: [
152
- "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"
153
- ]
154
- },
155
- ch: { // Simplified Chinese
156
- months: [
157
- "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
158
- ],
159
- dayOfWeek: [
160
- "日", "一", "二", "三", "四", "五", "六"
161
- ]
162
- },
163
- se: { // Swedish
164
- months: [
165
- "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
166
- ],
167
- dayOfWeek: [
168
- "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
169
- ]
170
- },
171
- kr: { // Korean
172
- months: [
173
- "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
174
- ],
175
- dayOfWeek: [
176
- "일", "월", "화", "수", "목", "금", "토"
177
- ]
178
- },
179
- it: { // Italian
180
- months: [
181
- "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"
182
- ],
183
- dayOfWeek: [
184
- "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"
185
- ]
186
- },
187
- da: { // Dansk
188
- months: [
189
- "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December"
190
- ],
191
- dayOfWeek: [
192
- "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
193
- ]
194
- },
195
- no: { // Norwegian
196
- months: [
197
- "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"
198
- ],
199
- dayOfWeek: [
200
- "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
201
- ]
202
- },
203
- ja: { // Japanese
204
- months: [
205
- "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"
206
- ],
207
- dayOfWeek: [
208
- "日", "月", "火", "水", "木", "金", "土"
209
- ]
210
- },
211
- vi: { // Vietnamese
212
- months: [
213
- "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"
214
- ],
215
- dayOfWeek: [
216
- "CN", "T2", "T3", "T4", "T5", "T6", "T7"
217
- ]
218
- },
219
- sl: { // Slovenščina
220
- months: [
221
- "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"
222
- ],
223
- dayOfWeek: [
224
- "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob"
225
- ]
226
- },
227
- cs: { // Čeština
228
- months: [
229
- "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"
230
- ],
231
- dayOfWeek: [
232
- "Ne", "Po", "Út", "St", "Čt", "Pá", "So"
233
- ]
234
- },
235
- hu: { // Hungarian
236
- months: [
237
- "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"
238
- ],
239
- dayOfWeek: [
240
- "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo"
241
- ]
242
- },
243
- az: { //Azerbaijanian (Azeri)
244
- months: [
245
- "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"
246
- ],
247
- dayOfWeek: [
248
- "B", "Be", "Ça", "Ç", "Ca", "C", "Ş"
249
- ]
250
- },
251
- bs: { //Bosanski
252
- months: [
253
- "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
254
- ],
255
- dayOfWeek: [
256
- "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
257
- ]
258
- },
259
- ca: { //Català
260
- months: [
261
- "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"
262
- ],
263
- dayOfWeek: [
264
- "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds"
265
- ]
266
- },
267
- 'en-GB': { //English (British)
268
- months: [
269
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
270
- ],
271
- dayOfWeek: [
272
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
273
- ]
274
- },
275
- et: { //"Eesti"
276
- months: [
277
- "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"
278
- ],
279
- dayOfWeek: [
280
- "P", "E", "T", "K", "N", "R", "L"
281
- ]
282
- },
283
- eu: { //Euskara
284
- months: [
285
- "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua"
286
- ],
287
- dayOfWeek: [
288
- "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La."
289
- ]
290
- },
291
- fi: { //Finnish (Suomi)
292
- months: [
293
- "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"
294
- ],
295
- dayOfWeek: [
296
- "Su", "Ma", "Ti", "Ke", "To", "Pe", "La"
297
- ]
298
- },
299
- gl: { //Galego
300
- months: [
301
- "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec"
302
- ],
303
- dayOfWeek: [
304
- "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab"
305
- ]
306
- },
307
- hr: { //Hrvatski
308
- months: [
309
- "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"
310
- ],
311
- dayOfWeek: [
312
- "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
313
- ]
314
- },
315
- ko: { //Korean (한국어)
316
- months: [
317
- "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
318
- ],
319
- dayOfWeek: [
320
- "일", "월", "화", "수", "목", "금", "토"
321
- ]
322
- },
323
- lt: { //Lithuanian (lietuvių)
324
- months: [
325
- "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio"
326
- ],
327
- dayOfWeek: [
328
- "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš"
329
- ]
330
- },
331
- lv: { //Latvian (Latviešu)
332
- months: [
333
- "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"
334
- ],
335
- dayOfWeek: [
336
- "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St"
337
- ]
338
- },
339
- mk: { //Macedonian (Македонски)
340
- months: [
341
- "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември"
342
- ],
343
- dayOfWeek: [
344
- "нед", "пон", "вто", "сре", "чет", "пет", "саб"
345
- ]
346
- },
347
- mn: { //Mongolian (Монгол)
348
- months: [
349
- "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар"
350
- ],
351
- dayOfWeek: [
352
- "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням"
353
- ]
354
- },
355
- 'pt-BR': { //Português(Brasil)
356
- months: [
357
- "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
358
- ],
359
- dayOfWeek: [
360
- "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"
361
- ]
362
- },
363
- sk: { //Slovenčina
364
- months: [
365
- "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"
366
- ],
367
- dayOfWeek: [
368
- "Ne", "Po", "Ut", "St", "Št", "Pi", "So"
369
- ]
370
- },
371
- sq: { //Albanian (Shqip)
372
- months: [
373
- "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
374
- ],
375
- dayOfWeek: [
376
- "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
377
- ]
378
- },
379
- 'sr-YU': { //Serbian (Srpski)
380
- months: [
381
- "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
382
- ],
383
- dayOfWeek: [
384
- "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub"
385
- ]
386
- },
387
- sr: { //Serbian Cyrillic (Српски)
388
- months: [
389
- "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар"
390
- ],
391
- dayOfWeek: [
392
- "нед", "пон", "уто", "сре", "чет", "пет", "суб"
393
- ]
394
- },
395
- sv: { //Svenska
396
- months: [
397
- "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
398
- ],
399
- dayOfWeek: [
400
- "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
401
- ]
402
- },
403
- 'zh-TW': { //Traditional Chinese (繁體中文)
404
- months: [
405
- "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
406
- ],
407
- dayOfWeek: [
408
- "日", "一", "二", "三", "四", "五", "六"
409
- ]
410
- },
411
- zh: { //Simplified Chinese (简体中文)
412
- months: [
413
- "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
414
- ],
415
- dayOfWeek: [
416
- "日", "一", "二", "三", "四", "五", "六"
417
- ]
418
- },
419
- he: { //Hebrew (עברית)
420
- months: [
421
- 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'
422
- ],
423
- dayOfWeek: [
424
- 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת'
425
- ]
426
- },
427
- hy: { // Armenian
428
- months: [
429
- "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր"
430
- ],
431
- dayOfWeek: [
432
- "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ"
433
- ]
434
- },
435
- kg: { // Kyrgyz
436
- months: [
437
- 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы'
438
- ],
439
- dayOfWeek: [
440
- "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише"
441
- ]
442
- }
443
- },
444
- value: '',
445
- lang: 'en',
446
-
447
- format: 'Y/m/d H:i',
448
- formatTime: 'H:i',
449
- formatDate: 'Y/m/d',
450
-
451
- startDate: false, // new Date(), '1986/12/08', '-1970/01/05','-1970/01/05',
452
- step: 60,
453
- monthChangeSpinner: true,
454
-
455
- closeOnDateSelect: false,
456
- closeOnTimeSelect: true,
457
- closeOnWithoutClick: true,
458
- closeOnInputClick: true,
459
-
460
- timepicker: true,
461
- datepicker: true,
462
- weeks: false,
463
-
464
- defaultTime: false, // use formatTime format (ex. '10:00' for formatTime: 'H:i')
465
- defaultDate: false, // use formatDate format (ex new Date() or '1986/12/08' or '-1970/01/05' or '-1970/01/05')
466
-
467
- minDate: false,
468
- maxDate: false,
469
- minTime: false,
470
- maxTime: false,
471
- disabledMinTime: false,
472
- disabledMaxTime: false,
473
-
474
- allowTimes: [],
475
- opened: false,
476
- initTime: true,
477
- inline: false,
478
- theme: '',
479
-
480
- onSelectDate: function () {},
481
- onSelectTime: function () {},
482
- onChangeMonth: function () {},
483
- onChangeYear: function () {},
484
- onChangeDateTime: function () {},
485
- onShow: function () {},
486
- onClose: function () {},
487
- onGenerate: function () {},
488
-
489
- withoutCopyright: true,
490
- inverseButton: false,
491
- hours12: false,
492
- next: 'xdsoft_next',
493
- prev : 'xdsoft_prev',
494
- dayOfWeekStart: 0,
495
- parentID: 'body',
496
- timeHeightInTimePicker: 25,
497
- timepickerScrollbar: true,
498
- todayButton: true,
499
- prevButton: true,
500
- nextButton: true,
501
- defaultSelect: true,
502
-
503
- scrollMonth: true,
504
- scrollTime: true,
505
- scrollInput: true,
506
-
507
- lazyInit: false,
508
- mask: false,
509
- validateOnBlur: true,
510
- allowBlank: true,
511
- yearStart: 1950,
512
- yearEnd: 2050,
513
- monthStart: 0,
514
- monthEnd: 11,
515
- style: '',
516
- id: '',
517
- fixed: false,
518
- roundTime: 'round', // ceil, floor
519
- className: '',
520
- weekends: [],
521
- highlightedDates: [],
522
- highlightedPeriods: [],
523
- disabledDates : [],
524
- disabledWeekDays: [],
525
- yearOffset: 0,
526
- beforeShowDay: null,
527
-
528
- enterLikeTab: true,
529
- showApplyButton: false
530
- };
531
- // fix for ie8
532
- if (!window.getComputedStyle) {
533
- window.getComputedStyle = function (el, pseudo) {
534
- this.el = el;
535
- this.getPropertyValue = function (prop) {
536
- var re = /(\-([a-z]){1})/g;
537
- if (prop === 'float') {
538
- prop = 'styleFloat';
539
- }
540
- if (re.test(prop)) {
541
- prop = prop.replace(re, function (a, b, c) {
542
- return c.toUpperCase();
543
- });
544
- }
545
- return el.currentStyle[prop] || null;
546
- };
547
- return this;
548
- };
549
- }
550
- if (!Array.prototype.indexOf) {
551
- Array.prototype.indexOf = function (obj, start) {
552
- var i, j;
553
- for (i = (start || 0), j = this.length; i < j; i += 1) {
554
- if (this[i] === obj) { return i; }
555
- }
556
- return -1;
557
- };
558
- }
559
- Date.prototype.countDaysInMonth = function () {
560
- return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate();
561
- };
562
- $.fn.xdsoftScroller = function (percent) {
563
- return this.each(function () {
564
- var timeboxparent = $(this),
565
- pointerEventToXY = function (e) {
566
- var out = {x: 0, y: 0},
567
- touch;
568
- if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') {
569
- touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
570
- out.x = touch.clientX;
571
- out.y = touch.clientY;
572
- } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'mousemove' || e.type === 'mouseover' || e.type === 'mouseout' || e.type === 'mouseenter' || e.type === 'mouseleave') {
573
- out.x = e.clientX;
574
- out.y = e.clientY;
575
- }
576
- return out;
577
- },
578
- move = 0,
579
- timebox,
580
- parentHeight,
581
- height,
582
- scrollbar,
583
- scroller,
584
- maximumOffset = 100,
585
- start = false,
586
- startY = 0,
587
- startTop = 0,
588
- h1 = 0,
589
- touchStart = false,
590
- startTopScroll = 0,
591
- calcOffset = function () {};
592
- if (percent === 'hide') {
593
- timeboxparent.find('.xdsoft_scrollbar').hide();
594
- return;
595
- }
596
- if (!$(this).hasClass('xdsoft_scroller_box')) {
597
- timebox = timeboxparent.children().eq(0);
598
- parentHeight = timeboxparent[0].clientHeight;
599
- height = timebox[0].offsetHeight;
600
- scrollbar = $('<div class="xdsoft_scrollbar"></div>');
601
- scroller = $('<div class="xdsoft_scroller"></div>');
602
- scrollbar.append(scroller);
603
-
604
- timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar);
605
- calcOffset = function calcOffset(event) {
606
- var offset = pointerEventToXY(event).y - startY + startTopScroll;
607
- if (offset < 0) {
608
- offset = 0;
609
- }
610
- if (offset + scroller[0].offsetHeight > h1) {
611
- offset = h1 - scroller[0].offsetHeight;
612
- }
613
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]);
614
- };
615
-
616
- scroller
617
- .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) {
618
- if (!parentHeight) {
619
- timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
620
- }
621
-
622
- startY = pointerEventToXY(event).y;
623
- startTopScroll = parseInt(scroller.css('margin-top'), 10);
624
- h1 = scrollbar[0].offsetHeight;
625
-
626
- if (event.type === 'mousedown') {
627
- if (document) {
628
- $(document.body).addClass('xdsoft_noselect');
629
- }
630
- $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() {
631
- $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee)
632
- .off('mousemove.xdsoft_scroller', calcOffset)
633
- .removeClass('xdsoft_noselect');
634
- });
635
- $(document.body).on('mousemove.xdsoft_scroller', calcOffset);
636
- } else {
637
- touchStart = true;
638
- event.stopPropagation();
639
- event.preventDefault();
640
- }
641
- })
642
- .on('touchmove', function (event) {
643
- if (touchStart) {
644
- event.preventDefault();
645
- calcOffset(event);
646
- }
647
- })
648
- .on('touchend touchcancel', function (event) {
649
- touchStart = false;
650
- startTopScroll = 0;
651
- });
652
-
653
- timeboxparent
654
- .on('scroll_element.xdsoft_scroller', function (event, percentage) {
655
- if (!parentHeight) {
656
- timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]);
657
- }
658
- percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage;
659
-
660
- scroller.css('margin-top', maximumOffset * percentage);
661
-
662
- setTimeout(function () {
663
- timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10));
664
- }, 10);
665
- })
666
- .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) {
667
- var percent, sh;
668
- parentHeight = timeboxparent[0].clientHeight;
669
- height = timebox[0].offsetHeight;
670
- percent = parentHeight / height;
671
- sh = percent * scrollbar[0].offsetHeight;
672
- if (percent > 1) {
673
- scroller.hide();
674
- } else {
675
- scroller.show();
676
- scroller.css('height', parseInt(sh > 10 ? sh : 10, 10));
677
- maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight;
678
- if (noTriggerScroll !== true) {
679
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]);
680
- }
681
- }
682
- });
683
-
684
- timeboxparent.on('mousewheel', function (event) {
685
- var top = Math.abs(parseInt(timebox.css('marginTop'), 10));
686
-
687
- top = top - (event.deltaY * 20);
688
- if (top < 0) {
689
- top = 0;
690
- }
691
-
692
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]);
693
- event.stopPropagation();
694
- return false;
695
- });
696
-
697
- timeboxparent.on('touchstart', function (event) {
698
- start = pointerEventToXY(event);
699
- startTop = Math.abs(parseInt(timebox.css('marginTop'), 10));
700
- });
701
-
702
- timeboxparent.on('touchmove', function (event) {
703
- if (start) {
704
- event.preventDefault();
705
- var coord = pointerEventToXY(event);
706
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]);
707
- }
708
- });
709
-
710
- timeboxparent.on('touchend touchcancel', function (event) {
711
- start = false;
712
- startTop = 0;
713
- });
714
- }
715
- timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
716
- });
717
- };
718
-
719
- $.fn.datetimepicker = function (opt) {
720
- var KEY0 = 48,
721
- KEY9 = 57,
722
- _KEY0 = 96,
723
- _KEY9 = 105,
724
- CTRLKEY = 17,
725
- DEL = 46,
726
- ENTER = 13,
727
- ESC = 27,
728
- BACKSPACE = 8,
729
- ARROWLEFT = 37,
730
- ARROWUP = 38,
731
- ARROWRIGHT = 39,
732
- ARROWDOWN = 40,
733
- TAB = 9,
734
- F5 = 116,
735
- AKEY = 65,
736
- CKEY = 67,
737
- VKEY = 86,
738
- ZKEY = 90,
739
- YKEY = 89,
740
- ctrlDown = false,
741
- options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options),
742
-
743
- lazyInitTimer = 0,
744
- createDateTimePicker,
745
- destroyDateTimePicker,
746
-
747
- lazyInit = function (input) {
748
- input
749
- .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) {
750
- if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) {
751
- return;
752
- }
753
- clearTimeout(lazyInitTimer);
754
- lazyInitTimer = setTimeout(function () {
755
-
756
- if (!input.data('xdsoft_datetimepicker')) {
757
- createDateTimePicker(input);
758
- }
759
- input
760
- .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback)
761
- .trigger('open.xdsoft');
762
- }, 100);
763
- });
764
- };
765
-
766
- createDateTimePicker = function (input) {
767
- var datetimepicker = $('<div class="xdsoft_datetimepicker xdsoft_noselect"></div>'),
768
- xdsoft_copyright = $('<div class="xdsoft_copyright"><a target="_blank" href="http://xdsoft.net/jqplugins/datetimepicker/">xdsoft.net</a></div>'),
769
- datepicker = $('<div class="xdsoft_datepicker active"></div>'),
770
- mounth_picker = $('<div class="xdsoft_mounthpicker"><button type="button" class="xdsoft_prev"></button><button type="button" class="xdsoft_today_button"></button>' +
771
- '<div class="xdsoft_label xdsoft_month"><span></span><i></i></div>' +
772
- '<div class="xdsoft_label xdsoft_year"><span></span><i></i></div>' +
773
- '<button type="button" class="xdsoft_next"></button></div>'),
774
- calendar = $('<div class="xdsoft_calendar"></div>'),
775
- timepicker = $('<div class="xdsoft_timepicker active"><button type="button" class="xdsoft_prev"></button><div class="xdsoft_time_box"></div><button type="button" class="xdsoft_next"></button></div>'),
776
- timeboxparent = timepicker.find('.xdsoft_time_box').eq(0),
777
- timebox = $('<div class="xdsoft_time_variant"></div>'),
778
- applyButton = $('<button type="button" class="xdsoft_save_selected blue-gradient-button">Save Selected</button>'),
779
- /*scrollbar = $('<div class="xdsoft_scrollbar"></div>'),
780
- scroller = $('<div class="xdsoft_scroller"></div>'),*/
781
- monthselect = $('<div class="xdsoft_select xdsoft_monthselect"><div></div></div>'),
782
- yearselect = $('<div class="xdsoft_select xdsoft_yearselect"><div></div></div>'),
783
- triggerAfterOpen = false,
784
- XDSoft_datetime,
785
- //scroll_element,
786
- xchangeTimer,
787
- timerclick,
788
- current_time_index,
789
- setPos,
790
- timer = 0,
791
- timer1 = 0,
792
- _xdsoft_datetime;
793
-
794
- if (options.id) {
795
- datetimepicker.attr('id', options.id);
796
- }
797
- if (options.style) {
798
- datetimepicker.attr('style', options.style);
799
- }
800
- if (options.weeks) {
801
- datetimepicker.addClass('xdsoft_showweeks');
802
- }
803
-
804
- datetimepicker.addClass('xdsoft_' + options.theme);
805
- datetimepicker.addClass(options.className);
806
-
807
- mounth_picker
808
- .find('.xdsoft_month span')
809
- .after(monthselect);
810
- mounth_picker
811
- .find('.xdsoft_year span')
812
- .after(yearselect);
813
-
814
- mounth_picker
815
- .find('.xdsoft_month,.xdsoft_year')
816
- .on('mousedown.xdsoft', function (event) {
817
- var select = $(this).find('.xdsoft_select').eq(0),
818
- val = 0,
819
- top = 0,
820
- visible = select.is(':visible'),
821
- items,
822
- i;
823
-
824
- mounth_picker
825
- .find('.xdsoft_select')
826
- .hide();
827
- if (_xdsoft_datetime.currentTime) {
828
- val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear']();
829
- }
830
-
831
- select[visible ? 'hide' : 'show']();
832
- for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) {
833
- if (items.eq(i).data('value') === val) {
834
- break;
835
- } else {
836
- top += items[0].offsetHeight;
837
- }
838
- }
839
-
840
- select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight)));
841
- event.stopPropagation();
842
- return false;
843
- });
844
-
845
- mounth_picker
846
- .find('.xdsoft_select')
847
- .xdsoftScroller()
848
- .on('mousedown.xdsoft', function (event) {
849
- event.stopPropagation();
850
- event.preventDefault();
851
- })
852
- .on('mousedown.xdsoft', '.xdsoft_option', function (event) {
853
-
854
- if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
855
- _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
856
- }
857
-
858
- var year = _xdsoft_datetime.currentTime.getFullYear();
859
- if (_xdsoft_datetime && _xdsoft_datetime.currentTime) {
860
- _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value'));
861
- }
862
-
863
- $(this).parent().parent().hide();
864
-
865
- datetimepicker.trigger('xchange.xdsoft');
866
- if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
867
- options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
868
- }
869
-
870
- if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
871
- options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
872
- }
873
- });
874
-
875
- datetimepicker.setOptions = function (_options) {
876
- var highlightedDates = {},
877
- getCaretPos = function (input) {
878
- try {
879
- if (document.selection && document.selection.createRange) {
880
- var range = document.selection.createRange();
881
- return range.getBookmark().charCodeAt(2) - 2;
882
- }
883
- if (input.setSelectionRange) {
884
- return input.selectionStart;
885
- }
886
- } catch (e) {
887
- return 0;
888
- }
889
- },
890
- setCaretPos = function (node, pos) {
891
- node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node;
892
- if (!node) {
893
- return false;
894
- }
895
- if (node.createTextRange) {
896
- var textRange = node.createTextRange();
897
- textRange.collapse(true);
898
- textRange.moveEnd('character', pos);
899
- textRange.moveStart('character', pos);
900
- textRange.select();
901
- return true;
902
- }
903
- if (node.setSelectionRange) {
904
- node.setSelectionRange(pos, pos);
905
- return true;
906
- }
907
- return false;
908
- },
909
- isValidValue = function (mask, value) {
910
- var reg = mask
911
- .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1')
912
- .replace(/_/g, '{digit+}')
913
- .replace(/([0-9]{1})/g, '{digit$1}')
914
- .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}')
915
- .replace(/\{digit[\+]\}/g, '[0-9_]{1}');
916
- return (new RegExp(reg)).test(value);
917
- };
918
- options = $.extend(true, {}, options, _options);
919
-
920
- if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) {
921
- options.allowTimes = $.extend(true, [], _options.allowTimes);
922
- }
923
-
924
- if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) {
925
- options.weekends = $.extend(true, [], _options.weekends);
926
- }
927
-
928
- if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) {
929
- $.each(_options.highlightedDates, function (index, value) {
930
- var splitData = $.map(value.split(','), $.trim),
931
- exDesc,
932
- hDate = new HighlightedDate(Date.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style
933
- keyDate = hDate.date.dateFormat(options.formatDate);
934
- if (highlightedDates[keyDate] !== undefined) {
935
- exDesc = highlightedDates[keyDate].desc;
936
- if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
937
- highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
938
- }
939
- } else {
940
- highlightedDates[keyDate] = hDate;
941
- }
942
- });
943
-
944
- options.highlightedDates = $.extend(true, [], highlightedDates);
945
- }
946
-
947
- if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) {
948
- highlightedDates = $.extend(true, [], options.highlightedDates);
949
- $.each(_options.highlightedPeriods, function (index, value) {
950
- var splitData = $.map(value.split(','), $.trim),
951
- dateTest = Date.parseDate(splitData[0], options.formatDate), // start date
952
- dateEnd = Date.parseDate(splitData[1], options.formatDate),
953
- desc = splitData[2],
954
- hDate,
955
- keyDate,
956
- exDesc,
957
- style = splitData[3];
958
-
959
- while (dateTest <= dateEnd) {
960
- hDate = new HighlightedDate(dateTest, desc, style);
961
- keyDate = dateTest.dateFormat(options.formatDate);
962
- dateTest.setDate(dateTest.getDate() + 1);
963
- if (highlightedDates[keyDate] !== undefined) {
964
- exDesc = highlightedDates[keyDate].desc;
965
- if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
966
- highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
967
- }
968
- } else {
969
- highlightedDates[keyDate] = hDate;
970
- }
971
- }
972
- });
973
-
974
- options.highlightedDates = $.extend(true, [], highlightedDates);
975
- }
976
-
977
- if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) {
978
- options.disabledDates = $.extend(true, [], _options.disabledDates);
979
- }
980
-
981
- if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) {
982
- options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays);
983
- }
984
-
985
- if ((options.open || options.opened) && (!options.inline)) {
986
- input.trigger('open.xdsoft');
987
- }
988
-
989
- if (options.inline) {
990
- triggerAfterOpen = true;
991
- datetimepicker.addClass('xdsoft_inline');
992
- input.after(datetimepicker).hide();
993
- }
994
-
995
- if (options.inverseButton) {
996
- options.next = 'xdsoft_prev';
997
- options.prev = 'xdsoft_next';
998
- }
999
-
1000
- if (options.datepicker) {
1001
- datepicker.addClass('active');
1002
- } else {
1003
- datepicker.removeClass('active');
1004
- }
1005
-
1006
- if (options.timepicker) {
1007
- timepicker.addClass('active');
1008
- } else {
1009
- timepicker.removeClass('active');
1010
- }
1011
-
1012
- if (options.value) {
1013
- _xdsoft_datetime.setCurrentTime(options.value);
1014
- if (input && input.val) {
1015
- input.val(_xdsoft_datetime.str);
1016
- }
1017
- }
1018
-
1019
- if (isNaN(options.dayOfWeekStart)) {
1020
- options.dayOfWeekStart = 0;
1021
- } else {
1022
- options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7;
1023
- }
1024
-
1025
- if (!options.timepickerScrollbar) {
1026
- timeboxparent.xdsoftScroller('hide');
1027
- }
1028
-
1029
- if (options.minDate && /^-(.*)$/.test(options.minDate)) {
1030
- options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate);
1031
- }
1032
-
1033
- if (options.maxDate && /^\+(.*)$/.test(options.maxDate)) {
1034
- options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate);
1035
- }
1036
-
1037
- applyButton.toggle(options.showApplyButton);
1038
-
1039
- mounth_picker
1040
- .find('.xdsoft_today_button')
1041
- .css('visibility', !options.todayButton ? 'hidden' : 'visible');
1042
-
1043
- mounth_picker
1044
- .find('.' + options.prev)
1045
- .css('visibility', !options.prevButton ? 'hidden' : 'visible');
1046
-
1047
- mounth_picker
1048
- .find('.' + options.next)
1049
- .css('visibility', !options.nextButton ? 'hidden' : 'visible');
1050
-
1051
- if (options.mask) {
1052
- input.off('keydown.xdsoft');
1053
-
1054
- if (options.mask === true) {
1055
- options.mask = options.format
1056
- .replace(/Y/g, '9999')
1057
- .replace(/F/g, '9999')
1058
- .replace(/m/g, '19')
1059
- .replace(/d/g, '39')
1060
- .replace(/H/g, '29')
1061
- .replace(/i/g, '59')
1062
- .replace(/s/g, '59');
1063
- }
1064
-
1065
- if ($.type(options.mask) === 'string') {
1066
- if (!isValidValue(options.mask, input.val())) {
1067
- input.val(options.mask.replace(/[0-9]/g, '_'));
1068
- }
1069
-
1070
- input.on('keydown.xdsoft', function (event) {
1071
- var val = this.value,
1072
- key = event.which,
1073
- pos,
1074
- digit;
1075
-
1076
- if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) {
1077
- pos = getCaretPos(this);
1078
- digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_';
1079
-
1080
- if ((key === BACKSPACE || key === DEL) && pos) {
1081
- pos -= 1;
1082
- digit = '_';
1083
- }
1084
-
1085
- while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
1086
- pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
1087
- }
1088
-
1089
- val = val.substr(0, pos) + digit + val.substr(pos + 1);
1090
- if ($.trim(val) === '') {
1091
- val = options.mask.replace(/[0-9]/g, '_');
1092
- } else {
1093
- if (pos === options.mask.length) {
1094
- event.preventDefault();
1095
- return false;
1096
- }
1097
- }
1098
-
1099
- pos += (key === BACKSPACE || key === DEL) ? 0 : 1;
1100
- while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
1101
- pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
1102
- }
1103
-
1104
- if (isValidValue(options.mask, val)) {
1105
- this.value = val;
1106
- setCaretPos(this, pos);
1107
- } else if ($.trim(val) === '') {
1108
- this.value = options.mask.replace(/[0-9]/g, '_');
1109
- } else {
1110
- input.trigger('error_input.xdsoft');
1111
- }
1112
- } else {
1113
- if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) {
1114
- return true;
1115
- }
1116
- }
1117
-
1118
- event.preventDefault();
1119
- return false;
1120
- });
1121
- }
1122
- }
1123
- if (options.validateOnBlur) {
1124
- input
1125
- .off('blur.xdsoft')
1126
- .on('blur.xdsoft', function () {
1127
- if (options.allowBlank && !$.trim($(this).val()).length) {
1128
- $(this).val(null);
1129
- datetimepicker.data('xdsoft_datetime').empty();
1130
- } else if (!Date.parseDate($(this).val(), options.format)) {
1131
- var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')),
1132
- splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join(''));
1133
-
1134
- // parse the numbers as 0312 => 03:12
1135
- if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) {
1136
- $(this).val([splittedHours, splittedMinutes].map(function (item) {
1137
- return item > 9 ? item : '0' + item;
1138
- }).join(':'));
1139
- } else {
1140
- $(this).val((_xdsoft_datetime.now()).dateFormat(options.format));
1141
- }
1142
-
1143
- datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val());
1144
- } else {
1145
- datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val());
1146
- }
1147
-
1148
- datetimepicker.trigger('changedatetime.xdsoft');
1149
- });
1150
- }
1151
- options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1;
1152
-
1153
- datetimepicker
1154
- .trigger('xchange.xdsoft')
1155
- .trigger('afterOpen.xdsoft');
1156
- };
1157
-
1158
- datetimepicker
1159
- .data('options', options)
1160
- .on('mousedown.xdsoft', function (event) {
1161
- event.stopPropagation();
1162
- event.preventDefault();
1163
- yearselect.hide();
1164
- monthselect.hide();
1165
- return false;
1166
- });
1167
-
1168
- //scroll_element = timepicker.find('.xdsoft_time_box');
1169
- timeboxparent.append(timebox);
1170
- timeboxparent.xdsoftScroller();
1171
-
1172
- datetimepicker.on('afterOpen.xdsoft', function () {
1173
- timeboxparent.xdsoftScroller();
1174
- });
1175
-
1176
- datetimepicker
1177
- .append(datepicker)
1178
- .append(timepicker);
1179
-
1180
- if (options.withoutCopyright !== true) {
1181
- datetimepicker
1182
- .append(xdsoft_copyright);
1183
- }
1184
-
1185
- datepicker
1186
- .append(mounth_picker)
1187
- .append(calendar)
1188
- .append(applyButton);
1189
-
1190
- $(options.parentID)
1191
- .append(datetimepicker);
1192
-
1193
- XDSoft_datetime = function () {
1194
- var _this = this;
1195
- _this.now = function (norecursion) {
1196
- var d = new Date(),
1197
- date,
1198
- time;
1199
-
1200
- if (!norecursion && options.defaultDate) {
1201
- date = _this.strToDateTime(options.defaultDate);
1202
- d.setFullYear(date.getFullYear());
1203
- d.setMonth(date.getMonth());
1204
- d.setDate(date.getDate());
1205
- }
1206
-
1207
- if (options.yearOffset) {
1208
- d.setFullYear(d.getFullYear() + options.yearOffset);
1209
- }
1210
-
1211
- if (!norecursion && options.defaultTime) {
1212
- time = _this.strtotime(options.defaultTime);
1213
- d.setHours(time.getHours());
1214
- d.setMinutes(time.getMinutes());
1215
- }
1216
- return d;
1217
- };
1218
-
1219
- _this.isValidDate = function (d) {
1220
- if (Object.prototype.toString.call(d) !== "[object Date]") {
1221
- return false;
1222
- }
1223
- return !isNaN(d.getTime());
1224
- };
1225
-
1226
- _this.setCurrentTime = function (dTime) {
1227
- _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now();
1228
- datetimepicker.trigger('xchange.xdsoft');
1229
- };
1230
-
1231
- _this.empty = function () {
1232
- _this.currentTime = null;
1233
- };
1234
-
1235
- _this.getCurrentTime = function (dTime) {
1236
- return _this.currentTime;
1237
- };
1238
-
1239
- _this.nextMonth = function () {
1240
-
1241
- if (_this.currentTime === undefined || _this.currentTime === null) {
1242
- _this.currentTime = _this.now();
1243
- }
1244
-
1245
- var month = _this.currentTime.getMonth() + 1,
1246
- year;
1247
- if (month === 12) {
1248
- _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1);
1249
- month = 0;
1250
- }
1251
-
1252
- year = _this.currentTime.getFullYear();
1253
-
1254
- _this.currentTime.setDate(
1255
- Math.min(
1256
- new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
1257
- _this.currentTime.getDate()
1258
- )
1259
- );
1260
- _this.currentTime.setMonth(month);
1261
-
1262
- if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
1263
- options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1264
- }
1265
-
1266
- if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
1267
- options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1268
- }
1269
-
1270
- datetimepicker.trigger('xchange.xdsoft');
1271
- return month;
1272
- };
1273
-
1274
- _this.prevMonth = function () {
1275
-
1276
- if (_this.currentTime === undefined || _this.currentTime === null) {
1277
- _this.currentTime = _this.now();
1278
- }
1279
-
1280
- var month = _this.currentTime.getMonth() - 1;
1281
- if (month === -1) {
1282
- _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1);
1283
- month = 11;
1284
- }
1285
- _this.currentTime.setDate(
1286
- Math.min(
1287
- new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
1288
- _this.currentTime.getDate()
1289
- )
1290
- );
1291
- _this.currentTime.setMonth(month);
1292
- if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
1293
- options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1294
- }
1295
- datetimepicker.trigger('xchange.xdsoft');
1296
- return month;
1297
- };
1298
-
1299
- _this.getWeekOfYear = function (datetime) {
1300
- var onejan = new Date(datetime.getFullYear(), 0, 1);
1301
- return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7);
1302
- };
1303
-
1304
- _this.strToDateTime = function (sDateTime) {
1305
- var tmpDate = [], timeOffset, currentTime;
1306
-
1307
- if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) {
1308
- return sDateTime;
1309
- }
1310
-
1311
- tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime);
1312
- if (tmpDate) {
1313
- tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate);
1314
- }
1315
- if (tmpDate && tmpDate[2]) {
1316
- timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000;
1317
- currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset);
1318
- } else {
1319
- currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now();
1320
- }
1321
-
1322
- if (!_this.isValidDate(currentTime)) {
1323
- currentTime = _this.now();
1324
- }
1325
-
1326
- return currentTime;
1327
- };
1328
-
1329
- _this.strToDate = function (sDate) {
1330
- if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) {
1331
- return sDate;
1332
- }
1333
-
1334
- var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true);
1335
- if (!_this.isValidDate(currentTime)) {
1336
- currentTime = _this.now(true);
1337
- }
1338
- return currentTime;
1339
- };
1340
-
1341
- _this.strtotime = function (sTime) {
1342
- if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) {
1343
- return sTime;
1344
- }
1345
- var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true);
1346
- if (!_this.isValidDate(currentTime)) {
1347
- currentTime = _this.now(true);
1348
- }
1349
- return currentTime;
1350
- };
1351
-
1352
- _this.str = function () {
1353
- return _this.currentTime.dateFormat(options.format);
1354
- };
1355
- _this.currentTime = this.now();
1356
- };
1357
-
1358
- _xdsoft_datetime = new XDSoft_datetime();
1359
-
1360
- applyButton.on('click', function (e) {//pathbrite
1361
- e.preventDefault();
1362
- datetimepicker.data('changed', true);
1363
- _xdsoft_datetime.setCurrentTime(getCurrentValue());
1364
- input.val(_xdsoft_datetime.str());
1365
- datetimepicker.trigger('close.xdsoft');
1366
- });
1367
- mounth_picker
1368
- .find('.xdsoft_today_button')
1369
- .on('mousedown.xdsoft', function () {
1370
- datetimepicker.data('changed', true);
1371
- _xdsoft_datetime.setCurrentTime(0);
1372
- datetimepicker.trigger('afterOpen.xdsoft');
1373
- }).on('dblclick.xdsoft', function () {
1374
- var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate;
1375
- currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
1376
- minDate = _xdsoft_datetime.strToDate(options.minDate);
1377
- minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
1378
- if (currentDate < minDate) {
1379
- return;
1380
- }
1381
- maxDate = _xdsoft_datetime.strToDate(options.maxDate);
1382
- maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate());
1383
- if (currentDate > maxDate) {
1384
- return;
1385
- }
1386
- input.val(_xdsoft_datetime.str());
1387
- datetimepicker.trigger('close.xdsoft');
1388
- });
1389
- mounth_picker
1390
- .find('.xdsoft_prev,.xdsoft_next')
1391
- .on('mousedown.xdsoft', function () {
1392
- var $this = $(this),
1393
- timer = 0,
1394
- stop = false;
1395
-
1396
- (function arguments_callee1(v) {
1397
- if ($this.hasClass(options.next)) {
1398
- _xdsoft_datetime.nextMonth();
1399
- } else if ($this.hasClass(options.prev)) {
1400
- _xdsoft_datetime.prevMonth();
1401
- }
1402
- if (options.monthChangeSpinner) {
1403
- if (!stop) {
1404
- timer = setTimeout(arguments_callee1, v || 100);
1405
- }
1406
- }
1407
- }(500));
1408
-
1409
- $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() {
1410
- clearTimeout(timer);
1411
- stop = true;
1412
- $([document.body, window]).off('mouseup.xdsoft', arguments_callee2);
1413
- });
1414
- });
1415
-
1416
- timepicker
1417
- .find('.xdsoft_prev,.xdsoft_next')
1418
- .on('mousedown.xdsoft', function () {
1419
- var $this = $(this),
1420
- timer = 0,
1421
- stop = false,
1422
- period = 110;
1423
- (function arguments_callee4(v) {
1424
- var pheight = timeboxparent[0].clientHeight,
1425
- height = timebox[0].offsetHeight,
1426
- top = Math.abs(parseInt(timebox.css('marginTop'), 10));
1427
- if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) {
1428
- timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px');
1429
- } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) {
1430
- timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px');
1431
- }
1432
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]);
1433
- period = (period > 10) ? 10 : period - 10;
1434
- if (!stop) {
1435
- timer = setTimeout(arguments_callee4, v || period);
1436
- }
1437
- }(500));
1438
- $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() {
1439
- clearTimeout(timer);
1440
- stop = true;
1441
- $([document.body, window])
1442
- .off('mouseup.xdsoft', arguments_callee5);
1443
- });
1444
- });
1445
-
1446
- xchangeTimer = 0;
1447
- // base handler - generating a calendar and timepicker
1448
- datetimepicker
1449
- .on('xchange.xdsoft', function (event) {
1450
- clearTimeout(xchangeTimer);
1451
- xchangeTimer = setTimeout(function () {
1452
-
1453
- if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
1454
- _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1455
- }
1456
-
1457
- var table = '',
1458
- start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0),
1459
- i = 0,
1460
- j,
1461
- today = _xdsoft_datetime.now(),
1462
- maxDate = false,
1463
- minDate = false,
1464
- hDate,
1465
- day,
1466
- d,
1467
- y,
1468
- m,
1469
- w,
1470
- classes = [],
1471
- customDateSettings,
1472
- newRow = true,
1473
- time = '',
1474
- h = '',
1475
- line_time,
1476
- description;
1477
-
1478
- while (start.getDay() !== options.dayOfWeekStart) {
1479
- start.setDate(start.getDate() - 1);
1480
- }
1481
-
1482
- table += '<table><thead><tr>';
1483
-
1484
- if (options.weeks) {
1485
- table += '<th></th>';
1486
- }
1487
-
1488
- for (j = 0; j < 7; j += 1) {
1489
- table += '<th>' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + '</th>';
1490
- }
1491
-
1492
- table += '</tr></thead>';
1493
- table += '<tbody>';
1494
-
1495
- if (options.maxDate !== false) {
1496
- maxDate = _xdsoft_datetime.strToDate(options.maxDate);
1497
- maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999);
1498
- }
1499
-
1500
- if (options.minDate !== false) {
1501
- minDate = _xdsoft_datetime.strToDate(options.minDate);
1502
- minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
1503
- }
1504
-
1505
- while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) {
1506
- classes = [];
1507
- i += 1;
1508
-
1509
- day = start.getDay();
1510
- d = start.getDate();
1511
- y = start.getFullYear();
1512
- m = start.getMonth();
1513
- w = _xdsoft_datetime.getWeekOfYear(start);
1514
- description = '';
1515
-
1516
- classes.push('xdsoft_date');
1517
-
1518
- if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) {
1519
- customDateSettings = options.beforeShowDay.call(datetimepicker, start);
1520
- } else {
1521
- customDateSettings = null;
1522
- }
1523
-
1524
- if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) {
1525
- classes.push('xdsoft_disabled');
1526
- } else if (options.disabledDates.indexOf(start.dateFormat(options.formatDate)) !== -1) {
1527
- classes.push('xdsoft_disabled');
1528
- } else if (options.disabledWeekDays.indexOf(day) !== -1) {
1529
- classes.push('xdsoft_disabled');
1530
- }
1531
-
1532
- if (customDateSettings && customDateSettings[1] !== "") {
1533
- classes.push(customDateSettings[1]);
1534
- }
1535
-
1536
- if (_xdsoft_datetime.currentTime.getMonth() !== m) {
1537
- classes.push('xdsoft_other_month');
1538
- }
1539
-
1540
- if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) {
1541
- classes.push('xdsoft_current');
1542
- }
1543
-
1544
- if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) {
1545
- classes.push('xdsoft_today');
1546
- }
1547
-
1548
- if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) !== -1) {
1549
- classes.push('xdsoft_weekend');
1550
- }
1551
-
1552
- if (options.highlightedDates[start.dateFormat(options.formatDate)] !== undefined) {
1553
- hDate = options.highlightedDates[start.dateFormat(options.formatDate)];
1554
- classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style);
1555
- description = hDate.desc === undefined ? '' : hDate.desc;
1556
- }
1557
-
1558
- if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) {
1559
- classes.push(options.beforeShowDay(start));
1560
- }
1561
-
1562
- if (newRow) {
1563
- table += '<tr>';
1564
- newRow = false;
1565
- if (options.weeks) {
1566
- table += '<th>' + w + '</th>';
1567
- }
1568
- }
1569
-
1570
- table += '<td data-date="' + d + '" data-month="' + m + '" data-year="' + y + '"' + ' class="xdsoft_date xdsoft_day_of_week' + start.getDay() + ' ' + classes.join(' ') + '" title="' + description + '">' +
1571
- '<div>' + d + '</div>' +
1572
- '</td>';
1573
-
1574
- if (start.getDay() === options.dayOfWeekStartPrev) {
1575
- table += '</tr>';
1576
- newRow = true;
1577
- }
1578
-
1579
- start.setDate(d + 1);
1580
- }
1581
- table += '</tbody></table>';
1582
-
1583
- calendar.html(table);
1584
-
1585
- mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]);
1586
- mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear());
1587
-
1588
- // generate timebox
1589
- time = '';
1590
- h = '';
1591
- m = '';
1592
- line_time = function line_time(h, m) {
1593
- var now = _xdsoft_datetime.now(), optionDateTime, current_time;
1594
- now.setHours(h);
1595
- h = parseInt(now.getHours(), 10);
1596
- now.setMinutes(m);
1597
- m = parseInt(now.getMinutes(), 10);
1598
- optionDateTime = new Date(_xdsoft_datetime.currentTime);
1599
- optionDateTime.setHours(h);
1600
- optionDateTime.setMinutes(m);
1601
- classes = [];
1602
- if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || (options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) {
1603
- classes.push('xdsoft_disabled');
1604
- }
1605
- if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || ((options.disabledMinTime !== false && now.getTime() > _xdsoft_datetime.strtotime(options.disabledMinTime).getTime()) && (options.disabledMaxTime !== false && now.getTime() < _xdsoft_datetime.strtotime(options.disabledMaxTime).getTime()))) {
1606
- classes.push('xdsoft_disabled');
1607
- }
1608
-
1609
- current_time = new Date(_xdsoft_datetime.currentTime);
1610
- current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10));
1611
- current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step);
1612
-
1613
- if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && (options.step > 59 || current_time.getMinutes() === parseInt(m, 10))) {
1614
- if (options.defaultSelect || datetimepicker.data('changed')) {
1615
- classes.push('xdsoft_current');
1616
- } else if (options.initTime) {
1617
- classes.push('xdsoft_init_time');
1618
- }
1619
- }
1620
- if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) {
1621
- classes.push('xdsoft_today');
1622
- }
1623
- time += '<div class="xdsoft_time ' + classes.join(' ') + '" data-hour="' + h + '" data-minute="' + m + '">' + now.dateFormat(options.formatTime) + '</div>';
1624
- };
1625
-
1626
- if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) {
1627
- for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) {
1628
- for (j = 0; j < 60; j += options.step) {
1629
- h = (i < 10 ? '0' : '') + i;
1630
- m = (j < 10 ? '0' : '') + j;
1631
- line_time(h, m);
1632
- }
1633
- }
1634
- } else {
1635
- for (i = 0; i < options.allowTimes.length; i += 1) {
1636
- h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours();
1637
- m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes();
1638
- line_time(h, m);
1639
- }
1640
- }
1641
-
1642
- timebox.html(time);
1643
-
1644
- opt = '';
1645
- i = 0;
1646
-
1647
- for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) {
1648
- opt += '<div class="xdsoft_option ' + (_xdsoft_datetime.currentTime.getFullYear() === i ? 'xdsoft_current' : '') + '" data-value="' + i + '">' + i + '</div>';
1649
- }
1650
- yearselect.children().eq(0)
1651
- .html(opt);
1652
-
1653
- for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) {
1654
- opt += '<div class="xdsoft_option ' + (_xdsoft_datetime.currentTime.getMonth() === i ? 'xdsoft_current' : '') + '" data-value="' + i + '">' + options.i18n[options.lang].months[i] + '</div>';
1655
- }
1656
- monthselect.children().eq(0).html(opt);
1657
- $(datetimepicker)
1658
- .trigger('generate.xdsoft');
1659
- }, 10);
1660
- event.stopPropagation();
1661
- })
1662
- .on('afterOpen.xdsoft', function () {
1663
- if (options.timepicker) {
1664
- var classType, pheight, height, top;
1665
- if (timebox.find('.xdsoft_current').length) {
1666
- classType = '.xdsoft_current';
1667
- } else if (timebox.find('.xdsoft_init_time').length) {
1668
- classType = '.xdsoft_init_time';
1669
- }
1670
- if (classType) {
1671
- pheight = timeboxparent[0].clientHeight;
1672
- height = timebox[0].offsetHeight;
1673
- top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1;
1674
- if ((height - pheight) < top) {
1675
- top = height - pheight;
1676
- }
1677
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]);
1678
- } else {
1679
- timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]);
1680
- }
1681
- }
1682
- });
1683
-
1684
- timerclick = 0;
1685
- calendar
1686
- .on('click.xdsoft', 'td', function (xdevent) {
1687
- xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap
1688
- timerclick += 1;
1689
- var $this = $(this),
1690
- currentTime = _xdsoft_datetime.currentTime;
1691
-
1692
- if (currentTime === undefined || currentTime === null) {
1693
- _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1694
- currentTime = _xdsoft_datetime.currentTime;
1695
- }
1696
-
1697
- if ($this.hasClass('xdsoft_disabled')) {
1698
- return false;
1699
- }
1700
-
1701
- currentTime.setDate(1);
1702
- currentTime.setFullYear($this.data('year'));
1703
- currentTime.setMonth($this.data('month'));
1704
- currentTime.setDate($this.data('date'));
1705
-
1706
- datetimepicker.trigger('select.xdsoft', [currentTime]);
1707
-
1708
- input.val(_xdsoft_datetime.str());
1709
- if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) {
1710
- datetimepicker.trigger('close.xdsoft');
1711
- }
1712
-
1713
- if (options.onSelectDate && $.isFunction(options.onSelectDate)) {
1714
- options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
1715
- }
1716
-
1717
- datetimepicker.data('changed', true);
1718
- datetimepicker.trigger('xchange.xdsoft');
1719
- datetimepicker.trigger('changedatetime.xdsoft');
1720
- setTimeout(function () {
1721
- timerclick = 0;
1722
- }, 200);
1723
- });
1724
-
1725
- timebox
1726
- .on('click.xdsoft', 'div', function (xdevent) {
1727
- xdevent.stopPropagation();
1728
- var $this = $(this),
1729
- currentTime = _xdsoft_datetime.currentTime;
1730
-
1731
- if (currentTime === undefined || currentTime === null) {
1732
- _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1733
- currentTime = _xdsoft_datetime.currentTime;
1734
- }
1735
-
1736
- if ($this.hasClass('xdsoft_disabled')) {
1737
- return false;
1738
- }
1739
- currentTime.setHours($this.data('hour'));
1740
- currentTime.setMinutes($this.data('minute'));
1741
- datetimepicker.trigger('select.xdsoft', [currentTime]);
1742
-
1743
- datetimepicker.data('input').val(_xdsoft_datetime.str());
1744
-
1745
- if (options.inline !== true && options.closeOnTimeSelect === true) {
1746
- datetimepicker.trigger('close.xdsoft');
1747
- }
1748
-
1749
- if (options.onSelectTime && $.isFunction(options.onSelectTime)) {
1750
- options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
1751
- }
1752
- datetimepicker.data('changed', true);
1753
- datetimepicker.trigger('xchange.xdsoft');
1754
- datetimepicker.trigger('changedatetime.xdsoft');
1755
- });
1756
-
1757
-
1758
- datepicker
1759
- .on('mousewheel.xdsoft', function (event) {
1760
- if (!options.scrollMonth) {
1761
- return true;
1762
- }
1763
- if (event.deltaY < 0) {
1764
- _xdsoft_datetime.nextMonth();
1765
- } else {
1766
- _xdsoft_datetime.prevMonth();
1767
- }
1768
- return false;
1769
- });
1770
-
1771
- input
1772
- .on('mousewheel.xdsoft', function (event) {
1773
- if (!options.scrollInput) {
1774
- return true;
1775
- }
1776
- if (!options.datepicker && options.timepicker) {
1777
- current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0;
1778
- if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) {
1779
- current_time_index += event.deltaY;
1780
- }
1781
- if (timebox.children().eq(current_time_index).length) {
1782
- timebox.children().eq(current_time_index).trigger('mousedown');
1783
- }
1784
- return false;
1785
- }
1786
- if (options.datepicker && !options.timepicker) {
1787
- datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]);
1788
- if (input.val) {
1789
- input.val(_xdsoft_datetime.str());
1790
- }
1791
- datetimepicker.trigger('changedatetime.xdsoft');
1792
- return false;
1793
- }
1794
- });
1795
-
1796
- datetimepicker
1797
- .on('changedatetime.xdsoft', function (event) {
1798
- if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) {
1799
- var $input = datetimepicker.data('input');
1800
- options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event);
1801
- delete options.value;
1802
- $input.trigger('change');
1803
- }
1804
- })
1805
- .on('generate.xdsoft', function () {
1806
- if (options.onGenerate && $.isFunction(options.onGenerate)) {
1807
- options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1808
- }
1809
- if (triggerAfterOpen) {
1810
- datetimepicker.trigger('afterOpen.xdsoft');
1811
- triggerAfterOpen = false;
1812
- }
1813
- })
1814
- .on('click.xdsoft', function (xdevent) {
1815
- xdevent.stopPropagation();
1816
- });
1817
-
1818
- current_time_index = 0;
1819
-
1820
- setPos = function () {
1821
- var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute", node;
1822
- if (options.fixed) {
1823
- top -= $(window).scrollTop();
1824
- left -= $(window).scrollLeft();
1825
- position = "fixed";
1826
- } else {
1827
- if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) {
1828
- top = offset.top - datetimepicker[0].offsetHeight + 1;
1829
- }
1830
- if (top < 0) {
1831
- top = 0;
1832
- }
1833
- if (left + datetimepicker[0].offsetWidth > $(window).width()) {
1834
- left = $(window).width() - datetimepicker[0].offsetWidth;
1835
- }
1836
- }
1837
-
1838
- node = datetimepicker[0];
1839
- do {
1840
- node = node.parentNode;
1841
- if (window.getComputedStyle(node).getPropertyValue('position') === 'relative' && $(window).width() >= node.offsetWidth) {
1842
- left = left - (($(window).width() - node.offsetWidth) / 2);
1843
- break;
1844
- }
1845
- } while (node.nodeName !== 'HTML');
1846
- datetimepicker.css({
1847
- left: left,
1848
- top: top,
1849
- position: position
1850
- });
1851
- };
1852
- datetimepicker
1853
- .on('open.xdsoft', function (event) {
1854
- var onShow = true;
1855
- if (options.onShow && $.isFunction(options.onShow)) {
1856
- onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
1857
- }
1858
- if (onShow !== false) {
1859
- datetimepicker.show();
1860
- setPos();
1861
- $(window)
1862
- .off('resize.xdsoft', setPos)
1863
- .on('resize.xdsoft', setPos);
1864
-
1865
- if (options.closeOnWithoutClick) {
1866
- $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() {
1867
- datetimepicker.trigger('close.xdsoft');
1868
- $([document.body, window]).off('mousedown.xdsoft', arguments_callee6);
1869
- });
1870
- }
1871
- }
1872
- })
1873
- .on('close.xdsoft', function (event) {
1874
- var onClose = true;
1875
- mounth_picker
1876
- .find('.xdsoft_month,.xdsoft_year')
1877
- .find('.xdsoft_select')
1878
- .hide();
1879
- if (options.onClose && $.isFunction(options.onClose)) {
1880
- onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
1881
- }
1882
- if (onClose !== false && !options.opened && !options.inline) {
1883
- datetimepicker.hide();
1884
- }
1885
- event.stopPropagation();
1886
- })
1887
- .on('toggle.xdsoft', function (event) {
1888
- if (datetimepicker.is(':visible')) {
1889
- datetimepicker.trigger('close.xdsoft');
1890
- } else {
1891
- datetimepicker.trigger('open.xdsoft');
1892
- }
1893
- })
1894
- .data('input', input);
1895
-
1896
- timer = 0;
1897
- timer1 = 0;
1898
-
1899
- datetimepicker.data('xdsoft_datetime', _xdsoft_datetime);
1900
- datetimepicker.setOptions(options);
1901
-
1902
- function getCurrentValue() {
1903
- var ct = false, time;
1904
-
1905
- if (options.startDate) {
1906
- ct = _xdsoft_datetime.strToDate(options.startDate);
1907
- } else {
1908
- ct = options.value || ((input && input.val && input.val()) ? input.val() : '');
1909
- if (ct) {
1910
- ct = _xdsoft_datetime.strToDateTime(ct);
1911
- } else if (options.defaultDate) {
1912
- ct = _xdsoft_datetime.strToDateTime(options.defaultDate);
1913
- if (options.defaultTime) {
1914
- time = _xdsoft_datetime.strtotime(options.defaultTime);
1915
- ct.setHours(time.getHours());
1916
- ct.setMinutes(time.getMinutes());
1917
- }
1918
- }
1919
- }
1920
-
1921
- if (ct && _xdsoft_datetime.isValidDate(ct)) {
1922
- datetimepicker.data('changed', true);
1923
- } else {
1924
- ct = '';
1925
- }
1926
-
1927
- return ct || 0;
1928
- }
1929
-
1930
- _xdsoft_datetime.setCurrentTime(getCurrentValue());
1931
-
1932
- input
1933
- .data('xdsoft_datetimepicker', datetimepicker)
1934
- .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) {
1935
- if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) {
1936
- return;
1937
- }
1938
- clearTimeout(timer);
1939
- timer = setTimeout(function () {
1940
- if (input.is(':disabled')) {
1941
- return;
1942
- }
1943
-
1944
- triggerAfterOpen = true;
1945
- _xdsoft_datetime.setCurrentTime(getCurrentValue());
1946
-
1947
- datetimepicker.trigger('open.xdsoft');
1948
- }, 100);
1949
- })
1950
- .on('keydown.xdsoft', function (event) {
1951
- var val = this.value, elementSelector,
1952
- key = event.which;
1953
- if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) {
1954
- elementSelector = $("input:visible,textarea:visible");
1955
- datetimepicker.trigger('close.xdsoft');
1956
- elementSelector.eq(elementSelector.index(this) + 1).focus();
1957
- return false;
1958
- }
1959
- if ([TAB].indexOf(key) !== -1) {
1960
- datetimepicker.trigger('close.xdsoft');
1961
- return true;
1962
- }
1963
- });
1964
- };
1965
- destroyDateTimePicker = function (input) {
1966
- var datetimepicker = input.data('xdsoft_datetimepicker');
1967
- if (datetimepicker) {
1968
- datetimepicker.data('xdsoft_datetime', null);
1969
- datetimepicker.remove();
1970
- input
1971
- .data('xdsoft_datetimepicker', null)
1972
- .off('.xdsoft');
1973
- $(window).off('resize.xdsoft');
1974
- $([window, document.body]).off('mousedown.xdsoft');
1975
- if (input.unmousewheel) {
1976
- input.unmousewheel();
1977
- }
1978
- }
1979
- };
1980
- $(document)
1981
- .off('keydown.xdsoftctrl keyup.xdsoftctrl')
1982
- .on('keydown.xdsoftctrl', function (e) {
1983
- if (e.keyCode === CTRLKEY) {
1984
- ctrlDown = true;
1985
- }
1986
- })
1987
- .on('keyup.xdsoftctrl', function (e) {
1988
- if (e.keyCode === CTRLKEY) {
1989
- ctrlDown = false;
1990
- }
1991
- });
1992
- return this.each(function () {
1993
- var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input;
1994
- if (datetimepicker) {
1995
- if ($.type(opt) === 'string') {
1996
- switch (opt) {
1997
- case 'show':
1998
- $(this).select().focus();
1999
- datetimepicker.trigger('open.xdsoft');
2000
- break;
2001
- case 'hide':
2002
- datetimepicker.trigger('close.xdsoft');
2003
- break;
2004
- case 'toggle':
2005
- datetimepicker.trigger('toggle.xdsoft');
2006
- break;
2007
- case 'destroy':
2008
- destroyDateTimePicker($(this));
2009
- break;
2010
- case 'reset':
2011
- this.value = this.defaultValue;
2012
- if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) {
2013
- datetimepicker.data('changed', false);
2014
- }
2015
- datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value);
2016
- break;
2017
- case 'validate':
2018
- $input = datetimepicker.data('input');
2019
- $input.trigger('blur.xdsoft');
2020
- break;
2021
- }
2022
- } else {
2023
- datetimepicker
2024
- .setOptions(opt);
2025
- }
2026
- return 0;
2027
- }
2028
- if ($.type(opt) !== 'string') {
2029
- if (!options.lazyInit || options.open || options.inline) {
2030
- createDateTimePicker($(this));
2031
- } else {
2032
- lazyInit($(this));
2033
- }
2034
- }
2035
- });
2036
- };
2037
- $.fn.datetimepicker.defaults = default_options;
2038
- }(jQuery));
2039
-
2040
- function HighlightedDate(date, desc, style) {
2041
- "use strict";
2042
- this.date = date;
2043
- this.desc = desc;
2044
- this.style = style;
2045
- }
2046
-
2047
- (function () {
2048
-
2049
- /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
2050
- * Licensed under the MIT License (LICENSE.txt).
2051
- *
2052
- * Version: 3.1.12
2053
- *
2054
- * Requires: jQuery 1.2.2+
2055
- */
2056
- !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
2057
-
2058
- // Parse and Format Library
2059
- //http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
2060
- /*
2061
- * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org>
2062
- *
2063
- * This program is free software; you can redistribute it and/or modify it
2064
- * under the terms of the GNU Lesser General Public License as published by the
2065
- * Free Software Foundation, version 2.1.
2066
- *
2067
- * This program is distributed in the hope that it will be useful, but WITHOUT
2068
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2069
- * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
2070
- * details.
2071
- */
2072
- Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var codePrefix="Date.prototype."+funcName+" = function() {return ";var code="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true;}else{if(special){special=false;code+="'"+String.escape(ch)+"' + ";}else{code+=Date.getFormatCode(ch);}}}if(code.length==0){code="\"\"";}else{code=code.substring(0,code.length-3);}eval(codePrefix+code+";}");};Date.getFormatCode=function(a){switch(a){case"d":return"String.leftPad(this.getDate(), 2, '0') + ";case"D":return"Date.dayNames[this.getDay()].substring(0, 3) + ";case"j":return"this.getDate() + ";case"l":return"Date.dayNames[this.getDay()] + ";case"S":return"this.getSuffix() + ";case"w":return"this.getDay() + ";case"z":return"this.getDayOfYear() + ";case"W":return"this.getWeekOfYear() + ";case"F":return"Date.monthNames[this.getMonth()] + ";case"m":return"String.leftPad(this.getMonth() + 1, 2, '0') + ";case"M":return"Date.monthNames[this.getMonth()].substring(0, 3) + ";case"n":return"(this.getMonth() + 1) + ";case"t":return"this.getDaysInMonth() + ";case"L":return"(this.isLeapYear() ? 1 : 0) + ";case"Y":return"this.getFullYear() + ";case"y":return"('' + this.getFullYear()).substring(2, 4) + ";case"a":return"(this.getHours() < 12 ? 'am' : 'pm') + ";case"A":return"(this.getHours() < 12 ? 'AM' : 'PM') + ";case"g":return"((this.getHours() %12) ? this.getHours() % 12 : 12) + ";case"G":return"this.getHours() + ";case"h":return"String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + ";case"H":return"String.leftPad(this.getHours(), 2, '0') + ";case"i":return"String.leftPad(this.getMinutes(), 2, '0') + ";case"s":return"String.leftPad(this.getSeconds(), 2, '0') + ";case"O":return"this.getGMTOffset() + ";case"T":return"this.getTimezone() + ";case"Z":return"(this.getTimezoneOffset() * -60) + ";default:return"'"+String.escape(a)+"' + ";}};Date.parseDate=function(a,c){if(c=="unixtime"){return new Date(!isNaN(parseInt(a))?parseInt(a)*1000:0);}if(Date.parseFunctions[c]==null){Date.createParser(c);}var b=Date.parseFunctions[c];return Date[b](a);};Date.createParser=function(format){var funcName="parse"+Date.parseFunctions.count++;var regexNum=Date.parseRegexes.length;var currentGroup=1;Date.parseFunctions[format]=funcName;var code="Date."+funcName+" = function(input) {\nvar y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, z = -1;\nvar d = new Date();\ny = d.getFullYear();\nm = d.getMonth();\nd = d.getDate();\nvar results = input.match(Date.parseRegexes["+regexNum+"]);\nif (results && results.length > 0) {";var regex="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true;}else{if(special){special=false;regex+=String.escape(ch);}else{obj=Date.formatCodeToRegex(ch,currentGroup);currentGroup+=obj.g;regex+=obj.s;if(obj.g&&obj.c){code+=obj.c;}}}}code+="if (y > 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$",'i');eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b<this.getMonth();++b){a+=Date.daysInMonth[b];}return a+this.getDate();};Date.prototype.getWeekOfYear=function(){var b=this.getDayOfYear()+(4-this.getDay());var a=new Date(this.getFullYear(),0,1);var c=(7-a.getDay()+4);return String.leftPad(Math.ceil((b-c)/7)+1,2,"0");};Date.prototype.isLeapYear=function(){var a=this.getFullYear();return((a&3)==0&&(a%100||(a%400==0&&a)));};Date.prototype.getFirstDayOfMonth=function(){var a=(this.getDay()-(this.getDate()-1))%7;return(a<0)?(a+7):a;};Date.prototype.getLastDayOfMonth=function(){var a=(this.getDay()+(Date.daysInMonth[this.getMonth()]-this.getDate()))%7;return(a<0)?(a+7):a;};Date.prototype.getDaysInMonth=function(){Date.daysInMonth[1]=this.isLeapYear()?29:28;return Date.daysInMonth[this.getMonth()];};Date.prototype.getSuffix=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};String.escape=function(a){return a.replace(/('|\\)/g,"\\$1");};String.leftPad=function(d,b,c){var a=new String(d);if(c==null){c=" ";}while(a.length<b){a=c+a;}return a;};Date.daysInMonth=[31,28,31,30,31,30,31,31,30,31,30,31];Date.monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];Date.dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];Date.y2kYear=50;Date.monthNumbers={Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11};Date.patterns={ISO8601LongPattern:"Y-m-d H:i:s",ISO8601ShortPattern:"Y-m-d",ShortDatePattern:"n/j/Y",LongDatePattern:"l, F d, Y",FullDateTimePattern:"l, F d, Y g:i:s A",MonthDayPattern:"F d",ShortTimePattern:"g:i A",LongTimePattern:"g:i:s A",SortableDateTimePattern:"Y-m-d\\TH:i:s",UniversalSortableDateTimePattern:"Y-m-d H:i:sO",YearMonthPattern:"F, Y"};
2073
- }());
1
+ /**
2
+ * @preserve jQuery DateTimePicker plugin v2.4.5
3
+ * @homepage http://xdsoft.net/jqplugins/datetimepicker/
4
+ * (c) 2014, Chupurnov Valeriy.
5
+ */
6
+ /*global document,window,jQuery,setTimeout,clearTimeout,HighlightedDate,getCurrentValue*/
7
+ (function ($) {
8
+ 'use strict';
9
+ var default_options = {
10
+ i18n: {
11
+ ar: { // Arabic
12
+ months: [
13
+ "كانون الثاني", "شباط", "آذار", "نيسان", "مايو", "حزيران", "تموز", "آب", "أيلول", "تشرين الأول", "تشرين الثاني", "كانون الأول"
14
+ ],
15
+ dayOfWeek: [
16
+ "ن", "ث", "ع", "خ", "ج", "س", "ح"
17
+ ]
18
+ },
19
+ ro: { // Romanian
20
+ months: [
21
+ "ianuarie", "februarie", "martie", "aprilie", "mai", "iunie", "iulie", "august", "septembrie", "octombrie", "noiembrie", "decembrie"
22
+ ],
23
+ dayOfWeek: [
24
+ "l", "ma", "mi", "j", "v", "s", "d"
25
+ ]
26
+ },
27
+ id: { // Indonesian
28
+ months: [
29
+ "Januari", "Februari", "Maret", "April", "Mei", "Juni", "Juli", "Agustus", "September", "Oktober", "November", "Desember"
30
+ ],
31
+ dayOfWeek: [
32
+ "Min", "Sen", "Sel", "Rab", "Kam", "Jum", "Sab"
33
+ ]
34
+ },
35
+ is: { // Icelandic
36
+ months: [
37
+ "Janúar", "Febrúar", "Mars", "Apríl", "Maí", "Júní", "Júlí", "Ágúst", "September", "Október", "Nóvember", "Desember"
38
+ ],
39
+ dayOfWeek: [
40
+ "Sun", "Mán", "Þrið", "Mið", "Fim", "Fös", "Lau"
41
+ ]
42
+ },
43
+ bg: { // Bulgarian
44
+ months: [
45
+ "Януари", "Февруари", "Март", "Април", "Май", "Юни", "Юли", "Август", "Септември", "Октомври", "Ноември", "Декември"
46
+ ],
47
+ dayOfWeek: [
48
+ "Нд", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
49
+ ]
50
+ },
51
+ fa: { // Persian/Farsi
52
+ months: [
53
+ 'فروردین', 'اردیبهشت', 'خرداد', 'تیر', 'مرداد', 'شهریور', 'مهر', 'آبان', 'آذر', 'دی', 'بهمن', 'اسفند'
54
+ ],
55
+ dayOfWeek: [
56
+ 'یکشنبه', 'دوشنبه', 'سه شنبه', 'چهارشنبه', 'پنجشنبه', 'جمعه', 'شنبه'
57
+ ]
58
+ },
59
+ ru: { // Russian
60
+ months: [
61
+ 'Январь', 'Февраль', 'Март', 'Апрель', 'Май', 'Июнь', 'Июль', 'Август', 'Сентябрь', 'Октябрь', 'Ноябрь', 'Декабрь'
62
+ ],
63
+ dayOfWeek: [
64
+ "Вск", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"
65
+ ]
66
+ },
67
+ uk: { // Ukrainian
68
+ months: [
69
+ 'Січень', 'Лютий', 'Березень', 'Квітень', 'Травень', 'Червень', 'Липень', 'Серпень', 'Вересень', 'Жовтень', 'Листопад', 'Грудень'
70
+ ],
71
+ dayOfWeek: [
72
+ "Ндл", "Пнд", "Втр", "Срд", "Чтв", "Птн", "Сбт"
73
+ ]
74
+ },
75
+ en: { // English
76
+ months: [
77
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
78
+ ],
79
+ dayOfWeek: [
80
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
81
+ ]
82
+ },
83
+ el: { // Ελληνικά
84
+ months: [
85
+ "Ιανουάριος", "Φεβρουάριος", "Μάρτιος", "Απρίλιος", "Μάιος", "Ιούνιος", "Ιούλιος", "Αύγουστος", "Σεπτέμβριος", "Οκτώβριος", "Νοέμβριος", "Δεκέμβριος"
86
+ ],
87
+ dayOfWeek: [
88
+ "Κυρ", "Δευ", "Τρι", "Τετ", "Πεμ", "Παρ", "Σαβ"
89
+ ]
90
+ },
91
+ de: { // German
92
+ months: [
93
+ 'Januar', 'Februar', 'März', 'April', 'Mai', 'Juni', 'Juli', 'August', 'September', 'Oktober', 'November', 'Dezember'
94
+ ],
95
+ dayOfWeek: [
96
+ "So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"
97
+ ]
98
+ },
99
+ nl: { // Dutch
100
+ months: [
101
+ "januari", "februari", "maart", "april", "mei", "juni", "juli", "augustus", "september", "oktober", "november", "december"
102
+ ],
103
+ dayOfWeek: [
104
+ "zo", "ma", "di", "wo", "do", "vr", "za"
105
+ ]
106
+ },
107
+ tr: { // Turkish
108
+ months: [
109
+ "Ocak", "Şubat", "Mart", "Nisan", "Mayıs", "Haziran", "Temmuz", "Ağustos", "Eylül", "Ekim", "Kasım", "Aralık"
110
+ ],
111
+ dayOfWeek: [
112
+ "Paz", "Pts", "Sal", "Çar", "Per", "Cum", "Cts"
113
+ ]
114
+ },
115
+ fr: { //French
116
+ months: [
117
+ "Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"
118
+ ],
119
+ dayOfWeek: [
120
+ "Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"
121
+ ]
122
+ },
123
+ es: { // Spanish
124
+ months: [
125
+ "Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"
126
+ ],
127
+ dayOfWeek: [
128
+ "Dom", "Lun", "Mar", "Mié", "Jue", "Vie", "Sáb"
129
+ ]
130
+ },
131
+ th: { // Thai
132
+ months: [
133
+ 'มกราคม', 'กุมภาพันธ์', 'มีนาคม', 'เมษายน', 'พฤษภาคม', 'มิถุนายน', 'กรกฎาคม', 'สิงหาคม', 'กันยายน', 'ตุลาคม', 'พฤศจิกายน', 'ธันวาคม'
134
+ ],
135
+ dayOfWeek: [
136
+ 'อา.', 'จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.'
137
+ ]
138
+ },
139
+ pl: { // Polish
140
+ months: [
141
+ "styczeń", "luty", "marzec", "kwiecień", "maj", "czerwiec", "lipiec", "sierpień", "wrzesień", "październik", "listopad", "grudzień"
142
+ ],
143
+ dayOfWeek: [
144
+ "nd", "pn", "wt", "śr", "cz", "pt", "sb"
145
+ ]
146
+ },
147
+ pt: { // Portuguese
148
+ months: [
149
+ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
150
+ ],
151
+ dayOfWeek: [
152
+ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sab"
153
+ ]
154
+ },
155
+ ch: { // Simplified Chinese
156
+ months: [
157
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
158
+ ],
159
+ dayOfWeek: [
160
+ "日", "一", "二", "三", "四", "五", "六"
161
+ ]
162
+ },
163
+ se: { // Swedish
164
+ months: [
165
+ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
166
+ ],
167
+ dayOfWeek: [
168
+ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
169
+ ]
170
+ },
171
+ kr: { // Korean
172
+ months: [
173
+ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
174
+ ],
175
+ dayOfWeek: [
176
+ "일", "월", "화", "수", "목", "금", "토"
177
+ ]
178
+ },
179
+ it: { // Italian
180
+ months: [
181
+ "Gennaio", "Febbraio", "Marzo", "Aprile", "Maggio", "Giugno", "Luglio", "Agosto", "Settembre", "Ottobre", "Novembre", "Dicembre"
182
+ ],
183
+ dayOfWeek: [
184
+ "Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"
185
+ ]
186
+ },
187
+ da: { // Dansk
188
+ months: [
189
+ "January", "Februar", "Marts", "April", "Maj", "Juni", "July", "August", "September", "Oktober", "November", "December"
190
+ ],
191
+ dayOfWeek: [
192
+ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
193
+ ]
194
+ },
195
+ no: { // Norwegian
196
+ months: [
197
+ "Januar", "Februar", "Mars", "April", "Mai", "Juni", "Juli", "August", "September", "Oktober", "November", "Desember"
198
+ ],
199
+ dayOfWeek: [
200
+ "Søn", "Man", "Tir", "Ons", "Tor", "Fre", "Lør"
201
+ ]
202
+ },
203
+ ja: { // Japanese
204
+ months: [
205
+ "1月", "2月", "3月", "4月", "5月", "6月", "7月", "8月", "9月", "10月", "11月", "12月"
206
+ ],
207
+ dayOfWeek: [
208
+ "日", "月", "火", "水", "木", "金", "土"
209
+ ]
210
+ },
211
+ vi: { // Vietnamese
212
+ months: [
213
+ "Tháng 1", "Tháng 2", "Tháng 3", "Tháng 4", "Tháng 5", "Tháng 6", "Tháng 7", "Tháng 8", "Tháng 9", "Tháng 10", "Tháng 11", "Tháng 12"
214
+ ],
215
+ dayOfWeek: [
216
+ "CN", "T2", "T3", "T4", "T5", "T6", "T7"
217
+ ]
218
+ },
219
+ sl: { // Slovenščina
220
+ months: [
221
+ "Januar", "Februar", "Marec", "April", "Maj", "Junij", "Julij", "Avgust", "September", "Oktober", "November", "December"
222
+ ],
223
+ dayOfWeek: [
224
+ "Ned", "Pon", "Tor", "Sre", "Čet", "Pet", "Sob"
225
+ ]
226
+ },
227
+ cs: { // Čeština
228
+ months: [
229
+ "Leden", "Únor", "Březen", "Duben", "Květen", "Červen", "Červenec", "Srpen", "Září", "Říjen", "Listopad", "Prosinec"
230
+ ],
231
+ dayOfWeek: [
232
+ "Ne", "Po", "Út", "St", "Čt", "Pá", "So"
233
+ ]
234
+ },
235
+ hu: { // Hungarian
236
+ months: [
237
+ "Január", "Február", "Március", "Április", "Május", "Június", "Július", "Augusztus", "Szeptember", "Október", "November", "December"
238
+ ],
239
+ dayOfWeek: [
240
+ "Va", "Hé", "Ke", "Sze", "Cs", "Pé", "Szo"
241
+ ]
242
+ },
243
+ az: { //Azerbaijanian (Azeri)
244
+ months: [
245
+ "Yanvar", "Fevral", "Mart", "Aprel", "May", "Iyun", "Iyul", "Avqust", "Sentyabr", "Oktyabr", "Noyabr", "Dekabr"
246
+ ],
247
+ dayOfWeek: [
248
+ "B", "Be", "Ça", "Ç", "Ca", "C", "Ş"
249
+ ]
250
+ },
251
+ bs: { //Bosanski
252
+ months: [
253
+ "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
254
+ ],
255
+ dayOfWeek: [
256
+ "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
257
+ ]
258
+ },
259
+ ca: { //Català
260
+ months: [
261
+ "Gener", "Febrer", "Març", "Abril", "Maig", "Juny", "Juliol", "Agost", "Setembre", "Octubre", "Novembre", "Desembre"
262
+ ],
263
+ dayOfWeek: [
264
+ "Dg", "Dl", "Dt", "Dc", "Dj", "Dv", "Ds"
265
+ ]
266
+ },
267
+ 'en-GB': { //English (British)
268
+ months: [
269
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
270
+ ],
271
+ dayOfWeek: [
272
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
273
+ ]
274
+ },
275
+ et: { //"Eesti"
276
+ months: [
277
+ "Jaanuar", "Veebruar", "Märts", "Aprill", "Mai", "Juuni", "Juuli", "August", "September", "Oktoober", "November", "Detsember"
278
+ ],
279
+ dayOfWeek: [
280
+ "P", "E", "T", "K", "N", "R", "L"
281
+ ]
282
+ },
283
+ eu: { //Euskara
284
+ months: [
285
+ "Urtarrila", "Otsaila", "Martxoa", "Apirila", "Maiatza", "Ekaina", "Uztaila", "Abuztua", "Iraila", "Urria", "Azaroa", "Abendua"
286
+ ],
287
+ dayOfWeek: [
288
+ "Ig.", "Al.", "Ar.", "Az.", "Og.", "Or.", "La."
289
+ ]
290
+ },
291
+ fi: { //Finnish (Suomi)
292
+ months: [
293
+ "Tammikuu", "Helmikuu", "Maaliskuu", "Huhtikuu", "Toukokuu", "Kesäkuu", "Heinäkuu", "Elokuu", "Syyskuu", "Lokakuu", "Marraskuu", "Joulukuu"
294
+ ],
295
+ dayOfWeek: [
296
+ "Su", "Ma", "Ti", "Ke", "To", "Pe", "La"
297
+ ]
298
+ },
299
+ gl: { //Galego
300
+ months: [
301
+ "Xan", "Feb", "Maz", "Abr", "Mai", "Xun", "Xul", "Ago", "Set", "Out", "Nov", "Dec"
302
+ ],
303
+ dayOfWeek: [
304
+ "Dom", "Lun", "Mar", "Mer", "Xov", "Ven", "Sab"
305
+ ]
306
+ },
307
+ hr: { //Hrvatski
308
+ months: [
309
+ "Siječanj", "Veljača", "Ožujak", "Travanj", "Svibanj", "Lipanj", "Srpanj", "Kolovoz", "Rujan", "Listopad", "Studeni", "Prosinac"
310
+ ],
311
+ dayOfWeek: [
312
+ "Ned", "Pon", "Uto", "Sri", "Čet", "Pet", "Sub"
313
+ ]
314
+ },
315
+ ko: { //Korean (한국어)
316
+ months: [
317
+ "1월", "2월", "3월", "4월", "5월", "6월", "7월", "8월", "9월", "10월", "11월", "12월"
318
+ ],
319
+ dayOfWeek: [
320
+ "일", "월", "화", "수", "목", "금", "토"
321
+ ]
322
+ },
323
+ lt: { //Lithuanian (lietuvių)
324
+ months: [
325
+ "Sausio", "Vasario", "Kovo", "Balandžio", "Gegužės", "Birželio", "Liepos", "Rugpjūčio", "Rugsėjo", "Spalio", "Lapkričio", "Gruodžio"
326
+ ],
327
+ dayOfWeek: [
328
+ "Sek", "Pir", "Ant", "Tre", "Ket", "Pen", "Šeš"
329
+ ]
330
+ },
331
+ lv: { //Latvian (Latviešu)
332
+ months: [
333
+ "Janvāris", "Februāris", "Marts", "Aprīlis ", "Maijs", "Jūnijs", "Jūlijs", "Augusts", "Septembris", "Oktobris", "Novembris", "Decembris"
334
+ ],
335
+ dayOfWeek: [
336
+ "Sv", "Pr", "Ot", "Tr", "Ct", "Pk", "St"
337
+ ]
338
+ },
339
+ mk: { //Macedonian (Македонски)
340
+ months: [
341
+ "јануари", "февруари", "март", "април", "мај", "јуни", "јули", "август", "септември", "октомври", "ноември", "декември"
342
+ ],
343
+ dayOfWeek: [
344
+ "нед", "пон", "вто", "сре", "чет", "пет", "саб"
345
+ ]
346
+ },
347
+ mn: { //Mongolian (Монгол)
348
+ months: [
349
+ "1-р сар", "2-р сар", "3-р сар", "4-р сар", "5-р сар", "6-р сар", "7-р сар", "8-р сар", "9-р сар", "10-р сар", "11-р сар", "12-р сар"
350
+ ],
351
+ dayOfWeek: [
352
+ "Дав", "Мяг", "Лха", "Пүр", "Бсн", "Бям", "Ням"
353
+ ]
354
+ },
355
+ 'pt-BR': { //Português(Brasil)
356
+ months: [
357
+ "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"
358
+ ],
359
+ dayOfWeek: [
360
+ "Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"
361
+ ]
362
+ },
363
+ sk: { //Slovenčina
364
+ months: [
365
+ "Január", "Február", "Marec", "Apríl", "Máj", "Jún", "Júl", "August", "September", "Október", "November", "December"
366
+ ],
367
+ dayOfWeek: [
368
+ "Ne", "Po", "Ut", "St", "Št", "Pi", "So"
369
+ ]
370
+ },
371
+ sq: { //Albanian (Shqip)
372
+ months: [
373
+ "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"
374
+ ],
375
+ dayOfWeek: [
376
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
377
+ ]
378
+ },
379
+ 'sr-YU': { //Serbian (Srpski)
380
+ months: [
381
+ "Januar", "Februar", "Mart", "April", "Maj", "Jun", "Jul", "Avgust", "Septembar", "Oktobar", "Novembar", "Decembar"
382
+ ],
383
+ dayOfWeek: [
384
+ "Ned", "Pon", "Uto", "Sre", "čet", "Pet", "Sub"
385
+ ]
386
+ },
387
+ sr: { //Serbian Cyrillic (Српски)
388
+ months: [
389
+ "јануар", "фебруар", "март", "април", "мај", "јун", "јул", "август", "септембар", "октобар", "новембар", "децембар"
390
+ ],
391
+ dayOfWeek: [
392
+ "нед", "пон", "уто", "сре", "чет", "пет", "суб"
393
+ ]
394
+ },
395
+ sv: { //Svenska
396
+ months: [
397
+ "Januari", "Februari", "Mars", "April", "Maj", "Juni", "Juli", "Augusti", "September", "Oktober", "November", "December"
398
+ ],
399
+ dayOfWeek: [
400
+ "Sön", "Mån", "Tis", "Ons", "Tor", "Fre", "Lör"
401
+ ]
402
+ },
403
+ 'zh-TW': { //Traditional Chinese (繁體中文)
404
+ months: [
405
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
406
+ ],
407
+ dayOfWeek: [
408
+ "日", "一", "二", "三", "四", "五", "六"
409
+ ]
410
+ },
411
+ zh: { //Simplified Chinese (简体中文)
412
+ months: [
413
+ "一月", "二月", "三月", "四月", "五月", "六月", "七月", "八月", "九月", "十月", "十一月", "十二月"
414
+ ],
415
+ dayOfWeek: [
416
+ "日", "一", "二", "三", "四", "五", "六"
417
+ ]
418
+ },
419
+ he: { //Hebrew (עברית)
420
+ months: [
421
+ 'ינואר', 'פברואר', 'מרץ', 'אפריל', 'מאי', 'יוני', 'יולי', 'אוגוסט', 'ספטמבר', 'אוקטובר', 'נובמבר', 'דצמבר'
422
+ ],
423
+ dayOfWeek: [
424
+ 'א\'', 'ב\'', 'ג\'', 'ד\'', 'ה\'', 'ו\'', 'שבת'
425
+ ]
426
+ },
427
+ hy: { // Armenian
428
+ months: [
429
+ "Հունվար", "Փետրվար", "Մարտ", "Ապրիլ", "Մայիս", "Հունիս", "Հուլիս", "Օգոստոս", "Սեպտեմբեր", "Հոկտեմբեր", "Նոյեմբեր", "Դեկտեմբեր"
430
+ ],
431
+ dayOfWeek: [
432
+ "Կի", "Երկ", "Երք", "Չոր", "Հնգ", "Ուրբ", "Շբթ"
433
+ ]
434
+ },
435
+ kg: { // Kyrgyz
436
+ months: [
437
+ 'Үчтүн айы', 'Бирдин айы', 'Жалган Куран', 'Чын Куран', 'Бугу', 'Кулжа', 'Теке', 'Баш Оона', 'Аяк Оона', 'Тогуздун айы', 'Жетинин айы', 'Бештин айы'
438
+ ],
439
+ dayOfWeek: [
440
+ "Жек", "Дүй", "Шей", "Шар", "Бей", "Жум", "Ише"
441
+ ]
442
+ }
443
+ },
444
+ value: '',
445
+ lang: 'en',
446
+
447
+ format: 'Y/m/d H:i',
448
+ formatTime: 'H:i',
449
+ formatDate: 'Y/m/d',
450
+
451
+ startDate: false, // new Date(), '1986/12/08', '-1970/01/05','-1970/01/05',
452
+ step: 60,
453
+ monthChangeSpinner: true,
454
+
455
+ closeOnDateSelect: false,
456
+ closeOnTimeSelect: true,
457
+ closeOnWithoutClick: true,
458
+ closeOnInputClick: true,
459
+
460
+ timepicker: true,
461
+ datepicker: true,
462
+ weeks: false,
463
+
464
+ defaultTime: false, // use formatTime format (ex. '10:00' for formatTime: 'H:i')
465
+ defaultDate: false, // use formatDate format (ex new Date() or '1986/12/08' or '-1970/01/05' or '-1970/01/05')
466
+
467
+ minDate: false,
468
+ maxDate: false,
469
+ minTime: false,
470
+ maxTime: false,
471
+ disabledMinTime: false,
472
+ disabledMaxTime: false,
473
+
474
+ allowTimes: [],
475
+ opened: false,
476
+ initTime: true,
477
+ inline: false,
478
+ theme: '',
479
+
480
+ onSelectDate: function () {},
481
+ onSelectTime: function () {},
482
+ onChangeMonth: function () {},
483
+ onChangeYear: function () {},
484
+ onChangeDateTime: function () {},
485
+ onShow: function () {},
486
+ onClose: function () {},
487
+ onGenerate: function () {},
488
+
489
+ withoutCopyright: true,
490
+ inverseButton: false,
491
+ hours12: false,
492
+ next: 'xdsoft_next',
493
+ prev : 'xdsoft_prev',
494
+ dayOfWeekStart: 0,
495
+ parentID: 'body',
496
+ timeHeightInTimePicker: 25,
497
+ timepickerScrollbar: true,
498
+ todayButton: true,
499
+ prevButton: true,
500
+ nextButton: true,
501
+ defaultSelect: true,
502
+
503
+ scrollMonth: true,
504
+ scrollTime: true,
505
+ scrollInput: true,
506
+
507
+ lazyInit: false,
508
+ mask: false,
509
+ validateOnBlur: true,
510
+ allowBlank: true,
511
+ yearStart: 1950,
512
+ yearEnd: 2050,
513
+ monthStart: 0,
514
+ monthEnd: 11,
515
+ style: '',
516
+ id: '',
517
+ fixed: false,
518
+ roundTime: 'round', // ceil, floor
519
+ className: '',
520
+ weekends: [],
521
+ highlightedDates: [],
522
+ highlightedPeriods: [],
523
+ disabledDates : [],
524
+ disabledWeekDays: [],
525
+ yearOffset: 0,
526
+ beforeShowDay: null,
527
+
528
+ enterLikeTab: true,
529
+ showApplyButton: false
530
+ };
531
+ // fix for ie8
532
+ if (!window.getComputedStyle) {
533
+ window.getComputedStyle = function (el, pseudo) {
534
+ this.el = el;
535
+ this.getPropertyValue = function (prop) {
536
+ var re = /(\-([a-z]){1})/g;
537
+ if (prop === 'float') {
538
+ prop = 'styleFloat';
539
+ }
540
+ if (re.test(prop)) {
541
+ prop = prop.replace(re, function (a, b, c) {
542
+ return c.toUpperCase();
543
+ });
544
+ }
545
+ return el.currentStyle[prop] || null;
546
+ };
547
+ return this;
548
+ };
549
+ }
550
+ if (!Array.prototype.indexOf) {
551
+ Array.prototype.indexOf = function (obj, start) {
552
+ var i, j;
553
+ for (i = (start || 0), j = this.length; i < j; i += 1) {
554
+ if (this[i] === obj) { return i; }
555
+ }
556
+ return -1;
557
+ };
558
+ }
559
+ Date.prototype.countDaysInMonth = function () {
560
+ return new Date(this.getFullYear(), this.getMonth() + 1, 0).getDate();
561
+ };
562
+ $.fn.xdsoftScroller = function (percent) {
563
+ return this.each(function () {
564
+ var timeboxparent = $(this),
565
+ pointerEventToXY = function (e) {
566
+ var out = {x: 0, y: 0},
567
+ touch;
568
+ if (e.type === 'touchstart' || e.type === 'touchmove' || e.type === 'touchend' || e.type === 'touchcancel') {
569
+ touch = e.originalEvent.touches[0] || e.originalEvent.changedTouches[0];
570
+ out.x = touch.clientX;
571
+ out.y = touch.clientY;
572
+ } else if (e.type === 'mousedown' || e.type === 'mouseup' || e.type === 'mousemove' || e.type === 'mouseover' || e.type === 'mouseout' || e.type === 'mouseenter' || e.type === 'mouseleave') {
573
+ out.x = e.clientX;
574
+ out.y = e.clientY;
575
+ }
576
+ return out;
577
+ },
578
+ move = 0,
579
+ timebox,
580
+ parentHeight,
581
+ height,
582
+ scrollbar,
583
+ scroller,
584
+ maximumOffset = 100,
585
+ start = false,
586
+ startY = 0,
587
+ startTop = 0,
588
+ h1 = 0,
589
+ touchStart = false,
590
+ startTopScroll = 0,
591
+ calcOffset = function () {};
592
+ if (percent === 'hide') {
593
+ timeboxparent.find('.xdsoft_scrollbar').hide();
594
+ return;
595
+ }
596
+ if (!$(this).hasClass('xdsoft_scroller_box')) {
597
+ timebox = timeboxparent.children().eq(0);
598
+ parentHeight = timeboxparent[0].clientHeight;
599
+ height = timebox[0].offsetHeight;
600
+ scrollbar = $('<div class="xdsoft_scrollbar"></div>');
601
+ scroller = $('<div class="xdsoft_scroller"></div>');
602
+ scrollbar.append(scroller);
603
+
604
+ timeboxparent.addClass('xdsoft_scroller_box').append(scrollbar);
605
+ calcOffset = function calcOffset(event) {
606
+ var offset = pointerEventToXY(event).y - startY + startTopScroll;
607
+ if (offset < 0) {
608
+ offset = 0;
609
+ }
610
+ if (offset + scroller[0].offsetHeight > h1) {
611
+ offset = h1 - scroller[0].offsetHeight;
612
+ }
613
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [maximumOffset ? offset / maximumOffset : 0]);
614
+ };
615
+
616
+ scroller
617
+ .on('touchstart.xdsoft_scroller mousedown.xdsoft_scroller', function (event) {
618
+ if (!parentHeight) {
619
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
620
+ }
621
+
622
+ startY = pointerEventToXY(event).y;
623
+ startTopScroll = parseInt(scroller.css('margin-top'), 10);
624
+ h1 = scrollbar[0].offsetHeight;
625
+
626
+ if (event.type === 'mousedown') {
627
+ if (document) {
628
+ $(document.body).addClass('xdsoft_noselect');
629
+ }
630
+ $([document.body, window]).on('mouseup.xdsoft_scroller', function arguments_callee() {
631
+ $([document.body, window]).off('mouseup.xdsoft_scroller', arguments_callee)
632
+ .off('mousemove.xdsoft_scroller', calcOffset)
633
+ .removeClass('xdsoft_noselect');
634
+ });
635
+ $(document.body).on('mousemove.xdsoft_scroller', calcOffset);
636
+ } else {
637
+ touchStart = true;
638
+ event.stopPropagation();
639
+ event.preventDefault();
640
+ }
641
+ })
642
+ .on('touchmove', function (event) {
643
+ if (touchStart) {
644
+ event.preventDefault();
645
+ calcOffset(event);
646
+ }
647
+ })
648
+ .on('touchend touchcancel', function (event) {
649
+ touchStart = false;
650
+ startTopScroll = 0;
651
+ });
652
+
653
+ timeboxparent
654
+ .on('scroll_element.xdsoft_scroller', function (event, percentage) {
655
+ if (!parentHeight) {
656
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percentage, true]);
657
+ }
658
+ percentage = percentage > 1 ? 1 : (percentage < 0 || isNaN(percentage)) ? 0 : percentage;
659
+
660
+ scroller.css('margin-top', maximumOffset * percentage);
661
+
662
+ setTimeout(function () {
663
+ timebox.css('marginTop', -parseInt((timebox[0].offsetHeight - parentHeight) * percentage, 10));
664
+ }, 10);
665
+ })
666
+ .on('resize_scroll.xdsoft_scroller', function (event, percentage, noTriggerScroll) {
667
+ var percent, sh;
668
+ parentHeight = timeboxparent[0].clientHeight;
669
+ height = timebox[0].offsetHeight;
670
+ percent = parentHeight / height;
671
+ sh = percent * scrollbar[0].offsetHeight;
672
+ if (percent > 1) {
673
+ scroller.hide();
674
+ } else {
675
+ scroller.show();
676
+ scroller.css('height', parseInt(sh > 10 ? sh : 10, 10));
677
+ maximumOffset = scrollbar[0].offsetHeight - scroller[0].offsetHeight;
678
+ if (noTriggerScroll !== true) {
679
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [percentage || Math.abs(parseInt(timebox.css('marginTop'), 10)) / (height - parentHeight)]);
680
+ }
681
+ }
682
+ });
683
+
684
+ timeboxparent.on('mousewheel', function (event) {
685
+ var top = Math.abs(parseInt(timebox.css('marginTop'), 10));
686
+
687
+ top = top - (event.deltaY * 20);
688
+ if (top < 0) {
689
+ top = 0;
690
+ }
691
+
692
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [top / (height - parentHeight)]);
693
+ event.stopPropagation();
694
+ return false;
695
+ });
696
+
697
+ timeboxparent.on('touchstart', function (event) {
698
+ start = pointerEventToXY(event);
699
+ startTop = Math.abs(parseInt(timebox.css('marginTop'), 10));
700
+ });
701
+
702
+ timeboxparent.on('touchmove', function (event) {
703
+ if (start) {
704
+ event.preventDefault();
705
+ var coord = pointerEventToXY(event);
706
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [(startTop - (coord.y - start.y)) / (height - parentHeight)]);
707
+ }
708
+ });
709
+
710
+ timeboxparent.on('touchend touchcancel', function (event) {
711
+ start = false;
712
+ startTop = 0;
713
+ });
714
+ }
715
+ timeboxparent.trigger('resize_scroll.xdsoft_scroller', [percent]);
716
+ });
717
+ };
718
+
719
+ $.fn.datetimepicker = function (opt) {
720
+ var KEY0 = 48,
721
+ KEY9 = 57,
722
+ _KEY0 = 96,
723
+ _KEY9 = 105,
724
+ CTRLKEY = 17,
725
+ DEL = 46,
726
+ ENTER = 13,
727
+ ESC = 27,
728
+ BACKSPACE = 8,
729
+ ARROWLEFT = 37,
730
+ ARROWUP = 38,
731
+ ARROWRIGHT = 39,
732
+ ARROWDOWN = 40,
733
+ TAB = 9,
734
+ F5 = 116,
735
+ AKEY = 65,
736
+ CKEY = 67,
737
+ VKEY = 86,
738
+ ZKEY = 90,
739
+ YKEY = 89,
740
+ ctrlDown = false,
741
+ options = ($.isPlainObject(opt) || !opt) ? $.extend(true, {}, default_options, opt) : $.extend(true, {}, default_options),
742
+
743
+ lazyInitTimer = 0,
744
+ createDateTimePicker,
745
+ destroyDateTimePicker,
746
+
747
+ lazyInit = function (input) {
748
+ input
749
+ .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function initOnActionCallback(event) {
750
+ if (input.is(':disabled') || input.data('xdsoft_datetimepicker')) {
751
+ return;
752
+ }
753
+ clearTimeout(lazyInitTimer);
754
+ lazyInitTimer = setTimeout(function () {
755
+
756
+ if (!input.data('xdsoft_datetimepicker')) {
757
+ createDateTimePicker(input);
758
+ }
759
+ input
760
+ .off('open.xdsoft focusin.xdsoft mousedown.xdsoft', initOnActionCallback)
761
+ .trigger('open.xdsoft');
762
+ }, 100);
763
+ });
764
+ };
765
+
766
+ createDateTimePicker = function (input) {
767
+ var datetimepicker = $('<div class="xdsoft_datetimepicker xdsoft_noselect"></div>'),
768
+ xdsoft_copyright = $('<div class="xdsoft_copyright"><a target="_blank" href="http://xdsoft.net/jqplugins/datetimepicker/">xdsoft.net</a></div>'),
769
+ datepicker = $('<div class="xdsoft_datepicker active"></div>'),
770
+ mounth_picker = $('<div class="xdsoft_mounthpicker"><button type="button" class="xdsoft_prev"></button><button type="button" class="xdsoft_today_button"></button>' +
771
+ '<div class="xdsoft_label xdsoft_month"><span></span><i></i></div>' +
772
+ '<div class="xdsoft_label xdsoft_year"><span></span><i></i></div>' +
773
+ '<button type="button" class="xdsoft_next"></button></div>'),
774
+ calendar = $('<div class="xdsoft_calendar"></div>'),
775
+ timepicker = $('<div class="xdsoft_timepicker active"><button type="button" class="xdsoft_prev"></button><div class="xdsoft_time_box"></div><button type="button" class="xdsoft_next"></button></div>'),
776
+ timeboxparent = timepicker.find('.xdsoft_time_box').eq(0),
777
+ timebox = $('<div class="xdsoft_time_variant"></div>'),
778
+ applyButton = $('<button type="button" class="xdsoft_save_selected blue-gradient-button">Save Selected</button>'),
779
+ /*scrollbar = $('<div class="xdsoft_scrollbar"></div>'),
780
+ scroller = $('<div class="xdsoft_scroller"></div>'),*/
781
+ monthselect = $('<div class="xdsoft_select xdsoft_monthselect"><div></div></div>'),
782
+ yearselect = $('<div class="xdsoft_select xdsoft_yearselect"><div></div></div>'),
783
+ triggerAfterOpen = false,
784
+ XDSoft_datetime,
785
+ //scroll_element,
786
+ xchangeTimer,
787
+ timerclick,
788
+ current_time_index,
789
+ setPos,
790
+ timer = 0,
791
+ timer1 = 0,
792
+ _xdsoft_datetime;
793
+
794
+ if (options.id) {
795
+ datetimepicker.attr('id', options.id);
796
+ }
797
+ if (options.style) {
798
+ datetimepicker.attr('style', options.style);
799
+ }
800
+ if (options.weeks) {
801
+ datetimepicker.addClass('xdsoft_showweeks');
802
+ }
803
+
804
+ datetimepicker.addClass('xdsoft_' + options.theme);
805
+ datetimepicker.addClass(options.className);
806
+
807
+ mounth_picker
808
+ .find('.xdsoft_month span')
809
+ .after(monthselect);
810
+ mounth_picker
811
+ .find('.xdsoft_year span')
812
+ .after(yearselect);
813
+
814
+ mounth_picker
815
+ .find('.xdsoft_month,.xdsoft_year')
816
+ .on('mousedown.xdsoft', function (event) {
817
+ var select = $(this).find('.xdsoft_select').eq(0),
818
+ val = 0,
819
+ top = 0,
820
+ visible = select.is(':visible'),
821
+ items,
822
+ i;
823
+
824
+ mounth_picker
825
+ .find('.xdsoft_select')
826
+ .hide();
827
+ if (_xdsoft_datetime.currentTime) {
828
+ val = _xdsoft_datetime.currentTime[$(this).hasClass('xdsoft_month') ? 'getMonth' : 'getFullYear']();
829
+ }
830
+
831
+ select[visible ? 'hide' : 'show']();
832
+ for (items = select.find('div.xdsoft_option'), i = 0; i < items.length; i += 1) {
833
+ if (items.eq(i).data('value') === val) {
834
+ break;
835
+ } else {
836
+ top += items[0].offsetHeight;
837
+ }
838
+ }
839
+
840
+ select.xdsoftScroller(top / (select.children()[0].offsetHeight - (select[0].clientHeight)));
841
+ event.stopPropagation();
842
+ return false;
843
+ });
844
+
845
+ mounth_picker
846
+ .find('.xdsoft_select')
847
+ .xdsoftScroller()
848
+ .on('mousedown.xdsoft', function (event) {
849
+ event.stopPropagation();
850
+ event.preventDefault();
851
+ })
852
+ .on('mousedown.xdsoft', '.xdsoft_option', function (event) {
853
+
854
+ if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
855
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
856
+ }
857
+
858
+ var year = _xdsoft_datetime.currentTime.getFullYear();
859
+ if (_xdsoft_datetime && _xdsoft_datetime.currentTime) {
860
+ _xdsoft_datetime.currentTime[$(this).parent().parent().hasClass('xdsoft_monthselect') ? 'setMonth' : 'setFullYear']($(this).data('value'));
861
+ }
862
+
863
+ $(this).parent().parent().hide();
864
+
865
+ datetimepicker.trigger('xchange.xdsoft');
866
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
867
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
868
+ }
869
+
870
+ if (year !== _xdsoft_datetime.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
871
+ options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
872
+ }
873
+ });
874
+
875
+ datetimepicker.setOptions = function (_options) {
876
+ var highlightedDates = {},
877
+ getCaretPos = function (input) {
878
+ try {
879
+ if (document.selection && document.selection.createRange) {
880
+ var range = document.selection.createRange();
881
+ return range.getBookmark().charCodeAt(2) - 2;
882
+ }
883
+ if (input.setSelectionRange) {
884
+ return input.selectionStart;
885
+ }
886
+ } catch (e) {
887
+ return 0;
888
+ }
889
+ },
890
+ setCaretPos = function (node, pos) {
891
+ node = (typeof node === "string" || node instanceof String) ? document.getElementById(node) : node;
892
+ if (!node) {
893
+ return false;
894
+ }
895
+ if (node.createTextRange) {
896
+ var textRange = node.createTextRange();
897
+ textRange.collapse(true);
898
+ textRange.moveEnd('character', pos);
899
+ textRange.moveStart('character', pos);
900
+ textRange.select();
901
+ return true;
902
+ }
903
+ if (node.setSelectionRange) {
904
+ node.setSelectionRange(pos, pos);
905
+ return true;
906
+ }
907
+ return false;
908
+ },
909
+ isValidValue = function (mask, value) {
910
+ var reg = mask
911
+ .replace(/([\[\]\/\{\}\(\)\-\.\+]{1})/g, '\\$1')
912
+ .replace(/_/g, '{digit+}')
913
+ .replace(/([0-9]{1})/g, '{digit$1}')
914
+ .replace(/\{digit([0-9]{1})\}/g, '[0-$1_]{1}')
915
+ .replace(/\{digit[\+]\}/g, '[0-9_]{1}');
916
+ return (new RegExp(reg)).test(value);
917
+ };
918
+ options = $.extend(true, {}, options, _options);
919
+
920
+ if (_options.allowTimes && $.isArray(_options.allowTimes) && _options.allowTimes.length) {
921
+ options.allowTimes = $.extend(true, [], _options.allowTimes);
922
+ }
923
+
924
+ if (_options.weekends && $.isArray(_options.weekends) && _options.weekends.length) {
925
+ options.weekends = $.extend(true, [], _options.weekends);
926
+ }
927
+
928
+ if (_options.highlightedDates && $.isArray(_options.highlightedDates) && _options.highlightedDates.length) {
929
+ $.each(_options.highlightedDates, function (index, value) {
930
+ var splitData = $.map(value.split(','), $.trim),
931
+ exDesc,
932
+ hDate = new HighlightedDate(Date.parseDate(splitData[0], options.formatDate), splitData[1], splitData[2]), // date, desc, style
933
+ keyDate = hDate.date.dateFormat(options.formatDate);
934
+ if (highlightedDates[keyDate] !== undefined) {
935
+ exDesc = highlightedDates[keyDate].desc;
936
+ if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
937
+ highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
938
+ }
939
+ } else {
940
+ highlightedDates[keyDate] = hDate;
941
+ }
942
+ });
943
+
944
+ options.highlightedDates = $.extend(true, [], highlightedDates);
945
+ }
946
+
947
+ if (_options.highlightedPeriods && $.isArray(_options.highlightedPeriods) && _options.highlightedPeriods.length) {
948
+ highlightedDates = $.extend(true, [], options.highlightedDates);
949
+ $.each(_options.highlightedPeriods, function (index, value) {
950
+ var splitData = $.map(value.split(','), $.trim),
951
+ dateTest = Date.parseDate(splitData[0], options.formatDate), // start date
952
+ dateEnd = Date.parseDate(splitData[1], options.formatDate),
953
+ desc = splitData[2],
954
+ hDate,
955
+ keyDate,
956
+ exDesc,
957
+ style = splitData[3];
958
+
959
+ while (dateTest <= dateEnd) {
960
+ hDate = new HighlightedDate(dateTest, desc, style);
961
+ keyDate = dateTest.dateFormat(options.formatDate);
962
+ dateTest.setDate(dateTest.getDate() + 1);
963
+ if (highlightedDates[keyDate] !== undefined) {
964
+ exDesc = highlightedDates[keyDate].desc;
965
+ if (exDesc && exDesc.length && hDate.desc && hDate.desc.length) {
966
+ highlightedDates[keyDate].desc = exDesc + "\n" + hDate.desc;
967
+ }
968
+ } else {
969
+ highlightedDates[keyDate] = hDate;
970
+ }
971
+ }
972
+ });
973
+
974
+ options.highlightedDates = $.extend(true, [], highlightedDates);
975
+ }
976
+
977
+ if (_options.disabledDates && $.isArray(_options.disabledDates) && _options.disabledDates.length) {
978
+ options.disabledDates = $.extend(true, [], _options.disabledDates);
979
+ }
980
+
981
+ if (_options.disabledWeekDays && $.isArray(_options.disabledWeekDays) && _options.disabledWeekDays.length) {
982
+ options.disabledWeekDays = $.extend(true, [], _options.disabledWeekDays);
983
+ }
984
+
985
+ if ((options.open || options.opened) && (!options.inline)) {
986
+ input.trigger('open.xdsoft');
987
+ }
988
+
989
+ if (options.inline) {
990
+ triggerAfterOpen = true;
991
+ datetimepicker.addClass('xdsoft_inline');
992
+ input.after(datetimepicker).hide();
993
+ }
994
+
995
+ if (options.inverseButton) {
996
+ options.next = 'xdsoft_prev';
997
+ options.prev = 'xdsoft_next';
998
+ }
999
+
1000
+ if (options.datepicker) {
1001
+ datepicker.addClass('active');
1002
+ } else {
1003
+ datepicker.removeClass('active');
1004
+ }
1005
+
1006
+ if (options.timepicker) {
1007
+ timepicker.addClass('active');
1008
+ } else {
1009
+ timepicker.removeClass('active');
1010
+ }
1011
+
1012
+ if (options.value) {
1013
+ _xdsoft_datetime.setCurrentTime(options.value);
1014
+ if (input && input.val) {
1015
+ input.val(_xdsoft_datetime.str);
1016
+ }
1017
+ }
1018
+
1019
+ if (isNaN(options.dayOfWeekStart)) {
1020
+ options.dayOfWeekStart = 0;
1021
+ } else {
1022
+ options.dayOfWeekStart = parseInt(options.dayOfWeekStart, 10) % 7;
1023
+ }
1024
+
1025
+ if (!options.timepickerScrollbar) {
1026
+ timeboxparent.xdsoftScroller('hide');
1027
+ }
1028
+
1029
+ if (options.minDate && /^-(.*)$/.test(options.minDate)) {
1030
+ options.minDate = _xdsoft_datetime.strToDateTime(options.minDate).dateFormat(options.formatDate);
1031
+ }
1032
+
1033
+ if (options.maxDate && /^\+(.*)$/.test(options.maxDate)) {
1034
+ options.maxDate = _xdsoft_datetime.strToDateTime(options.maxDate).dateFormat(options.formatDate);
1035
+ }
1036
+
1037
+ applyButton.toggle(options.showApplyButton);
1038
+
1039
+ mounth_picker
1040
+ .find('.xdsoft_today_button')
1041
+ .css('visibility', !options.todayButton ? 'hidden' : 'visible');
1042
+
1043
+ mounth_picker
1044
+ .find('.' + options.prev)
1045
+ .css('visibility', !options.prevButton ? 'hidden' : 'visible');
1046
+
1047
+ mounth_picker
1048
+ .find('.' + options.next)
1049
+ .css('visibility', !options.nextButton ? 'hidden' : 'visible');
1050
+
1051
+ if (options.mask) {
1052
+ input.off('keydown.xdsoft');
1053
+
1054
+ if (options.mask === true) {
1055
+ options.mask = options.format
1056
+ .replace(/Y/g, '9999')
1057
+ .replace(/F/g, '9999')
1058
+ .replace(/m/g, '19')
1059
+ .replace(/d/g, '39')
1060
+ .replace(/H/g, '29')
1061
+ .replace(/i/g, '59')
1062
+ .replace(/s/g, '59');
1063
+ }
1064
+
1065
+ if ($.type(options.mask) === 'string') {
1066
+ if (!isValidValue(options.mask, input.val())) {
1067
+ input.val(options.mask.replace(/[0-9]/g, '_'));
1068
+ }
1069
+
1070
+ input.on('keydown.xdsoft', function (event) {
1071
+ var val = this.value,
1072
+ key = event.which,
1073
+ pos,
1074
+ digit;
1075
+
1076
+ if (((key >= KEY0 && key <= KEY9) || (key >= _KEY0 && key <= _KEY9)) || (key === BACKSPACE || key === DEL)) {
1077
+ pos = getCaretPos(this);
1078
+ digit = (key !== BACKSPACE && key !== DEL) ? String.fromCharCode((_KEY0 <= key && key <= _KEY9) ? key - KEY0 : key) : '_';
1079
+
1080
+ if ((key === BACKSPACE || key === DEL) && pos) {
1081
+ pos -= 1;
1082
+ digit = '_';
1083
+ }
1084
+
1085
+ while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
1086
+ pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
1087
+ }
1088
+
1089
+ val = val.substr(0, pos) + digit + val.substr(pos + 1);
1090
+ if ($.trim(val) === '') {
1091
+ val = options.mask.replace(/[0-9]/g, '_');
1092
+ } else {
1093
+ if (pos === options.mask.length) {
1094
+ event.preventDefault();
1095
+ return false;
1096
+ }
1097
+ }
1098
+
1099
+ pos += (key === BACKSPACE || key === DEL) ? 0 : 1;
1100
+ while (/[^0-9_]/.test(options.mask.substr(pos, 1)) && pos < options.mask.length && pos > 0) {
1101
+ pos += (key === BACKSPACE || key === DEL) ? -1 : 1;
1102
+ }
1103
+
1104
+ if (isValidValue(options.mask, val)) {
1105
+ this.value = val;
1106
+ setCaretPos(this, pos);
1107
+ } else if ($.trim(val) === '') {
1108
+ this.value = options.mask.replace(/[0-9]/g, '_');
1109
+ } else {
1110
+ input.trigger('error_input.xdsoft');
1111
+ }
1112
+ } else {
1113
+ if (([AKEY, CKEY, VKEY, ZKEY, YKEY].indexOf(key) !== -1 && ctrlDown) || [ESC, ARROWUP, ARROWDOWN, ARROWLEFT, ARROWRIGHT, F5, CTRLKEY, TAB, ENTER].indexOf(key) !== -1) {
1114
+ return true;
1115
+ }
1116
+ }
1117
+
1118
+ event.preventDefault();
1119
+ return false;
1120
+ });
1121
+ }
1122
+ }
1123
+ if (options.validateOnBlur) {
1124
+ input
1125
+ .off('blur.xdsoft')
1126
+ .on('blur.xdsoft', function () {
1127
+ if (options.allowBlank && !$.trim($(this).val()).length) {
1128
+ $(this).val(null);
1129
+ datetimepicker.data('xdsoft_datetime').empty();
1130
+ } else if (!Date.parseDate($(this).val(), options.format)) {
1131
+ var splittedHours = +([$(this).val()[0], $(this).val()[1]].join('')),
1132
+ splittedMinutes = +([$(this).val()[2], $(this).val()[3]].join(''));
1133
+
1134
+ // parse the numbers as 0312 => 03:12
1135
+ if (!options.datepicker && options.timepicker && splittedHours >= 0 && splittedHours < 24 && splittedMinutes >= 0 && splittedMinutes < 60) {
1136
+ $(this).val([splittedHours, splittedMinutes].map(function (item) {
1137
+ return item > 9 ? item : '0' + item;
1138
+ }).join(':'));
1139
+ } else {
1140
+ $(this).val((_xdsoft_datetime.now()).dateFormat(options.format));
1141
+ }
1142
+
1143
+ datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val());
1144
+ } else {
1145
+ datetimepicker.data('xdsoft_datetime').setCurrentTime($(this).val());
1146
+ }
1147
+
1148
+ datetimepicker.trigger('changedatetime.xdsoft');
1149
+ });
1150
+ }
1151
+ options.dayOfWeekStartPrev = (options.dayOfWeekStart === 0) ? 6 : options.dayOfWeekStart - 1;
1152
+
1153
+ datetimepicker
1154
+ .trigger('xchange.xdsoft')
1155
+ .trigger('afterOpen.xdsoft');
1156
+ };
1157
+
1158
+ datetimepicker
1159
+ .data('options', options)
1160
+ .on('mousedown.xdsoft', function (event) {
1161
+ event.stopPropagation();
1162
+ event.preventDefault();
1163
+ yearselect.hide();
1164
+ monthselect.hide();
1165
+ return false;
1166
+ });
1167
+
1168
+ //scroll_element = timepicker.find('.xdsoft_time_box');
1169
+ timeboxparent.append(timebox);
1170
+ timeboxparent.xdsoftScroller();
1171
+
1172
+ datetimepicker.on('afterOpen.xdsoft', function () {
1173
+ timeboxparent.xdsoftScroller();
1174
+ });
1175
+
1176
+ datetimepicker
1177
+ .append(datepicker)
1178
+ .append(timepicker);
1179
+
1180
+ if (options.withoutCopyright !== true) {
1181
+ datetimepicker
1182
+ .append(xdsoft_copyright);
1183
+ }
1184
+
1185
+ datepicker
1186
+ .append(mounth_picker)
1187
+ .append(calendar)
1188
+ .append(applyButton);
1189
+
1190
+ $(options.parentID)
1191
+ .append(datetimepicker);
1192
+
1193
+ XDSoft_datetime = function () {
1194
+ var _this = this;
1195
+ _this.now = function (norecursion) {
1196
+ var d = new Date(),
1197
+ date,
1198
+ time;
1199
+
1200
+ if (!norecursion && options.defaultDate) {
1201
+ date = _this.strToDateTime(options.defaultDate);
1202
+ d.setFullYear(date.getFullYear());
1203
+ d.setMonth(date.getMonth());
1204
+ d.setDate(date.getDate());
1205
+ }
1206
+
1207
+ if (options.yearOffset) {
1208
+ d.setFullYear(d.getFullYear() + options.yearOffset);
1209
+ }
1210
+
1211
+ if (!norecursion && options.defaultTime) {
1212
+ time = _this.strtotime(options.defaultTime);
1213
+ d.setHours(time.getHours());
1214
+ d.setMinutes(time.getMinutes());
1215
+ }
1216
+ return d;
1217
+ };
1218
+
1219
+ _this.isValidDate = function (d) {
1220
+ if (Object.prototype.toString.call(d) !== "[object Date]") {
1221
+ return false;
1222
+ }
1223
+ return !isNaN(d.getTime());
1224
+ };
1225
+
1226
+ _this.setCurrentTime = function (dTime) {
1227
+ _this.currentTime = (typeof dTime === 'string') ? _this.strToDateTime(dTime) : _this.isValidDate(dTime) ? dTime : _this.now();
1228
+ datetimepicker.trigger('xchange.xdsoft');
1229
+ };
1230
+
1231
+ _this.empty = function () {
1232
+ _this.currentTime = null;
1233
+ };
1234
+
1235
+ _this.getCurrentTime = function (dTime) {
1236
+ return _this.currentTime;
1237
+ };
1238
+
1239
+ _this.nextMonth = function () {
1240
+
1241
+ if (_this.currentTime === undefined || _this.currentTime === null) {
1242
+ _this.currentTime = _this.now();
1243
+ }
1244
+
1245
+ var month = _this.currentTime.getMonth() + 1,
1246
+ year;
1247
+ if (month === 12) {
1248
+ _this.currentTime.setFullYear(_this.currentTime.getFullYear() + 1);
1249
+ month = 0;
1250
+ }
1251
+
1252
+ year = _this.currentTime.getFullYear();
1253
+
1254
+ _this.currentTime.setDate(
1255
+ Math.min(
1256
+ new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
1257
+ _this.currentTime.getDate()
1258
+ )
1259
+ );
1260
+ _this.currentTime.setMonth(month);
1261
+
1262
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
1263
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1264
+ }
1265
+
1266
+ if (year !== _this.currentTime.getFullYear() && $.isFunction(options.onChangeYear)) {
1267
+ options.onChangeYear.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1268
+ }
1269
+
1270
+ datetimepicker.trigger('xchange.xdsoft');
1271
+ return month;
1272
+ };
1273
+
1274
+ _this.prevMonth = function () {
1275
+
1276
+ if (_this.currentTime === undefined || _this.currentTime === null) {
1277
+ _this.currentTime = _this.now();
1278
+ }
1279
+
1280
+ var month = _this.currentTime.getMonth() - 1;
1281
+ if (month === -1) {
1282
+ _this.currentTime.setFullYear(_this.currentTime.getFullYear() - 1);
1283
+ month = 11;
1284
+ }
1285
+ _this.currentTime.setDate(
1286
+ Math.min(
1287
+ new Date(_this.currentTime.getFullYear(), month + 1, 0).getDate(),
1288
+ _this.currentTime.getDate()
1289
+ )
1290
+ );
1291
+ _this.currentTime.setMonth(month);
1292
+ if (options.onChangeMonth && $.isFunction(options.onChangeMonth)) {
1293
+ options.onChangeMonth.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1294
+ }
1295
+ datetimepicker.trigger('xchange.xdsoft');
1296
+ return month;
1297
+ };
1298
+
1299
+ _this.getWeekOfYear = function (datetime) {
1300
+ var onejan = new Date(datetime.getFullYear(), 0, 1);
1301
+ return Math.ceil((((datetime - onejan) / 86400000) + onejan.getDay() + 1) / 7);
1302
+ };
1303
+
1304
+ _this.strToDateTime = function (sDateTime) {
1305
+ var tmpDate = [], timeOffset, currentTime;
1306
+
1307
+ if (sDateTime && sDateTime instanceof Date && _this.isValidDate(sDateTime)) {
1308
+ return sDateTime;
1309
+ }
1310
+
1311
+ tmpDate = /^(\+|\-)(.*)$/.exec(sDateTime);
1312
+ if (tmpDate) {
1313
+ tmpDate[2] = Date.parseDate(tmpDate[2], options.formatDate);
1314
+ }
1315
+ if (tmpDate && tmpDate[2]) {
1316
+ timeOffset = tmpDate[2].getTime() - (tmpDate[2].getTimezoneOffset()) * 60000;
1317
+ currentTime = new Date((_this.now(true)).getTime() + parseInt(tmpDate[1] + '1', 10) * timeOffset);
1318
+ } else {
1319
+ currentTime = sDateTime ? Date.parseDate(sDateTime, options.format) : _this.now();
1320
+ }
1321
+
1322
+ if (!_this.isValidDate(currentTime)) {
1323
+ currentTime = _this.now();
1324
+ }
1325
+
1326
+ return currentTime;
1327
+ };
1328
+
1329
+ _this.strToDate = function (sDate) {
1330
+ if (sDate && sDate instanceof Date && _this.isValidDate(sDate)) {
1331
+ return sDate;
1332
+ }
1333
+
1334
+ var currentTime = sDate ? Date.parseDate(sDate, options.formatDate) : _this.now(true);
1335
+ if (!_this.isValidDate(currentTime)) {
1336
+ currentTime = _this.now(true);
1337
+ }
1338
+ return currentTime;
1339
+ };
1340
+
1341
+ _this.strtotime = function (sTime) {
1342
+ if (sTime && sTime instanceof Date && _this.isValidDate(sTime)) {
1343
+ return sTime;
1344
+ }
1345
+ var currentTime = sTime ? Date.parseDate(sTime, options.formatTime) : _this.now(true);
1346
+ if (!_this.isValidDate(currentTime)) {
1347
+ currentTime = _this.now(true);
1348
+ }
1349
+ return currentTime;
1350
+ };
1351
+
1352
+ _this.str = function () {
1353
+ return _this.currentTime.dateFormat(options.format);
1354
+ };
1355
+ _this.currentTime = this.now();
1356
+ };
1357
+
1358
+ _xdsoft_datetime = new XDSoft_datetime();
1359
+
1360
+ applyButton.on('click', function (e) {//pathbrite
1361
+ e.preventDefault();
1362
+ datetimepicker.data('changed', true);
1363
+ _xdsoft_datetime.setCurrentTime(getCurrentValue());
1364
+ input.val(_xdsoft_datetime.str());
1365
+ datetimepicker.trigger('close.xdsoft');
1366
+ });
1367
+ mounth_picker
1368
+ .find('.xdsoft_today_button')
1369
+ .on('mousedown.xdsoft', function () {
1370
+ datetimepicker.data('changed', true);
1371
+ _xdsoft_datetime.setCurrentTime(0);
1372
+ datetimepicker.trigger('afterOpen.xdsoft');
1373
+ }).on('dblclick.xdsoft', function () {
1374
+ var currentDate = _xdsoft_datetime.getCurrentTime(), minDate, maxDate;
1375
+ currentDate = new Date(currentDate.getFullYear(), currentDate.getMonth(), currentDate.getDate());
1376
+ minDate = _xdsoft_datetime.strToDate(options.minDate);
1377
+ minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
1378
+ if (currentDate < minDate) {
1379
+ return;
1380
+ }
1381
+ maxDate = _xdsoft_datetime.strToDate(options.maxDate);
1382
+ maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate());
1383
+ if (currentDate > maxDate) {
1384
+ return;
1385
+ }
1386
+ input.val(_xdsoft_datetime.str());
1387
+ datetimepicker.trigger('close.xdsoft');
1388
+ });
1389
+ mounth_picker
1390
+ .find('.xdsoft_prev,.xdsoft_next')
1391
+ .on('mousedown.xdsoft', function () {
1392
+ var $this = $(this),
1393
+ timer = 0,
1394
+ stop = false;
1395
+
1396
+ (function arguments_callee1(v) {
1397
+ if ($this.hasClass(options.next)) {
1398
+ _xdsoft_datetime.nextMonth();
1399
+ } else if ($this.hasClass(options.prev)) {
1400
+ _xdsoft_datetime.prevMonth();
1401
+ }
1402
+ if (options.monthChangeSpinner) {
1403
+ if (!stop) {
1404
+ timer = setTimeout(arguments_callee1, v || 100);
1405
+ }
1406
+ }
1407
+ }(500));
1408
+
1409
+ $([document.body, window]).on('mouseup.xdsoft', function arguments_callee2() {
1410
+ clearTimeout(timer);
1411
+ stop = true;
1412
+ $([document.body, window]).off('mouseup.xdsoft', arguments_callee2);
1413
+ });
1414
+ });
1415
+
1416
+ timepicker
1417
+ .find('.xdsoft_prev,.xdsoft_next')
1418
+ .on('mousedown.xdsoft', function () {
1419
+ var $this = $(this),
1420
+ timer = 0,
1421
+ stop = false,
1422
+ period = 110;
1423
+ (function arguments_callee4(v) {
1424
+ var pheight = timeboxparent[0].clientHeight,
1425
+ height = timebox[0].offsetHeight,
1426
+ top = Math.abs(parseInt(timebox.css('marginTop'), 10));
1427
+ if ($this.hasClass(options.next) && (height - pheight) - options.timeHeightInTimePicker >= top) {
1428
+ timebox.css('marginTop', '-' + (top + options.timeHeightInTimePicker) + 'px');
1429
+ } else if ($this.hasClass(options.prev) && top - options.timeHeightInTimePicker >= 0) {
1430
+ timebox.css('marginTop', '-' + (top - options.timeHeightInTimePicker) + 'px');
1431
+ }
1432
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [Math.abs(parseInt(timebox.css('marginTop'), 10) / (height - pheight))]);
1433
+ period = (period > 10) ? 10 : period - 10;
1434
+ if (!stop) {
1435
+ timer = setTimeout(arguments_callee4, v || period);
1436
+ }
1437
+ }(500));
1438
+ $([document.body, window]).on('mouseup.xdsoft', function arguments_callee5() {
1439
+ clearTimeout(timer);
1440
+ stop = true;
1441
+ $([document.body, window])
1442
+ .off('mouseup.xdsoft', arguments_callee5);
1443
+ });
1444
+ });
1445
+
1446
+ xchangeTimer = 0;
1447
+ // base handler - generating a calendar and timepicker
1448
+ datetimepicker
1449
+ .on('xchange.xdsoft', function (event) {
1450
+ clearTimeout(xchangeTimer);
1451
+ xchangeTimer = setTimeout(function () {
1452
+
1453
+ if (_xdsoft_datetime.currentTime === undefined || _xdsoft_datetime.currentTime === null) {
1454
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1455
+ }
1456
+
1457
+ var table = '',
1458
+ start = new Date(_xdsoft_datetime.currentTime.getFullYear(), _xdsoft_datetime.currentTime.getMonth(), 1, 12, 0, 0),
1459
+ i = 0,
1460
+ j,
1461
+ today = _xdsoft_datetime.now(),
1462
+ maxDate = false,
1463
+ minDate = false,
1464
+ hDate,
1465
+ day,
1466
+ d,
1467
+ y,
1468
+ m,
1469
+ w,
1470
+ classes = [],
1471
+ customDateSettings,
1472
+ newRow = true,
1473
+ time = '',
1474
+ h = '',
1475
+ line_time,
1476
+ description;
1477
+
1478
+ while (start.getDay() !== options.dayOfWeekStart) {
1479
+ start.setDate(start.getDate() - 1);
1480
+ }
1481
+
1482
+ table += '<table><thead><tr>';
1483
+
1484
+ if (options.weeks) {
1485
+ table += '<th></th>';
1486
+ }
1487
+
1488
+ for (j = 0; j < 7; j += 1) {
1489
+ table += '<th>' + options.i18n[options.lang].dayOfWeek[(j + options.dayOfWeekStart) % 7] + '</th>';
1490
+ }
1491
+
1492
+ table += '</tr></thead>';
1493
+ table += '<tbody>';
1494
+
1495
+ if (options.maxDate !== false) {
1496
+ maxDate = _xdsoft_datetime.strToDate(options.maxDate);
1497
+ maxDate = new Date(maxDate.getFullYear(), maxDate.getMonth(), maxDate.getDate(), 23, 59, 59, 999);
1498
+ }
1499
+
1500
+ if (options.minDate !== false) {
1501
+ minDate = _xdsoft_datetime.strToDate(options.minDate);
1502
+ minDate = new Date(minDate.getFullYear(), minDate.getMonth(), minDate.getDate());
1503
+ }
1504
+
1505
+ while (i < _xdsoft_datetime.currentTime.countDaysInMonth() || start.getDay() !== options.dayOfWeekStart || _xdsoft_datetime.currentTime.getMonth() === start.getMonth()) {
1506
+ classes = [];
1507
+ i += 1;
1508
+
1509
+ day = start.getDay();
1510
+ d = start.getDate();
1511
+ y = start.getFullYear();
1512
+ m = start.getMonth();
1513
+ w = _xdsoft_datetime.getWeekOfYear(start);
1514
+ description = '';
1515
+
1516
+ classes.push('xdsoft_date');
1517
+
1518
+ if (options.beforeShowDay && $.isFunction(options.beforeShowDay.call)) {
1519
+ customDateSettings = options.beforeShowDay.call(datetimepicker, start);
1520
+ } else {
1521
+ customDateSettings = null;
1522
+ }
1523
+
1524
+ if ((maxDate !== false && start > maxDate) || (minDate !== false && start < minDate) || (customDateSettings && customDateSettings[0] === false)) {
1525
+ classes.push('xdsoft_disabled');
1526
+ } else if (options.disabledDates.indexOf(start.dateFormat(options.formatDate)) !== -1) {
1527
+ classes.push('xdsoft_disabled');
1528
+ } else if (options.disabledWeekDays.indexOf(day) !== -1) {
1529
+ classes.push('xdsoft_disabled');
1530
+ }
1531
+
1532
+ if (customDateSettings && customDateSettings[1] !== "") {
1533
+ classes.push(customDateSettings[1]);
1534
+ }
1535
+
1536
+ if (_xdsoft_datetime.currentTime.getMonth() !== m) {
1537
+ classes.push('xdsoft_other_month');
1538
+ }
1539
+
1540
+ if ((options.defaultSelect || datetimepicker.data('changed')) && _xdsoft_datetime.currentTime.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) {
1541
+ classes.push('xdsoft_current');
1542
+ }
1543
+
1544
+ if (today.dateFormat(options.formatDate) === start.dateFormat(options.formatDate)) {
1545
+ classes.push('xdsoft_today');
1546
+ }
1547
+
1548
+ if (start.getDay() === 0 || start.getDay() === 6 || options.weekends.indexOf(start.dateFormat(options.formatDate)) !== -1) {
1549
+ classes.push('xdsoft_weekend');
1550
+ }
1551
+
1552
+ if (options.highlightedDates[start.dateFormat(options.formatDate)] !== undefined) {
1553
+ hDate = options.highlightedDates[start.dateFormat(options.formatDate)];
1554
+ classes.push(hDate.style === undefined ? 'xdsoft_highlighted_default' : hDate.style);
1555
+ description = hDate.desc === undefined ? '' : hDate.desc;
1556
+ }
1557
+
1558
+ if (options.beforeShowDay && $.isFunction(options.beforeShowDay)) {
1559
+ classes.push(options.beforeShowDay(start));
1560
+ }
1561
+
1562
+ if (newRow) {
1563
+ table += '<tr>';
1564
+ newRow = false;
1565
+ if (options.weeks) {
1566
+ table += '<th>' + w + '</th>';
1567
+ }
1568
+ }
1569
+
1570
+ table += '<td data-date="' + d + '" data-month="' + m + '" data-year="' + y + '"' + ' class="xdsoft_date xdsoft_day_of_week' + start.getDay() + ' ' + classes.join(' ') + '" title="' + description + '">' +
1571
+ '<div>' + d + '</div>' +
1572
+ '</td>';
1573
+
1574
+ if (start.getDay() === options.dayOfWeekStartPrev) {
1575
+ table += '</tr>';
1576
+ newRow = true;
1577
+ }
1578
+
1579
+ start.setDate(d + 1);
1580
+ }
1581
+ table += '</tbody></table>';
1582
+
1583
+ calendar.html(table);
1584
+
1585
+ mounth_picker.find('.xdsoft_label span').eq(0).text(options.i18n[options.lang].months[_xdsoft_datetime.currentTime.getMonth()]);
1586
+ mounth_picker.find('.xdsoft_label span').eq(1).text(_xdsoft_datetime.currentTime.getFullYear());
1587
+
1588
+ // generate timebox
1589
+ time = '';
1590
+ h = '';
1591
+ m = '';
1592
+ line_time = function line_time(h, m) {
1593
+ var now = _xdsoft_datetime.now(), optionDateTime, current_time;
1594
+ now.setHours(h);
1595
+ h = parseInt(now.getHours(), 10);
1596
+ now.setMinutes(m);
1597
+ m = parseInt(now.getMinutes(), 10);
1598
+ optionDateTime = new Date(_xdsoft_datetime.currentTime);
1599
+ optionDateTime.setHours(h);
1600
+ optionDateTime.setMinutes(m);
1601
+ classes = [];
1602
+ if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || (options.maxTime !== false && _xdsoft_datetime.strtotime(options.maxTime).getTime() < now.getTime()) || (options.minTime !== false && _xdsoft_datetime.strtotime(options.minTime).getTime() > now.getTime())) {
1603
+ classes.push('xdsoft_disabled');
1604
+ }
1605
+ if ((options.minDateTime !== false && options.minDateTime > optionDateTime) || ((options.disabledMinTime !== false && now.getTime() > _xdsoft_datetime.strtotime(options.disabledMinTime).getTime()) && (options.disabledMaxTime !== false && now.getTime() < _xdsoft_datetime.strtotime(options.disabledMaxTime).getTime()))) {
1606
+ classes.push('xdsoft_disabled');
1607
+ }
1608
+
1609
+ current_time = new Date(_xdsoft_datetime.currentTime);
1610
+ current_time.setHours(parseInt(_xdsoft_datetime.currentTime.getHours(), 10));
1611
+ current_time.setMinutes(Math[options.roundTime](_xdsoft_datetime.currentTime.getMinutes() / options.step) * options.step);
1612
+
1613
+ if ((options.initTime || options.defaultSelect || datetimepicker.data('changed')) && current_time.getHours() === parseInt(h, 10) && (options.step > 59 || current_time.getMinutes() === parseInt(m, 10))) {
1614
+ if (options.defaultSelect || datetimepicker.data('changed')) {
1615
+ classes.push('xdsoft_current');
1616
+ } else if (options.initTime) {
1617
+ classes.push('xdsoft_init_time');
1618
+ }
1619
+ }
1620
+ if (parseInt(today.getHours(), 10) === parseInt(h, 10) && parseInt(today.getMinutes(), 10) === parseInt(m, 10)) {
1621
+ classes.push('xdsoft_today');
1622
+ }
1623
+ time += '<div class="xdsoft_time ' + classes.join(' ') + '" data-hour="' + h + '" data-minute="' + m + '">' + now.dateFormat(options.formatTime) + '</div>';
1624
+ };
1625
+
1626
+ if (!options.allowTimes || !$.isArray(options.allowTimes) || !options.allowTimes.length) {
1627
+ for (i = 0, j = 0; i < (options.hours12 ? 12 : 24); i += 1) {
1628
+ for (j = 0; j < 60; j += options.step) {
1629
+ h = (i < 10 ? '0' : '') + i;
1630
+ m = (j < 10 ? '0' : '') + j;
1631
+ line_time(h, m);
1632
+ }
1633
+ }
1634
+ } else {
1635
+ for (i = 0; i < options.allowTimes.length; i += 1) {
1636
+ h = _xdsoft_datetime.strtotime(options.allowTimes[i]).getHours();
1637
+ m = _xdsoft_datetime.strtotime(options.allowTimes[i]).getMinutes();
1638
+ line_time(h, m);
1639
+ }
1640
+ }
1641
+
1642
+ timebox.html(time);
1643
+
1644
+ opt = '';
1645
+ i = 0;
1646
+
1647
+ for (i = parseInt(options.yearStart, 10) + options.yearOffset; i <= parseInt(options.yearEnd, 10) + options.yearOffset; i += 1) {
1648
+ opt += '<div class="xdsoft_option ' + (_xdsoft_datetime.currentTime.getFullYear() === i ? 'xdsoft_current' : '') + '" data-value="' + i + '">' + i + '</div>';
1649
+ }
1650
+ yearselect.children().eq(0)
1651
+ .html(opt);
1652
+
1653
+ for (i = parseInt(options.monthStart, 10), opt = ''; i <= parseInt(options.monthEnd, 10); i += 1) {
1654
+ opt += '<div class="xdsoft_option ' + (_xdsoft_datetime.currentTime.getMonth() === i ? 'xdsoft_current' : '') + '" data-value="' + i + '">' + options.i18n[options.lang].months[i] + '</div>';
1655
+ }
1656
+ monthselect.children().eq(0).html(opt);
1657
+ $(datetimepicker)
1658
+ .trigger('generate.xdsoft');
1659
+ }, 10);
1660
+ event.stopPropagation();
1661
+ })
1662
+ .on('afterOpen.xdsoft', function () {
1663
+ if (options.timepicker) {
1664
+ var classType, pheight, height, top;
1665
+ if (timebox.find('.xdsoft_current').length) {
1666
+ classType = '.xdsoft_current';
1667
+ } else if (timebox.find('.xdsoft_init_time').length) {
1668
+ classType = '.xdsoft_init_time';
1669
+ }
1670
+ if (classType) {
1671
+ pheight = timeboxparent[0].clientHeight;
1672
+ height = timebox[0].offsetHeight;
1673
+ top = timebox.find(classType).index() * options.timeHeightInTimePicker + 1;
1674
+ if ((height - pheight) < top) {
1675
+ top = height - pheight;
1676
+ }
1677
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [parseInt(top, 10) / (height - pheight)]);
1678
+ } else {
1679
+ timeboxparent.trigger('scroll_element.xdsoft_scroller', [0]);
1680
+ }
1681
+ }
1682
+ });
1683
+
1684
+ timerclick = 0;
1685
+ calendar
1686
+ .on('click.xdsoft', 'td', function (xdevent) {
1687
+ xdevent.stopPropagation(); // Prevents closing of Pop-ups, Modals and Flyouts in Bootstrap
1688
+ timerclick += 1;
1689
+ var $this = $(this),
1690
+ currentTime = _xdsoft_datetime.currentTime;
1691
+
1692
+ if (currentTime === undefined || currentTime === null) {
1693
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1694
+ currentTime = _xdsoft_datetime.currentTime;
1695
+ }
1696
+
1697
+ if ($this.hasClass('xdsoft_disabled')) {
1698
+ return false;
1699
+ }
1700
+
1701
+ currentTime.setDate(1);
1702
+ currentTime.setFullYear($this.data('year'));
1703
+ currentTime.setMonth($this.data('month'));
1704
+ currentTime.setDate($this.data('date'));
1705
+
1706
+ datetimepicker.trigger('select.xdsoft', [currentTime]);
1707
+
1708
+ input.val(_xdsoft_datetime.str());
1709
+ if ((timerclick > 1 || (options.closeOnDateSelect === true || (options.closeOnDateSelect === false && !options.timepicker))) && !options.inline) {
1710
+ datetimepicker.trigger('close.xdsoft');
1711
+ }
1712
+
1713
+ if (options.onSelectDate && $.isFunction(options.onSelectDate)) {
1714
+ options.onSelectDate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
1715
+ }
1716
+
1717
+ datetimepicker.data('changed', true);
1718
+ datetimepicker.trigger('xchange.xdsoft');
1719
+ datetimepicker.trigger('changedatetime.xdsoft');
1720
+ setTimeout(function () {
1721
+ timerclick = 0;
1722
+ }, 200);
1723
+ });
1724
+
1725
+ timebox
1726
+ .on('click.xdsoft', 'div', function (xdevent) {
1727
+ xdevent.stopPropagation();
1728
+ var $this = $(this),
1729
+ currentTime = _xdsoft_datetime.currentTime;
1730
+
1731
+ if (currentTime === undefined || currentTime === null) {
1732
+ _xdsoft_datetime.currentTime = _xdsoft_datetime.now();
1733
+ currentTime = _xdsoft_datetime.currentTime;
1734
+ }
1735
+
1736
+ if ($this.hasClass('xdsoft_disabled')) {
1737
+ return false;
1738
+ }
1739
+ currentTime.setHours($this.data('hour'));
1740
+ currentTime.setMinutes($this.data('minute'));
1741
+ datetimepicker.trigger('select.xdsoft', [currentTime]);
1742
+
1743
+ datetimepicker.data('input').val(_xdsoft_datetime.str());
1744
+
1745
+ if (options.inline !== true && options.closeOnTimeSelect === true) {
1746
+ datetimepicker.trigger('close.xdsoft');
1747
+ }
1748
+
1749
+ if (options.onSelectTime && $.isFunction(options.onSelectTime)) {
1750
+ options.onSelectTime.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), xdevent);
1751
+ }
1752
+ datetimepicker.data('changed', true);
1753
+ datetimepicker.trigger('xchange.xdsoft');
1754
+ datetimepicker.trigger('changedatetime.xdsoft');
1755
+ });
1756
+
1757
+
1758
+ datepicker
1759
+ .on('mousewheel.xdsoft', function (event) {
1760
+ if (!options.scrollMonth) {
1761
+ return true;
1762
+ }
1763
+ if (event.deltaY < 0) {
1764
+ _xdsoft_datetime.nextMonth();
1765
+ } else {
1766
+ _xdsoft_datetime.prevMonth();
1767
+ }
1768
+ return false;
1769
+ });
1770
+
1771
+ input
1772
+ .on('mousewheel.xdsoft', function (event) {
1773
+ if (!options.scrollInput) {
1774
+ return true;
1775
+ }
1776
+ if (!options.datepicker && options.timepicker) {
1777
+ current_time_index = timebox.find('.xdsoft_current').length ? timebox.find('.xdsoft_current').eq(0).index() : 0;
1778
+ if (current_time_index + event.deltaY >= 0 && current_time_index + event.deltaY < timebox.children().length) {
1779
+ current_time_index += event.deltaY;
1780
+ }
1781
+ if (timebox.children().eq(current_time_index).length) {
1782
+ timebox.children().eq(current_time_index).trigger('mousedown');
1783
+ }
1784
+ return false;
1785
+ }
1786
+ if (options.datepicker && !options.timepicker) {
1787
+ datepicker.trigger(event, [event.deltaY, event.deltaX, event.deltaY]);
1788
+ if (input.val) {
1789
+ input.val(_xdsoft_datetime.str());
1790
+ }
1791
+ datetimepicker.trigger('changedatetime.xdsoft');
1792
+ return false;
1793
+ }
1794
+ });
1795
+
1796
+ datetimepicker
1797
+ .on('changedatetime.xdsoft', function (event) {
1798
+ if (options.onChangeDateTime && $.isFunction(options.onChangeDateTime)) {
1799
+ var $input = datetimepicker.data('input');
1800
+ options.onChangeDateTime.call(datetimepicker, _xdsoft_datetime.currentTime, $input, event);
1801
+ delete options.value;
1802
+ $input.trigger('change');
1803
+ }
1804
+ })
1805
+ .on('generate.xdsoft', function () {
1806
+ if (options.onGenerate && $.isFunction(options.onGenerate)) {
1807
+ options.onGenerate.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'));
1808
+ }
1809
+ if (triggerAfterOpen) {
1810
+ datetimepicker.trigger('afterOpen.xdsoft');
1811
+ triggerAfterOpen = false;
1812
+ }
1813
+ })
1814
+ .on('click.xdsoft', function (xdevent) {
1815
+ xdevent.stopPropagation();
1816
+ });
1817
+
1818
+ current_time_index = 0;
1819
+
1820
+ setPos = function () {
1821
+ var offset = datetimepicker.data('input').offset(), top = offset.top + datetimepicker.data('input')[0].offsetHeight - 1, left = offset.left, position = "absolute", node;
1822
+ if (options.fixed) {
1823
+ top -= $(window).scrollTop();
1824
+ left -= $(window).scrollLeft();
1825
+ position = "fixed";
1826
+ } else {
1827
+ if (top + datetimepicker[0].offsetHeight > $(window).height() + $(window).scrollTop()) {
1828
+ top = offset.top - datetimepicker[0].offsetHeight + 1;
1829
+ }
1830
+ if (top < 0) {
1831
+ top = 0;
1832
+ }
1833
+ if (left + datetimepicker[0].offsetWidth > $(window).width()) {
1834
+ left = $(window).width() - datetimepicker[0].offsetWidth;
1835
+ }
1836
+ }
1837
+
1838
+ node = datetimepicker[0];
1839
+ do {
1840
+ node = node.parentNode;
1841
+ if (window.getComputedStyle(node).getPropertyValue('position') === 'relative' && $(window).width() >= node.offsetWidth) {
1842
+ left = left - (($(window).width() - node.offsetWidth) / 2);
1843
+ break;
1844
+ }
1845
+ } while (node.nodeName !== 'HTML');
1846
+ datetimepicker.css({
1847
+ left: left,
1848
+ top: top,
1849
+ position: position
1850
+ });
1851
+ };
1852
+ datetimepicker
1853
+ .on('open.xdsoft', function (event) {
1854
+ var onShow = true;
1855
+ if (options.onShow && $.isFunction(options.onShow)) {
1856
+ onShow = options.onShow.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
1857
+ }
1858
+ if (onShow !== false) {
1859
+ datetimepicker.show();
1860
+ setPos();
1861
+ $(window)
1862
+ .off('resize.xdsoft', setPos)
1863
+ .on('resize.xdsoft', setPos);
1864
+
1865
+ if (options.closeOnWithoutClick) {
1866
+ $([document.body, window]).on('mousedown.xdsoft', function arguments_callee6() {
1867
+ datetimepicker.trigger('close.xdsoft');
1868
+ $([document.body, window]).off('mousedown.xdsoft', arguments_callee6);
1869
+ });
1870
+ }
1871
+ }
1872
+ })
1873
+ .on('close.xdsoft', function (event) {
1874
+ var onClose = true;
1875
+ mounth_picker
1876
+ .find('.xdsoft_month,.xdsoft_year')
1877
+ .find('.xdsoft_select')
1878
+ .hide();
1879
+ if (options.onClose && $.isFunction(options.onClose)) {
1880
+ onClose = options.onClose.call(datetimepicker, _xdsoft_datetime.currentTime, datetimepicker.data('input'), event);
1881
+ }
1882
+ if (onClose !== false && !options.opened && !options.inline) {
1883
+ datetimepicker.hide();
1884
+ }
1885
+ event.stopPropagation();
1886
+ })
1887
+ .on('toggle.xdsoft', function (event) {
1888
+ if (datetimepicker.is(':visible')) {
1889
+ datetimepicker.trigger('close.xdsoft');
1890
+ } else {
1891
+ datetimepicker.trigger('open.xdsoft');
1892
+ }
1893
+ })
1894
+ .data('input', input);
1895
+
1896
+ timer = 0;
1897
+ timer1 = 0;
1898
+
1899
+ datetimepicker.data('xdsoft_datetime', _xdsoft_datetime);
1900
+ datetimepicker.setOptions(options);
1901
+
1902
+ function getCurrentValue() {
1903
+ var ct = false, time;
1904
+
1905
+ if (options.startDate) {
1906
+ ct = _xdsoft_datetime.strToDate(options.startDate);
1907
+ } else {
1908
+ ct = options.value || ((input && input.val && input.val()) ? input.val() : '');
1909
+ if (ct) {
1910
+ ct = _xdsoft_datetime.strToDateTime(ct);
1911
+ } else if (options.defaultDate) {
1912
+ ct = _xdsoft_datetime.strToDateTime(options.defaultDate);
1913
+ if (options.defaultTime) {
1914
+ time = _xdsoft_datetime.strtotime(options.defaultTime);
1915
+ ct.setHours(time.getHours());
1916
+ ct.setMinutes(time.getMinutes());
1917
+ }
1918
+ }
1919
+ }
1920
+
1921
+ if (ct && _xdsoft_datetime.isValidDate(ct)) {
1922
+ datetimepicker.data('changed', true);
1923
+ } else {
1924
+ ct = '';
1925
+ }
1926
+
1927
+ return ct || 0;
1928
+ }
1929
+
1930
+ _xdsoft_datetime.setCurrentTime(getCurrentValue());
1931
+
1932
+ input
1933
+ .data('xdsoft_datetimepicker', datetimepicker)
1934
+ .on('open.xdsoft focusin.xdsoft mousedown.xdsoft', function (event) {
1935
+ if (input.is(':disabled') || (input.data('xdsoft_datetimepicker').is(':visible') && options.closeOnInputClick)) {
1936
+ return;
1937
+ }
1938
+ clearTimeout(timer);
1939
+ timer = setTimeout(function () {
1940
+ if (input.is(':disabled')) {
1941
+ return;
1942
+ }
1943
+
1944
+ triggerAfterOpen = true;
1945
+ _xdsoft_datetime.setCurrentTime(getCurrentValue());
1946
+
1947
+ datetimepicker.trigger('open.xdsoft');
1948
+ }, 100);
1949
+ })
1950
+ .on('keydown.xdsoft', function (event) {
1951
+ var val = this.value, elementSelector,
1952
+ key = event.which;
1953
+ if ([ENTER].indexOf(key) !== -1 && options.enterLikeTab) {
1954
+ elementSelector = $("input:visible,textarea:visible");
1955
+ datetimepicker.trigger('close.xdsoft');
1956
+ elementSelector.eq(elementSelector.index(this) + 1).focus();
1957
+ return false;
1958
+ }
1959
+ if ([TAB].indexOf(key) !== -1) {
1960
+ datetimepicker.trigger('close.xdsoft');
1961
+ return true;
1962
+ }
1963
+ });
1964
+ };
1965
+ destroyDateTimePicker = function (input) {
1966
+ var datetimepicker = input.data('xdsoft_datetimepicker');
1967
+ if (datetimepicker) {
1968
+ datetimepicker.data('xdsoft_datetime', null);
1969
+ datetimepicker.remove();
1970
+ input
1971
+ .data('xdsoft_datetimepicker', null)
1972
+ .off('.xdsoft');
1973
+ $(window).off('resize.xdsoft');
1974
+ $([window, document.body]).off('mousedown.xdsoft');
1975
+ if (input.unmousewheel) {
1976
+ input.unmousewheel();
1977
+ }
1978
+ }
1979
+ };
1980
+ $(document)
1981
+ .off('keydown.xdsoftctrl keyup.xdsoftctrl')
1982
+ .on('keydown.xdsoftctrl', function (e) {
1983
+ if (e.keyCode === CTRLKEY) {
1984
+ ctrlDown = true;
1985
+ }
1986
+ })
1987
+ .on('keyup.xdsoftctrl', function (e) {
1988
+ if (e.keyCode === CTRLKEY) {
1989
+ ctrlDown = false;
1990
+ }
1991
+ });
1992
+ return this.each(function () {
1993
+ var datetimepicker = $(this).data('xdsoft_datetimepicker'), $input;
1994
+ if (datetimepicker) {
1995
+ if ($.type(opt) === 'string') {
1996
+ switch (opt) {
1997
+ case 'show':
1998
+ $(this).select().focus();
1999
+ datetimepicker.trigger('open.xdsoft');
2000
+ break;
2001
+ case 'hide':
2002
+ datetimepicker.trigger('close.xdsoft');
2003
+ break;
2004
+ case 'toggle':
2005
+ datetimepicker.trigger('toggle.xdsoft');
2006
+ break;
2007
+ case 'destroy':
2008
+ destroyDateTimePicker($(this));
2009
+ break;
2010
+ case 'reset':
2011
+ this.value = this.defaultValue;
2012
+ if (!this.value || !datetimepicker.data('xdsoft_datetime').isValidDate(Date.parseDate(this.value, options.format))) {
2013
+ datetimepicker.data('changed', false);
2014
+ }
2015
+ datetimepicker.data('xdsoft_datetime').setCurrentTime(this.value);
2016
+ break;
2017
+ case 'validate':
2018
+ $input = datetimepicker.data('input');
2019
+ $input.trigger('blur.xdsoft');
2020
+ break;
2021
+ }
2022
+ } else {
2023
+ datetimepicker
2024
+ .setOptions(opt);
2025
+ }
2026
+ return 0;
2027
+ }
2028
+ if ($.type(opt) !== 'string') {
2029
+ if (!options.lazyInit || options.open || options.inline) {
2030
+ createDateTimePicker($(this));
2031
+ } else {
2032
+ lazyInit($(this));
2033
+ }
2034
+ }
2035
+ });
2036
+ };
2037
+ $.fn.datetimepicker.defaults = default_options;
2038
+ }(jQuery));
2039
+
2040
+ function HighlightedDate(date, desc, style) {
2041
+ "use strict";
2042
+ this.date = date;
2043
+ this.desc = desc;
2044
+ this.style = style;
2045
+ }
2046
+
2047
+ (function () {
2048
+
2049
+ /*! Copyright (c) 2013 Brandon Aaron (http://brandon.aaron.sh)
2050
+ * Licensed under the MIT License (LICENSE.txt).
2051
+ *
2052
+ * Version: 3.1.12
2053
+ *
2054
+ * Requires: jQuery 1.2.2+
2055
+ */
2056
+ !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):"object"==typeof exports?module.exports=a:a(jQuery)}(function(a){function b(b){var g=b||window.event,h=i.call(arguments,1),j=0,l=0,m=0,n=0,o=0,p=0;if(b=a.event.fix(g),b.type="mousewheel","detail"in g&&(m=-1*g.detail),"wheelDelta"in g&&(m=g.wheelDelta),"wheelDeltaY"in g&&(m=g.wheelDeltaY),"wheelDeltaX"in g&&(l=-1*g.wheelDeltaX),"axis"in g&&g.axis===g.HORIZONTAL_AXIS&&(l=-1*m,m=0),j=0===m?l:m,"deltaY"in g&&(m=-1*g.deltaY,j=m),"deltaX"in g&&(l=g.deltaX,0===m&&(j=-1*l)),0!==m||0!==l){if(1===g.deltaMode){var q=a.data(this,"mousewheel-line-height");j*=q,m*=q,l*=q}else if(2===g.deltaMode){var r=a.data(this,"mousewheel-page-height");j*=r,m*=r,l*=r}if(n=Math.max(Math.abs(m),Math.abs(l)),(!f||f>n)&&(f=n,d(g,n)&&(f/=40)),d(g,n)&&(j/=40,l/=40,m/=40),j=Math[j>=1?"floor":"ceil"](j/f),l=Math[l>=1?"floor":"ceil"](l/f),m=Math[m>=1?"floor":"ceil"](m/f),k.settings.normalizeOffset&&this.getBoundingClientRect){var s=this.getBoundingClientRect();o=b.clientX-s.left,p=b.clientY-s.top}return b.deltaX=l,b.deltaY=m,b.deltaFactor=f,b.offsetX=o,b.offsetY=p,b.deltaMode=0,h.unshift(b,j,l,m),e&&clearTimeout(e),e=setTimeout(c,200),(a.event.dispatch||a.event.handle).apply(this,h)}}function c(){f=null}function d(a,b){return k.settings.adjustOldDeltas&&"mousewheel"===a.type&&b%120===0}var e,f,g=["wheel","mousewheel","DOMMouseScroll","MozMousePixelScroll"],h="onwheel"in document||document.documentMode>=9?["wheel"]:["mousewheel","DomMouseScroll","MozMousePixelScroll"],i=Array.prototype.slice;if(a.event.fixHooks)for(var j=g.length;j;)a.event.fixHooks[g[--j]]=a.event.mouseHooks;var k=a.event.special.mousewheel={version:"3.1.12",setup:function(){if(this.addEventListener)for(var c=h.length;c;)this.addEventListener(h[--c],b,!1);else this.onmousewheel=b;a.data(this,"mousewheel-line-height",k.getLineHeight(this)),a.data(this,"mousewheel-page-height",k.getPageHeight(this))},teardown:function(){if(this.removeEventListener)for(var c=h.length;c;)this.removeEventListener(h[--c],b,!1);else this.onmousewheel=null;a.removeData(this,"mousewheel-line-height"),a.removeData(this,"mousewheel-page-height")},getLineHeight:function(b){var c=a(b),d=c["offsetParent"in a.fn?"offsetParent":"parent"]();return d.length||(d=a("body")),parseInt(d.css("fontSize"),10)||parseInt(c.css("fontSize"),10)||16},getPageHeight:function(b){return a(b).height()},settings:{adjustOldDeltas:!0,normalizeOffset:!0}};a.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})});
2057
+
2058
+ // Parse and Format Library
2059
+ //http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
2060
+ /*
2061
+ * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org>
2062
+ *
2063
+ * This program is free software; you can redistribute it and/or modify it
2064
+ * under the terms of the GNU Lesser General Public License as published by the
2065
+ * Free Software Foundation, version 2.1.
2066
+ *
2067
+ * This program is distributed in the hope that it will be useful, but WITHOUT
2068
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
2069
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
2070
+ * details.
2071
+ */
2072
+ Date.parseFunctions={count:0};Date.parseRegexes=[];Date.formatFunctions={count:0};Date.prototype.dateFormat=function(b){if(b=="unixtime"){return parseInt(this.getTime()/1000);}if(Date.formatFunctions[b]==null){Date.createNewFormat(b);}var a=Date.formatFunctions[b];return this[a]();};Date.createNewFormat=function(format){var funcName="format"+Date.formatFunctions.count++;Date.formatFunctions[format]=funcName;var codePrefix="Date.prototype."+funcName+" = function() {return ";var code="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true;}else{if(special){special=false;code+="'"+String.escape(ch)+"' + ";}else{code+=Date.getFormatCode(ch);}}}if(code.length==0){code="\"\"";}else{code=code.substring(0,code.length-3);}eval(codePrefix+code+";}");};Date.getFormatCode=function(a){switch(a){case"d":return"String.leftPad(this.getDate(), 2, '0') + ";case"D":return"Date.dayNames[this.getDay()].substring(0, 3) + ";case"j":return"this.getDate() + ";case"l":return"Date.dayNames[this.getDay()] + ";case"S":return"this.getSuffix() + ";case"w":return"this.getDay() + ";case"z":return"this.getDayOfYear() + ";case"W":return"this.getWeekOfYear() + ";case"F":return"Date.monthNames[this.getMonth()] + ";case"m":return"String.leftPad(this.getMonth() + 1, 2, '0') + ";case"M":return"Date.monthNames[this.getMonth()].substring(0, 3) + ";case"n":return"(this.getMonth() + 1) + ";case"t":return"this.getDaysInMonth() + ";case"L":return"(this.isLeapYear() ? 1 : 0) + ";case"Y":return"this.getFullYear() + ";case"y":return"('' + this.getFullYear()).substring(2, 4) + ";case"a":return"(this.getHours() < 12 ? 'am' : 'pm') + ";case"A":return"(this.getHours() < 12 ? 'AM' : 'PM') + ";case"g":return"((this.getHours() %12) ? this.getHours() % 12 : 12) + ";case"G":return"this.getHours() + ";case"h":return"String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + ";case"H":return"String.leftPad(this.getHours(), 2, '0') + ";case"i":return"String.leftPad(this.getMinutes(), 2, '0') + ";case"s":return"String.leftPad(this.getSeconds(), 2, '0') + ";case"O":return"this.getGMTOffset() + ";case"T":return"this.getTimezone() + ";case"Z":return"(this.getTimezoneOffset() * -60) + ";default:return"'"+String.escape(a)+"' + ";}};Date.parseDate=function(a,c){if(c=="unixtime"){return new Date(!isNaN(parseInt(a))?parseInt(a)*1000:0);}if(Date.parseFunctions[c]==null){Date.createParser(c);}var b=Date.parseFunctions[c];return Date[b](a);};Date.createParser=function(format){var funcName="parse"+Date.parseFunctions.count++;var regexNum=Date.parseRegexes.length;var currentGroup=1;Date.parseFunctions[format]=funcName;var code="Date."+funcName+" = function(input) {\nvar y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, z = -1;\nvar d = new Date();\ny = d.getFullYear();\nm = d.getMonth();\nd = d.getDate();\nvar results = input.match(Date.parseRegexes["+regexNum+"]);\nif (results && results.length > 0) {";var regex="";var special=false;var ch="";for(var i=0;i<format.length;++i){ch=format.charAt(i);if(!special&&ch=="\\"){special=true;}else{if(special){special=false;regex+=String.escape(ch);}else{obj=Date.formatCodeToRegex(ch,currentGroup);currentGroup+=obj.g;regex+=obj.s;if(obj.g&&obj.c){code+=obj.c;}}}}code+="if (y > 0 && z > 0){\nvar doyDate = new Date(y,0);\ndoyDate.setDate(z);\nm = doyDate.getMonth();\nd = doyDate.getDate();\n}";code+="if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n{return new Date(y, m, d, h, i, s);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n{return new Date(y, m, d, h, i);}\nelse if (y > 0 && m >= 0 && d > 0 && h >= 0)\n{return new Date(y, m, d, h);}\nelse if (y > 0 && m >= 0 && d > 0)\n{return new Date(y, m, d);}\nelse if (y > 0 && m >= 0)\n{return new Date(y, m);}\nelse if (y > 0)\n{return new Date(y);}\n}return null;}";Date.parseRegexes[regexNum]=new RegExp("^"+regex+"$",'i');eval(code);};Date.formatCodeToRegex=function(b,a){switch(b){case"D":return{g:0,c:null,s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};case"j":case"d":return{g:1,c:"d = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"l":return{g:0,c:null,s:"(?:"+Date.dayNames.join("|")+")"};case"S":return{g:0,c:null,s:"(?:st|nd|rd|th)"};case"w":return{g:0,c:null,s:"\\d"};case"z":return{g:1,c:"z = parseInt(results["+a+"], 10);\n",s:"(\\d{1,3})"};case"W":return{g:0,c:null,s:"(?:\\d{2})"};case"F":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"].substring(0, 3)], 10);\n",s:"("+Date.monthNames.join("|")+")"};case"M":return{g:1,c:"m = parseInt(Date.monthNumbers[results["+a+"]], 10);\n",s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};case"n":case"m":return{g:1,c:"m = parseInt(results["+a+"], 10) - 1;\n",s:"(\\d{1,2})"};case"t":return{g:0,c:null,s:"\\d{1,2}"};case"L":return{g:0,c:null,s:"(?:1|0)"};case"Y":return{g:1,c:"y = parseInt(results["+a+"], 10);\n",s:"(\\d{4})"};case"y":return{g:1,c:"var ty = parseInt(results["+a+"], 10);\ny = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",s:"(\\d{1,2})"};case"a":return{g:1,c:"if (results["+a+"] == 'am') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(am|pm)"};case"A":return{g:1,c:"if (results["+a+"] == 'AM') {\nif (h == 12) { h = 0; }\n} else { if (h < 12) { h += 12; }}",s:"(AM|PM)"};case"g":case"G":case"h":case"H":return{g:1,c:"h = parseInt(results["+a+"], 10);\n",s:"(\\d{1,2})"};case"i":return{g:1,c:"i = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"s":return{g:1,c:"s = parseInt(results["+a+"], 10);\n",s:"(\\d{2})"};case"O":return{g:0,c:null,s:"[+-]\\d{4}"};case"T":return{g:0,c:null,s:"[A-Z]{3}"};case"Z":return{g:0,c:null,s:"[+-]\\d{1,5}"};default:return{g:0,c:null,s:String.escape(b)};}};Date.prototype.getTimezone=function(){return this.toString().replace(/^.*? ([A-Z]{3}) [0-9]{4}.*$/,"$1").replace(/^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/,"$1$2$3");};Date.prototype.getGMTOffset=function(){return(this.getTimezoneOffset()>0?"-":"+")+String.leftPad(Math.floor(Math.abs(this.getTimezoneOffset())/60),2,"0")+String.leftPad(Math.abs(this.getTimezoneOffset())%60,2,"0");};Date.prototype.getDayOfYear=function(){var a=0;Date.daysInMonth[1]=this.isLeapYear()?29:28;for(var b=0;b<this.getMonth();++b){a+=Date.daysInMonth[b];}return a+this.getDate();};Date.prototype.getWeekOfYear=function(){var b=this.getDayOfYear()+(4-this.getDay());var a=new Date(this.getFullYear(),0,1);var c=(7-a.getDay()+4);return String.leftPad(Math.ceil((b-c)/7)+1,2,"0");};Date.prototype.isLeapYear=function(){var a=this.getFullYear();return((a&3)==0&&(a%100||(a%400==0&&a)));};Date.prototype.getFirstDayOfMonth=function(){var a=(this.getDay()-(this.getDate()-1))%7;return(a<0)?(a+7):a;};Date.prototype.getLastDayOfMonth=function(){var a=(this.getDay()+(Date.daysInMonth[this.getMonth()]-this.getDate()))%7;return(a<0)?(a+7):a;};Date.prototype.getDaysInMonth=function(){Date.daysInMonth[1]=this.isLeapYear()?29:28;return Date.daysInMonth[this.getMonth()];};Date.prototype.getSuffix=function(){switch(this.getDate()){case 1:case 21:case 31:return"st";case 2:case 22:return"nd";case 3:case 23:return"rd";default:return"th";}};String.escape=function(a){return a.replace(/('|\\)/g,"\\$1");};String.leftPad=function(d,b,c){var a=new String(d);if(c==null){c=" ";}while(a.length<b){a=c+a;}return a;};Date.daysInMonth=[31,28,31,30,31,30,31,31,30,31,30,31];Date.monthNames=["January","February","March","April","May","June","July","August","September","October","November","December"];Date.dayNames=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];Date.y2kYear=50;Date.monthNumbers={Jan:0,Feb:1,Mar:2,Apr:3,May:4,Jun:5,Jul:6,Aug:7,Sep:8,Oct:9,Nov:10,Dec:11};Date.patterns={ISO8601LongPattern:"Y-m-d H:i:s",ISO8601ShortPattern:"Y-m-d",ShortDatePattern:"n/j/Y",LongDatePattern:"l, F d, Y",FullDateTimePattern:"l, F d, Y g:i:s A",MonthDayPattern:"F d",ShortTimePattern:"g:i A",LongTimePattern:"g:i:s A",SortableDateTimePattern:"Y-m-d\\TH:i:s",UniversalSortableDateTimePattern:"Y-m-d H:i:sO",YearMonthPattern:"F, Y"};
2073
+ }());
assets/libraries/datetimepicker/package.json CHANGED
@@ -1,28 +1,28 @@
1
- {
2
- "name": "jquery-datetimepicker",
3
- "version": "2.4.5",
4
- "description": "jQuery Plugin DateTimePicker it is DatePicker and TimePicker in one",
5
- "main": "jquery.datetimepicker.js",
6
- "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
8
- },
9
- "repository": {
10
- "type": "git",
11
- "url": "https://github.com/xdan/datetimepicker.git"
12
- },
13
- "keywords": [
14
- "jquery-plugin",
15
- "calendar",
16
- "date",
17
- "time",
18
- "datetime",
19
- "datepicker",
20
- "timepicker"
21
- ],
22
- "author": "Chupurnov <chupurnov@gmail.com> (http://xdsoft.net/)",
23
- "license": "MIT",
24
- "bugs": {
25
- "url": "https://github.com/xdan/datetimepicker/issues"
26
- },
27
- "homepage": "https://github.com/xdan/datetimepicker"
28
- }
1
+ {
2
+ "name": "jquery-datetimepicker",
3
+ "version": "2.4.5",
4
+ "description": "jQuery Plugin DateTimePicker it is DatePicker and TimePicker in one",
5
+ "main": "jquery.datetimepicker.js",
6
+ "scripts": {
7
+ "test": "echo \"Error: no test specified\" && exit 1"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/xdan/datetimepicker.git"
12
+ },
13
+ "keywords": [
14
+ "jquery-plugin",
15
+ "calendar",
16
+ "date",
17
+ "time",
18
+ "datetime",
19
+ "datepicker",
20
+ "timepicker"
21
+ ],
22
+ "author": "Chupurnov <chupurnov@gmail.com> (http://xdsoft.net/)",
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/xdan/datetimepicker/issues"
26
+ },
27
+ "homepage": "https://github.com/xdan/datetimepicker"
28
+ }
assets/libraries/datetimepicker/picker_functions.js CHANGED
@@ -1,56 +1,56 @@
1
- jQuery(document).ready(function ($) {
2
-
3
- /* Populates date and timepicker values */
4
-
5
- /* Activates the datetime picker widget */
6
- jQuery('.start.date').datetimepicker({
7
- timepicker:false,
8
- format:'Y-m-d'
9
- });
10
- jQuery('.time-picker').datetimepicker({
11
- datepicker:false,
12
- format: 'H:i'
13
- });
14
-
15
- if ($('.current_lander .new-date').length) { // implies *not* zero
16
- var current_val = jQuery(".current_lander .new-date").val();
17
- } else {
18
- var current_val = jQuery(".new-date").val();
19
- }
20
- // if no timepicker in options fix it
21
- if (typeof (current_val) == "undefined" || current_val === null || current_val == "") {
22
- var current_val = '';
23
- }
24
-
25
- jQuery('.new-date').each(function(){
26
- var the_val = $(this).val();
27
- if (typeof (the_val) == "undefined" || the_val === null || the_val == "") {
28
- var the_val = '';
29
- }
30
- var ret = the_val.split(" ");
31
- var current_date = ret[0];
32
- var current_time = ret[1];
33
- jQuery(this).parent().parent().find(".date.start").val(current_date);
34
- jQuery(this).parent().parent().find(".time-picker").val(current_time);
35
- });
36
-
37
- jQuery("body").on('change', '.jquery-date-picker .date.start', function () {
38
- var date_chosen = jQuery(this).val();
39
- var time_chosen = jQuery(this).parent().parent().find(".jquery-date-picker .time-picker").val();
40
- var total_time = date_chosen + " " + time_chosen;
41
- jQuery(this).parent().parent().find(".new-date").val(total_time);
42
-
43
- });
44
-
45
- jQuery("body").on('change', '.jquery-date-picker .time-picker', function () {
46
- var date_chosen = jQuery(this).parent().parent().find(".jquery-date-picker .date.start").val();
47
- var time_chosen = jQuery(this).val();
48
- if (typeof (time_chosen) === "undefined" && time_chosen == null && time_chosen === "") {
49
- var time_chosen = "00:00";
50
- }
51
- var total_time = date_chosen + " " + time_chosen;
52
- jQuery(this).parent().find(".new-date").val(total_time);
53
-
54
- });
55
-
56
  });
1
+ jQuery(document).ready(function ($) {
2
+
3
+ /* Populates date and timepicker values */
4
+
5
+ /* Activates the datetime picker widget */
6
+ jQuery('.start.date').datetimepicker({
7
+ timepicker:false,
8
+ format:'Y-m-d'
9
+ });
10
+ jQuery('.time-picker').datetimepicker({
11
+ datepicker:false,
12
+ format: 'H:i'
13
+ });
14
+
15
+ if ($('.current_lander .new-date').length) { // implies *not* zero
16
+ var current_val = jQuery(".current_lander .new-date").val();
17
+ } else {
18
+ var current_val = jQuery(".new-date").val();
19
+ }
20
+ // if no timepicker in options fix it
21
+ if (typeof (current_val) == "undefined" || current_val === null || current_val == "") {
22
+ var current_val = '';
23
+ }
24
+
25
+ jQuery('.new-date').each(function(){
26
+ var the_val = $(this).val();
27
+ if (typeof (the_val) == "undefined" || the_val === null || the_val == "") {
28
+ var the_val = '';
29
+ }
30
+ var ret = the_val.split(" ");
31
+ var current_date = ret[0];
32
+ var current_time = ret[1];
33
+ jQuery(this).parent().parent().find(".date.start").val(current_date);
34
+ jQuery(this).parent().parent().find(".time-picker").val(current_time);
35
+ });
36
+
37
+ jQuery("body").on('change', '.jquery-date-picker .date.start', function () {
38
+ var date_chosen = jQuery(this).val();
39
+ var time_chosen = jQuery(this).parent().parent().find(".jquery-date-picker .time-picker").val();
40
+ var total_time = date_chosen + " " + time_chosen;
41
+ jQuery(this).parent().parent().find(".new-date").val(total_time);
42
+
43
+ });
44
+
45
+ jQuery("body").on('change', '.jquery-date-picker .time-picker', function () {
46
+ var date_chosen = jQuery(this).parent().parent().find(".jquery-date-picker .date.start").val();
47
+ var time_chosen = jQuery(this).val();
48
+ if (typeof (time_chosen) === "undefined" && time_chosen == null && time_chosen === "") {
49
+ var time_chosen = "00:00";
50
+ }
51
+ var total_time = date_chosen + " " + time_chosen;
52
+ jQuery(this).parent().find(".new-date").val(total_time);
53
+
54
+ });
55
+
56
  });
assets/libraries/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/libraries/jpicker/css/jPicker-1.1.6.css CHANGED
@@ -1,232 +1,232 @@
1
- .jPicker .Icon {
2
- display: inline-block;
3
- height: 24px; /* change this value if using a different sized color picker icon */
4
- position: relative; /* make this element an absolute positioning container */
5
- text-align: left; /* make the zero width children position to the left of container */
6
- width: 25px; /* change this value if using a different sized color picker icon */
7
- }
8
- .jPicker .Icon span.Color, .jPicker .Icon span.Alpha {
9
- background-position: 2px 2px;
10
- display: block;
11
- height: 100%;
12
- left: 0px;
13
- position: absolute;
14
- top: 0px;
15
- width: 100%;
16
- }
17
- .jPicker .Icon span.Image {
18
- background-repeat: no-repeat;
19
- cursor: pointer;
20
- display: block;
21
- height: 100%;
22
- left: 0px;
23
- position: absolute;
24
- top: 0px;
25
- width: 100%;
26
- }
27
- .jPicker.Container {
28
- color: #000;
29
- z-index: 10;
30
- }
31
- table.jPicker {
32
- background-color: #efefef;
33
- border: 1px outset #666;
34
- font-family: Arial, Helvetica, Sans-Serif;
35
- font-size: 12px !important;
36
- margin: 0px;
37
- padding: 5px;
38
- width: 545px;
39
- z-index: 20;
40
- }
41
- .jPicker .Move {
42
- background-color: #dddddd;
43
- border-color: #fff #666 #666 #fff;
44
- border-style: solid;
45
- border-width: 1px;
46
- cursor: move;
47
- height: 12px;
48
- padding: 0px;
49
- }
50
- .jPicker .Title {
51
- font-size: 11px !important;
52
- font-weight: bold;
53
- margin: -2px 0px 0px 0px;
54
- padding: 10px 0px 0px 0px;
55
- text-align: center;
56
- width: 100%;
57
- }
58
- .jPicker div.Map {
59
- border-bottom: 2px solid #fff;
60
- border-left: 2px solid #9a9a9a;
61
- border-right: 2px solid #fff;
62
- border-top: 2px solid #9a9a9a;
63
- cursor: crosshair;
64
- height: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
65
- margin: 0px 10px 10px 10px;
66
- overflow: hidden; /* hide the overdraw of the Color Map icon when at edge of viewing box */
67
- padding: 0px;
68
- position: relative; /* make this element an absolute positioning container */
69
- width: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
70
- }
71
- .jPicker div[class="Map"] {
72
- height: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
73
- width: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
74
- }
75
- .jPicker div.Bar {
76
- border-bottom: 2px solid #fff;
77
- border-left: 2px solid #9a9a9a;
78
- border-right: 2px solid #fff;
79
- border-top: 2px solid #9a9a9a;
80
- cursor: n-resize;
81
- height: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
82
- margin: 12px 10px 0px 5px;
83
- overflow: hidden;
84
- padding: 0px;
85
- position: relative;
86
- width: 24px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 20px later */
87
- }
88
- .jPicker div[class="Bar"] {
89
- height: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
90
- width: 20px; /* correct to 20px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
91
- }
92
- .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3, .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4, .jPicker .Bar .Map5, .jPicker .Bar .Map6 {
93
- background-color: transparent;
94
- background-image: none;
95
- display: block;
96
- left: 0px;
97
- position: absolute;
98
- top: 0px;
99
- }
100
- .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3 {
101
- height: 2596px;
102
- width: 256px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
103
- would not be drawn if its overflow is set to hidden. */
104
- }
105
- .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
106
- height: 3896px;
107
- width: 20px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
108
- would not be drawn if its overflow is set to hidden. */
109
- }
110
- .jPicker .Bar .Map5, .jPicker .Bar .Map6 {
111
- height: 256px;
112
- width: 20px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
113
- would not be drawn if its overflow is set to hidden. */
114
- }
115
- .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Bar .Map6 {
116
- background-repeat: no-repeat;
117
- }
118
- .jPicker .Map .Map3, .jPicker .Bar .Map5 {
119
- background-repeat: repeat;
120
- }
121
- .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
122
- background-repeat: repeat-x;
123
- }
124
- .jPicker .Map .Arrow {
125
- display: block;
126
- position: absolute;
127
- }
128
- .jPicker .Bar .Arrow {
129
- display: block;
130
- left: 0px; /* (arrow width / 2) - (element width / 2) - position arrows' center in elements' center */
131
- position: absolute;
132
- }
133
- .jPicker .Preview {
134
- font-size: 9px;
135
- padding: 5px 0px 0px 0px;
136
- text-align: center;
137
- }
138
- .jPicker .Preview div {
139
- border: 2px inset #eee;
140
- height: 62px;
141
- margin: 0px auto;
142
- padding: 0px;
143
- width: 62px;
144
- }
145
- .jPicker .Preview div span {
146
- border: 1px solid #000;
147
- display: block;
148
- height: 30px;
149
- margin: 0px auto;
150
- padding: 0px;
151
- width: 60px;
152
- }
153
- .jPicker .Preview .Active {
154
- border-bottom-width: 0px;
155
- }
156
- .jPicker .Preview .Current {
157
- border-top-width: 0px;
158
- cursor: pointer;
159
- }
160
- .jPicker input {
161
- font-size: 13px;
162
- }
163
- .jPicker .Button {
164
- text-align: center;
165
- padding: 0px 4px;
166
- width: 115px;
167
- }
168
- .jPicker .Button input {
169
- padding: 2px 0px;
170
- width: 100px;
171
- }
172
- .jPicker .Button .Ok {
173
- margin: 12px 0px 5px 0px;
174
- }
175
- .jPicker td {
176
- margin: 0px;
177
- padding: 0px;
178
- }
179
- .jPicker td.Radio {
180
- margin: 0px;
181
- padding: 0px;
182
- width: 31px;
183
- }
184
- .jPicker td.Radio input {
185
- margin: 0px 5px 0px 0px;
186
- padding: 0px;
187
- }
188
- .jPicker td.Text {
189
- font-size: 12px !important;
190
- height: 22px;
191
- margin: 0px;
192
- padding: 0px;
193
- text-align: left;
194
- width: 70px;
195
- }
196
- .jPicker tr.Hex td.Text {
197
- width: 100px;
198
- }
199
- .jPicker td.Text input {
200
- background-color: #fff;
201
- border: 1px inset #aaa;
202
- height: 19px;
203
- margin: 0px 0px 0px 5px;
204
- text-align: left;
205
- width: 30px;
206
- }
207
- .jPicker td[class="Text"] input {
208
- height: 15px;
209
- }
210
- .jPicker tr.Hex td.Text input.Hex {
211
- width: 50px;
212
- }
213
- .jPicker tr.Hex td.Text input.AHex {
214
- width: 20px;
215
- }
216
- .jPicker .Grid {
217
- text-align: center;
218
- width: 114px;
219
- }
220
- .jPicker .Grid span.QuickColor {
221
- border: 1px inset #aaa;
222
- cursor: pointer;
223
- display: inline-block;
224
- height: 15px;
225
- line-height: 15px;
226
- margin: 0px;
227
- padding: 0px;
228
- width: 19px;
229
- }
230
- .jPicker .Grid span[class="QuickColor"] {
231
- width: 17px;
232
  }
1
+ .jPicker .Icon {
2
+ display: inline-block;
3
+ height: 24px; /* change this value if using a different sized color picker icon */
4
+ position: relative; /* make this element an absolute positioning container */
5
+ text-align: left; /* make the zero width children position to the left of container */
6
+ width: 25px; /* change this value if using a different sized color picker icon */
7
+ }
8
+ .jPicker .Icon span.Color, .jPicker .Icon span.Alpha {
9
+ background-position: 2px 2px;
10
+ display: block;
11
+ height: 100%;
12
+ left: 0px;
13
+ position: absolute;
14
+ top: 0px;
15
+ width: 100%;
16
+ }
17
+ .jPicker .Icon span.Image {
18
+ background-repeat: no-repeat;
19
+ cursor: pointer;
20
+ display: block;
21
+ height: 100%;
22
+ left: 0px;
23
+ position: absolute;
24
+ top: 0px;
25
+ width: 100%;
26
+ }
27
+ .jPicker.Container {
28
+ color: #000;
29
+ z-index: 10;
30
+ }
31
+ table.jPicker {
32
+ background-color: #efefef;
33
+ border: 1px outset #666;
34
+ font-family: Arial, Helvetica, Sans-Serif;
35
+ font-size: 12px !important;
36
+ margin: 0px;
37
+ padding: 5px;
38
+ width: 545px;
39
+ z-index: 20;
40
+ }
41
+ .jPicker .Move {
42
+ background-color: #dddddd;
43
+ border-color: #fff #666 #666 #fff;
44
+ border-style: solid;
45
+ border-width: 1px;
46
+ cursor: move;
47
+ height: 12px;
48
+ padding: 0px;
49
+ }
50
+ .jPicker .Title {
51
+ font-size: 11px !important;
52
+ font-weight: bold;
53
+ margin: -2px 0px 0px 0px;
54
+ padding: 10px 0px 0px 0px;
55
+ text-align: center;
56
+ width: 100%;
57
+ }
58
+ .jPicker div.Map {
59
+ border-bottom: 2px solid #fff;
60
+ border-left: 2px solid #9a9a9a;
61
+ border-right: 2px solid #fff;
62
+ border-top: 2px solid #9a9a9a;
63
+ cursor: crosshair;
64
+ height: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
65
+ margin: 0px 10px 10px 10px;
66
+ overflow: hidden; /* hide the overdraw of the Color Map icon when at edge of viewing box */
67
+ padding: 0px;
68
+ position: relative; /* make this element an absolute positioning container */
69
+ width: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
70
+ }
71
+ .jPicker div[class="Map"] {
72
+ height: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
73
+ width: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
74
+ }
75
+ .jPicker div.Bar {
76
+ border-bottom: 2px solid #fff;
77
+ border-left: 2px solid #9a9a9a;
78
+ border-right: 2px solid #fff;
79
+ border-top: 2px solid #9a9a9a;
80
+ cursor: n-resize;
81
+ height: 260px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 256px later */
82
+ margin: 12px 10px 0px 5px;
83
+ overflow: hidden;
84
+ padding: 0px;
85
+ position: relative;
86
+ width: 24px; /* IE 6 incorrectly draws border inside the width and height instead of outside - We will fix this to 20px later */
87
+ }
88
+ .jPicker div[class="Bar"] {
89
+ height: 256px; /* correct to 256px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
90
+ width: 20px; /* correct to 20px for browsers that support the "[class="xxx"]" selector (IE7+,Firefox,Safari,Chrome,Opera,etc.) */
91
+ }
92
+ .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3, .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4, .jPicker .Bar .Map5, .jPicker .Bar .Map6 {
93
+ background-color: transparent;
94
+ background-image: none;
95
+ display: block;
96
+ left: 0px;
97
+ position: absolute;
98
+ top: 0px;
99
+ }
100
+ .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Map .Map3 {
101
+ height: 2596px;
102
+ width: 256px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
103
+ would not be drawn if its overflow is set to hidden. */
104
+ }
105
+ .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
106
+ height: 3896px;
107
+ width: 20px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
108
+ would not be drawn if its overflow is set to hidden. */
109
+ }
110
+ .jPicker .Bar .Map5, .jPicker .Bar .Map6 {
111
+ height: 256px;
112
+ width: 20px; /* must specify pixel width. IE7/8 Quirks mode ignores opacity for an absolutely positioned item in a relative container with "overflow: visible". The marker in the colorBar
113
+ would not be drawn if its overflow is set to hidden. */
114
+ }
115
+ .jPicker .Map .Map1, .jPicker .Map .Map2, .jPicker .Bar .Map6 {
116
+ background-repeat: no-repeat;
117
+ }
118
+ .jPicker .Map .Map3, .jPicker .Bar .Map5 {
119
+ background-repeat: repeat;
120
+ }
121
+ .jPicker .Bar .Map1, .jPicker .Bar .Map2, .jPicker .Bar .Map3, .jPicker .Bar .Map4 {
122
+ background-repeat: repeat-x;
123
+ }
124
+ .jPicker .Map .Arrow {
125
+ display: block;
126
+ position: absolute;
127
+ }
128
+ .jPicker .Bar .Arrow {
129
+ display: block;
130
+ left: 0px; /* (arrow width / 2) - (element width / 2) - position arrows' center in elements' center */
131
+ position: absolute;
132
+ }
133
+ .jPicker .Preview {
134
+ font-size: 9px;
135
+ padding: 5px 0px 0px 0px;
136
+ text-align: center;
137
+ }
138
+ .jPicker .Preview div {
139
+ border: 2px inset #eee;
140
+ height: 62px;
141
+ margin: 0px auto;
142
+ padding: 0px;
143
+ width: 62px;
144
+ }
145
+ .jPicker .Preview div span {
146
+ border: 1px solid #000;
147
+ display: block;
148
+ height: 30px;
149
+ margin: 0px auto;
150
+ padding: 0px;
151
+ width: 60px;
152
+ }
153
+ .jPicker .Preview .Active {
154
+ border-bottom-width: 0px;
155
+ }
156
+ .jPicker .Preview .Current {
157
+ border-top-width: 0px;
158
+ cursor: pointer;
159
+ }
160
+ .jPicker input {
161
+ font-size: 13px;
162
+ }
163
+ .jPicker .Button {
164
+ text-align: center;
165
+ padding: 0px 4px;
166
+ width: 115px;
167
+ }
168
+ .jPicker .Button input {
169
+ padding: 2px 0px;
170
+ width: 100px;
171
+ }
172
+ .jPicker .Button .Ok {
173
+ margin: 12px 0px 5px 0px;
174
+ }
175
+ .jPicker td {
176
+ margin: 0px;
177
+ padding: 0px;
178
+ }
179
+ .jPicker td.Radio {
180
+ margin: 0px;
181
+ padding: 0px;
182
+ width: 31px;
183
+ }
184
+ .jPicker td.Radio input {
185
+ margin: 0px 5px 0px 0px;
186
+ padding: 0px;
187
+ }
188
+ .jPicker td.Text {
189
+ font-size: 12px !important;
190
+ height: 22px;
191
+ margin: 0px;
192
+ padding: 0px;
193
+ text-align: left;
194
+ width: 70px;
195
+ }
196
+ .jPicker tr.Hex td.Text {
197
+ width: 100px;
198
+ }
199
+ .jPicker td.Text input {
200
+ background-color: #fff;
201
+ border: 1px inset #aaa;
202
+ height: 19px;
203
+ margin: 0px 0px 0px 5px;
204
+ text-align: left;
205
+ width: 30px;
206
+ }
207
+ .jPicker td[class="Text"] input {
208
+ height: 15px;
209
+ }
210
+ .jPicker tr.Hex td.Text input.Hex {
211
+ width: 50px;
212
+ }
213
+ .jPicker tr.Hex td.Text input.AHex {
214
+ width: 20px;
215
+ }
216
+ .jPicker .Grid {
217
+ text-align: center;
218
+ width: 114px;
219
+ }
220
+ .jPicker .Grid span.QuickColor {
221
+ border: 1px inset #aaa;
222
+ cursor: pointer;
223
+ display: inline-block;
224
+ height: 15px;
225
+ line-height: 15px;
226
+ margin: 0px;
227
+ padding: 0px;
228
+ width: 19px;
229
+ }
230
+ .jPicker .Grid span[class="QuickColor"] {
231
+ width: 17px;
232
  }
assets/libraries/jpicker/css/jPicker.css CHANGED
@@ -1,17 +1,17 @@
1
- @media all
2
- {
3
- #jPicker { margin: 0px 8px; text-align: left; }
4
- #jPicker ul { font-size: 15px; margin: 0px 0px 0px 15px; padding: 0px; }
5
- #jPicker ul li { list-style: disc; padding: 2px 0px; }
6
- #jPicker ul li ul { margin-bottom: 10px; }
7
- #jPicker ul li ul li { list-style: circle; }
8
- #jPicker p { font-size: 13px; padding: 0px 10px; }
9
- #jPicker hr { clear: both; }
10
- #jPicker h2.jPicker { font-size: 16px; padding: 20px 10px; }
11
- #jPicker code { color: #8bd; font-size: 14px; font-weight: bold; }
12
- #jPicker pre { background: #eee; border: 1px solid #000; color: #000; display: block; font-size: 11px; margin: 10px 5px; padding: 5px; }
13
- #jPicker span { font-size: 13px; text-align: center; }
14
- #jPicker a { color: #ff8050; }
15
- #jPicker input { font-size: 13px; padding: 2px 5px; }
16
- #jPicker h2 { font-size: 16px; margin: 10px 0px; }
17
  }
1
+ @media all
2
+ {
3
+ #jPicker { margin: 0px 8px; text-align: left; }
4
+ #jPicker ul { font-size: 15px; margin: 0px 0px 0px 15px; padding: 0px; }
5
+ #jPicker ul li { list-style: disc; padding: 2px 0px; }
6
+ #jPicker ul li ul { margin-bottom: 10px; }
7
+ #jPicker ul li ul li { list-style: circle; }
8
+ #jPicker p { font-size: 13px; padding: 0px 10px; }
9
+ #jPicker hr { clear: both; }
10
+ #jPicker h2.jPicker { font-size: 16px; padding: 20px 10px; }
11
+ #jPicker code { color: #8bd; font-size: 14px; font-weight: bold; }
12
+ #jPicker pre { background: #eee; border: 1px solid #000; color: #000; display: block; font-size: 11px; margin: 10px 5px; padding: 5px; }
13
+ #jPicker span { font-size: 13px; text-align: center; }
14
+ #jPicker a { color: #ff8050; }
15
+ #jPicker input { font-size: 13px; padding: 2px 5px; }
16
+ #jPicker h2 { font-size: 16px; margin: 10px 0px; }
17
  }
assets/libraries/jquery-qtip/jquery.qtip.min.js CHANGED
@@ -1,2 +1,2 @@
1
- /*! qtip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
2
  (function(e,t,n){(function(e){"use strict";typeof define=="function"&&define.amd?define(["jquery"],e):jQuery&&!jQuery.fn.qtip&&e(jQuery)})(function(r){function _(n){E={pageX:n.pageX,pageY:n.pageY,type:"mousemove",scrollX:e.pageXOffset||t.body.scrollLeft||t.documentElement.scrollLeft,scrollY:e.pageYOffset||t.body.scrollTop||t.documentElement.scrollTop}}function D(e){var t=function(e){return e===o||"object"!=typeof e},n=function(e){return!r.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery&&!e.then)};if(!e||"object"!=typeof e)return s;t(e.metadata)&&(e.metadata={type:e.metadata});if("content"in e){if(t(e.content)||e.content.jquery)e.content={text:e.content};n(e.content.text||s)&&(e.content.text=s),"title"in e.content&&(t(e.content.title)&&(e.content.title={text:e.content.title}),n(e.content.title.text||s)&&(e.content.title.text=s))}return"position"in e&&t(e.position)&&(e.position={my:e.position,at:e.position}),"show"in e&&t(e.show)&&(e.show=e.show.jquery?{target:e.show}:{event:e.show}),"hide"in e&&t(e.hide)&&(e.hide=e.hide.jquery?{target:e.hide}:{event:e.hide}),"style"in e&&t(e.style)&&(e.style={classes:e.style}),r.each(w,function(){this.sanitize&&this.sanitize(e)}),e}function P(u,a,f,l){function q(e){var t=0,n,r=a,i=e.split(".");while(r=r[i[t++]])t<i.length&&(n=r);return[n||a,i.pop()]}function R(e){return T.concat("").join(e?"-"+e+" ":" ")}function U(){var e=a.style.widget,t=H.hasClass(j);H.removeClass(j),j=e?"ui-state-disabled":"qtip-disabled",H.toggleClass(j,t),H.toggleClass("ui-helper-reset "+R(),e).toggleClass(C,a.style.def&&!e),F.content&&F.content.toggleClass(R("content"),e),F.titlebar&&F.titlebar.toggleClass(R("header"),e),F.button&&F.button.toggleClass(S+"-icon",!e)}function z(e){F.title&&(F.titlebar.remove(),F.titlebar=F.title=F.button=o,e!==s&&m.reposition())}function W(){var e=a.content.title.button,t=typeof e=="string",n=t?e:"Close tooltip";F.button&&F.button.remove(),e.jquery?F.button=e:F.button=r("<a />",{"class":"qtip-close "+(a.style.widget?"":S+"-icon"),title:n,"aria-label":n}).prepend(r("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),F.button.appendTo(F.titlebar||H).attr("role","button").click(function(e){return H.hasClass(j)||m.hide(e),s})}function X(){var e=y+"-title";F.titlebar&&z(),F.titlebar=r("<div />",{"class":S+"-titlebar "+(a.style.widget?R("header"):"")}).append(F.title=r("<div />",{id:e,"class":S+"-title","aria-atomic":i})).insertBefore(F.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(e){r(this).toggleClass("ui-state-active ui-state-focus",e.type.substr(-4)==="down")}).delegate(".qtip-close","mouseover mouseout",function(e){r(this).toggleClass("ui-state-hover",e.type==="mouseover")}),a.content.title.button&&W()}function V(e){var t=F.button;if(!m.rendered)return s;e?W():t.remove()}function J(e,t){var n=F.title;if(!m.rendered||!e)return s;r.isFunction(e)&&(e=e.call(u,I.event,m));if(e===s||!e&&e!=="")return z(s);e.jquery&&e.length>0?n.empty().append(e.css({display:"block"})):n.html(e),t!==s&&m.rendered&&H[0].offsetWidth>0&&m.reposition(I.event)}function K(e){e&&r.isFunction(e.done)&&e.done(function(e){Q(e,null,s)})}function Q(e,t,i){function f(e){function a(n){n&&(delete u[n.src],clearTimeout(m.timers.img[n.src]),r(n).unbind(B)),r.isEmptyObject(u)&&(t!==s&&m.reposition(I.event),e())}var i,u={};if((i=o.find("img[src]:not([height]):not([width])")).length===0)return a();i.each(function(e,t){if(u[t.src]!==n)return;var i=0,s=3;(function o(){if(t.height||t.width||i>s)return a(t);i+=1,m.timers.img[t.src]=setTimeout(o,700)})(),r(t).bind("error"+B+" load"+B,function(){a(this)}),u[t.src]=t})}var o=F.content;return!m.rendered||!e?s:(r.isFunction(e)&&(e=e.call(u,I.event,m)||""),i!==s&&K(a.content.deferred),e.jquery&&e.length>0?o.empty().append(e.css({display:"block"})):o.html(e),m.rendered<0?H.queue("fx",f):(P=0,f(r.noop)),m)}function G(){function h(e){if(H.hasClass(j))return s;clearTimeout(m.timers.show),clearTimeout(m.timers.hide);var t=function(){m.toggle(i,e)};a.show.delay>0?m.timers.show=setTimeout(t,a.show.delay):t()}function p(e){if(H.hasClass(j)||A||P)return s;var t=r(e.relatedTarget||e.target),i=t.closest(N)[0]===H[0],u=t[0]===o.show[0];clearTimeout(m.timers.show),clearTimeout(m.timers.hide);if(n.target==="mouse"&&i||a.hide.fixed&&/mouse(out|leave|move)/.test(e.type)&&(i||u)){try{e.preventDefault(),e.stopImmediatePropagation()}catch(f){}return}a.hide.delay>0?m.timers.hide=setTimeout(function(){m.hide(e)},a.hide.delay):m.hide(e)}function d(e){if(H.hasClass(j))return s;clearTimeout(m.timers.inactive),m.timers.inactive=setTimeout(function(){m.hide(e)},a.hide.inactive)}function v(e){m.rendered&&H[0].offsetWidth>0&&m.reposition(e)}var n=a.position,o={show:a.show.target,hide:a.hide.target,viewport:r(n.viewport),document:r(t),body:r(t.body),window:r(e)},l={show:r.trim(""+a.show.event).split(" "),hide:r.trim(""+a.hide.event).split(" ")},c=r.browser.msie&&parseInt(r.browser.version,10)===6;H.bind("mouseenter"+B+" mouseleave"+B,function(e){var t=e.type==="mouseenter";t&&m.focus(e),H.toggleClass(L,t)}),/mouse(out|leave)/i.test(a.hide.event)&&a.hide.leave==="window"&&o.window.bind("mouseout"+B+" blur"+B,function(e){!/select|option/.test(e.target.nodeName)&&!e.relatedTarget&&m.hide(e)}),a.hide.fixed?(o.hide=o.hide.add(H),H.bind("mouseover"+B,function(){H.hasClass(j)||clearTimeout(m.timers.hide)})):/mouse(over|enter)/i.test(a.show.event)&&o.hide.bind("mouseleave"+B,function(e){clearTimeout(m.timers.show)}),(""+a.hide.event).indexOf("unfocus")>-1&&n.container.closest("html").bind("mousedown"+B+" touchstart"+B,function(e){var t=r(e.target),n=m.rendered&&!H.hasClass(j)&&H[0].offsetWidth>0,i=t.parents(N).filter(H[0]).length>0;t[0]!==u[0]&&t[0]!==H[0]&&!i&&!u.has(t[0]).length&&!t.attr("disabled")&&m.hide(e)}),"number"==typeof a.hide.inactive&&(o.show.bind("qtip-"+f+"-inactive",d),r.each(b.inactiveEvents,function(e,t){o.hide.add(F.tooltip).bind(t+B+"-inactive",d)})),r.each(l.hide,function(e,t){var n=r.inArray(t,l.show),i=r(o.hide);n>-1&&i.add(o.show).length===i.length||t==="unfocus"?(o.show.bind(t+B,function(e){H[0].offsetWidth>0?p(e):h(e)}),delete l.show[n]):o.hide.bind(t+B,p)}),r.each(l.show,function(e,t){o.show.bind(t+B,h)}),"number"==typeof a.hide.distance&&o.show.add(H).bind("mousemove"+B,function(e){var t=I.origin||{},n=a.hide.distance,r=Math.abs;(r(e.pageX-t.pageX)>=n||r(e.pageY-t.pageY)>=n)&&m.hide(e)}),n.target==="mouse"&&(o.show.bind("mousemove"+B,_),n.adjust.mouse&&(a.hide.event&&(H.bind("mouseleave"+B,function(e){(e.relatedTarget||e.target)!==o.show[0]&&m.hide(e)}),F.target.bind("mouseenter"+B+" mouseleave"+B,function(e){I.onTarget=e.type==="mouseenter"})),o.document.bind("mousemove"+B,function(e){m.rendered&&I.onTarget&&!H.hasClass(j)&&H[0].offsetWidth>0&&m.reposition(e||E)}))),(n.adjust.resize||o.viewport.length)&&(r.event.special.resize?o.viewport:o.window).bind("resize"+B,v),o.window.bind("scroll"+B,v)}function Y(){var n=[a.show.target[0],a.hide.target[0],m.rendered&&F.tooltip[0],a.position.container[0],a.position.viewport[0],a.position.container.closest("html")[0],e,t];m.rendered?r([]).pushStack(r.grep(n,function(e){return typeof e=="object"})).unbind(B):a.show.target.unbind(B+"-create")}var m=this,g=t.body,y=S+"-"+f,A=0,P=0,H=r(),B=".qtip-"+f,j="qtip-disabled",F,I;m.id=f,m.rendered=s,m.destroyed=s,m.elements=F={target:u},m.timers={img:{}},m.options=a,m.checks={},m.plugins={},m.cache=I={event:{},target:r(),disabled:s,attr:l,onTarget:s,lastClass:""},m.checks.builtin={"^id$":function(e,t,n){var o=n===i?b.nextid:n,u=S+"-"+o;o!==s&&o.length>0&&!r("#"+u).length&&(H[0].id=u,F.content[0].id=u+"-content",F.title[0].id=u+"-title")},"^content.text$":function(e,t,n){Q(a.content.text)},"^content.deferred$":function(e,t,n){K(a.content.deferred)},"^content.title.text$":function(e,t,n){if(!n)return z();!F.title&&n&&X(),J(n)},"^content.title.button$":function(e,t,n){V(n)},"^position.(my|at)$":function(e,t,n){"string"==typeof n&&(e[t]=new w.Corner(n))},"^position.container$":function(e,t,n){m.rendered&&H.appendTo(n)},"^show.ready$":function(){m.rendered?m.toggle(i):m.render(1)},"^style.classes$":function(e,t,n){H.attr("class",S+" qtip "+n)},"^style.width|height":function(e,t,n){H.css(t,n)},"^style.widget|content.title":U,"^events.(render|show|move|hide|focus|blur)$":function(e,t,n){H[(r.isFunction(n)?"":"un")+"bind"]("tooltip"+t,n)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var e=a.position;H.attr("tracking",e.target==="mouse"&&e.adjust.mouse),Y(),G()}},r.extend(m,{_triggerEvent:function(e,t,n){var i=r.Event("tooltip"+e);return i.originalEvent=(n?r.extend({},n):o)||I.event||o,H.trigger(i,[m].concat(t||[])),!i.isDefaultPrevented()},render:function(e){if(m.rendered)return m;var t=a.content.text,n=a.content.title,o=a.position;return r.attr(u[0],"aria-describedby",y),H=F.tooltip=r("<div/>",{id:y,"class":[S,C,a.style.classes,S+"-pos-"+a.position.my.abbrev()].join(" "),width:a.style.width||"",height:a.style.height||"",tracking:o.target==="mouse"&&o.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":s,"aria-describedby":y+"-content","aria-hidden":i}).toggleClass(j,I.disabled).data("qtip",m).appendTo(a.position.container).append(F.content=r("<div />",{"class":S+"-content",id:y+"-content","aria-atomic":i})),m.rendered=-1,A=1,n.text?(X(),r.isFunction(n.text)||J(n.text,s)):n.button&&W(),(!r.isFunction(t)||t.then)&&Q(t,s),m.rendered=i,U(),r.each(a.events,function(e,t){r.isFunction(t)&&H.bind(e==="toggle"?"tooltipshow tooltiphide":"tooltip"+e,t)}),r.each(w,function(){this.initialize==="render"&&this(m)}),G(),H.queue("fx",function(t){m._triggerEvent("render"),A=0,(a.show.ready||e)&&m.toggle(i,I.event,s),t()}),m},get:function(e){var t,n;switch(e.toLowerCase()){case"dimensions":t={height:H.outerHeight(s),width:H.outerWidth(s)};break;case"offset":t=w.offset(H,a.position.container);break;default:n=q(e.toLowerCase()),t=n[0][n[1]],t=t.precedance?t.string():t}return t},set:function(e,t){function h(e,t){var n,r,i;for(n in l)for(r in l[n])if(i=(new RegExp(r,"i")).exec(e))t.push(i),l[n][r].apply(m,t)}var n=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,u=/^content\.(title|attr)|style/i,f=s,l=m.checks,c;return"string"==typeof e?(c=e,e={},e[c]=t):e=r.extend(i,{},e),r.each(e,function(t,i){var s=q(t.toLowerCase()),o;o=s[0][s[1]],s[0][s[1]]="object"==typeof i&&i.nodeType?r(i):i,e[t]=[s[0],s[1],i,o],f=n.test(t)||f}),D(a),A=1,r.each(e,h),A=0,m.rendered&&H[0].offsetWidth>0&&f&&m.reposition(a.position.target==="mouse"?o:I.event),m},toggle:function(e,n){function b(){e?(r.browser.msie&&H[0].style.removeAttribute("filter"),H.css("overflow",""),"string"==typeof u.autofocus&&r(u.autofocus,H).focus(),u.target.trigger("qtip-"+f+"-inactive")):H.css({display:"",visibility:"",opacity:"",left:"",top:""}),m._triggerEvent(e?"visible":"hidden")}if(n){if(/over|enter/.test(n.type)&&/out|leave/.test(I.event.type)&&a.show.target.add(n.target).length===a.show.target.length&&H.has(n.relatedTarget).length)return m;I.event=r.extend({},n)}if(!m.rendered)return e?m.render(1):m;var o=e?"show":"hide",u=a[o],l=a[e?"hide":"show"],c=a.position,h=a.content,p=H[0].offsetWidth>0,d=e||u.target.length===1,v=!n||u.target.length<2||I.target[0]===n.target,g,y;return(typeof e).search("boolean|number")&&(e=!p),!H.is(":animated")&&p===e&&v?m:m._triggerEvent(o,[90])?(r.attr(H[0],"aria-hidden",!e),e?(I.origin=r.extend({},E),m.focus(n),r.isFunction(h.text)&&Q(h.text,s),r.isFunction(h.title.text)&&J(h.title.text,s),!M&&c.target==="mouse"&&c.adjust.mouse&&(r(t).bind("mousemove.qtip",_),M=i),m.reposition(n,arguments[2]),!u.solo||r(N,u.solo).not(H).qtip("hide",r.Event("tooltipsolo"))):(clearTimeout(m.timers.show),delete I.origin,M&&!r(N+'[tracking="true"]:visible',u.solo).not(H).length&&(r(t).unbind("mousemove.qtip"),M=s),m.blur(n)),u.effect===s||d===s?(H[o](),b.call(H)):r.isFunction(u.effect)?(H.stop(1,1),u.effect.call(H,m),H.queue("fx",function(e){b(),e()})):H.fadeTo(90,e?1:0,b),e&&u.target.trigger("qtip-"+f+"-inactive"),m):m},show:function(e){return m.toggle(i,e)},hide:function(e){return m.toggle(s,e)},focus:function(e){if(!m.rendered)return m;var t=r(N),n=parseInt(H[0].style.zIndex,10),i=b.zindex+t.length,s=r.extend({},e),o;return H.hasClass(k)||m._triggerEvent("focus",[i],s)&&(n!==i&&(t.each(function(){this.style.zIndex>n&&(this.style.zIndex=this.style.zIndex-1)}),t.filter("."+k).qtip("blur",s)),H.addClass(k)[0].style.zIndex=i),m},blur:function(e){return H.removeClass(k),m._triggerEvent("blur",[H.css("zIndex")],e),m},reposition:function(n,i){if(!m.rendered||A)return m;A=1;var o=a.position.target,u=a.position,f=u.my,l=u.at,g=u.adjust,y=g.method.split(" "),b=H.outerWidth(s),S=H.outerHeight(s),x=0,T=0,N=H.css("position"),C=u.viewport,k={left:0,top:0},L=u.container,O=H[0].offsetWidth>0,M=n&&n.type==="scroll",_=r(e),D,P;if(r.isArray(o)&&o.length===2)l={x:h,y:c},k={left:o[0],top:o[1]};else if(o==="mouse"&&(n&&n.pageX||I.event.pageX))l={x:h,y:c},n=E&&E.pageX&&(g.mouse||!n||!n.pageX)?{pageX:E.pageX,pageY:E.pageY}:(!n||n.type!=="resize"&&n.type!=="scroll"?n&&n.pageX&&n.type==="mousemove"?n:!g.mouse&&I.origin&&I.origin.pageX&&a.show.distance?I.origin:n:I.event)||n||I.event||E||{},N!=="static"&&(k=L.offset()),k={left:n.pageX-k.left,top:n.pageY-k.top},g.mouse&&M&&(k.left-=E.scrollX-_.scrollLeft(),k.top-=E.scrollY-_.scrollTop());else{o==="event"&&n&&n.target&&n.type!=="scroll"&&n.type!=="resize"?I.target=r(n.target):o!=="event"&&(I.target=r(o.jquery?o:F.target)),o=I.target,o=r(o).eq(0);if(o.length===0)return m;o[0]===t||o[0]===e?(x=w.iOS?e.innerWidth:o.width(),T=w.iOS?e.innerHeight:o.height(),o[0]===e&&(k={top:(C||o).scrollTop(),left:(C||o).scrollLeft()})):w.imagemap&&o.is("area")?D=w.imagemap(m,o,l,w.viewport?y:s):w.svg&&o[0].ownerSVGElement?D=w.svg(m,o,l,w.viewport?y:s):(x=o.outerWidth(s),T=o.outerHeight(s),k=w.offset(o,L)),D&&(x=D.width,T=D.height,P=D.offset,k=D.position);if(w.iOS>3.1&&w.iOS<4.1||w.iOS>=4.3&&w.iOS<4.33||!w.iOS&&N==="fixed")k.left-=_.scrollLeft(),k.top-=_.scrollTop();k.left+=l.x===d?x:l.x===v?x/2:0,k.top+=l.y===p?T:l.y===v?T/2:0}return k.left+=g.x+(f.x===d?-b:f.x===v?-b/2:0),k.top+=g.y+(f.y===p?-S:f.y===v?-S/2:0),w.viewport?(k.adjusted=w.viewport(m,k,u,x,T,b,S),P&&k.adjusted.left&&(k.left+=P.left),P&&k.adjusted.top&&(k.top+=P.top)):k.adjusted={left:0,top:0},m._triggerEvent("move",[k,C.elem||C],n)?(delete k.adjusted,i===s||!O||isNaN(k.left)||isNaN(k.top)||o==="mouse"||!r.isFunction(u.effect)?H.css(k):r.isFunction(u.effect)&&(u.effect.call(H,m,r.extend({},k)),H.queue(function(e){r(this).css({opacity:"",height:""}),r.browser.msie&&this.style.removeAttribute("filter"),e()})),A=0,m):m},disable:function(e){return"boolean"!=typeof e&&(e=!H.hasClass(j)&&!I.disabled),m.rendered?(H.toggleClass(j,e),r.attr(H[0],"aria-disabled",e)):I.disabled=!!e,m},enable:function(){return m.disable(s)},destroy:function(){var e=u[0],t=r.attr(e,O),n=u.data("qtip");m.destroyed=i,m.rendered&&(H.stop(1,0).remove(),r.each(m.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(m.timers.show),clearTimeout(m.timers.hide),Y();if(!n||m===n)r.removeData(e,"qtip"),a.suppress&&t&&(r.attr(e,"title",t),u.removeAttr(O)),u.removeAttr("aria-describedby");return u.unbind(".qtip-"+f),delete x[m.id],u}})}function H(e,n){var u,a,f,l,c,h=r(this),p=r(t.body),d=this===t?p:h,v=h.metadata?h.metadata(n.metadata):o,m=n.metadata.type==="html5"&&v?v[n.metadata.name]:o,g=h.data(n.metadata.name||"qtipopts");try{g=typeof g=="string"?r.parseJSON(g):g}catch(y){}l=r.extend(i,{},b.defaults,n,typeof g=="object"?D(g):o,D(m||v)),a=l.position,l.id=e;if("boolean"==typeof l.content.text){f=h.attr(l.content.attr);if(l.content.attr===s||!f)return s;l.content.text=f}a.container.length||(a.container=p),a.target===s&&(a.target=d),l.show.target===s&&(l.show.target=d),l.show.solo===i&&(l.show.solo=a.container.closest("body")),l.hide.target===s&&(l.hide.target=d),l.position.viewport===i&&(l.position.viewport=a.container),a.container=a.container.eq(0),a.at=new w.Corner(a.at),a.my=new w.Corner(a.my);if(r.data(this,"qtip"))if(l.overwrite)h.qtip("destroy");else if(l.overwrite===s)return s;return l.suppress&&(c=r.attr(this,"title"))&&r(this).removeAttr("title").attr(O,c).attr("title",""),u=new P(h,l,e,!!f),r.data(this,"qtip",u),h.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){u.destroy()}),u}function B(e){var t=this,n=e.elements.tooltip,o=e.options.content.ajax,u=b.defaults.content.ajax,a=".qtip-ajax",f=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,l=i,c=s,h;e.checks.ajax={"^content.ajax":function(e,r,i){r==="ajax"&&(o=i),r==="once"?t.init():o&&o.url?t.load():n.unbind(a)}},r.extend(t,{init:function(){return o&&o.url&&n.unbind(a)[o.once?"one":"bind"]("tooltipshow"+a,t.load),t},load:function(n){function g(){var t;if(e.destroyed)return;l=s,v&&(c=i,e.show(n.originalEvent)),(t=u.complete||o.complete)&&r.isFunction(t)&&t.apply(o.context||e,arguments)}function y(t,n,i){var s;if(e.destroyed)return;d&&"string"==typeof t&&(t=r("<div/>").append(t.replace(f,"")).find(d)),(s=u.success||o.success)&&r.isFunction(s)?s.call(o.context||e,t,n,i):e.set("content.text",t)}function b(t,n,r){if(e.destroyed||t.status===0)return;e.set("content.text",n+": "+r)}if(c){c=s;return}var a=o.url.lastIndexOf(" "),p=o.url,d,v=!o.loading&&l;if(v)try{n.preventDefault()}catch(m){}else if(n&&n.isDefaultPrevented())return t;h&&h.abort&&h.abort(),a>-1&&(d=p.substr(a),p=p.substr(0,a)),h=r.ajax(r.extend({error:u.error||b,context:e},o,{url:p,success:y,complete:g}))},destroy:function(){h&&h.abort&&h.abort(),e.destroyed=i}}),t.init()}function j(e,t,n){var r=Math.ceil(t/2),i=Math.ceil(n/2),s={bottomright:[[0,0],[t,n],[t,0]],bottomleft:[[0,0],[t,0],[0,n]],topright:[[0,n],[t,0],[t,n]],topleft:[[0,0],[0,n],[t,n]],topcenter:[[0,n],[r,0],[t,n]],bottomcenter:[[0,0],[t,0],[r,n]],rightcenter:[[0,0],[t,i],[0,n]],leftcenter:[[t,0],[t,n],[0,i]]};return s.lefttop=s.bottomright,s.righttop=s.bottomleft,s.leftbottom=s.topright,s.rightbottom=s.topleft,s[e.string()]}function F(e,t){function A(e){var t=E.is(":visible");E.show(),e(),E.toggle(t)}function O(){x.width=g.height,x.height=g.width}function M(){x.width=g.width,x.height=g.height}function _(t,r,o,f){if(!b.tip)return;var l=m.corner.clone(),w=o.adjusted,E=e.options.position.adjust.method.split(" "),x=E[0],T=E[1]||E[0],N={left:s,top:s,x:0,y:0},C,k={},L;m.corner.fixed!==i&&(x===y&&l.precedance===u&&w.left&&l.y!==v?l.precedance=l.precedance===u?a:u:x!==y&&w.left&&(l.x=l.x===v?w.left>0?h:d:l.x===h?d:h),T===y&&l.precedance===a&&w.top&&l.x!==v?l.precedance=l.precedance===a?u:a:T!==y&&w.top&&(l.y=l.y===v?w.top>0?c:p:l.y===c?p:c),l.string()!==S.corner.string()&&(S.top!==w.top||S.left!==w.left)&&m.update(l,s)),C=m.position(l,w),C[l.x]+=P(l,l.x),C[l.y]+=P(l,l.y),C.right!==n&&(C.left=-C.right),C.bottom!==n&&(C.top=-C.bottom),C.user=Math.max(0,g.offset);if(N.left=x===y&&!!w.left)l.x===v?k["margin-left"]=N.x=C["margin-left"]:(L=C.right!==n?[w.left,-C.left]:[-w.left,C.left],(N.x=Math.max(L[0],L[1]))>L[0]&&(o.left-=w.left,N.left=s),k[C.right!==n?d:h]=N.x);if(N.top=T===y&&!!w.top)l.y===v?k["margin-top"]=N.y=C["margin-top"]:(L=C.bottom!==n?[w.top,-C.top]:[-w.top,C.top],(N.y=Math.max(L[0],L[1]))>L[0]&&(o.top-=w.top,N.top=s),k[C.bottom!==n?p:c]=N.y);b.tip.css(k).toggle(!(N.x&&N.y||l.x===v&&N.y||l.y===v&&N.x)),o.left-=C.left.charAt?C.user:x!==y||N.top||!N.left&&!N.top?C.left:0,o.top-=C.top.charAt?C.user:T!==y||N.left||!N.left&&!N.top?C.top:0,S.left=w.left,S.top=w.top,S.corner=l.clone()}function D(){var t=g.corner,n=e.options.position,r=n.at,o=n.my.string?n.my.string():n.my;return t===s||o===s&&r===s?s:(t===i?m.corner=new w.Corner(o):t.string||(m.corner=new w.Corner(t),m.corner.fixed=i),S.corner=new w.Corner(m.corner.string()),m.corner.string()!=="centercenter")}function P(e,t,n){t=t?t:e[e.precedance];var r=b.titlebar&&e.y===c,i=r?b.titlebar:E,s="border-"+t+"-width",o=function(e){return parseInt(e.css(s),10)},u;return A(function(){u=(n?o(n):o(b.content)||o(i)||o(E))||0}),u}function H(e){var t=b.titlebar&&e.y===c,n=t?b.titlebar:b.content,i=r.browser.mozilla,s=i?"-moz-":r.browser.webkit?"-webkit-":"",o="border-radius-"+e.y+e.x,u="border-"+e.y+"-"+e.x+"-radius",a=function(e){return parseInt(n.css(e),10)||parseInt(E.css(e),10)},f;return A(function(){f=a(u)||a(s+u)||a(s+o)||a(o)||0}),f}function B(e){function N(e,t,n){var r=e.css(t)||p;return n&&r===e.css(n)?s:f.test(r)?s:r}var t,n,o,u=b.tip.css("cssText",""),a=e||m.corner,f=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,l="border-"+a[a.precedance]+"-color",h="background-color",p="transparent",d=" !important",y=b.titlebar,w=y&&(a.y===c||a.y===v&&u.position().top+x.height/2+g.offset<y.outerHeight(i)),S=w?y:b.content;A(function(){T.fill=N(u,h)||N(S,h)||N(b.content,h)||N(E,h)||u.css(h),T.border=N(u,l,"color")||N(S,l,"color")||N(b.content,l,"color")||N(E,l,"color")||E.css(l),r("*",u).add(u).css("cssText",h+":"+p+d+";border:0"+d+";")})}function F(e){var t=e.precedance===a,n=x[t?f:l],r=x[t?l:f],i=e.string().indexOf(v)>-1,s=n*(i?.5:1),o=Math.pow,u=Math.round,c,h,p,d=Math.sqrt(o(s,2)+o(r,2)),m=[N/s*d,N/r*d];return m[2]=Math.sqrt(o(m[0],2)-o(N,2)),m[3]=Math.sqrt(o(m[1],2)-o(N,2)),c=d+m[2]+m[3]+(i?0:m[0]),h=c/d,p=[u(h*r),u(h*n)],{height:p[t?0:1],width:p[t?1:0]}}function I(e,t,n){return"<qvml:"+e+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(t||"")+' style="behavior: url(#default#VML); '+(n||"")+'" />'}var m=this,g=e.options.style.tip,b=e.elements,E=b.tooltip,S={top:0,left:0},x={width:g.width,height:g.height},T={},N=g.border||0,C=".qtip-tip",k=!!(r("<canvas />")[0]||{}).getContext,L;m.corner=o,m.mimic=o,m.border=N,m.offset=g.offset,m.size=x,e.checks.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){m.init()||m.destroy(),e.reposition()},"^style.tip.(height|width)$":function(){x={width:g.width,height:g.height},m.create(),m.update(),e.reposition()},"^content.title.text|style.(classes|widget)$":function(){b.tip&&b.tip.length&&m.update()}},r.extend(m,{init:function(){var e=D()&&(k||r.browser.msie);return e&&(m.create(),m.update(),E.unbind(C).bind("tooltipmove"+C,_)),e},create:function(){var e=x.width,t=x.height,n;b.tip&&b.tip.remove(),b.tip=r("<div />",{"class":"qtip-tip"}).css({width:e,height:t}).prependTo(E),k?r("<canvas />").appendTo(b.tip)[0].getContext("2d").save():(n=I("shape",'coordorigin="0,0"',"position:absolute;"),b.tip.html(n+n),r("*",b.tip).bind("click mousedown",function(e){e.stopPropagation()}))},update:function(e,t){var n=b.tip,f=n.children(),l=x.width,y=x.height,C=g.mimic,L=Math.round,A,_,D,H,q;e||(e=S.corner||m.corner),C===s?C=e:(C=new w.Corner(C),C.precedance=e.precedance,C.x==="inherit"?C.x=e.x:C.y==="inherit"?C.y=e.y:C.x===C.y&&(C[e.precedance]=e[e.precedance])),A=C.precedance,e.precedance===u?O():M(),b.tip.css({width:l=x.width,height:y=x.height}),B(e),T.border!=="transparent"?(N=P(e,o),g.border===0&&N>0&&(T.fill=T.border),m.border=N=g.border!==i?g.border:N):m.border=N=0,D=j(C,l,y),m.size=q=F(e),n.css(q).css("line-height",q.height+"px"),e.precedance===a?H=[L(C.x===h?N:C.x===d?q.width-l-N:(q.width-l)/2),L(C.y===c?q.height-y:0)]:H=[L(C.x===h?q.width-l:0),L(C.y===c?N:C.y===p?q.height-y-N:(q.height-y)/2)],k?(f.attr(q),_=f[0].getContext("2d"),_.restore(),_.save(),_.clearRect(0,0,3e3,3e3),_.fillStyle=T.fill,_.strokeStyle=T.border,_.lineWidth=N*2,_.lineJoin="miter",_.miterLimit=100,_.translate(H[0],H[1]),_.beginPath(),_.moveTo(D[0][0],D[0][1]),_.lineTo(D[1][0],D[1][1]),_.lineTo(D[2][0],D[2][1]),_.closePath(),N&&(E.css("background-clip")==="border-box"&&(_.strokeStyle=T.fill,_.stroke()),_.strokeStyle=T.border,_.stroke()),_.fill()):(D="m"+D[0][0]+","+D[0][1]+" l"+D[1][0]+","+D[1][1]+" "+D[2][0]+","+D[2][1]+" xe",H[2]=N&&/^(r|b)/i.test(e.string())?parseFloat(r.browser.version,10)===8?2:1:0,f.css({coordsize:l+N+" "+(y+N),antialias:""+(C.string().indexOf(v)>-1),left:H[0],top:H[1],width:l+N,height:y+N}).each(function(e){var t=r(this);t[t.prop?"prop":"attr"]({coordsize:l+N+" "+(y+N),path:D,fillcolor:T.fill,filled:!!e,stroked:!e}).toggle(!!N||!!e),!e&&t.html()===""&&t.html(I("stroke",'weight="'+N*2+'px" color="'+T.border+'" miterlimit="1000" joinstyle="miter"'))})),t!==s&&m.position(e)},position:function(e){var t=b.tip,n={},i=Math.max(0,g.offset),o,p,d;return g.corner===s||!t?s:(e=e||m.corner,o=e.precedance,p=F(e),d=[e.x,e.y],o===u&&d.reverse(),r.each(d,function(t,r){var s,u,d;r===v?(s=o===a?h:c,n[s]="50%",n["margin-"+s]=-Math.round(p[o===a?f:l]/2)+i):(s=P(e,r),u=P(e,r,b.content),d=H(e),n[r]=t?u:i+(d>s?d:-s))}),n[e[o]]-=p[o===u?f:l],t.css({top:"",bottom:"",left:"",right:"",margin:""}).css(n),n)},destroy:function(){b.tip&&b.tip.remove(),b.tip=!1,E.unbind(C)}}),m.init()}function I(n){function y(){m=r(v,f).not("[disabled]").map(function(){return typeof this.focus=="function"?this:null})}function b(e){m.length<1&&e.length?e.not("body").blur():m.first().focus()}function E(e){var t=r(e.target),n=t.closest(".qtip"),i;i=n.length<1?s:parseInt(n[0].style.zIndex,10)>parseInt(f[0].style.zIndex,10),!i&&r(e.target).closest(N)[0]!==f[0]&&b(t)}var o=this,u=n.options.show.modal,a=n.elements,f=a.tooltip,l="#qtip-overlay",c=".qtipmodal",h=c+n.id,p="is-modal-qtip",d=r(t.body),v=w.modal.focusable.join(","),m={},g;n.checks.modal={"^show.modal.(on|blur)$":function(){o.init(),a.overlay.toggle(f.is(":visible"))},"^content.text$":function(){y()}},r.extend(o,{init:function(){return u.on?(g=o.create(),f.attr(p,i).css("z-index",w.modal.zindex+r(N+"["+p+"]").length).unbind(c).unbind(h).bind("tooltipshow"+c+" tooltiphide"+c,function(e,t,n){var i=e.originalEvent;if(e.target===f[0])if(i&&e.type==="tooltiphide"&&/mouse(leave|enter)/.test(i.type)&&r(i.relatedTarget).closest(g[0]).length)try{e.preventDefault()}catch(s){}else(!i||i&&!i.solo)&&o[e.type.replace("tooltip","")](e,n)}).bind("tooltipfocus"+c,function(e){if(e.isDefaultPrevented()||e.target!==f[0])return;var t=r(N).filter("["+p+"]"),n=w.modal.zindex+t.length,i=parseInt(f[0].style.zIndex,10);g[0].style.zIndex=n-2,t.each(function(){this.style.zIndex>i&&(this.style.zIndex-=1)}),t.end().filter("."+k).qtip("blur",e.originalEvent),f.addClass(k)[0].style.zIndex=n;try{e.preventDefault()}catch(s){}}).bind("tooltiphide"+c,function(e){e.target===f[0]&&r("["+p+"]").filter(":visible").not(f).last().qtip("focus",e)}),u.escape&&r(t).unbind(h).bind("keydown"+h,function(e){e.keyCode===27&&f.hasClass(k)&&n.hide(e)}),u.blur&&a.overlay.unbind(h).bind("click"+h,function(e){f.hasClass(k)&&n.hide(e)}),y(),o):o},create:function(){function i(){g.css({height:n.height(),width:n.width()})}var t=r(l),n=r(e);return t.length?a.overlay=t.insertAfter(r(N).last()):(g=a.overlay=r("<div />",{id:l.substr(1),html:"<div></div>",mousedown:function(){return s}}).hide().insertAfter(r(N).last()),n.unbind(c).bind("resize"+c,i),i(),g)},toggle:function(e,t,n){if(e&&e.isDefaultPrevented())return o;var a=u.effect,l=t?"show":"hide",c=g.is(":visible"),v=r("["+p+"]").filter(":visible").not(f),m;return g||(g=o.create()),g.is(":animated")&&c===t&&g.data("toggleState")!==s||!t&&v.length?o:(t?(g.css({left:0,top:0}),g.toggleClass("blurs",u.blur),u.stealfocus!==s&&(d.bind("focusin"+h,E),b(r("body :focus")))):d.unbind("focusin"+h),g.stop(i,s).data("toggleState",t),r.isFunction(a)?a.call(g,t):a===s?g[l]():g.fadeTo(parseInt(n,10)||90,t?1:0,function(){t||r(this).hide()}),t||g.queue(function(e){g.css({left:"",top:""}).removeData("toggleState"),e()}),o)},show:function(e,t){return o.toggle(e,i,t)},hide:function(e,t){return o.toggle(e,s,t)},destroy:function(){var e=g;return e&&(e=r("["+p+"]").not(f).length<1,e?(a.overlay.remove(),r(t).unbind(c)):a.overlay.unbind(c+n.id),d.unbind("focusin"+h)),f.removeAttr(p).unbind(c)}}),o.init()}function q(n){var o=this,u=n.elements,a=n.options,c=u.tooltip,h=".ie6-"+n.id,p=r("select, object").length<1,d=0,v=s,m;n.checks.ie6={"^content|style$":function(e,t,n){redraw()}},r.extend(o,{init:function(){var n=r(e),s;p&&(u.bgiframe=r('<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>'),u.bgiframe.appendTo(c),c.bind("tooltipmove"+h,o.adjustBGIFrame)),m=r("<div/>",{id:"qtip-rcontainer"}).appendTo(t.body),o.redraw(),u.overlay&&!v&&(s=function(){u.overlay[0].style.top=n.scrollTop()+"px"},n.bind("scroll.qtip-ie6, resize.qtip-ie6",s),s(),u.overlay.addClass("qtipmodal-ie6fix"),v=i)},adjustBGIFrame:function(){var e=n.get("dimensions"),t=n.plugins.tip,r=u.tip,i,s;s=parseInt(c.css("border-left-width"),10)||0,s={left:-s,top:-s},t&&r&&(i=t.corner.precedance==="x"?["width","left"]:["height","top"],s[i[1]]-=r[i[0]]()),u.bgiframe.css(s).css(e)},redraw:function(){if(n.rendered<1||d)return o;var e=a.style,t=a.position.container,r,i,s,u;return d=1,e.height&&c.css(l,e.height),e.width?c.css(f,e.width):(c.css(f,"").appendTo(m),i=c.width(),i%2<1&&(i+=1),s=c.css("max-width")||"",u=c.css("min-width")||"",r=(s+u).indexOf("%")>-1?t.width()/100:0,s=(s.indexOf("%")>-1?r:1)*parseInt(s,10)||i,u=(u.indexOf("%")>-1?r:1)*parseInt(u,10)||0,i=s+u?Math.min(Math.max(i,u),s):i,c.css(f,Math.round(i)).appendTo(t)),d=0,o},destroy:function(){p&&u.bgiframe.remove(),c.unbind(h)}}),o.init()}var i=!0,s=!1,o=null,u="x",a="y",f="width",l="height",c="top",h="left",p="bottom",d="right",v="center",m="flip",g="flipinvert",y="shift",b,w,E,S="qtip",x={},T=["ui-widget","ui-tooltip"],N="div.qtip."+S,C=S+"-default",k=S+"-focus",L=S+"-hover",A="_replacedByqTip",O="oldtitle",M;b=r.fn.qtip=function(e,t,u){var a=(""+e).toLowerCase(),f=o,l=r.makeArray(arguments).slice(1),c=l[l.length-1],h=this[0]?r.data(this[0],"qtip"):o;if(!arguments.length&&h||a==="api")return h;if("string"==typeof e)return this.each(function(){var e=r.data(this,"qtip");if(!e)return i;c&&c.timeStamp&&(e.cache.event=c);if(a!=="option"&&a!=="options"||!t)e[a]&&e[a].apply(e[a],l);else{if(!r.isPlainObject(t)&&u===n)return f=e.get(t),s;e.set(t,u)}}),f!==o?f:this;if("object"==typeof e||!arguments.length)return h=D(r.extend(i,{},e)),b.bind.call(this,h,c)},b.bind=function(e,t){return this.each(function(o){function p(e){function t(){c.render(typeof e=="object"||u.show.ready),a.show.add(a.hide).unbind(l)}if(c.cache.disabled)return s;c.cache.event=r.extend({},e),c.cache.target=e?r(e.target):[n],u.show.delay>0?(clearTimeout(c.timers.show),c.timers.show=setTimeout(t,u.show.delay),f.show!==f.hide&&a.hide.bind(f.hide,function(){clearTimeout(c.timers.show)})):t()}var u,a,f,l,c,h;h=r.isArray(e.id)?e.id[o]:e.id,h=!h||h===s||h.length<1||x[h]?b.nextid++:x[h]=h,l=".qtip-"+h+"-create",c=H.call(this,h,e);if(c===s)return i;u=c.options,r.each(w,function(){this.initialize==="initialize"&&this(c)}),a={show:u.show.target,hide:u.hide.target},f={show:r.trim(""+u.show.event).replace(/ /g,l+" ")+l,hide:r.trim(""+u.hide.event).replace(/ /g,l+" ")+l},/mouse(over|enter)/i.test(f.show)&&!/mouse(out|leave)/i.test(f.hide)&&(f.hide+=" mouseleave"+l),a.show.bind("mousemove"+l,function(e){_(e),c.cache.onTarget=i}),a.show.bind(f.show,p),(u.show.ready||u.prerender)&&p(t)}).attr("data-hasqtip",i)},w=b.plugins={Corner:function(e){e=(""+e).replace(/([A-Z])/," $1").replace(/middle/gi,v).toLowerCase(),this.x=(e.match(/left|right/i)||e.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(e.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var t=e.charAt(0);this.precedance=t==="t"||t==="b"?a:u,this.string=function(){return this.precedance===a?this.y+this.x:this.x+this.y},this.abbrev=function(){var e=this.x.substr(0,1),t=this.y.substr(0,1);return e===t?e:this.precedance===a?t+e:e+t},this.invertx=function(e){this.x=this.x===h?d:this.x===d?h:e||this.x},this.inverty=function(e){this.y=this.y===c?p:this.y===p?c:e||this.y},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone,invertx:this.invertx,inverty:this.inverty}}},offset:function(e,n){function c(e,t){i.left+=t*e.scrollLeft(),i.top+=t*e.scrollTop()}var i=e.offset(),s=e.closest("body"),o=r.browser.msie&&t.compatMode!=="CSS1Compat",u=n,a,f,l;if(u){do u.css("position")!=="static"&&(f=u.position(),i.left-=f.left+(parseInt(u.css("borderLeftWidth"),10)||0)+(parseInt(u.css("marginLeft"),10)||0),i.top-=f.top+(parseInt(u.css("borderTopWidth"),10)||0)+(parseInt(u.css("marginTop"),10)||0),!a&&(l=u.css("overflow"))!=="hidden"&&l!=="visible"&&(a=u));while((u=r(u[0].offsetParent)).length);(a&&a[0]!==s[0]||o)&&c(a||s,1)}return i},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||s,fn:{attr:function(e,t){if(this.length){var n=this[0],i="title",s=r.data(n,"qtip");if(e===i&&s&&"object"==typeof s&&s.options.suppress)return arguments.length<2?r.attr(n,O):(s&&s.options.content.attr===i&&s.cache.attr&&s.set("content.text",t),this.attr(O,t))}return r.fn["attr"+A].apply(this,arguments)},clone:function(e){var t=r([]),n="title",i=r.fn["clone"+A].apply(this,arguments);return e||i.filter("["+O+"]").attr("title",function(){return r.attr(this,O)}).removeAttr(O),i}}},r.each(w.fn,function(e,t){if(!t||r.fn[e+A])return i;var n=r.fn[e+A]=r.fn[e];r.fn[e]=function(){return t.apply(this,arguments)||n.apply(this,arguments)}}),r.ui||(r["cleanData"+A]=r.cleanData,r.cleanData=function(e){for(var t=0,i;(i=e[t])!==n;t++)try{r(i).triggerHandler("removeqtip")}catch(s){}r["cleanData"+A](e)}),b.version="2.0.0-nightly-15f5c6bc20",b.nextid=0,b.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),b.zindex=15e3,b.defaults={prerender:s,id:s,overwrite:i,suppress:i,content:{text:i,attr:"title",deferred:s,title:{text:s,button:s}},position:{my:"top left",at:"bottom right",target:s,container:s,viewport:s,adjust:{x:0,y:0,mouse:i,resize:i,method:"flipinvert flipinvert"},effect:function(e,t,n){r(this).animate(t,{duration:200,queue:s})}},show:{target:s,event:"mouseenter",effect:i,delay:90,solo:s,ready:s,autofocus:s},hide:{target:s,event:"mouseleave",effect:i,delay:0,fixed:s,inactive:s,leave:"window",distance:s},style:{classes:"",widget:s,width:s,height:s,def:i},events:{render:o,move:o,show:o,hide:o,toggle:o,visible:o,hidden:o,focus:o,blur:o}},w.svg=function(e,n,i,s){var o=r(t),u=n[0],a={width:0,height:0,position:{top:1e10,left:1e10}},f,l,c,h,p;while(!u.getBBox)u=u.parentNode;if(u.getBBox&&u.parentNode){f=u.getBBox(),l=u.getScreenCTM(),c=u.farthestViewportElement||u;if(!c.createSVGPoint)return a;h=c.createSVGPoint(),h.x=f.x,h.y=f.y,p=h.matrixTransform(l),a.position.left=p.x,a.position.top=p.y,h.x+=f.width,h.y+=f.height,p=h.matrixTransform(l),a.width=p.x-a.position.left,a.height=p.y-a.position.top,a.position.left+=o.scrollLeft(),a.position.top+=o.scrollTop()}return a},w.ajax=function(e){var t=e.plugins.ajax;return"object"==typeof t?t:e.plugins.ajax=new B(e)},w.ajax.initialize="render",w.ajax.sanitize=function(e){var t=e.content,n;t&&"ajax"in t&&(n=t.ajax,typeof n!="object"&&(n=e.content.ajax={url:n}),"boolean"!=typeof n.once&&n.once&&(n.once=!!n.once))},r.extend(i,b.defaults,{content:{ajax:{loading:i,once:i}}}),w.tip=function(e){var t=e.plugins.tip;return"object"==typeof t?t:e.plugins.tip=new F(e)},w.tip.initialize="render",w.tip.sanitize=function(e){var t=e.style,n;t&&"tip"in t&&(n=e.style.tip,typeof n!="object"&&(e.style.tip={corner:n}),/string|boolean/i.test(typeof n.corner)||(n.corner=i),typeof n.width!="number"&&delete n.width,typeof n.height!="number"&&delete n.height,typeof n.border!="number"&&n.border!==i&&delete n.border,typeof n.offset!="number"&&delete n.offset)},r.extend(i,b.defaults,{style:{tip:{corner:i,mimic:s,width:6,height:6,border:i,offset:0}}}),w.modal=function(e){var t=e.plugins.modal;return"object"==typeof t?t:e.plugins.modal=new I(e)},w.modal.initialize="render",w.modal.sanitize=function(e){e.show&&(typeof e.show.modal!="object"?e.show.modal={on:!!e.show.modal}:typeof e.show.modal.on=="undefined"&&(e.show.modal.on=i))},w.modal.zindex=b.zindex-200,w.modal.focusable=["a[href]","area[href]","input","select","textarea","button","iframe","object","embed","[tabindex]","[contenteditable]"],r.extend(i,b.defaults,{show:{modal:{on:s,effect:i,blur:i,stealfocus:i,escape:i}}}),w.viewport=function(n,r,i,s,o,m,b){function j(e,t,n,i,s,o,u,a,f){var l=r[s],c=x[e],h=T[e],p=n===y,d=-O.offset[s]+A.offset[s]+A["scroll"+s],m=c===s?f:c===o?-f:-f/2,b=h===s?a:h===o?-a:-a/2,w=_&&_.size?_.size[u]||0:0,E=_&&_.corner&&_.corner.precedance===e&&!p?w:0,S=d-l+E,N=l+f-A[u]-d+E,C=m-(x.precedance===e||c===x[t]?b:0)-(h===v?a/2:0);return p?(E=_&&_.corner&&_.corner.precedance===t?w:0,C=(c===s?1:-1)*m-E,r[s]+=S>0?S:N>0?-N:0,r[s]=Math.max(-O.offset[s]+A.offset[s]+(E&&_.corner[e]===v?_.offset:0),l-C,Math.min(Math.max(-O.offset[s]+A.offset[s]+A[u],l+C),r[s]))):(i*=n===g?2:0,S>0&&(c!==s||N>0)?(r[s]-=C+i,H["invert"+e](s)):N>0&&(c!==o||S>0)&&(r[s]-=(c===v?-C:C)+i,H["invert"+e](o)),r[s]<d&&-r[s]>N&&(r[s]=l,H=x.clone())),r[s]-l}var w=i.target,E=n.elements.tooltip,x=i.my,T=i.at,N=i.adjust,C=N.method.split(" "),k=C[0],L=C[1]||C[0],A=i.viewport,O=i.container,M=n.cache,_=n.plugins.tip,D={left:0,top:0},P,H,B;if(!A.jquery||w[0]===e||w[0]===t.body||N.method==="none")return D;P=E.css("position")==="fixed",A={elem:A,height:A[(A[0]===e?"h":"outerH")+"eight"](),width:A[(A[0]===e?"w":"outerW")+"idth"](),scrollleft:P?0:A.scrollLeft(),scrolltop:P?0:A.scrollTop(),offset:A.offset()||{left:0,top:0}},O={elem:O,scrollLeft:O.scrollLeft(),scrollTop:O.scrollTop(),offset:O.offset()||{left:0,top:0}};if(k!=="shift"||L!=="shift")H=x.clone();return D={left:k!=="none"?j(u,a,k,N.x,h,d,f,s,m):0,top:L!=="none"?j(a,u,L,N.y,c,p,l,o,b):0},H&&M.lastClass!==(B=S+"-pos-"+H.abbrev())&&E.removeClass(n.cache.lastClass).addClass(n.cache.lastClass=B),D},w.imagemap=function(e,t,n,i){function E(e,t,n){var r=0,i=1,s=1,o=0,u=0,a=e.width,f=e.height;while(a>0&&f>0&&i>0&&s>0){a=Math.floor(a/2),f=Math.floor(f/2),n.x===h?i=a:n.x===d?i=e.width-a:i+=Math.floor(a/2),n.y===c?s=f:n.y===p?s=e.height-f:s+=Math.floor(f/2),r=t.length;while(r--){if(t.length<2)break;o=t[r][0]-e.position.left,u=t[r][1]-e.position.top,(n.x===h&&o>=i||n.x===d&&o<=i||n.x===v&&(o<i||o>e.width-i)||n.y===c&&u>=s||n.y===p&&u<=s||n.y===v&&(u<s||u>e.height-s))&&t.splice(r,1)}}return{left:t[0][0],top:t[0][1]}}t.jquery||(t=r(t));var s=e.cache.areas={},o=(t[0].shape||t.attr("shape")).toLowerCase(),u=t[0].coords||t.attr("coords"),a=u.split(","),f=[],l=r('img[usemap="#'+t.parent("map").attr("name")+'"]'),m=l.offset(),g={width:0,height:0,position:{top:1e10,right:0,bottom:0,left:1e10}},y=0,b=0,w;m.left+=Math.ceil((l.outerWidth()-l.width())/2),m.top+=Math.ceil((l.outerHeight()-l.height())/2);if(o==="poly"){y=a.length;while(y--)b=[parseInt(a[--y],10),parseInt(a[y+1],10)],b[0]>g.position.right&&(g.position.right=b[0]),b[0]<g.position.left&&(g.position.left=b[0]),b[1]>g.position.bottom&&(g.position.bottom=b[1]),b[1]<g.position.top&&(g.position.top=b[1]),f.push(b)}else{y=-1;while(y++<a.length)f.push(parseInt(a[y],10))}switch(o){case"rect":g={width:Math.abs(f[2]-f[0]),height:Math.abs(f[3]-f[1]),position:{left:Math.min(f[0],f[2]),top:Math.min(f[1],f[3])}};break;case"circle":g={width:f[2]+2,height:f[2]+2,position:{left:f[0],top:f[1]}};break;case"poly":g.width=Math.abs(g.position.right-g.position.left),g.height=Math.abs(g.position.bottom-g.position.top),n.abbrev()==="c"?g.position={left:g.position.left+g.width/2,top:g.position.top+g.height/2}:(s[n+u]||(g.position=E(g,f.slice(),n),i&&(i[0]==="flip"||i[1]==="flip")&&(g.offset=E(g,f.slice(),{x:n.x===h?d:n.x===d?h:v,y:n.y===c?p:n.y===p?c:v}),g.offset.left-=g.position.left,g.offset.top-=g.position.top),s[n+u]=g),g=s[n+u]),g.width=g.height=0}return g.position.left+=m.left,g.position.top+=m.top,g},w.ie6=function(e){var t=r.browser,n=e.plugins.ie6;return!t.msie||(""+t.version).charAt(0)!=="6"?s:"object"==typeof n?n:e.plugins.ie6=new q(e)},w.ie6.initialize="render"})})(window,document);
1
+ /*! qtip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
2
  (function(e,t,n){(function(e){"use strict";typeof define=="function"&&define.amd?define(["jquery"],e):jQuery&&!jQuery.fn.qtip&&e(jQuery)})(function(r){function _(n){E={pageX:n.pageX,pageY:n.pageY,type:"mousemove",scrollX:e.pageXOffset||t.body.scrollLeft||t.documentElement.scrollLeft,scrollY:e.pageYOffset||t.body.scrollTop||t.documentElement.scrollTop}}function D(e){var t=function(e){return e===o||"object"!=typeof e},n=function(e){return!r.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery&&!e.then)};if(!e||"object"!=typeof e)return s;t(e.metadata)&&(e.metadata={type:e.metadata});if("content"in e){if(t(e.content)||e.content.jquery)e.content={text:e.content};n(e.content.text||s)&&(e.content.text=s),"title"in e.content&&(t(e.content.title)&&(e.content.title={text:e.content.title}),n(e.content.title.text||s)&&(e.content.title.text=s))}return"position"in e&&t(e.position)&&(e.position={my:e.position,at:e.position}),"show"in e&&t(e.show)&&(e.show=e.show.jquery?{target:e.show}:{event:e.show}),"hide"in e&&t(e.hide)&&(e.hide=e.hide.jquery?{target:e.hide}:{event:e.hide}),"style"in e&&t(e.style)&&(e.style={classes:e.style}),r.each(w,function(){this.sanitize&&this.sanitize(e)}),e}function P(u,a,f,l){function q(e){var t=0,n,r=a,i=e.split(".");while(r=r[i[t++]])t<i.length&&(n=r);return[n||a,i.pop()]}function R(e){return T.concat("").join(e?"-"+e+" ":" ")}function U(){var e=a.style.widget,t=H.hasClass(j);H.removeClass(j),j=e?"ui-state-disabled":"qtip-disabled",H.toggleClass(j,t),H.toggleClass("ui-helper-reset "+R(),e).toggleClass(C,a.style.def&&!e),F.content&&F.content.toggleClass(R("content"),e),F.titlebar&&F.titlebar.toggleClass(R("header"),e),F.button&&F.button.toggleClass(S+"-icon",!e)}function z(e){F.title&&(F.titlebar.remove(),F.titlebar=F.title=F.button=o,e!==s&&m.reposition())}function W(){var e=a.content.title.button,t=typeof e=="string",n=t?e:"Close tooltip";F.button&&F.button.remove(),e.jquery?F.button=e:F.button=r("<a />",{"class":"qtip-close "+(a.style.widget?"":S+"-icon"),title:n,"aria-label":n}).prepend(r("<span />",{"class":"ui-icon ui-icon-close",html:"&times;"})),F.button.appendTo(F.titlebar||H).attr("role","button").click(function(e){return H.hasClass(j)||m.hide(e),s})}function X(){var e=y+"-title";F.titlebar&&z(),F.titlebar=r("<div />",{"class":S+"-titlebar "+(a.style.widget?R("header"):"")}).append(F.title=r("<div />",{id:e,"class":S+"-title","aria-atomic":i})).insertBefore(F.content).delegate(".qtip-close","mousedown keydown mouseup keyup mouseout",function(e){r(this).toggleClass("ui-state-active ui-state-focus",e.type.substr(-4)==="down")}).delegate(".qtip-close","mouseover mouseout",function(e){r(this).toggleClass("ui-state-hover",e.type==="mouseover")}),a.content.title.button&&W()}function V(e){var t=F.button;if(!m.rendered)return s;e?W():t.remove()}function J(e,t){var n=F.title;if(!m.rendered||!e)return s;r.isFunction(e)&&(e=e.call(u,I.event,m));if(e===s||!e&&e!=="")return z(s);e.jquery&&e.length>0?n.empty().append(e.css({display:"block"})):n.html(e),t!==s&&m.rendered&&H[0].offsetWidth>0&&m.reposition(I.event)}function K(e){e&&r.isFunction(e.done)&&e.done(function(e){Q(e,null,s)})}function Q(e,t,i){function f(e){function a(n){n&&(delete u[n.src],clearTimeout(m.timers.img[n.src]),r(n).unbind(B)),r.isEmptyObject(u)&&(t!==s&&m.reposition(I.event),e())}var i,u={};if((i=o.find("img[src]:not([height]):not([width])")).length===0)return a();i.each(function(e,t){if(u[t.src]!==n)return;var i=0,s=3;(function o(){if(t.height||t.width||i>s)return a(t);i+=1,m.timers.img[t.src]=setTimeout(o,700)})(),r(t).bind("error"+B+" load"+B,function(){a(this)}),u[t.src]=t})}var o=F.content;return!m.rendered||!e?s:(r.isFunction(e)&&(e=e.call(u,I.event,m)||""),i!==s&&K(a.content.deferred),e.jquery&&e.length>0?o.empty().append(e.css({display:"block"})):o.html(e),m.rendered<0?H.queue("fx",f):(P=0,f(r.noop)),m)}function G(){function h(e){if(H.hasClass(j))return s;clearTimeout(m.timers.show),clearTimeout(m.timers.hide);var t=function(){m.toggle(i,e)};a.show.delay>0?m.timers.show=setTimeout(t,a.show.delay):t()}function p(e){if(H.hasClass(j)||A||P)return s;var t=r(e.relatedTarget||e.target),i=t.closest(N)[0]===H[0],u=t[0]===o.show[0];clearTimeout(m.timers.show),clearTimeout(m.timers.hide);if(n.target==="mouse"&&i||a.hide.fixed&&/mouse(out|leave|move)/.test(e.type)&&(i||u)){try{e.preventDefault(),e.stopImmediatePropagation()}catch(f){}return}a.hide.delay>0?m.timers.hide=setTimeout(function(){m.hide(e)},a.hide.delay):m.hide(e)}function d(e){if(H.hasClass(j))return s;clearTimeout(m.timers.inactive),m.timers.inactive=setTimeout(function(){m.hide(e)},a.hide.inactive)}function v(e){m.rendered&&H[0].offsetWidth>0&&m.reposition(e)}var n=a.position,o={show:a.show.target,hide:a.hide.target,viewport:r(n.viewport),document:r(t),body:r(t.body),window:r(e)},l={show:r.trim(""+a.show.event).split(" "),hide:r.trim(""+a.hide.event).split(" ")},c=r.browser.msie&&parseInt(r.browser.version,10)===6;H.bind("mouseenter"+B+" mouseleave"+B,function(e){var t=e.type==="mouseenter";t&&m.focus(e),H.toggleClass(L,t)}),/mouse(out|leave)/i.test(a.hide.event)&&a.hide.leave==="window"&&o.window.bind("mouseout"+B+" blur"+B,function(e){!/select|option/.test(e.target.nodeName)&&!e.relatedTarget&&m.hide(e)}),a.hide.fixed?(o.hide=o.hide.add(H),H.bind("mouseover"+B,function(){H.hasClass(j)||clearTimeout(m.timers.hide)})):/mouse(over|enter)/i.test(a.show.event)&&o.hide.bind("mouseleave"+B,function(e){clearTimeout(m.timers.show)}),(""+a.hide.event).indexOf("unfocus")>-1&&n.container.closest("html").bind("mousedown"+B+" touchstart"+B,function(e){var t=r(e.target),n=m.rendered&&!H.hasClass(j)&&H[0].offsetWidth>0,i=t.parents(N).filter(H[0]).length>0;t[0]!==u[0]&&t[0]!==H[0]&&!i&&!u.has(t[0]).length&&!t.attr("disabled")&&m.hide(e)}),"number"==typeof a.hide.inactive&&(o.show.bind("qtip-"+f+"-inactive",d),r.each(b.inactiveEvents,function(e,t){o.hide.add(F.tooltip).bind(t+B+"-inactive",d)})),r.each(l.hide,function(e,t){var n=r.inArray(t,l.show),i=r(o.hide);n>-1&&i.add(o.show).length===i.length||t==="unfocus"?(o.show.bind(t+B,function(e){H[0].offsetWidth>0?p(e):h(e)}),delete l.show[n]):o.hide.bind(t+B,p)}),r.each(l.show,function(e,t){o.show.bind(t+B,h)}),"number"==typeof a.hide.distance&&o.show.add(H).bind("mousemove"+B,function(e){var t=I.origin||{},n=a.hide.distance,r=Math.abs;(r(e.pageX-t.pageX)>=n||r(e.pageY-t.pageY)>=n)&&m.hide(e)}),n.target==="mouse"&&(o.show.bind("mousemove"+B,_),n.adjust.mouse&&(a.hide.event&&(H.bind("mouseleave"+B,function(e){(e.relatedTarget||e.target)!==o.show[0]&&m.hide(e)}),F.target.bind("mouseenter"+B+" mouseleave"+B,function(e){I.onTarget=e.type==="mouseenter"})),o.document.bind("mousemove"+B,function(e){m.rendered&&I.onTarget&&!H.hasClass(j)&&H[0].offsetWidth>0&&m.reposition(e||E)}))),(n.adjust.resize||o.viewport.length)&&(r.event.special.resize?o.viewport:o.window).bind("resize"+B,v),o.window.bind("scroll"+B,v)}function Y(){var n=[a.show.target[0],a.hide.target[0],m.rendered&&F.tooltip[0],a.position.container[0],a.position.viewport[0],a.position.container.closest("html")[0],e,t];m.rendered?r([]).pushStack(r.grep(n,function(e){return typeof e=="object"})).unbind(B):a.show.target.unbind(B+"-create")}var m=this,g=t.body,y=S+"-"+f,A=0,P=0,H=r(),B=".qtip-"+f,j="qtip-disabled",F,I;m.id=f,m.rendered=s,m.destroyed=s,m.elements=F={target:u},m.timers={img:{}},m.options=a,m.checks={},m.plugins={},m.cache=I={event:{},target:r(),disabled:s,attr:l,onTarget:s,lastClass:""},m.checks.builtin={"^id$":function(e,t,n){var o=n===i?b.nextid:n,u=S+"-"+o;o!==s&&o.length>0&&!r("#"+u).length&&(H[0].id=u,F.content[0].id=u+"-content",F.title[0].id=u+"-title")},"^content.text$":function(e,t,n){Q(a.content.text)},"^content.deferred$":function(e,t,n){K(a.content.deferred)},"^content.title.text$":function(e,t,n){if(!n)return z();!F.title&&n&&X(),J(n)},"^content.title.button$":function(e,t,n){V(n)},"^position.(my|at)$":function(e,t,n){"string"==typeof n&&(e[t]=new w.Corner(n))},"^position.container$":function(e,t,n){m.rendered&&H.appendTo(n)},"^show.ready$":function(){m.rendered?m.toggle(i):m.render(1)},"^style.classes$":function(e,t,n){H.attr("class",S+" qtip "+n)},"^style.width|height":function(e,t,n){H.css(t,n)},"^style.widget|content.title":U,"^events.(render|show|move|hide|focus|blur)$":function(e,t,n){H[(r.isFunction(n)?"":"un")+"bind"]("tooltip"+t,n)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var e=a.position;H.attr("tracking",e.target==="mouse"&&e.adjust.mouse),Y(),G()}},r.extend(m,{_triggerEvent:function(e,t,n){var i=r.Event("tooltip"+e);return i.originalEvent=(n?r.extend({},n):o)||I.event||o,H.trigger(i,[m].concat(t||[])),!i.isDefaultPrevented()},render:function(e){if(m.rendered)return m;var t=a.content.text,n=a.content.title,o=a.position;return r.attr(u[0],"aria-describedby",y),H=F.tooltip=r("<div/>",{id:y,"class":[S,C,a.style.classes,S+"-pos-"+a.position.my.abbrev()].join(" "),width:a.style.width||"",height:a.style.height||"",tracking:o.target==="mouse"&&o.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":s,"aria-describedby":y+"-content","aria-hidden":i}).toggleClass(j,I.disabled).data("qtip",m).appendTo(a.position.container).append(F.content=r("<div />",{"class":S+"-content",id:y+"-content","aria-atomic":i})),m.rendered=-1,A=1,n.text?(X(),r.isFunction(n.text)||J(n.text,s)):n.button&&W(),(!r.isFunction(t)||t.then)&&Q(t,s),m.rendered=i,U(),r.each(a.events,function(e,t){r.isFunction(t)&&H.bind(e==="toggle"?"tooltipshow tooltiphide":"tooltip"+e,t)}),r.each(w,function(){this.initialize==="render"&&this(m)}),G(),H.queue("fx",function(t){m._triggerEvent("render"),A=0,(a.show.ready||e)&&m.toggle(i,I.event,s),t()}),m},get:function(e){var t,n;switch(e.toLowerCase()){case"dimensions":t={height:H.outerHeight(s),width:H.outerWidth(s)};break;case"offset":t=w.offset(H,a.position.container);break;default:n=q(e.toLowerCase()),t=n[0][n[1]],t=t.precedance?t.string():t}return t},set:function(e,t){function h(e,t){var n,r,i;for(n in l)for(r in l[n])if(i=(new RegExp(r,"i")).exec(e))t.push(i),l[n][r].apply(m,t)}var n=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,u=/^content\.(title|attr)|style/i,f=s,l=m.checks,c;return"string"==typeof e?(c=e,e={},e[c]=t):e=r.extend(i,{},e),r.each(e,function(t,i){var s=q(t.toLowerCase()),o;o=s[0][s[1]],s[0][s[1]]="object"==typeof i&&i.nodeType?r(i):i,e[t]=[s[0],s[1],i,o],f=n.test(t)||f}),D(a),A=1,r.each(e,h),A=0,m.rendered&&H[0].offsetWidth>0&&f&&m.reposition(a.position.target==="mouse"?o:I.event),m},toggle:function(e,n){function b(){e?(r.browser.msie&&H[0].style.removeAttribute("filter"),H.css("overflow",""),"string"==typeof u.autofocus&&r(u.autofocus,H).focus(),u.target.trigger("qtip-"+f+"-inactive")):H.css({display:"",visibility:"",opacity:"",left:"",top:""}),m._triggerEvent(e?"visible":"hidden")}if(n){if(/over|enter/.test(n.type)&&/out|leave/.test(I.event.type)&&a.show.target.add(n.target).length===a.show.target.length&&H.has(n.relatedTarget).length)return m;I.event=r.extend({},n)}if(!m.rendered)return e?m.render(1):m;var o=e?"show":"hide",u=a[o],l=a[e?"hide":"show"],c=a.position,h=a.content,p=H[0].offsetWidth>0,d=e||u.target.length===1,v=!n||u.target.length<2||I.target[0]===n.target,g,y;return(typeof e).search("boolean|number")&&(e=!p),!H.is(":animated")&&p===e&&v?m:m._triggerEvent(o,[90])?(r.attr(H[0],"aria-hidden",!e),e?(I.origin=r.extend({},E),m.focus(n),r.isFunction(h.text)&&Q(h.text,s),r.isFunction(h.title.text)&&J(h.title.text,s),!M&&c.target==="mouse"&&c.adjust.mouse&&(r(t).bind("mousemove.qtip",_),M=i),m.reposition(n,arguments[2]),!u.solo||r(N,u.solo).not(H).qtip("hide",r.Event("tooltipsolo"))):(clearTimeout(m.timers.show),delete I.origin,M&&!r(N+'[tracking="true"]:visible',u.solo).not(H).length&&(r(t).unbind("mousemove.qtip"),M=s),m.blur(n)),u.effect===s||d===s?(H[o](),b.call(H)):r.isFunction(u.effect)?(H.stop(1,1),u.effect.call(H,m),H.queue("fx",function(e){b(),e()})):H.fadeTo(90,e?1:0,b),e&&u.target.trigger("qtip-"+f+"-inactive"),m):m},show:function(e){return m.toggle(i,e)},hide:function(e){return m.toggle(s,e)},focus:function(e){if(!m.rendered)return m;var t=r(N),n=parseInt(H[0].style.zIndex,10),i=b.zindex+t.length,s=r.extend({},e),o;return H.hasClass(k)||m._triggerEvent("focus",[i],s)&&(n!==i&&(t.each(function(){this.style.zIndex>n&&(this.style.zIndex=this.style.zIndex-1)}),t.filter("."+k).qtip("blur",s)),H.addClass(k)[0].style.zIndex=i),m},blur:function(e){return H.removeClass(k),m._triggerEvent("blur",[H.css("zIndex")],e),m},reposition:function(n,i){if(!m.rendered||A)return m;A=1;var o=a.position.target,u=a.position,f=u.my,l=u.at,g=u.adjust,y=g.method.split(" "),b=H.outerWidth(s),S=H.outerHeight(s),x=0,T=0,N=H.css("position"),C=u.viewport,k={left:0,top:0},L=u.container,O=H[0].offsetWidth>0,M=n&&n.type==="scroll",_=r(e),D,P;if(r.isArray(o)&&o.length===2)l={x:h,y:c},k={left:o[0],top:o[1]};else if(o==="mouse"&&(n&&n.pageX||I.event.pageX))l={x:h,y:c},n=E&&E.pageX&&(g.mouse||!n||!n.pageX)?{pageX:E.pageX,pageY:E.pageY}:(!n||n.type!=="resize"&&n.type!=="scroll"?n&&n.pageX&&n.type==="mousemove"?n:!g.mouse&&I.origin&&I.origin.pageX&&a.show.distance?I.origin:n:I.event)||n||I.event||E||{},N!=="static"&&(k=L.offset()),k={left:n.pageX-k.left,top:n.pageY-k.top},g.mouse&&M&&(k.left-=E.scrollX-_.scrollLeft(),k.top-=E.scrollY-_.scrollTop());else{o==="event"&&n&&n.target&&n.type!=="scroll"&&n.type!=="resize"?I.target=r(n.target):o!=="event"&&(I.target=r(o.jquery?o:F.target)),o=I.target,o=r(o).eq(0);if(o.length===0)return m;o[0]===t||o[0]===e?(x=w.iOS?e.innerWidth:o.width(),T=w.iOS?e.innerHeight:o.height(),o[0]===e&&(k={top:(C||o).scrollTop(),left:(C||o).scrollLeft()})):w.imagemap&&o.is("area")?D=w.imagemap(m,o,l,w.viewport?y:s):w.svg&&o[0].ownerSVGElement?D=w.svg(m,o,l,w.viewport?y:s):(x=o.outerWidth(s),T=o.outerHeight(s),k=w.offset(o,L)),D&&(x=D.width,T=D.height,P=D.offset,k=D.position);if(w.iOS>3.1&&w.iOS<4.1||w.iOS>=4.3&&w.iOS<4.33||!w.iOS&&N==="fixed")k.left-=_.scrollLeft(),k.top-=_.scrollTop();k.left+=l.x===d?x:l.x===v?x/2:0,k.top+=l.y===p?T:l.y===v?T/2:0}return k.left+=g.x+(f.x===d?-b:f.x===v?-b/2:0),k.top+=g.y+(f.y===p?-S:f.y===v?-S/2:0),w.viewport?(k.adjusted=w.viewport(m,k,u,x,T,b,S),P&&k.adjusted.left&&(k.left+=P.left),P&&k.adjusted.top&&(k.top+=P.top)):k.adjusted={left:0,top:0},m._triggerEvent("move",[k,C.elem||C],n)?(delete k.adjusted,i===s||!O||isNaN(k.left)||isNaN(k.top)||o==="mouse"||!r.isFunction(u.effect)?H.css(k):r.isFunction(u.effect)&&(u.effect.call(H,m,r.extend({},k)),H.queue(function(e){r(this).css({opacity:"",height:""}),r.browser.msie&&this.style.removeAttribute("filter"),e()})),A=0,m):m},disable:function(e){return"boolean"!=typeof e&&(e=!H.hasClass(j)&&!I.disabled),m.rendered?(H.toggleClass(j,e),r.attr(H[0],"aria-disabled",e)):I.disabled=!!e,m},enable:function(){return m.disable(s)},destroy:function(){var e=u[0],t=r.attr(e,O),n=u.data("qtip");m.destroyed=i,m.rendered&&(H.stop(1,0).remove(),r.each(m.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(m.timers.show),clearTimeout(m.timers.hide),Y();if(!n||m===n)r.removeData(e,"qtip"),a.suppress&&t&&(r.attr(e,"title",t),u.removeAttr(O)),u.removeAttr("aria-describedby");return u.unbind(".qtip-"+f),delete x[m.id],u}})}function H(e,n){var u,a,f,l,c,h=r(this),p=r(t.body),d=this===t?p:h,v=h.metadata?h.metadata(n.metadata):o,m=n.metadata.type==="html5"&&v?v[n.metadata.name]:o,g=h.data(n.metadata.name||"qtipopts");try{g=typeof g=="string"?r.parseJSON(g):g}catch(y){}l=r.extend(i,{},b.defaults,n,typeof g=="object"?D(g):o,D(m||v)),a=l.position,l.id=e;if("boolean"==typeof l.content.text){f=h.attr(l.content.attr);if(l.content.attr===s||!f)return s;l.content.text=f}a.container.length||(a.container=p),a.target===s&&(a.target=d),l.show.target===s&&(l.show.target=d),l.show.solo===i&&(l.show.solo=a.container.closest("body")),l.hide.target===s&&(l.hide.target=d),l.position.viewport===i&&(l.position.viewport=a.container),a.container=a.container.eq(0),a.at=new w.Corner(a.at),a.my=new w.Corner(a.my);if(r.data(this,"qtip"))if(l.overwrite)h.qtip("destroy");else if(l.overwrite===s)return s;return l.suppress&&(c=r.attr(this,"title"))&&r(this).removeAttr("title").attr(O,c).attr("title",""),u=new P(h,l,e,!!f),r.data(this,"qtip",u),h.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){u.destroy()}),u}function B(e){var t=this,n=e.elements.tooltip,o=e.options.content.ajax,u=b.defaults.content.ajax,a=".qtip-ajax",f=/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,l=i,c=s,h;e.checks.ajax={"^content.ajax":function(e,r,i){r==="ajax"&&(o=i),r==="once"?t.init():o&&o.url?t.load():n.unbind(a)}},r.extend(t,{init:function(){return o&&o.url&&n.unbind(a)[o.once?"one":"bind"]("tooltipshow"+a,t.load),t},load:function(n){function g(){var t;if(e.destroyed)return;l=s,v&&(c=i,e.show(n.originalEvent)),(t=u.complete||o.complete)&&r.isFunction(t)&&t.apply(o.context||e,arguments)}function y(t,n,i){var s;if(e.destroyed)return;d&&"string"==typeof t&&(t=r("<div/>").append(t.replace(f,"")).find(d)),(s=u.success||o.success)&&r.isFunction(s)?s.call(o.context||e,t,n,i):e.set("content.text",t)}function b(t,n,r){if(e.destroyed||t.status===0)return;e.set("content.text",n+": "+r)}if(c){c=s;return}var a=o.url.lastIndexOf(" "),p=o.url,d,v=!o.loading&&l;if(v)try{n.preventDefault()}catch(m){}else if(n&&n.isDefaultPrevented())return t;h&&h.abort&&h.abort(),a>-1&&(d=p.substr(a),p=p.substr(0,a)),h=r.ajax(r.extend({error:u.error||b,context:e},o,{url:p,success:y,complete:g}))},destroy:function(){h&&h.abort&&h.abort(),e.destroyed=i}}),t.init()}function j(e,t,n){var r=Math.ceil(t/2),i=Math.ceil(n/2),s={bottomright:[[0,0],[t,n],[t,0]],bottomleft:[[0,0],[t,0],[0,n]],topright:[[0,n],[t,0],[t,n]],topleft:[[0,0],[0,n],[t,n]],topcenter:[[0,n],[r,0],[t,n]],bottomcenter:[[0,0],[t,0],[r,n]],rightcenter:[[0,0],[t,i],[0,n]],leftcenter:[[t,0],[t,n],[0,i]]};return s.lefttop=s.bottomright,s.righttop=s.bottomleft,s.leftbottom=s.topright,s.rightbottom=s.topleft,s[e.string()]}function F(e,t){function A(e){var t=E.is(":visible");E.show(),e(),E.toggle(t)}function O(){x.width=g.height,x.height=g.width}function M(){x.width=g.width,x.height=g.height}function _(t,r,o,f){if(!b.tip)return;var l=m.corner.clone(),w=o.adjusted,E=e.options.position.adjust.method.split(" "),x=E[0],T=E[1]||E[0],N={left:s,top:s,x:0,y:0},C,k={},L;m.corner.fixed!==i&&(x===y&&l.precedance===u&&w.left&&l.y!==v?l.precedance=l.precedance===u?a:u:x!==y&&w.left&&(l.x=l.x===v?w.left>0?h:d:l.x===h?d:h),T===y&&l.precedance===a&&w.top&&l.x!==v?l.precedance=l.precedance===a?u:a:T!==y&&w.top&&(l.y=l.y===v?w.top>0?c:p:l.y===c?p:c),l.string()!==S.corner.string()&&(S.top!==w.top||S.left!==w.left)&&m.update(l,s)),C=m.position(l,w),C[l.x]+=P(l,l.x),C[l.y]+=P(l,l.y),C.right!==n&&(C.left=-C.right),C.bottom!==n&&(C.top=-C.bottom),C.user=Math.max(0,g.offset);if(N.left=x===y&&!!w.left)l.x===v?k["margin-left"]=N.x=C["margin-left"]:(L=C.right!==n?[w.left,-C.left]:[-w.left,C.left],(N.x=Math.max(L[0],L[1]))>L[0]&&(o.left-=w.left,N.left=s),k[C.right!==n?d:h]=N.x);if(N.top=T===y&&!!w.top)l.y===v?k["margin-top"]=N.y=C["margin-top"]:(L=C.bottom!==n?[w.top,-C.top]:[-w.top,C.top],(N.y=Math.max(L[0],L[1]))>L[0]&&(o.top-=w.top,N.top=s),k[C.bottom!==n?p:c]=N.y);b.tip.css(k).toggle(!(N.x&&N.y||l.x===v&&N.y||l.y===v&&N.x)),o.left-=C.left.charAt?C.user:x!==y||N.top||!N.left&&!N.top?C.left:0,o.top-=C.top.charAt?C.user:T!==y||N.left||!N.left&&!N.top?C.top:0,S.left=w.left,S.top=w.top,S.corner=l.clone()}function D(){var t=g.corner,n=e.options.position,r=n.at,o=n.my.string?n.my.string():n.my;return t===s||o===s&&r===s?s:(t===i?m.corner=new w.Corner(o):t.string||(m.corner=new w.Corner(t),m.corner.fixed=i),S.corner=new w.Corner(m.corner.string()),m.corner.string()!=="centercenter")}function P(e,t,n){t=t?t:e[e.precedance];var r=b.titlebar&&e.y===c,i=r?b.titlebar:E,s="border-"+t+"-width",o=function(e){return parseInt(e.css(s),10)},u;return A(function(){u=(n?o(n):o(b.content)||o(i)||o(E))||0}),u}function H(e){var t=b.titlebar&&e.y===c,n=t?b.titlebar:b.content,i=r.browser.mozilla,s=i?"-moz-":r.browser.webkit?"-webkit-":"",o="border-radius-"+e.y+e.x,u="border-"+e.y+"-"+e.x+"-radius",a=function(e){return parseInt(n.css(e),10)||parseInt(E.css(e),10)},f;return A(function(){f=a(u)||a(s+u)||a(s+o)||a(o)||0}),f}function B(e){function N(e,t,n){var r=e.css(t)||p;return n&&r===e.css(n)?s:f.test(r)?s:r}var t,n,o,u=b.tip.css("cssText",""),a=e||m.corner,f=/rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,l="border-"+a[a.precedance]+"-color",h="background-color",p="transparent",d=" !important",y=b.titlebar,w=y&&(a.y===c||a.y===v&&u.position().top+x.height/2+g.offset<y.outerHeight(i)),S=w?y:b.content;A(function(){T.fill=N(u,h)||N(S,h)||N(b.content,h)||N(E,h)||u.css(h),T.border=N(u,l,"color")||N(S,l,"color")||N(b.content,l,"color")||N(E,l,"color")||E.css(l),r("*",u).add(u).css("cssText",h+":"+p+d+";border:0"+d+";")})}function F(e){var t=e.precedance===a,n=x[t?f:l],r=x[t?l:f],i=e.string().indexOf(v)>-1,s=n*(i?.5:1),o=Math.pow,u=Math.round,c,h,p,d=Math.sqrt(o(s,2)+o(r,2)),m=[N/s*d,N/r*d];return m[2]=Math.sqrt(o(m[0],2)-o(N,2)),m[3]=Math.sqrt(o(m[1],2)-o(N,2)),c=d+m[2]+m[3]+(i?0:m[0]),h=c/d,p=[u(h*r),u(h*n)],{height:p[t?0:1],width:p[t?1:0]}}function I(e,t,n){return"<qvml:"+e+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(t||"")+' style="behavior: url(#default#VML); '+(n||"")+'" />'}var m=this,g=e.options.style.tip,b=e.elements,E=b.tooltip,S={top:0,left:0},x={width:g.width,height:g.height},T={},N=g.border||0,C=".qtip-tip",k=!!(r("<canvas />")[0]||{}).getContext,L;m.corner=o,m.mimic=o,m.border=N,m.offset=g.offset,m.size=x,e.checks.tip={"^position.my|style.tip.(corner|mimic|border)$":function(){m.init()||m.destroy(),e.reposition()},"^style.tip.(height|width)$":function(){x={width:g.width,height:g.height},m.create(),m.update(),e.reposition()},"^content.title.text|style.(classes|widget)$":function(){b.tip&&b.tip.length&&m.update()}},r.extend(m,{init:function(){var e=D()&&(k||r.browser.msie);return e&&(m.create(),m.update(),E.unbind(C).bind("tooltipmove"+C,_)),e},create:function(){var e=x.width,t=x.height,n;b.tip&&b.tip.remove(),b.tip=r("<div />",{"class":"qtip-tip"}).css({width:e,height:t}).prependTo(E),k?r("<canvas />").appendTo(b.tip)[0].getContext("2d").save():(n=I("shape",'coordorigin="0,0"',"position:absolute;"),b.tip.html(n+n),r("*",b.tip).bind("click mousedown",function(e){e.stopPropagation()}))},update:function(e,t){var n=b.tip,f=n.children(),l=x.width,y=x.height,C=g.mimic,L=Math.round,A,_,D,H,q;e||(e=S.corner||m.corner),C===s?C=e:(C=new w.Corner(C),C.precedance=e.precedance,C.x==="inherit"?C.x=e.x:C.y==="inherit"?C.y=e.y:C.x===C.y&&(C[e.precedance]=e[e.precedance])),A=C.precedance,e.precedance===u?O():M(),b.tip.css({width:l=x.width,height:y=x.height}),B(e),T.border!=="transparent"?(N=P(e,o),g.border===0&&N>0&&(T.fill=T.border),m.border=N=g.border!==i?g.border:N):m.border=N=0,D=j(C,l,y),m.size=q=F(e),n.css(q).css("line-height",q.height+"px"),e.precedance===a?H=[L(C.x===h?N:C.x===d?q.width-l-N:(q.width-l)/2),L(C.y===c?q.height-y:0)]:H=[L(C.x===h?q.width-l:0),L(C.y===c?N:C.y===p?q.height-y-N:(q.height-y)/2)],k?(f.attr(q),_=f[0].getContext("2d"),_.restore(),_.save(),_.clearRect(0,0,3e3,3e3),_.fillStyle=T.fill,_.strokeStyle=T.border,_.lineWidth=N*2,_.lineJoin="miter",_.miterLimit=100,_.translate(H[0],H[1]),_.beginPath(),_.moveTo(D[0][0],D[0][1]),_.lineTo(D[1][0],D[1][1]),_.lineTo(D[2][0],D[2][1]),_.closePath(),N&&(E.css("background-clip")==="border-box"&&(_.strokeStyle=T.fill,_.stroke()),_.strokeStyle=T.border,_.stroke()),_.fill()):(D="m"+D[0][0]+","+D[0][1]+" l"+D[1][0]+","+D[1][1]+" "+D[2][0]+","+D[2][1]+" xe",H[2]=N&&/^(r|b)/i.test(e.string())?parseFloat(r.browser.version,10)===8?2:1:0,f.css({coordsize:l+N+" "+(y+N),antialias:""+(C.string().indexOf(v)>-1),left:H[0],top:H[1],width:l+N,height:y+N}).each(function(e){var t=r(this);t[t.prop?"prop":"attr"]({coordsize:l+N+" "+(y+N),path:D,fillcolor:T.fill,filled:!!e,stroked:!e}).toggle(!!N||!!e),!e&&t.html()===""&&t.html(I("stroke",'weight="'+N*2+'px" color="'+T.border+'" miterlimit="1000" joinstyle="miter"'))})),t!==s&&m.position(e)},position:function(e){var t=b.tip,n={},i=Math.max(0,g.offset),o,p,d;return g.corner===s||!t?s:(e=e||m.corner,o=e.precedance,p=F(e),d=[e.x,e.y],o===u&&d.reverse(),r.each(d,function(t,r){var s,u,d;r===v?(s=o===a?h:c,n[s]="50%",n["margin-"+s]=-Math.round(p[o===a?f:l]/2)+i):(s=P(e,r),u=P(e,r,b.content),d=H(e),n[r]=t?u:i+(d>s?d:-s))}),n[e[o]]-=p[o===u?f:l],t.css({top:"",bottom:"",left:"",right:"",margin:""}).css(n),n)},destroy:function(){b.tip&&b.tip.remove(),b.tip=!1,E.unbind(C)}}),m.init()}function I(n){function y(){m=r(v,f).not("[disabled]").map(function(){return typeof this.focus=="function"?this:null})}function b(e){m.length<1&&e.length?e.not("body").blur():m.first().focus()}function E(e){var t=r(e.target),n=t.closest(".qtip"),i;i=n.length<1?s:parseInt(n[0].style.zIndex,10)>parseInt(f[0].style.zIndex,10),!i&&r(e.target).closest(N)[0]!==f[0]&&b(t)}var o=this,u=n.options.show.modal,a=n.elements,f=a.tooltip,l="#qtip-overlay",c=".qtipmodal",h=c+n.id,p="is-modal-qtip",d=r(t.body),v=w.modal.focusable.join(","),m={},g;n.checks.modal={"^show.modal.(on|blur)$":function(){o.init(),a.overlay.toggle(f.is(":visible"))},"^content.text$":function(){y()}},r.extend(o,{init:function(){return u.on?(g=o.create(),f.attr(p,i).css("z-index",w.modal.zindex+r(N+"["+p+"]").length).unbind(c).unbind(h).bind("tooltipshow"+c+" tooltiphide"+c,function(e,t,n){var i=e.originalEvent;if(e.target===f[0])if(i&&e.type==="tooltiphide"&&/mouse(leave|enter)/.test(i.type)&&r(i.relatedTarget).closest(g[0]).length)try{e.preventDefault()}catch(s){}else(!i||i&&!i.solo)&&o[e.type.replace("tooltip","")](e,n)}).bind("tooltipfocus"+c,function(e){if(e.isDefaultPrevented()||e.target!==f[0])return;var t=r(N).filter("["+p+"]"),n=w.modal.zindex+t.length,i=parseInt(f[0].style.zIndex,10);g[0].style.zIndex=n-2,t.each(function(){this.style.zIndex>i&&(this.style.zIndex-=1)}),t.end().filter("."+k).qtip("blur",e.originalEvent),f.addClass(k)[0].style.zIndex=n;try{e.preventDefault()}catch(s){}}).bind("tooltiphide"+c,function(e){e.target===f[0]&&r("["+p+"]").filter(":visible").not(f).last().qtip("focus",e)}),u.escape&&r(t).unbind(h).bind("keydown"+h,function(e){e.keyCode===27&&f.hasClass(k)&&n.hide(e)}),u.blur&&a.overlay.unbind(h).bind("click"+h,function(e){f.hasClass(k)&&n.hide(e)}),y(),o):o},create:function(){function i(){g.css({height:n.height(),width:n.width()})}var t=r(l),n=r(e);return t.length?a.overlay=t.insertAfter(r(N).last()):(g=a.overlay=r("<div />",{id:l.substr(1),html:"<div></div>",mousedown:function(){return s}}).hide().insertAfter(r(N).last()),n.unbind(c).bind("resize"+c,i),i(),g)},toggle:function(e,t,n){if(e&&e.isDefaultPrevented())return o;var a=u.effect,l=t?"show":"hide",c=g.is(":visible"),v=r("["+p+"]").filter(":visible").not(f),m;return g||(g=o.create()),g.is(":animated")&&c===t&&g.data("toggleState")!==s||!t&&v.length?o:(t?(g.css({left:0,top:0}),g.toggleClass("blurs",u.blur),u.stealfocus!==s&&(d.bind("focusin"+h,E),b(r("body :focus")))):d.unbind("focusin"+h),g.stop(i,s).data("toggleState",t),r.isFunction(a)?a.call(g,t):a===s?g[l]():g.fadeTo(parseInt(n,10)||90,t?1:0,function(){t||r(this).hide()}),t||g.queue(function(e){g.css({left:"",top:""}).removeData("toggleState"),e()}),o)},show:function(e,t){return o.toggle(e,i,t)},hide:function(e,t){return o.toggle(e,s,t)},destroy:function(){var e=g;return e&&(e=r("["+p+"]").not(f).length<1,e?(a.overlay.remove(),r(t).unbind(c)):a.overlay.unbind(c+n.id),d.unbind("focusin"+h)),f.removeAttr(p).unbind(c)}}),o.init()}function q(n){var o=this,u=n.elements,a=n.options,c=u.tooltip,h=".ie6-"+n.id,p=r("select, object").length<1,d=0,v=s,m;n.checks.ie6={"^content|style$":function(e,t,n){redraw()}},r.extend(o,{init:function(){var n=r(e),s;p&&(u.bgiframe=r('<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>'),u.bgiframe.appendTo(c),c.bind("tooltipmove"+h,o.adjustBGIFrame)),m=r("<div/>",{id:"qtip-rcontainer"}).appendTo(t.body),o.redraw(),u.overlay&&!v&&(s=function(){u.overlay[0].style.top=n.scrollTop()+"px"},n.bind("scroll.qtip-ie6, resize.qtip-ie6",s),s(),u.overlay.addClass("qtipmodal-ie6fix"),v=i)},adjustBGIFrame:function(){var e=n.get("dimensions"),t=n.plugins.tip,r=u.tip,i,s;s=parseInt(c.css("border-left-width"),10)||0,s={left:-s,top:-s},t&&r&&(i=t.corner.precedance==="x"?["width","left"]:["height","top"],s[i[1]]-=r[i[0]]()),u.bgiframe.css(s).css(e)},redraw:function(){if(n.rendered<1||d)return o;var e=a.style,t=a.position.container,r,i,s,u;return d=1,e.height&&c.css(l,e.height),e.width?c.css(f,e.width):(c.css(f,"").appendTo(m),i=c.width(),i%2<1&&(i+=1),s=c.css("max-width")||"",u=c.css("min-width")||"",r=(s+u).indexOf("%")>-1?t.width()/100:0,s=(s.indexOf("%")>-1?r:1)*parseInt(s,10)||i,u=(u.indexOf("%")>-1?r:1)*parseInt(u,10)||0,i=s+u?Math.min(Math.max(i,u),s):i,c.css(f,Math.round(i)).appendTo(t)),d=0,o},destroy:function(){p&&u.bgiframe.remove(),c.unbind(h)}}),o.init()}var i=!0,s=!1,o=null,u="x",a="y",f="width",l="height",c="top",h="left",p="bottom",d="right",v="center",m="flip",g="flipinvert",y="shift",b,w,E,S="qtip",x={},T=["ui-widget","ui-tooltip"],N="div.qtip."+S,C=S+"-default",k=S+"-focus",L=S+"-hover",A="_replacedByqTip",O="oldtitle",M;b=r.fn.qtip=function(e,t,u){var a=(""+e).toLowerCase(),f=o,l=r.makeArray(arguments).slice(1),c=l[l.length-1],h=this[0]?r.data(this[0],"qtip"):o;if(!arguments.length&&h||a==="api")return h;if("string"==typeof e)return this.each(function(){var e=r.data(this,"qtip");if(!e)return i;c&&c.timeStamp&&(e.cache.event=c);if(a!=="option"&&a!=="options"||!t)e[a]&&e[a].apply(e[a],l);else{if(!r.isPlainObject(t)&&u===n)return f=e.get(t),s;e.set(t,u)}}),f!==o?f:this;if("object"==typeof e||!arguments.length)return h=D(r.extend(i,{},e)),b.bind.call(this,h,c)},b.bind=function(e,t){return this.each(function(o){function p(e){function t(){c.render(typeof e=="object"||u.show.ready),a.show.add(a.hide).unbind(l)}if(c.cache.disabled)return s;c.cache.event=r.extend({},e),c.cache.target=e?r(e.target):[n],u.show.delay>0?(clearTimeout(c.timers.show),c.timers.show=setTimeout(t,u.show.delay),f.show!==f.hide&&a.hide.bind(f.hide,function(){clearTimeout(c.timers.show)})):t()}var u,a,f,l,c,h;h=r.isArray(e.id)?e.id[o]:e.id,h=!h||h===s||h.length<1||x[h]?b.nextid++:x[h]=h,l=".qtip-"+h+"-create",c=H.call(this,h,e);if(c===s)return i;u=c.options,r.each(w,function(){this.initialize==="initialize"&&this(c)}),a={show:u.show.target,hide:u.hide.target},f={show:r.trim(""+u.show.event).replace(/ /g,l+" ")+l,hide:r.trim(""+u.hide.event).replace(/ /g,l+" ")+l},/mouse(over|enter)/i.test(f.show)&&!/mouse(out|leave)/i.test(f.hide)&&(f.hide+=" mouseleave"+l),a.show.bind("mousemove"+l,function(e){_(e),c.cache.onTarget=i}),a.show.bind(f.show,p),(u.show.ready||u.prerender)&&p(t)}).attr("data-hasqtip",i)},w=b.plugins={Corner:function(e){e=(""+e).replace(/([A-Z])/," $1").replace(/middle/gi,v).toLowerCase(),this.x=(e.match(/left|right/i)||e.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(e.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var t=e.charAt(0);this.precedance=t==="t"||t==="b"?a:u,this.string=function(){return this.precedance===a?this.y+this.x:this.x+this.y},this.abbrev=function(){var e=this.x.substr(0,1),t=this.y.substr(0,1);return e===t?e:this.precedance===a?t+e:e+t},this.invertx=function(e){this.x=this.x===h?d:this.x===d?h:e||this.x},this.inverty=function(e){this.y=this.y===c?p:this.y===p?c:e||this.y},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone,invertx:this.invertx,inverty:this.inverty}}},offset:function(e,n){function c(e,t){i.left+=t*e.scrollLeft(),i.top+=t*e.scrollTop()}var i=e.offset(),s=e.closest("body"),o=r.browser.msie&&t.compatMode!=="CSS1Compat",u=n,a,f,l;if(u){do u.css("position")!=="static"&&(f=u.position(),i.left-=f.left+(parseInt(u.css("borderLeftWidth"),10)||0)+(parseInt(u.css("marginLeft"),10)||0),i.top-=f.top+(parseInt(u.css("borderTopWidth"),10)||0)+(parseInt(u.css("marginTop"),10)||0),!a&&(l=u.css("overflow"))!=="hidden"&&l!=="visible"&&(a=u));while((u=r(u[0].offsetParent)).length);(a&&a[0]!==s[0]||o)&&c(a||s,1)}return i},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||s,fn:{attr:function(e,t){if(this.length){var n=this[0],i="title",s=r.data(n,"qtip");if(e===i&&s&&"object"==typeof s&&s.options.suppress)return arguments.length<2?r.attr(n,O):(s&&s.options.content.attr===i&&s.cache.attr&&s.set("content.text",t),this.attr(O,t))}return r.fn["attr"+A].apply(this,arguments)},clone:function(e){var t=r([]),n="title",i=r.fn["clone"+A].apply(this,arguments);return e||i.filter("["+O+"]").attr("title",function(){return r.attr(this,O)}).removeAttr(O),i}}},r.each(w.fn,function(e,t){if(!t||r.fn[e+A])return i;var n=r.fn[e+A]=r.fn[e];r.fn[e]=function(){return t.apply(this,arguments)||n.apply(this,arguments)}}),r.ui||(r["cleanData"+A]=r.cleanData,r.cleanData=function(e){for(var t=0,i;(i=e[t])!==n;t++)try{r(i).triggerHandler("removeqtip")}catch(s){}r["cleanData"+A](e)}),b.version="2.0.0-nightly-15f5c6bc20",b.nextid=0,b.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),b.zindex=15e3,b.defaults={prerender:s,id:s,overwrite:i,suppress:i,content:{text:i,attr:"title",deferred:s,title:{text:s,button:s}},position:{my:"top left",at:"bottom right",target:s,container:s,viewport:s,adjust:{x:0,y:0,mouse:i,resize:i,method:"flipinvert flipinvert"},effect:function(e,t,n){r(this).animate(t,{duration:200,queue:s})}},show:{target:s,event:"mouseenter",effect:i,delay:90,solo:s,ready:s,autofocus:s},hide:{target:s,event:"mouseleave",effect:i,delay:0,fixed:s,inactive:s,leave:"window",distance:s},style:{classes:"",widget:s,width:s,height:s,def:i},events:{render:o,move:o,show:o,hide:o,toggle:o,visible:o,hidden:o,focus:o,blur:o}},w.svg=function(e,n,i,s){var o=r(t),u=n[0],a={width:0,height:0,position:{top:1e10,left:1e10}},f,l,c,h,p;while(!u.getBBox)u=u.parentNode;if(u.getBBox&&u.parentNode){f=u.getBBox(),l=u.getScreenCTM(),c=u.farthestViewportElement||u;if(!c.createSVGPoint)return a;h=c.createSVGPoint(),h.x=f.x,h.y=f.y,p=h.matrixTransform(l),a.position.left=p.x,a.position.top=p.y,h.x+=f.width,h.y+=f.height,p=h.matrixTransform(l),a.width=p.x-a.position.left,a.height=p.y-a.position.top,a.position.left+=o.scrollLeft(),a.position.top+=o.scrollTop()}return a},w.ajax=function(e){var t=e.plugins.ajax;return"object"==typeof t?t:e.plugins.ajax=new B(e)},w.ajax.initialize="render",w.ajax.sanitize=function(e){var t=e.content,n;t&&"ajax"in t&&(n=t.ajax,typeof n!="object"&&(n=e.content.ajax={url:n}),"boolean"!=typeof n.once&&n.once&&(n.once=!!n.once))},r.extend(i,b.defaults,{content:{ajax:{loading:i,once:i}}}),w.tip=function(e){var t=e.plugins.tip;return"object"==typeof t?t:e.plugins.tip=new F(e)},w.tip.initialize="render",w.tip.sanitize=function(e){var t=e.style,n;t&&"tip"in t&&(n=e.style.tip,typeof n!="object"&&(e.style.tip={corner:n}),/string|boolean/i.test(typeof n.corner)||(n.corner=i),typeof n.width!="number"&&delete n.width,typeof n.height!="number"&&delete n.height,typeof n.border!="number"&&n.border!==i&&delete n.border,typeof n.offset!="number"&&delete n.offset)},r.extend(i,b.defaults,{style:{tip:{corner:i,mimic:s,width:6,height:6,border:i,offset:0}}}),w.modal=function(e){var t=e.plugins.modal;return"object"==typeof t?t:e.plugins.modal=new I(e)},w.modal.initialize="render",w.modal.sanitize=function(e){e.show&&(typeof e.show.modal!="object"?e.show.modal={on:!!e.show.modal}:typeof e.show.modal.on=="undefined"&&(e.show.modal.on=i))},w.modal.zindex=b.zindex-200,w.modal.focusable=["a[href]","area[href]","input","select","textarea","button","iframe","object","embed","[tabindex]","[contenteditable]"],r.extend(i,b.defaults,{show:{modal:{on:s,effect:i,blur:i,stealfocus:i,escape:i}}}),w.viewport=function(n,r,i,s,o,m,b){function j(e,t,n,i,s,o,u,a,f){var l=r[s],c=x[e],h=T[e],p=n===y,d=-O.offset[s]+A.offset[s]+A["scroll"+s],m=c===s?f:c===o?-f:-f/2,b=h===s?a:h===o?-a:-a/2,w=_&&_.size?_.size[u]||0:0,E=_&&_.corner&&_.corner.precedance===e&&!p?w:0,S=d-l+E,N=l+f-A[u]-d+E,C=m-(x.precedance===e||c===x[t]?b:0)-(h===v?a/2:0);return p?(E=_&&_.corner&&_.corner.precedance===t?w:0,C=(c===s?1:-1)*m-E,r[s]+=S>0?S:N>0?-N:0,r[s]=Math.max(-O.offset[s]+A.offset[s]+(E&&_.corner[e]===v?_.offset:0),l-C,Math.min(Math.max(-O.offset[s]+A.offset[s]+A[u],l+C),r[s]))):(i*=n===g?2:0,S>0&&(c!==s||N>0)?(r[s]-=C+i,H["invert"+e](s)):N>0&&(c!==o||S>0)&&(r[s]-=(c===v?-C:C)+i,H["invert"+e](o)),r[s]<d&&-r[s]>N&&(r[s]=l,H=x.clone())),r[s]-l}var w=i.target,E=n.elements.tooltip,x=i.my,T=i.at,N=i.adjust,C=N.method.split(" "),k=C[0],L=C[1]||C[0],A=i.viewport,O=i.container,M=n.cache,_=n.plugins.tip,D={left:0,top:0},P,H,B;if(!A.jquery||w[0]===e||w[0]===t.body||N.method==="none")return D;P=E.css("position")==="fixed",A={elem:A,height:A[(A[0]===e?"h":"outerH")+"eight"](),width:A[(A[0]===e?"w":"outerW")+"idth"](),scrollleft:P?0:A.scrollLeft(),scrolltop:P?0:A.scrollTop(),offset:A.offset()||{left:0,top:0}},O={elem:O,scrollLeft:O.scrollLeft(),scrollTop:O.scrollTop(),offset:O.offset()||{left:0,top:0}};if(k!=="shift"||L!=="shift")H=x.clone();return D={left:k!=="none"?j(u,a,k,N.x,h,d,f,s,m):0,top:L!=="none"?j(a,u,L,N.y,c,p,l,o,b):0},H&&M.lastClass!==(B=S+"-pos-"+H.abbrev())&&E.removeClass(n.cache.lastClass).addClass(n.cache.lastClass=B),D},w.imagemap=function(e,t,n,i){function E(e,t,n){var r=0,i=1,s=1,o=0,u=0,a=e.width,f=e.height;while(a>0&&f>0&&i>0&&s>0){a=Math.floor(a/2),f=Math.floor(f/2),n.x===h?i=a:n.x===d?i=e.width-a:i+=Math.floor(a/2),n.y===c?s=f:n.y===p?s=e.height-f:s+=Math.floor(f/2),r=t.length;while(r--){if(t.length<2)break;o=t[r][0]-e.position.left,u=t[r][1]-e.position.top,(n.x===h&&o>=i||n.x===d&&o<=i||n.x===v&&(o<i||o>e.width-i)||n.y===c&&u>=s||n.y===p&&u<=s||n.y===v&&(u<s||u>e.height-s))&&t.splice(r,1)}}return{left:t[0][0],top:t[0][1]}}t.jquery||(t=r(t));var s=e.cache.areas={},o=(t[0].shape||t.attr("shape")).toLowerCase(),u=t[0].coords||t.attr("coords"),a=u.split(","),f=[],l=r('img[usemap="#'+t.parent("map").attr("name")+'"]'),m=l.offset(),g={width:0,height:0,position:{top:1e10,right:0,bottom:0,left:1e10}},y=0,b=0,w;m.left+=Math.ceil((l.outerWidth()-l.width())/2),m.top+=Math.ceil((l.outerHeight()-l.height())/2);if(o==="poly"){y=a.length;while(y--)b=[parseInt(a[--y],10),parseInt(a[y+1],10)],b[0]>g.position.right&&(g.position.right=b[0]),b[0]<g.position.left&&(g.position.left=b[0]),b[1]>g.position.bottom&&(g.position.bottom=b[1]),b[1]<g.position.top&&(g.position.top=b[1]),f.push(b)}else{y=-1;while(y++<a.length)f.push(parseInt(a[y],10))}switch(o){case"rect":g={width:Math.abs(f[2]-f[0]),height:Math.abs(f[3]-f[1]),position:{left:Math.min(f[0],f[2]),top:Math.min(f[1],f[3])}};break;case"circle":g={width:f[2]+2,height:f[2]+2,position:{left:f[0],top:f[1]}};break;case"poly":g.width=Math.abs(g.position.right-g.position.left),g.height=Math.abs(g.position.bottom-g.position.top),n.abbrev()==="c"?g.position={left:g.position.left+g.width/2,top:g.position.top+g.height/2}:(s[n+u]||(g.position=E(g,f.slice(),n),i&&(i[0]==="flip"||i[1]==="flip")&&(g.offset=E(g,f.slice(),{x:n.x===h?d:n.x===d?h:v,y:n.y===c?p:n.y===p?c:v}),g.offset.left-=g.position.left,g.offset.top-=g.position.top),s[n+u]=g),g=s[n+u]),g.width=g.height=0}return g.position.left+=m.left,g.position.top+=m.top,g},w.ie6=function(e){var t=r.browser,n=e.plugins.ie6;return!t.msie||(""+t.version).charAt(0)!=="6"?s:"object"==typeof n?n:e.plugins.ie6=new q(e)},w.ie6.initialize="render"})})(window,document);
assets/libraries/jquery-qtip/load.qtip.js CHANGED
@@ -1,56 +1,56 @@
1
- jQuery(document).ready(function($) {
2
-
3
-
4
- jQuery('.lp_tooltip').on('mouseover', function(event) {
5
- // Bind the qTip within the event handler
6
- jQuery(this).qtip({
7
- overwrite: false, // Make sure the tooltip won't be overridden once created
8
- content: {
9
- title: {
10
- text: 'What does this do?'
11
- }
12
- },
13
- position: {
14
- my: 'bottom center', // Use the corner...
15
- at: 'top center', // ...and opposite corner
16
- viewport: jQuery(window)
17
- },
18
- style: {
19
- classes: 'qtip-shadow qtip-jtools',
20
- },
21
- show: {
22
- event: event.type, // Use the same show event as the one that triggered the event handler
23
- ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
24
- solo: true
25
- },
26
- hide: 'unfocus'
27
- }, event); // Pass through our original event to qTip
28
- })
29
-
30
- jQuery('.lp_tooltip_templates').on('mouseover', function(event) {
31
- // Bind the qTip within the event handler
32
- jQuery(this).qtip({
33
- overwrite: false, // Make sure the tooltip won't be overridden once created
34
- content: {
35
- title: {
36
- text: 'About this Template'
37
- }
38
- },
39
- position: {
40
- my: 'bottom center', // Use the corner...
41
- at: 'top center', // ...and opposite corner
42
- viewport: jQuery(window)
43
- },
44
- style: {
45
- classes: 'qtip-shadow qtip-jtools',
46
- },
47
- show: {
48
- event: event.type, // Use the same show event as the one that triggered the event handler
49
- ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
50
- solo: true
51
- },
52
- hide: 'unfocus'
53
- }, event); // Pass through our original event to qTip
54
- })
55
- // put all your jQuery goodness in here.
56
  });
1
+ jQuery(document).ready(function($) {
2
+
3
+
4
+ jQuery('.lp_tooltip').on('mouseover', function(event) {
5
+ // Bind the qTip within the event handler
6
+ jQuery(this).qtip({
7
+ overwrite: false, // Make sure the tooltip won't be overridden once created
8
+ content: {
9
+ title: {
10
+ text: 'What does this do?'
11
+ }
12
+ },
13
+ position: {
14
+ my: 'bottom center', // Use the corner...
15
+ at: 'top center', // ...and opposite corner
16
+ viewport: jQuery(window)
17
+ },
18
+ style: {
19
+ classes: 'qtip-shadow qtip-jtools',
20
+ },
21
+ show: {
22
+ event: event.type, // Use the same show event as the one that triggered the event handler
23
+ ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
24
+ solo: true
25
+ },
26
+ hide: 'unfocus'
27
+ }, event); // Pass through our original event to qTip
28
+ })
29
+
30
+ jQuery('.lp_tooltip_templates').on('mouseover', function(event) {
31
+ // Bind the qTip within the event handler
32
+ jQuery(this).qtip({
33
+ overwrite: false, // Make sure the tooltip won't be overridden once created
34
+ content: {
35
+ title: {
36
+ text: 'About this Template'
37
+ }
38
+ },
39
+ position: {
40
+ my: 'bottom center', // Use the corner...
41
+ at: 'top center', // ...and opposite corner
42
+ viewport: jQuery(window)
43
+ },
44
+ style: {
45
+ classes: 'qtip-shadow qtip-jtools',
46
+ },
47
+ show: {
48
+ event: event.type, // Use the same show event as the one that triggered the event handler
49
+ ready: true, // Show the tooltip as soon as it's bound, vital so it shows up the first time you hover!
50
+ solo: true
51
+ },
52
+ hide: 'unfocus'
53
+ }, event); // Pass through our original event to qTip
54
+ })
55
+ // put all your jQuery goodness in here.
56
  });
assets/libraries/jquery.zoomer.js CHANGED
@@ -1,441 +1,441 @@
1
- /*
2
- * jQuery Zoomer v1.1
3
- *
4
- * By HubSpot >('_')<
5
- *
6
- * Licensed under the MIT license:
7
- * http://www.opensource.org/licenses/mit-license.php
8
- *
9
- * Example usage:
10
-
11
- $('iframe').zoomer({ width: 200, zoom: 0.5 });
12
-
13
- *
14
- */
15
-
16
- (function($){
17
-
18
- var methods,
19
- pluginName = 'zoomer',
20
- defaults = {
21
- width: 'auto',
22
- height: 'auto',
23
- zoom: 0.4,
24
- tranformOrigin: '0 0',
25
- //loading
26
- loadingType: 'message', // other type: 'spinner'
27
- loadingMessage: 'Generating preview...',
28
- spinnerURL: 'http://oi46.tinypic.com/6y375z.jpg', // requires loadingType: 'spinner'
29
- //hover
30
- message: 'Open Page',
31
- ieMessageButtonClass: 'btn btn-secondary', // used in IE only
32
- messageURL: false,
33
- onComplete: function() {}
34
- },
35
- visible = {
36
- visibility: 'visible'
37
- },
38
- invisible = {
39
- visibility: 'hidden'
40
- },
41
- unselectable = {
42
- '-webkit-user-select': 'none',
43
- '-khtml-user-select': 'none',
44
- '-moz-user-select': 'none',
45
- '-o-user-select': 'none',
46
- 'user-select': 'none',
47
- 'overflow': 'hidden'
48
- },
49
- absolute = {
50
- top: 0,
51
- position: 'absolute'
52
- },
53
- relative = {
54
- position: 'relative'
55
- },
56
- isMSIE = navigator.userAgent.match(/MSIE/),
57
- MSIEVersion = navigator.userAgent.match(/MSIE (\d\.\d+)/) ? parseInt(RegExp.$1, 10) : null
58
- ;
59
-
60
- methods = {
61
-
62
- init: function(opts) {
63
- return this.each(function(){
64
- var $el = $(this),
65
- options = $.extend({}, defaults, opts)
66
- ;
67
-
68
- options.src = $el.attr('src');
69
-
70
- $el.data(pluginName, options);
71
-
72
- $el[pluginName]('zoomer');
73
- });
74
- },
75
-
76
- zoomer: function() {
77
- var $el = $(this), options = $el.data(pluginName);
78
-
79
- $el
80
- .css(invisible)
81
- .css(unselectable)
82
- ;
83
-
84
- if (options.zoom === 'auto') {
85
- if (options.width === 'auto' && options.height === 'auto') {
86
- $.error('jQuery.zoomer: You must set either zoom or height and width.');
87
- return;
88
- }
89
- options.zoom = options.width / $(window).width();
90
- }
91
-
92
- if (options.width === 'auto') {
93
- options.width = $(window).height() * options.zoom;
94
- }
95
-
96
- if (options.height === 'auto') {
97
- options.height = $(window).height() * options.zoom;
98
- }
99
-
100
- if (options.loadingType === 'spinner') {
101
- options.loadingMessage = '<img style="padding: ' + parseInt((options.height - 17) / 2, 10) + 'px 0" src="' + options.spinnerURL + '" />';
102
- }
103
-
104
- //fix bug in older version of chrome:
105
- //http://stackoverflow.com/questions/5159713/
106
- if (navigator.userAgent.indexOf('Chrome/10.0.648') > -1) {
107
- options.zoom = Math.sqrt(1 / options.zoom);
108
- }
109
-
110
- options.externalSrc = true;
111
-
112
- try {
113
- if ($el.get(0).contentWindow.document) {
114
- options.externalSrc = false;
115
- }
116
- } catch (e) {}
117
-
118
- $el[pluginName]('setUpWrapper');
119
-
120
- $el[pluginName]('zoom');
121
-
122
- return $el;
123
- },
124
-
125
- setUpWrapper: function() {
126
- var $el = $(this), options = $el.data(pluginName);
127
-
128
- if (!$el.parents('.zoomer-wrapper').length) {
129
- $el
130
- .wrap(
131
- $('<div/>')
132
- .addClass('zoomer-wrapper')
133
- .css(unselectable)
134
- .css(relative)
135
- )
136
- .wrap(
137
- $('<div/>')
138
- .addClass('zoomer-small')
139
- .css(invisible)
140
- .css(unselectable)
141
- )
142
- ;
143
- }
144
-
145
- options.zoomerWrapper = $el.parents('.zoomer-wrapper');
146
-
147
- options.zoomerSmall = $el.parents('.zoomer-small');
148
-
149
- options.zoomerCover = $('<div/>')
150
- .addClass('zoomer-cover')
151
- .css(unselectable)
152
- .css(absolute)
153
- .css({
154
- textAlign: 'center',
155
- fontSize: '15px'
156
- })
157
- ;
158
-
159
- options.zoomerLink = $('<a/>')
160
- .attr('target', '_blank')
161
- .html("View Page<span style='line-height:12px;position:absolute; bottom:10px;left:0%;width:100%; text-align: center;'>(preview not always to scale)</span>")
162
- .css({
163
- height: options.height,
164
- width: options.width,
165
- color: '#444',
166
- display: 'block',
167
- lineHeight: (parseInt(options.height, 10) - parseInt((options.height - 80) / 10, 10)) + 'px',
168
- textDecoration: 'none'
169
- })
170
- .css('background', '-moz-radial-gradient(center center, circle farthest-corner, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.4) 100%) repeat scroll 0 0 transparent')
171
- .css('background-image', '-webkit-gradient(radial, center center, 0, center center, ' + parseInt(options.width, 10) + ', from(rgba(255, 255, 255, 0.95)), to(rgba(255, 255, 255, 0.4)))')
172
- .mousedown(function(){
173
- $(this).css('box-shadow', 'inset 0px 2px 8px rgba(100, 100, 100, 0.4)');
174
- })
175
- .bind('mouseout mouseup', function(){
176
- $(this).css('box-shadow', 'none');
177
- })
178
- .hide()
179
- ;
180
-
181
- if (isMSIE) {
182
- options.zoomerLink.css({
183
- backgroundColor: 'rgba(255, 255, 255, 0.5)'
184
- });
185
- }
186
-
187
- if (options.click) {
188
- options.zoomerLink
189
- .attr('href', options.messageURL || options.src || '#')
190
- .unbind('click').bind('click', options.click)
191
- ;
192
- } else {
193
- options.zoomerLink.attr('href', options.messageURL || options.src);
194
- }
195
-
196
- options.zoomerCover
197
- .append(options.zoomerLink)
198
- .hover(function(){
199
- options.zoomerLink.show();
200
- $(this).css('box-shadow', 'inset 2px 2px ' + (parseInt(options.width, 10) * 2) + 'px rgba(255, 255, 255, 0.2)');
201
- }, function(){
202
- options.zoomerLink.hide();
203
- $(this).css('box-shadow', 'none');
204
- })
205
- .mousedown(function(){
206
- $(this).css('box-shadow', 'inset 2px 2px ' + (parseInt(options.width, 10) * 2) + 'px rgba(200, 200, 200, 0.8)');
207
- })
208
- .bind('mouseout mouseup', function(){
209
- $(this).css('box-shadow', 'none');
210
- })
211
- ;
212
-
213
- options.zoomerLoader = $('<div/>')
214
- .addClass('zoomer-loader')
215
- .css(invisible)
216
- .css(unselectable)
217
- .css(absolute)
218
- .css({
219
- textAlign: 'center',
220
- fontSize: '15px',
221
- lineHeight: (parseInt(options.height, 10) - parseInt((options.height - 80) / 10, 10)) + 'px',
222
- background: '#fff'
223
- })
224
- .html(options.loadingMessage)
225
- ;
226
-
227
- options.zoomerWrapper
228
- .append(options.zoomerCover)
229
- .append(options.zoomerLoader)
230
- ;
231
-
232
- if (isMSIE) { options.zoomerLoader.css(invisible); }
233
-
234
- return $el[pluginName]('updateWrapper')[pluginName]('fadeOut');
235
- },
236
-
237
- updateWrapper: function() {
238
- var $el = $(this), options = $el.data(pluginName);
239
-
240
- $.each([options.zoomerWrapper.get(0), options.zoomerCover.get(0), options.zoomerLoader.get(0), options.zoomerSmall.get(0)], function(){
241
- $(this).css({
242
- height: options.height,
243
- width: options.width
244
- });
245
- });
246
-
247
- return $el;
248
- },
249
-
250
- fadeIn: function() {
251
- var $el = $(this), options = $el.data(pluginName);
252
-
253
- if (isMSIE) { return $el; }
254
-
255
- $el.css(invisible);
256
-
257
- options.zoomerSmall
258
- .stop()
259
- .css('opacity', 0)
260
- .css(visible)
261
- .animate({ 'opacity': 1 }, 150, function(){
262
- $el
263
- .css(visible)
264
- .css('opacity', 0)
265
- .animate({ 'opacity': 1 }, 500)
266
- ;
267
- })
268
- ;
269
-
270
- options.zoomerLoader
271
- .show()
272
- .animate({ 'opacity': 0 }, 300, function(){
273
- $(this).hide();
274
- })
275
- ;
276
-
277
- return $el;
278
- },
279
-
280
- fadeOut: function() {
281
- var $el = $(this), options = $el.data(pluginName);
282
-
283
- if (isMSIE) { return $el; }
284
-
285
- options.zoomerSmall
286
- .stop()
287
- .animate({ 'opacity': 0 }, 300, function(){
288
- $(this).css('visibility', 'hidden');
289
- })
290
- ;
291
-
292
- options.zoomerLoader
293
- .css('opacity', 0)
294
- .css(visible)
295
- .show()
296
- .animate({ opacity: 1 }, 100)
297
- ;
298
-
299
- return $el;
300
- },
301
-
302
- zoom: function() {
303
- var $el = $(this), options = $el.data(pluginName);
304
-
305
- if (isMSIE) {
306
- setTimeout(function(){
307
- $el
308
- .css({
309
- zoom: options.zoom,
310
- height: parseInt((options.height / options.zoom) * (1 / (MSIEVersion >= 9 ? 1 : options.zoom)), 10),
311
- width: parseInt((options.width / options.zoom) * (1 / (MSIEVersion >= 9 ? 1 : options.zoom)), 10)
312
- })
313
- .css(visible)
314
- ;
315
-
316
- options.zoomerLink.remove();
317
-
318
- options.zoomerCover
319
- .unbind('hover mouseover mouseout')
320
- .addClass(options.ieMessageButtonClass)
321
- .html(options.message)
322
- .css({
323
- width: 94,
324
- height: 14,
325
- fontSize: 12,
326
- padding: '6px 18px 6px 18px',
327
- top: parseInt(options.height - (12 + (2 * 6) + 2 + 10), 10),
328
- left: parseInt((options.width - (94 + (2 * 18))) / 2, 10)
329
- })
330
- .show()
331
- ;
332
-
333
- if (!options.click) {
334
- options.click = function() {
335
- location.href = options.messageURL || options.src;
336
- };
337
- }
338
-
339
- options.zoomerCover.unbind('click').bind('click', options.click);
340
-
341
- options.onComplete($el);
342
- }, 1000);
343
-
344
- return $el;
345
- }
346
-
347
- if (options.externalSrc) {
348
- $el
349
- .css({
350
- height: options.height / options.zoom,
351
- width: options.width / options.zoom,
352
- 'transform-origin': options.tranformOrigin,
353
- '-webkit-transform-origin': options.tranformOrigin,
354
- '-moz-transform-origin': options.tranformOrigin,
355
- '-o-transform-origin': options.tranformOrigin,
356
- 'transform': 'scale(' + options.zoom + ')',
357
- '-webkit-transform': 'scale(' + options.zoom + ')',
358
- '-moz-transform': 'scale(' + options.zoom + ')',
359
- '-o-transform': 'scale(' + options.zoom + ')'
360
- })
361
- .css(visible)
362
- ;
363
-
364
- $el[pluginName]('fadeIn');
365
-
366
- options.onComplete($el);
367
-
368
- return $el;
369
- }
370
-
371
- $el
372
- .css({
373
- height: options.height / options.zoom,
374
- width: options.width / options.zoom
375
- })
376
- .load(function(){
377
- $el.contents().find("meta[name=viewport]").remove();
378
- console.log('remove viewport');
379
- $el.contents().find('html').css({
380
- 'transform-origin': options.tranformOrigin,
381
- '-webkit-transform-origin': options.tranformOrigin,
382
- '-moz-transform-origin': options.tranformOrigin,
383
- '-o-transform-origin': options.tranformOrigin,
384
- 'transform': 'scale(' + options.zoom + ')',
385
- '-webkit-transform': 'scale(' + options.zoom + ')',
386
- '-moz-transform': 'scale(' + options.zoom + ')',
387
- '-o-transform': 'scale(' + options.zoom + ')',
388
- 'width': '100%',
389
- });
390
-
391
-
392
-
393
- $el[pluginName]('fadeIn');
394
-
395
- options.onComplete($el);
396
- })
397
- ;
398
-
399
- return $el;
400
- },
401
-
402
- src: function(src) {
403
- var $el = $(this),
404
- options = $el.data(pluginName)
405
- ;
406
-
407
- options.src = src;
408
-
409
- $el[pluginName]('fadeOut').attr('src', src);
410
-
411
- return $el;
412
- },
413
-
414
- refresh: function() {
415
- var $el = $(this), options = $el.data(pluginName);
416
-
417
- return $el[pluginName]('src', options.src);
418
- },
419
-
420
- zoomedBodyHeight: function() {
421
- var $el = $(this), options = $el.data(pluginName);
422
-
423
- if (options.externalSrc) {
424
- return $.error('jQuery.zoomer: cannot access bodyHeight of an external iFrame');
425
- }
426
-
427
- return options.zoom * $($el.get(0).contentWindow.document).height();
428
- }
429
- };
430
-
431
- $.fn[pluginName] = function(options) {
432
- if (methods[options]) {
433
- return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
434
- } else if (typeof options === 'object' || ! options) {
435
- return methods.init.apply(this, arguments);
436
- } else {
437
- $.error('jQuery.' + pluginName + ': Method ' + options + ' does not exist');
438
- }
439
- };
440
-
441
  })(jQuery);
1
+ /*
2
+ * jQuery Zoomer v1.1
3
+ *
4
+ * By HubSpot >('_')<
5
+ *
6
+ * Licensed under the MIT license:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ *
9
+ * Example usage:
10
+
11
+ $('iframe').zoomer({ width: 200, zoom: 0.5 });
12
+
13
+ *
14
+ */
15
+
16
+ (function($){
17
+
18
+ var methods,
19
+ pluginName = 'zoomer',
20
+ defaults = {
21
+ width: 'auto',
22
+ height: 'auto',
23
+ zoom: 0.4,
24
+ tranformOrigin: '0 0',
25
+ //loading
26
+ loadingType: 'message', // other type: 'spinner'
27
+ loadingMessage: 'Generating preview...',
28
+ spinnerURL: 'http://oi46.tinypic.com/6y375z.jpg', // requires loadingType: 'spinner'
29
+ //hover
30
+ message: 'Open Page',
31
+ ieMessageButtonClass: 'btn btn-secondary', // used in IE only
32
+ messageURL: false,
33
+ onComplete: function() {}
34
+ },
35
+ visible = {
36
+ visibility: 'visible'
37
+ },
38
+ invisible = {
39
+ visibility: 'hidden'
40
+ },
41
+ unselectable = {
42
+ '-webkit-user-select': 'none',
43
+ '-khtml-user-select': 'none',
44
+ '-moz-user-select': 'none',
45
+ '-o-user-select': 'none',
46
+ 'user-select': 'none',
47
+ 'overflow': 'hidden'
48
+ },
49
+ absolute = {
50
+ top: 0,
51
+ position: 'absolute'
52
+ },
53
+ relative = {
54
+ position: 'relative'
55
+ },
56
+ isMSIE = navigator.userAgent.match(/MSIE/),
57
+ MSIEVersion = navigator.userAgent.match(/MSIE (\d\.\d+)/) ? parseInt(RegExp.$1, 10) : null
58
+ ;
59
+
60
+ methods = {
61
+
62
+ init: function(opts) {
63
+ return this.each(function(){
64
+ var $el = $(this),
65
+ options = $.extend({}, defaults, opts)
66
+ ;
67
+
68
+ options.src = $el.attr('src');
69
+
70
+ $el.data(pluginName, options);
71
+
72
+ $el[pluginName]('zoomer');
73
+ });
74
+ },
75
+
76
+ zoomer: function() {
77
+ var $el = $(this), options = $el.data(pluginName);
78
+
79
+ $el
80
+ .css(invisible)
81
+ .css(unselectable)
82
+ ;
83
+
84
+ if (options.zoom === 'auto') {
85
+ if (options.width === 'auto' && options.height === 'auto') {
86
+ $.error('jQuery.zoomer: You must set either zoom or height and width.');
87
+ return;
88
+ }
89
+ options.zoom = options.width / $(window).width();
90
+ }
91
+
92
+ if (options.width === 'auto') {
93
+ options.width = $(window).height() * options.zoom;
94
+ }
95
+
96
+ if (options.height === 'auto') {
97
+ options.height = $(window).height() * options.zoom;
98
+ }
99
+
100
+ if (options.loadingType === 'spinner') {
101
+ options.loadingMessage = '<img style="padding: ' + parseInt((options.height - 17) / 2, 10) + 'px 0" src="' + options.spinnerURL + '" />';
102
+ }
103
+
104
+ //fix bug in older version of chrome:
105
+ //http://stackoverflow.com/questions/5159713/
106
+ if (navigator.userAgent.indexOf('Chrome/10.0.648') > -1) {
107
+ options.zoom = Math.sqrt(1 / options.zoom);
108
+ }
109
+
110
+ options.externalSrc = true;
111
+
112
+ try {
113
+ if ($el.get(0).contentWindow.document) {
114
+ options.externalSrc = false;
115
+ }
116
+ } catch (e) {}
117
+
118
+ $el[pluginName]('setUpWrapper');
119
+
120
+ $el[pluginName]('zoom');
121
+
122
+ return $el;
123
+ },
124
+
125
+ setUpWrapper: function() {
126
+ var $el = $(this), options = $el.data(pluginName);
127
+
128
+ if (!$el.parents('.zoomer-wrapper').length) {
129
+ $el
130
+ .wrap(
131
+ $('<div/>')
132
+ .addClass('zoomer-wrapper')
133
+ .css(unselectable)
134
+ .css(relative)
135
+ )
136
+ .wrap(
137
+ $('<div/>')
138
+ .addClass('zoomer-small')
139
+ .css(invisible)
140
+ .css(unselectable)
141
+ )
142
+ ;
143
+ }
144
+
145
+ options.zoomerWrapper = $el.parents('.zoomer-wrapper');
146
+
147
+ options.zoomerSmall = $el.parents('.zoomer-small');
148
+
149
+ options.zoomerCover = $('<div/>')
150
+ .addClass('zoomer-cover')
151
+ .css(unselectable)
152
+ .css(absolute)
153
+ .css({
154
+ textAlign: 'center',
155
+ fontSize: '15px'
156
+ })
157
+ ;
158
+
159
+ options.zoomerLink = $('<a/>')
160
+ .attr('target', '_blank')
161
+ .html("View Page<span style='line-height:12px;position:absolute; bottom:10px;left:0%;width:100%; text-align: center;'>(preview not always to scale)</span>")
162
+ .css({
163
+ height: options.height,
164
+ width: options.width,
165
+ color: '#444',
166
+ display: 'block',
167
+ lineHeight: (parseInt(options.height, 10) - parseInt((options.height - 80) / 10, 10)) + 'px',
168
+ textDecoration: 'none'
169
+ })
170
+ .css('background', '-moz-radial-gradient(center center, circle farthest-corner, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.4) 100%) repeat scroll 0 0 transparent')
171
+ .css('background-image', '-webkit-gradient(radial, center center, 0, center center, ' + parseInt(options.width, 10) + ', from(rgba(255, 255, 255, 0.95)), to(rgba(255, 255, 255, 0.4)))')
172
+ .mousedown(function(){
173
+ $(this).css('box-shadow', 'inset 0px 2px 8px rgba(100, 100, 100, 0.4)');
174
+ })
175
+ .bind('mouseout mouseup', function(){
176
+ $(this).css('box-shadow', 'none');
177
+ })
178
+ .hide()
179
+ ;
180
+
181
+ if (isMSIE) {
182
+ options.zoomerLink.css({
183
+ backgroundColor: 'rgba(255, 255, 255, 0.5)'
184
+ });
185
+ }
186
+
187
+ if (options.click) {
188
+ options.zoomerLink
189
+ .attr('href', options.messageURL || options.src || '#')
190
+ .unbind('click').bind('click', options.click)
191
+ ;
192
+ } else {
193
+ options.zoomerLink.attr('href', options.messageURL || options.src);
194
+ }
195
+
196
+ options.zoomerCover
197
+ .append(options.zoomerLink)
198
+ .hover(function(){
199
+ options.zoomerLink.show();
200
+ $(this).css('box-shadow', 'inset 2px 2px ' + (parseInt(options.width, 10) * 2) + 'px rgba(255, 255, 255, 0.2)');
201
+ }, function(){
202
+ options.zoomerLink.hide();
203
+ $(this).css('box-shadow', 'none');
204
+ })
205
+ .mousedown(function(){
206
+ $(this).css('box-shadow', 'inset 2px 2px ' + (parseInt(options.width, 10) * 2) + 'px rgba(200, 200, 200, 0.8)');
207
+ })
208
+ .bind('mouseout mouseup', function(){
209
+ $(this).css('box-shadow', 'none');
210
+ })
211
+ ;
212
+
213
+ options.zoomerLoader = $('<div/>')
214
+ .addClass('zoomer-loader')
215
+ .css(invisible)
216
+ .css(unselectable)
217
+ .css(absolute)
218
+ .css({
219
+ textAlign: 'center',
220
+ fontSize: '15px',
221
+ lineHeight: (parseInt(options.height, 10) - parseInt((options.height - 80) / 10, 10)) + 'px',
222
+ background: '#fff'
223
+ })
224
+ .html(options.loadingMessage)
225
+ ;
226
+
227
+ options.zoomerWrapper
228
+ .append(options.zoomerCover)
229
+ .append(options.zoomerLoader)
230
+ ;
231
+
232
+ if (isMSIE) { options.zoomerLoader.css(invisible); }
233
+
234
+ return $el[pluginName]('updateWrapper')[pluginName]('fadeOut');
235
+ },
236
+
237
+ updateWrapper: function() {
238
+ var $el = $(this), options = $el.data(pluginName);
239
+
240
+ $.each([options.zoomerWrapper.get(0), options.zoomerCover.get(0), options.zoomerLoader.get(0), options.zoomerSmall.get(0)], function(){
241
+ $(this).css({
242
+ height: options.height,
243
+ width: options.width
244
+ });
245
+ });
246
+
247
+ return $el;
248
+ },
249
+
250
+ fadeIn: function() {
251
+ var $el = $(this), options = $el.data(pluginName);
252
+
253
+ if (isMSIE) { return $el; }
254
+
255
+ $el.css(invisible);
256
+
257
+ options.zoomerSmall
258
+ .stop()
259
+ .css('opacity', 0)
260
+ .css(visible)
261
+ .animate({ 'opacity': 1 }, 150, function(){
262
+ $el
263
+ .css(visible)
264
+ .css('opacity', 0)
265
+ .animate({ 'opacity': 1 }, 500)
266
+ ;
267
+ })
268
+ ;
269
+
270
+ options.zoomerLoader
271
+ .show()
272
+ .animate({ 'opacity': 0 }, 300, function(){
273
+ $(this).hide();
274
+ })
275
+ ;
276
+
277
+ return $el;
278
+ },
279
+
280
+ fadeOut: function() {
281
+ var $el = $(this), options = $el.data(pluginName);
282
+
283
+ if (isMSIE) { return $el; }
284
+
285
+ options.zoomerSmall
286
+ .stop()
287
+ .animate({ 'opacity': 0 }, 300, function(){
288
+ $(this).css('visibility', 'hidden');
289
+ })
290
+ ;
291
+
292
+ options.zoomerLoader
293
+ .css('opacity', 0)
294
+ .css(visible)
295
+ .show()
296
+ .animate({ opacity: 1 }, 100)
297
+ ;
298
+
299
+ return $el;
300
+ },
301
+
302
+ zoom: function() {
303
+ var $el = $(this), options = $el.data(pluginName);
304
+
305
+ if (isMSIE) {
306
+ setTimeout(function(){
307
+ $el
308
+ .css({
309
+ zoom: options.zoom,
310
+ height: parseInt((options.height / options.zoom) * (1 / (MSIEVersion >= 9 ? 1 : options.zoom)), 10),
311
+ width: parseInt((options.width / options.zoom) * (1 / (MSIEVersion >= 9 ? 1 : options.zoom)), 10)
312
+ })
313
+ .css(visible)
314
+ ;
315
+
316
+ options.zoomerLink.remove();
317
+
318
+ options.zoomerCover
319
+ .unbind('hover mouseover mouseout')
320
+ .addClass(options.ieMessageButtonClass)
321
+ .html(options.message)
322
+ .css({
323
+ width: 94,
324
+ height: 14,
325
+ fontSize: 12,
326
+ padding: '6px 18px 6px 18px',
327
+ top: parseInt(options.height - (12 + (2 * 6) + 2 + 10), 10),
328
+ left: parseInt((options.width - (94 + (2 * 18))) / 2, 10)
329
+ })
330
+ .show()
331
+ ;
332
+
333
+ if (!options.click) {
334
+ options.click = function() {
335
+ location.href = options.messageURL || options.src;
336
+ };
337
+ }
338
+
339
+ options.zoomerCover.unbind('click').bind('click', options.click);
340
+
341
+ options.onComplete($el);
342
+ }, 1000);
343
+
344
+ return $el;
345
+ }
346
+
347
+ if (options.externalSrc) {
348
+ $el
349
+ .css({
350
+ height: options.height / options.zoom,
351
+ width: options.width / options.zoom,
352
+ 'transform-origin': options.tranformOrigin,
353
+ '-webkit-transform-origin': options.tranformOrigin,
354
+ '-moz-transform-origin': options.tranformOrigin,
355
+ '-o-transform-origin': options.tranformOrigin,
356
+ 'transform': 'scale(' + options.zoom + ')',
357
+ '-webkit-transform': 'scale(' + options.zoom + ')',
358
+ '-moz-transform': 'scale(' + options.zoom + ')',
359
+ '-o-transform': 'scale(' + options.zoom + ')'
360
+ })
361
+ .css(visible)
362
+ ;
363
+
364
+ $el[pluginName]('fadeIn');
365
+
366
+ options.onComplete($el);
367
+
368
+ return $el;
369
+ }
370
+
371
+ $el
372
+ .css({
373
+ height: options.height / options.zoom,
374
+ width: options.width / options.zoom
375
+ })
376
+ .load(function(){
377
+ $el.contents().find("meta[name=viewport]").remove();
378
+ console.log('remove viewport');
379
+ $el.contents().find('html').css({
380
+ 'transform-origin': options.tranformOrigin,
381
+ '-webkit-transform-origin': options.tranformOrigin,
382
+ '-moz-transform-origin': options.tranformOrigin,
383
+ '-o-transform-origin': options.tranformOrigin,
384
+ 'transform': 'scale(' + options.zoom + ')',
385
+ '-webkit-transform': 'scale(' + options.zoom + ')',
386
+ '-moz-transform': 'scale(' + options.zoom + ')',
387
+ '-o-transform': 'scale(' + options.zoom + ')',
388
+ 'width': '100%',
389
+ });
390
+
391
+
392
+
393
+ $el[pluginName]('fadeIn');
394
+
395
+ options.onComplete($el);
396
+ })
397
+ ;
398
+
399
+ return $el;
400
+ },
401
+
402
+ src: function(src) {
403
+ var $el = $(this),
404
+ options = $el.data(pluginName)
405
+ ;
406
+
407
+ options.src = src;
408
+
409
+ $el[pluginName]('fadeOut').attr('src', src);
410
+
411
+ return $el;
412
+ },
413
+
414
+ refresh: function() {
415
+ var $el = $(this), options = $el.data(pluginName);
416
+
417
+ return $el[pluginName]('src', options.src);
418
+ },
419
+
420
+ zoomedBodyHeight: function() {
421
+ var $el = $(this), options = $el.data(pluginName);
422
+
423
+ if (options.externalSrc) {
424
+ return $.error('jQuery.zoomer: cannot access bodyHeight of an external iFrame');
425
+ }
426
+
427
+ return options.zoom * $($el.get(0).contentWindow.document).height();
428
+ }
429
+ };
430
+
431
+ $.fn[pluginName] = function(options) {
432
+ if (methods[options]) {
433
+ return methods[options].apply(this, Array.prototype.slice.call(arguments, 1));
434
+ } else if (typeof options === 'object' || ! options) {
435
+ return methods.init.apply(this, arguments);
436
+ } else {
437
+ $.error('jQuery.' + pluginName + ': Method ' + options + ' does not exist');
438
+ }
439
+ };
440
+
441
  })(jQuery);
assets/libraries/script.js CHANGED
@@ -1,36 +1,36 @@
1
- $(function(){
2
-
3
- var note = $('#note'),
4
- ts = new Date(2012, 0, 1),
5
- newYear = true;
6
-
7
- if((new Date()) > ts){
8
- // The new year is here! Count towards something else.
9
- // Notice the *1000 at the end - time must be in milliseconds
10
- ts = (new Date()).getTime() + 10*24*60*60*1000;
11
- newYear = false;
12
- }
13
-
14
- $('#countdown').countdown({
15
- timestamp : ts,
16
- callback : function(days, hours, minutes, seconds){
17
-
18
- var message = "";
19
-
20
- message += days + " day" + ( days==1 ? '':'s' ) + ", ";
21
- message += hours + " hour" + ( hours==1 ? '':'s' ) + ", ";
22
- message += minutes + " minute" + ( minutes==1 ? '':'s' ) + " and ";
23
- message += seconds + " second" + ( seconds==1 ? '':'s' ) + " <br />";
24
-
25
- if(newYear){
26
- message += "left until the new year!";
27
- }
28
- else {
29
- message += "left to 10 days from now!";
30
- }
31
-
32
- note.html(message);
33
- }
34
- });
35
-
36
- });
1
+ $(function(){
2
+
3
+ var note = $('#note'),
4
+ ts = new Date(2012, 0, 1),
5
+ newYear = true;
6
+
7
+ if((new Date()) > ts){
8
+ // The new year is here! Count towards something else.
9
+ // Notice the *1000 at the end - time must be in milliseconds
10
+ ts = (new Date()).getTime() + 10*24*60*60*1000;
11
+ newYear = false;
12
+ }
13
+
14
+ $('#countdown').countdown({
15
+ timestamp : ts,
16
+ callback : function(days, hours, minutes, seconds){
17
+
18
+ var message = "";
19
+
20
+ message += days + " day" + ( days==1 ? '':'s' ) + ", ";
21
+ message += hours + " hour" + ( hours==1 ? '':'s' ) + ", ";
22
+ message += minutes + " minute" + ( minutes==1 ? '':'s' ) + " and ";
23
+ message += seconds + " second" + ( seconds==1 ? '':'s' ) + " <br />";
24
+
25
+ if(newYear){
26
+ message += "left until the new year!";
27
+ }
28
+ else {
29
+ message += "left to 10 days from now!";
30
+ }
31
+
32
+ note.html(message);
33
+ }
34
+ });
35
+
36
+ });
assets/libraries/shareme/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/libraries/shareme/sharrre/index.php CHANGED
@@ -1,2 +1,2 @@
1
- <?php
2
  # Silence is golden.
1
+ <?php
2
  # Silence is golden.
assets/libraries/shareme/sharrre/jquery.sharrre-1.3.3.js CHANGED
@@ -1,584 +1,584 @@
1
- /*
2
- * Sharrre.com - Make your sharing widget!
3
- * Version: beta 1.3.3
4
- * Author: Julien Hany
5
- * License: MIT http://en.wikipedia.org/wiki/MIT_License or GPLv2 http://en.wikipedia.org/wiki/GNU_General_Public_License
6
- */
7
-
8
- ;(function ( $, window, document, undefined ) {
9
-
10
- /* Defaults
11
- ================================================== */
12
- var pluginName = 'sharrre',
13
- defaults = {
14
- className: 'sharrre',
15
- share: {
16
- googlePlus: false,
17
- facebook: false,
18
- twitter: false,
19
- digg: false,
20
- delicious: false,
21
- stumbleupon: false,
22
- linkedin: false,
23
- pinterest: false
24
- },
25
- shareTotal: 0,
26
- template: '',
27
- title: '',
28
- url: document.location.href,
29
- text: document.title,
30
- urlCurl: 'sharrre.php', //PHP script for google plus...
31
- count: {}, //counter by social network
32
- total: 0, //total of sharing
33
- shorterTotal: true, //show total by k or M when number is to big
34
- enableHover: true, //disable if you want to personalize hover event with callback
35
- enableCounter: true, //disable if you just want use buttons
36
- enableTracking: false, //tracking with google analitycs
37
- hover: function(){}, //personalize hover event with this callback function
38
- hide: function(){}, //personalize hide event with this callback function
39
- click: function(){}, //personalize click event with this callback function
40
- render: function(){}, //personalize render event with this callback function
41
- buttons: { //settings for buttons
42
- googlePlus : { //http://www.google.com/webmasters/+1/button/
43
- url: '', //if you need to personnalize button url
44
- urlCount: false, //if you want to use personnalize button url on global counter
45
- size: 'medium',
46
- lang: 'en-US',
47
- annotation: ''
48
- },
49
- facebook: { //http://developers.facebook.com/docs/reference/plugins/like/
50
- url: '', //if you need to personalize url button
51
- urlCount: false, //if you want to use personnalize button url on global counter
52
- action: 'like',
53
- layout: 'button_count',
54
- width: '',
55
- send: 'false',
56
- faces: 'false',
57
- colorscheme: '',
58
- font: '',
59
- lang: 'en_US'
60
- },
61
- twitter: { //http://twitter.com/about/resources/tweetbutton
62
- url: '', //if you need to personalize url button
63
- urlCount: false, //if you want to use personnalize button url on global counter
64
- count: 'horizontal',
65
- hashtags: '',
66
- via: '',
67
- related: '',
68
- lang: 'en'
69
- },
70
- digg: { //http://about.digg.com/downloads/button/smart
71
- url: '', //if you need to personalize url button
72
- urlCount: false, //if you want to use personnalize button url on global counter
73
- type: 'DiggCompact'
74
- },
75
- delicious: {
76
- url: '', //if you need to personalize url button
77
- urlCount: false, //if you want to use personnalize button url on global counter
78
- size: 'medium' //medium or tall
79
- },
80
- stumbleupon: { //http://www.stumbleupon.com/badges/
81
- url: '', //if you need to personalize url button
82
- urlCount: false, //if you want to use personnalize button url on global counter
83
- layout: '1'
84
- },
85
- linkedin: { //http://developer.linkedin.com/plugins/share-button
86
- url: '', //if you need to personalize url button
87
- urlCount: false, //if you want to use personnalize button url on global counter
88
- counter: ''
89
- },
90
- pinterest: { //http://pinterest.com/about/goodies/
91
- url: '', //if you need to personalize url button
92
- media: '',
93
- description: '',
94
- layout: 'horizontal'
95
- }
96
- }
97
- },
98
- /* Json URL to get count number
99
- ================================================== */
100
- urlJson = {
101
- googlePlus: "",
102
- facebook: "http://graph.facebook.com/?id={url}&callback=?",
103
- //facebook : "http://api.ak.facebook.com/restserver.php?v=1.0&method=links.getStats&urls={url}&format=json"
104
- twitter: "http://cdn.api.twitter.com/1/urls/count.json?url={url}&callback=?",
105
- digg: "http://services.digg.com/2.0/story.getInfo?links={url}&type=javascript&callback=?",
106
- delicious: 'http://feeds.delicious.com/v2/json/urlinfo/data?url={url}&callback=?',
107
- //stumbleupon: "http://www.stumbleupon.com/services/1.01/badge.getinfo?url={url}&format=jsonp&callback=?",
108
- stumbleupon: "",
109
- linkedin: "http://www.linkedin.com/countserv/count/share?format=jsonp&url={url}&callback=?",
110
- pinterest: ""
111
- },
112
- /* Load share buttons asynchronously
113
- ================================================== */
114
- loadButton = {
115
- googlePlus : function(self){
116
- var sett = self.options.buttons.googlePlus;
117
- //$(self.element).find('.buttons').append('<div class="button googleplus"><g:plusone size="'+self.options.buttons.googlePlus.size+'" href="'+self.options.url+'"></g:plusone></div>');
118
- $(self.element).find('.buttons').append('<div class="button googleplus"><div class="g-plusone" data-size="'+sett.size+'" data-href="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-annotation="'+sett.annotation+'"></div></div>');
119
- window.___gcfg = {
120
- lang: self.options.buttons.googlePlus.lang
121
- };
122
- var loading = 0;
123
- if(typeof gapi === 'undefined' && loading == 0){
124
- loading = 1;
125
- (function() {
126
- var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
127
- po.src = '//apis.google.com/js/plusone.js';
128
- var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
129
- })();
130
- }
131
- else{
132
- gapi.plusone.go();
133
- }
134
- },
135
- facebook : function(self){
136
- var sett = self.options.buttons.facebook;
137
- $(self.element).find('.buttons').append('<div class="button facebook"><div id="fb-root"></div><div class="fb-like" data-href="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-send="'+sett.send+'" data-layout="'+sett.layout+'" data-width="'+sett.width+'" data-show-faces="'+sett.faces+'" data-action="'+sett.action+'" data-colorscheme="'+sett.colorscheme+'" data-font="'+sett.font+'" data-via="'+sett.via+'"></div></div>');
138
- var loading = 0;
139
- if(typeof FB === 'undefined' && loading == 0){
140
- loading = 1;
141
- (function(d, s, id) {
142
- var js, fjs = d.getElementsByTagName(s)[0];
143
- if (d.getElementById(id)) {return;}
144
- js = d.createElement(s); js.id = id;
145
- js.src = '//connect.facebook.net/'+sett.lang+'/all.js#xfbml=1';
146
- fjs.parentNode.insertBefore(js, fjs);
147
- }(document, 'script', 'facebook-jssdk'));
148
- }
149
- else{
150
- FB.XFBML.parse();
151
- }
152
- },
153
- twitter : function(self){
154
- var sett = self.options.buttons.twitter;
155
- $(self.element).find('.buttons').append('<div class="button twitter"><a href="https://twitter.com/share" class="twitter-share-button" data-url="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-count="'+sett.count+'" data-text="'+self.options.text+'" data-via="'+sett.via+'" data-hashtags="'+sett.hashtags+'" data-related="'+sett.related+'" data-lang="'+sett.lang+'">Tweet</a></div>');
156
- var loading = 0;
157
- if(typeof twttr === 'undefined' && loading == 0){
158
- loading = 1;
159
- (function() {
160
- var twitterScriptTag = document.createElement('script');
161
- twitterScriptTag.type = 'text/javascript';
162
- twitterScriptTag.async = true;
163
- twitterScriptTag.src = '//platform.twitter.com/widgets.js';
164
- var s = document.getElementsByTagName('script')[0];
165
- s.parentNode.insertBefore(twitterScriptTag, s);
166
- })();
167
- }
168
- else{
169
- $.ajax({ url: '//platform.twitter.com/widgets.js', dataType: 'script', cache:true}); //http://stackoverflow.com/q/6536108
170
- }
171
- },
172
- digg : function(self){
173
- var sett = self.options.buttons.digg;
174
- $(self.element).find('.buttons').append('<div class="button digg"><a class="DiggThisButton '+sett.type+'" rel="nofollow external" href="http://digg.com/submit?url='+encodeURIComponent((sett.url !== '' ? sett.url : self.options.url))+'"></a></div>');
175
- var loading = 0;
176
- if(typeof __DBW === 'undefined' && loading == 0){
177
- loading = 1;
178
- (function() {
179
- var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];
180
- s.type = 'text/javascript';
181
- s.async = true;
182
- s.src = '//widgets.digg.com/buttons.js';
183
- s1.parentNode.insertBefore(s, s1);
184
- })();
185
- }
186
- },
187
- delicious : function(self){
188
- if(self.options.buttons.delicious.size == 'tall'){//tall
189
- var css = 'width:50px;',
190
- cssCount = 'height:35px;width:50px;font-size:15px;line-height:35px;',
191
- cssShare = 'height:18px;line-height:18px;margin-top:3px;';
192
- }
193
- else{//medium
194
- var css = 'width:93px;',
195
- cssCount = 'float:right;padding:0 3px;height:20px;width:26px;line-height:20px;',
196
- cssShare = 'float:left;height:20px;line-height:20px;';
197
- }
198
- var count = self.shorterTotal(self.options.count.delicious);
199
- if(typeof count === "undefined"){
200
- count = 0;
201
- }
202
- $(self.element).find('.buttons').append(
203
- '<div class="button delicious"><div style="'+css+'font:12px Arial,Helvetica,sans-serif;cursor:pointer;color:#666666;display:inline-block;float:none;height:20px;line-height:normal;margin:0;padding:0;text-indent:0;vertical-align:baseline;">'+
204
- '<div style="'+cssCount+'background-color:#fff;margin-bottom:5px;overflow:hidden;text-align:center;border:1px solid #ccc;border-radius:3px;">'+count+'</div>'+
205
- '<div style="'+cssShare+'display:block;padding:0;text-align:center;text-decoration:none;width:50px;background-color:#7EACEE;border:1px solid #40679C;border-radius:3px;color:#fff;">'+
206
- '<img src="http://www.delicious.com/static/img/delicious.small.gif" height="10" width="10" alt="Delicious" /> Add</div></div></div>');
207
-
208
- $(self.element).find('.delicious').on('click', function(){
209
- self.openPopup('delicious');
210
- });
211
- },
212
- stumbleupon : function(self){
213
- var sett = self.options.buttons.stumbleupon;
214
- $(self.element).find('.buttons').append('<div class="button stumbleupon"><su:badge layout="'+sett.layout+'" location="'+(sett.url !== '' ? sett.url : self.options.url)+'"></su:badge></div>');
215
- var loading = 0;
216
- if(typeof STMBLPN === 'undefined' && loading == 0){
217
- loading = 1;
218
- (function() {
219
- var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
220
- li.src = '//platform.stumbleupon.com/1/widgets.js';
221
- var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
222
- })();
223
- s = window.setTimeout(function(){
224
- if(typeof STMBLPN !== 'undefined'){
225
- STMBLPN.processWidgets();
226
- clearInterval(s);
227
- }
228
- },500);
229
- }
230
- else{
231
- STMBLPN.processWidgets();
232
- }
233
- },
234
- linkedin : function(self){
235
- var sett = self.options.buttons.linkedin;
236
- $(self.element).find('.buttons').append('<div class="button linkedin"><script type="in/share" data-url="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-counter="'+sett.counter+'"></script></div>');
237
- var loading = 0;
238
- if(typeof window.IN === 'undefined' && loading == 0){
239
- loading = 1;
240
- (function() {
241
- var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
242
- li.src = '//platform.linkedin.com/in.js';
243
- var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
244
- })();
245
- }
246
- else{
247
- window.IN.init();
248
- }
249
- },
250
- pinterest : function(self){
251
- var sett = self.options.buttons.pinterest;
252
- $(self.element).find('.buttons').append('<div class="button pinterest"><a href="http://pinterest.com/pin/create/button/?url='+(sett.url !== '' ? sett.url : self.options.url)+'&media='+sett.media+'&description='+sett.description+'" class="pin-it-button" count-layout="'+sett.layout+'">Pin It</a></div>');
253
-
254
- (function() {
255
- var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
256
- li.src = '//assets.pinterest.com/js/pinit.js';
257
- var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
258
- })();
259
- }
260
- },
261
- /* Tracking for Google Analytics
262
- ================================================== */
263
- tracking = {
264
- googlePlus: function(){},
265
- facebook: function(){
266
- //console.log('facebook');
267
- fb = window.setInterval(function(){
268
- if (typeof FB !== 'undefined') {
269
- FB.Event.subscribe('edge.create', function(targetUrl) {
270
- _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]);
271
- });
272
- FB.Event.subscribe('edge.remove', function(targetUrl) {
273
- _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]);
274
- });
275
- FB.Event.subscribe('message.send', function(targetUrl) {
276
- _gaq.push(['_trackSocial', 'facebook', 'send', targetUrl]);
277
- });
278
- //console.log('ok');
279
- clearInterval(fb);
280
- }
281
- },1000);
282
- },
283
- twitter: function(){
284
- //console.log('twitter');
285
- tw = window.setInterval(function(){
286
- if (typeof twttr !== 'undefined') {
287
- twttr.events.bind('tweet', function(event) {
288
- if (event) {
289
- _gaq.push(['_trackSocial', 'twitter', 'tweet']);
290
- }
291
- });
292
- //console.log('ok');
293
- clearInterval(tw);
294
- }
295
- },1000);
296
- },
297
- digg: function(){
298
- //if somenone find a solution, mail me !
299
- /*$(this.element).find('.digg').on('click', function(){
300
- _gaq.push(['_trackSocial', 'digg', 'add']);
301
- });*/
302
- },
303
- delicious: function(){},
304
- stumbleupon: function(){},
305
- linkedin: function(){
306
- function LinkedInShare() {
307
- _gaq.push(['_trackSocial', 'linkedin', 'share']);
308
- }
309
- },
310
- pinterest: function(){
311
- //if somenone find a solution, mail me !
312
- }
313
- },
314
- /* Popup for each social network
315
- ================================================== */
316
- popup = {
317
- googlePlus: function(opt){
318
- window.open("https://plus.google.com/share?hl="+opt.buttons.googlePlus.lang+"&url="+encodeURIComponent((opt.buttons.googlePlus.url !== '' ? opt.buttons.googlePlus.url : opt.url)), "", "toolbar=0, status=0, width=900, height=500");
319
- },
320
- facebook: function(opt){
321
- window.open("http://www.facebook.com/sharer.php?u="+encodeURIComponent((opt.buttons.facebook.url !== '' ? opt.buttons.facebook.url : opt.url))+"&t="+opt.text+"", "", "toolbar=0, status=0, width=900, height=500");
322
- },
323
- twitter: function(opt){
324
- window.open("https://twitter.com/intent/tweet?text="+encodeURIComponent(opt.text)+"&url="+encodeURIComponent((opt.buttons.twitter.url !== '' ? opt.buttons.twitter.url : opt.url))+(opt.buttons.twitter.via !== '' ? '&via='+opt.buttons.twitter.via : ''), "", "toolbar=0, status=0, width=650, height=360");
325
- },
326
- digg: function(opt){
327
- window.open("http://digg.com/tools/diggthis/submit?url="+encodeURIComponent((opt.buttons.digg.url !== '' ? opt.buttons.digg.url : opt.url))+"&title="+opt.text+"&related=true&style=true", "", "toolbar=0, status=0, width=650, height=360");
328
- },
329
- delicious: function(opt){
330
- window.open('http://www.delicious.com/save?v=5&noui&jump=close&url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url))+'&title='+opt.text, 'delicious', 'toolbar=no,width=550,height=550');
331
- },
332
- stumbleupon: function(opt){
333
- window.open('http://www.stumbleupon.com/badge/?url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url)), 'stumbleupon', 'toolbar=no,width=550,height=550');
334
- },
335
- linkedin: function(opt){
336
- window.open('https://www.linkedin.com/cws/share?url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url))+'&token=&isFramed=true', 'linkedin', 'toolbar=no,width=550,height=550');
337
- },
338
- pinterest: function(opt){
339
- window.open('http://pinterest.com/pin/create/button/?url='+encodeURIComponent((opt.buttons.pinterest.url !== '' ? opt.buttons.pinterest.url : opt.url))+'&media='+encodeURIComponent(opt.buttons.pinterest.media)+'&description='+opt.buttons.pinterest.description, 'pinterest', 'toolbar=no,width=700,height=300');
340
- }
341
- };
342
-
343
- /* Plugin constructor
344
- ================================================== */
345
- function Plugin( element, options ) {
346
- this.element = element;
347
-
348
- this.options = $.extend( true, {}, defaults, options);
349
- this.options.share = options.share; //simple solution to allow order of buttons
350
-
351
- this._defaults = defaults;
352
- this._name = pluginName;
353
-
354
- this.init();
355
- };
356
-
357
- /* Initialization method
358
- ================================================== */
359
- Plugin.prototype.init = function () {
360
- var self = this;
361
- if(this.options.urlCurl !== ''){
362
- urlJson.googlePlus = this.options.urlCurl + '?url={url}&type=googlePlus'; // PHP script for GooglePlus...
363
- urlJson.stumbleupon = this.options.urlCurl + '?url={url}&type=stumbleupon'; // PHP script for Stumbleupon...
364
- urlJson.pinterest = this.options.urlCurl + '?url={url}&type=pinterest'; // PHP script for Pinterest...
365
- }
366
- $(this.element).addClass(this.options.className); //add class
367
-
368
- //HTML5 Custom data
369
- if(typeof $(this.element).data('title') !== 'undefined'){
370
- this.options.title = $(this.element).attr('data-title');
371
- }
372
- if(typeof $(this.element).data('url') !== 'undefined'){
373
- this.options.url = $(this.element).data('url');
374
- }
375
- if(typeof $(this.element).data('text') !== 'undefined'){
376
- this.options.text = $(this.element).data('text');
377
- }
378
-
379
- //how many social website have been selected
380
- $.each(this.options.share, function(name, val) {
381
- if(val === true){
382
- self.options.shareTotal ++;
383
- }
384
- });
385
-
386
- if(self.options.enableCounter === true){ //if for some reason you don't need counter
387
- //get count of social share that have been selected
388
- $.each(this.options.share, function(name, val) {
389
- if(val === true){
390
- //self.getSocialJson(name);
391
- try {
392
- self.getSocialJson(name);
393
- } catch(e){
394
- }
395
- }
396
- });
397
- }
398
- else if(self.options.template !== ''){ //for personalized button (with template)
399
- this.options.render(this, this.options);
400
- }
401
- else{ // if you want to use official button like example 3 or 5
402
- this.loadButtons();
403
- }
404
-
405
- //add hover event
406
- $(this.element).hover(function(){
407
- //load social button if enable and 1 time
408
- if($(this).find('.buttons').length === 0 && self.options.enableHover === true){
409
- self.loadButtons();
410
- }
411
- self.options.hover(self, self.options);
412
- }, function(){
413
- self.options.hide(self, self.options);
414
- });
415
-
416
- //click event
417
- $(this.element).click(function(){
418
- self.options.click(self, self.options);
419
- return false;
420
- });
421
- };
422
-
423
- /* loadButtons methode
424
- ================================================== */
425
- Plugin.prototype.loadButtons = function () {
426
- var self = this;
427
- $(this.element).append('<div class="buttons"></div>');
428
- $.each(self.options.share, function(name, val) {
429
- if(val == true){
430
- loadButton[name](self);
431
- if(self.options.enableTracking === true){ //add tracking
432
- tracking[name]();
433
- }
434
- }
435
- });
436
- };
437
-
438
- /* getSocialJson methode
439
- ================================================== */
440
- Plugin.prototype.getSocialJson = function (name) {
441
- var self = this,
442
- count = 0,
443
- url = urlJson[name].replace('{url}', encodeURIComponent(this.options.url));
444
- if(this.options.buttons[name].urlCount === true && this.options.buttons[name].url !== ''){
445
- url = urlJson[name].replace('{url}', this.options.buttons[name].url);
446
- }
447
- //console.log('name : ' + name + ' - url : '+url); //debug
448
- if(url != '' && self.options.urlCurl !== ''){ //urlCurl = '' if you don't want to used PHP script but used social button
449
- $.getJSON(url, function(json){
450
- if(typeof json.count !== "undefined"){ //GooglePlus, Stumbleupon, Twitter and Digg
451
- var temp = json.count + '';
452
- temp = temp.replace('\u00c2\u00a0', ''); //remove google plus special chars
453
- count += parseInt(temp, 10);
454
- }
455
- else if(typeof json.likes !== "undefined"){ //Facebook Fan page
456
- count += parseInt(json.likes, 10); //changed shares to likes to use with fanPage url
457
- }
458
- else if(typeof json.shares !== "undefined"){ //Facebook
459
- count += parseInt(json.shares, 10);
460
- }
461
- else if(typeof json[0] !== "undefined"){ //Delicious
462
- count += parseInt(json[0].total_posts, 10);
463
- }
464
- else if(typeof json[0] !== "undefined"){ //Stumbleupon
465
- }
466
- self.options.count[name] = count;
467
- self.options.total += count;
468
- self.renderer();
469
- self.rendererPerso();
470
- //console.log(json); //debug
471
- })
472
- .error(function() {
473
- self.options.count[name] = 0;
474
- self.rendererPerso();
475
- });
476
- }
477
- else{
478
- self.renderer();
479
- self.options.count[name] = 0;
480
- self.rendererPerso();
481
- }
482
- };
483
-
484
- /* launch render methode
485
- ================================================== */
486
- Plugin.prototype.rendererPerso = function () {
487
- //check if this is the last social website to launch render
488
- var shareCount = 0;
489
- for (e in this.options.count) { shareCount++; }
490
- if(shareCount === this.options.shareTotal){
491
- this.options.render(this, this.options);
492
- }
493
- };
494
-
495
- /* render methode
496
- ================================================== */
497
- Plugin.prototype.renderer = function () {
498
- var total = this.options.total,
499
- template = this.options.template;
500
- if(this.options.shorterTotal === true){ //format number like 1.2k or 5M
501
- total = this.shorterTotal(total);
502
- }
503
-
504
- if(template !== ''){ //if there is a template
505
- template = template.replace('{total}', total);
506
- $(this.element).html(template);
507
- }
508
- else{ //template by defaults
509
- $(this.element).html(
510
- '<div class="box"><a class="count" href="#">' + total + '</a>' +
511
- (this.options.title !== '' ? '<a class="share" href="#">' + this.options.title + '</a>' : '') +
512
- '</div>'
513
- );
514
- }
515
- };
516
-
517
- /* format total numbers like 1.2k or 5M
518
- ================================================== */
519
- Plugin.prototype.shorterTotal = function (num) {
520
- if (num >= 1e6){
521
- num = (num / 1e6).toFixed(2) + "M"
522
- } else if (num >= 1e3){
523
- num = (num / 1e3).toFixed(1) + "k"
524
- }
525
- return num;
526
- };
527
-
528
- /* Methode for open popup
529
- ================================================== */
530
- Plugin.prototype.openPopup = function (site) {
531
- popup[site](this.options); //open
532
- if(this.options.enableTracking === true){ //tracking!
533
- var tracking = {
534
- googlePlus: {site: 'Google', action: '+1'},
535
- facebook: {site: 'facebook', action: 'like'},
536
- twitter: {site: 'twitter', action: 'tweet'},
537
- digg: {site: 'digg', action: 'add'},
538
- delicious: {site: 'delicious', action: 'add'},
539
- stumbleupon: {site: 'stumbleupon', action: 'add'},
540
- linkedin: {site: 'linkedin', action: 'share'},
541
- pinterest: {site: 'pinterest', action: 'pin'}
542
- };
543
- _gaq.push(['_trackSocial', tracking[site].site, tracking[site].action]);
544
- }
545
- };
546
-
547
- /* Methode for add +1 to a counter
548
- ================================================== */
549
- Plugin.prototype.simulateClick = function () {
550
- var html = $(this.element).html();
551
- $(this.element).html(html.replace(this.options.total, this.options.total+1));
552
- };
553
-
554
- /* Methode for add +1 to a counter
555
- ================================================== */
556
- Plugin.prototype.update = function (url, text) {
557
- if(url !== ''){
558
- this.options.url = url;
559
- }
560
- if(text !== ''){
561
- this.options.text = text;
562
- }
563
- };
564
-
565
- /* A really lightweight plugin wrapper around the constructor, preventing against multiple instantiations
566
- ================================================== */
567
- $.fn[pluginName] = function ( options ) {
568
- var args = arguments;
569
- if (options === undefined || typeof options === 'object') {
570
- return this.each(function () {
571
- if (!$.data(this, 'plugin_' + pluginName)) {
572
- $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
573
- }
574
- });
575
- } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
576
- return this.each(function () {
577
- var instance = $.data(this, 'plugin_' + pluginName);
578
- if (instance instanceof Plugin && typeof instance[options] === 'function') {
579
- instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
580
- }
581
- });
582
- }
583
- };
584
- })(jQuery, window, document);
1
+ /*
2
+ * Sharrre.com - Make your sharing widget!
3
+ * Version: beta 1.3.3
4
+ * Author: Julien Hany
5
+ * License: MIT http://en.wikipedia.org/wiki/MIT_License or GPLv2 http://en.wikipedia.org/wiki/GNU_General_Public_License
6
+ */
7
+
8
+ ;(function ( $, window, document, undefined ) {
9
+
10
+ /* Defaults
11
+ ================================================== */
12
+ var pluginName = 'sharrre',
13
+ defaults = {
14
+ className: 'sharrre',
15
+ share: {
16
+ googlePlus: false,
17
+ facebook: false,
18
+ twitter: false,
19
+ digg: false,
20
+ delicious: false,
21
+ stumbleupon: false,
22
+ linkedin: false,
23
+ pinterest: false
24
+ },
25
+ shareTotal: 0,
26
+ template: '',
27
+ title: '',
28
+ url: document.location.href,
29
+ text: document.title,
30
+ urlCurl: 'sharrre.php', //PHP script for google plus...
31
+ count: {}, //counter by social network
32
+ total: 0, //total of sharing
33
+ shorterTotal: true, //show total by k or M when number is to big
34
+ enableHover: true, //disable if you want to personalize hover event with callback
35
+ enableCounter: true, //disable if you just want use buttons
36
+ enableTracking: false, //tracking with google analitycs
37
+ hover: function(){}, //personalize hover event with this callback function
38
+ hide: function(){}, //personalize hide event with this callback function
39
+ click: function(){}, //personalize click event with this callback function
40
+ render: function(){}, //personalize render event with this callback function
41
+ buttons: { //settings for buttons
42
+ googlePlus : { //http://www.google.com/webmasters/+1/button/
43
+ url: '', //if you need to personnalize button url
44
+ urlCount: false, //if you want to use personnalize button url on global counter
45
+ size: 'medium',
46
+ lang: 'en-US',
47
+ annotation: ''
48
+ },
49
+ facebook: { //http://developers.facebook.com/docs/reference/plugins/like/
50
+ url: '', //if you need to personalize url button
51
+ urlCount: false, //if you want to use personnalize button url on global counter
52
+ action: 'like',
53
+ layout: 'button_count',
54
+ width: '',
55
+ send: 'false',
56
+ faces: 'false',
57
+ colorscheme: '',
58
+ font: '',
59
+ lang: 'en_US'
60
+ },
61
+ twitter: { //http://twitter.com/about/resources/tweetbutton
62
+ url: '', //if you need to personalize url button
63
+ urlCount: false, //if you want to use personnalize button url on global counter
64
+ count: 'horizontal',
65
+ hashtags: '',
66
+ via: '',
67
+ related: '',
68
+ lang: 'en'
69
+ },
70
+ digg: { //http://about.digg.com/downloads/button/smart
71
+ url: '', //if you need to personalize url button
72
+ urlCount: false, //if you want to use personnalize button url on global counter
73
+ type: 'DiggCompact'
74
+ },
75
+ delicious: {
76
+ url: '', //if you need to personalize url button
77
+ urlCount: false, //if you want to use personnalize button url on global counter
78
+ size: 'medium' //medium or tall
79
+ },
80
+ stumbleupon: { //http://www.stumbleupon.com/badges/
81
+ url: '', //if you need to personalize url button
82
+ urlCount: false, //if you want to use personnalize button url on global counter
83
+ layout: '1'
84
+ },
85
+ linkedin: { //http://developer.linkedin.com/plugins/share-button
86
+ url: '', //if you need to personalize url button
87
+ urlCount: false, //if you want to use personnalize button url on global counter
88
+ counter: ''
89
+ },
90
+ pinterest: { //http://pinterest.com/about/goodies/
91
+ url: '', //if you need to personalize url button
92
+ media: '',
93
+ description: '',
94
+ layout: 'horizontal'
95
+ }
96
+ }
97
+ },
98
+ /* Json URL to get count number
99
+ ================================================== */
100
+ urlJson = {
101
+ googlePlus: "",
102
+ facebook: "http://graph.facebook.com/?id={url}&callback=?",
103
+ //facebook : "http://api.ak.facebook.com/restserver.php?v=1.0&method=links.getStats&urls={url}&format=json"
104
+ twitter: "http://cdn.api.twitter.com/1/urls/count.json?url={url}&callback=?",
105
+ digg: "http://services.digg.com/2.0/story.getInfo?links={url}&type=javascript&callback=?",
106
+ delicious: 'http://feeds.delicious.com/v2/json/urlinfo/data?url={url}&callback=?',
107
+ //stumbleupon: "http://www.stumbleupon.com/services/1.01/badge.getinfo?url={url}&format=jsonp&callback=?",
108
+ stumbleupon: "",
109
+ linkedin: "http://www.linkedin.com/countserv/count/share?format=jsonp&url={url}&callback=?",
110
+ pinterest: ""
111
+ },
112
+ /* Load share buttons asynchronously
113
+ ================================================== */
114
+ loadButton = {
115
+ googlePlus : function(self){
116
+ var sett = self.options.buttons.googlePlus;
117
+ //$(self.element).find('.buttons').append('<div class="button googleplus"><g:plusone size="'+self.options.buttons.googlePlus.size+'" href="'+self.options.url+'"></g:plusone></div>');
118
+ $(self.element).find('.buttons').append('<div class="button googleplus"><div class="g-plusone" data-size="'+sett.size+'" data-href="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-annotation="'+sett.annotation+'"></div></div>');
119
+ window.___gcfg = {
120
+ lang: self.options.buttons.googlePlus.lang
121
+ };
122
+ var loading = 0;
123
+ if(typeof gapi === 'undefined' && loading == 0){
124
+ loading = 1;
125
+ (function() {
126
+ var po = document.createElement('script'); po.type = 'text/javascript'; po.async = true;
127
+ po.src = '//apis.google.com/js/plusone.js';
128
+ var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(po, s);
129
+ })();
130
+ }
131
+ else{
132
+ gapi.plusone.go();
133
+ }
134
+ },
135
+ facebook : function(self){
136
+ var sett = self.options.buttons.facebook;
137
+ $(self.element).find('.buttons').append('<div class="button facebook"><div id="fb-root"></div><div class="fb-like" data-href="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-send="'+sett.send+'" data-layout="'+sett.layout+'" data-width="'+sett.width+'" data-show-faces="'+sett.faces+'" data-action="'+sett.action+'" data-colorscheme="'+sett.colorscheme+'" data-font="'+sett.font+'" data-via="'+sett.via+'"></div></div>');
138
+ var loading = 0;
139
+ if(typeof FB === 'undefined' && loading == 0){
140
+ loading = 1;
141
+ (function(d, s, id) {
142
+ var js, fjs = d.getElementsByTagName(s)[0];
143
+ if (d.getElementById(id)) {return;}
144
+ js = d.createElement(s); js.id = id;
145
+ js.src = '//connect.facebook.net/'+sett.lang+'/all.js#xfbml=1';
146
+ fjs.parentNode.insertBefore(js, fjs);
147
+ }(document, 'script', 'facebook-jssdk'));
148
+ }
149
+ else{
150
+ FB.XFBML.parse();
151
+ }
152
+ },
153
+ twitter : function(self){
154
+ var sett = self.options.buttons.twitter;
155
+ $(self.element).find('.buttons').append('<div class="button twitter"><a href="https://twitter.com/share" class="twitter-share-button" data-url="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-count="'+sett.count+'" data-text="'+self.options.text+'" data-via="'+sett.via+'" data-hashtags="'+sett.hashtags+'" data-related="'+sett.related+'" data-lang="'+sett.lang+'">Tweet</a></div>');
156
+ var loading = 0;
157
+ if(typeof twttr === 'undefined' && loading == 0){
158
+ loading = 1;
159
+ (function() {
160
+ var twitterScriptTag = document.createElement('script');
161
+ twitterScriptTag.type = 'text/javascript';
162
+ twitterScriptTag.async = true;
163
+ twitterScriptTag.src = '//platform.twitter.com/widgets.js';
164
+ var s = document.getElementsByTagName('script')[0];
165
+ s.parentNode.insertBefore(twitterScriptTag, s);
166
+ })();
167
+ }
168
+ else{
169
+ $.ajax({ url: '//platform.twitter.com/widgets.js', dataType: 'script', cache:true}); //http://stackoverflow.com/q/6536108
170
+ }
171
+ },
172
+ digg : function(self){
173
+ var sett = self.options.buttons.digg;
174
+ $(self.element).find('.buttons').append('<div class="button digg"><a class="DiggThisButton '+sett.type+'" rel="nofollow external" href="http://digg.com/submit?url='+encodeURIComponent((sett.url !== '' ? sett.url : self.options.url))+'"></a></div>');
175
+ var loading = 0;
176
+ if(typeof __DBW === 'undefined' && loading == 0){
177
+ loading = 1;
178
+ (function() {
179
+ var s = document.createElement('SCRIPT'), s1 = document.getElementsByTagName('SCRIPT')[0];
180
+ s.type = 'text/javascript';
181
+ s.async = true;
182
+ s.src = '//widgets.digg.com/buttons.js';
183
+ s1.parentNode.insertBefore(s, s1);
184
+ })();
185
+ }
186
+ },
187
+ delicious : function(self){
188
+ if(self.options.buttons.delicious.size == 'tall'){//tall
189
+ var css = 'width:50px;',
190
+ cssCount = 'height:35px;width:50px;font-size:15px;line-height:35px;',
191
+ cssShare = 'height:18px;line-height:18px;margin-top:3px;';
192
+ }
193
+ else{//medium
194
+ var css = 'width:93px;',
195
+ cssCount = 'float:right;padding:0 3px;height:20px;width:26px;line-height:20px;',
196
+ cssShare = 'float:left;height:20px;line-height:20px;';
197
+ }
198
+ var count = self.shorterTotal(self.options.count.delicious);
199
+ if(typeof count === "undefined"){
200
+ count = 0;
201
+ }
202
+ $(self.element).find('.buttons').append(
203
+ '<div class="button delicious"><div style="'+css+'font:12px Arial,Helvetica,sans-serif;cursor:pointer;color:#666666;display:inline-block;float:none;height:20px;line-height:normal;margin:0;padding:0;text-indent:0;vertical-align:baseline;">'+
204
+ '<div style="'+cssCount+'background-color:#fff;margin-bottom:5px;overflow:hidden;text-align:center;border:1px solid #ccc;border-radius:3px;">'+count+'</div>'+
205
+ '<div style="'+cssShare+'display:block;padding:0;text-align:center;text-decoration:none;width:50px;background-color:#7EACEE;border:1px solid #40679C;border-radius:3px;color:#fff;">'+
206
+ '<img src="http://www.delicious.com/static/img/delicious.small.gif" height="10" width="10" alt="Delicious" /> Add</div></div></div>');
207
+
208
+ $(self.element).find('.delicious').on('click', function(){
209
+ self.openPopup('delicious');
210
+ });
211
+ },
212
+ stumbleupon : function(self){
213
+ var sett = self.options.buttons.stumbleupon;
214
+ $(self.element).find('.buttons').append('<div class="button stumbleupon"><su:badge layout="'+sett.layout+'" location="'+(sett.url !== '' ? sett.url : self.options.url)+'"></su:badge></div>');
215
+ var loading = 0;
216
+ if(typeof STMBLPN === 'undefined' && loading == 0){
217
+ loading = 1;
218
+ (function() {
219
+ var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
220
+ li.src = '//platform.stumbleupon.com/1/widgets.js';
221
+ var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
222
+ })();
223
+ s = window.setTimeout(function(){
224
+ if(typeof STMBLPN !== 'undefined'){
225
+ STMBLPN.processWidgets();
226
+ clearInterval(s);
227
+ }
228
+ },500);
229
+ }
230
+ else{
231
+ STMBLPN.processWidgets();
232
+ }
233
+ },
234
+ linkedin : function(self){
235
+ var sett = self.options.buttons.linkedin;
236
+ $(self.element).find('.buttons').append('<div class="button linkedin"><script type="in/share" data-url="'+(sett.url !== '' ? sett.url : self.options.url)+'" data-counter="'+sett.counter+'"></script></div>');
237
+ var loading = 0;
238
+ if(typeof window.IN === 'undefined' && loading == 0){
239
+ loading = 1;
240
+ (function() {
241
+ var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
242
+ li.src = '//platform.linkedin.com/in.js';
243
+ var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
244
+ })();
245
+ }
246
+ else{
247
+ window.IN.init();
248
+ }
249
+ },
250
+ pinterest : function(self){
251
+ var sett = self.options.buttons.pinterest;
252
+ $(self.element).find('.buttons').append('<div class="button pinterest"><a href="http://pinterest.com/pin/create/button/?url='+(sett.url !== '' ? sett.url : self.options.url)+'&media='+sett.media+'&description='+sett.description+'" class="pin-it-button" count-layout="'+sett.layout+'">Pin It</a></div>');
253
+
254
+ (function() {
255
+ var li = document.createElement('script');li.type = 'text/javascript';li.async = true;
256
+ li.src = '//assets.pinterest.com/js/pinit.js';
257
+ var s = document.getElementsByTagName('script')[0];s.parentNode.insertBefore(li, s);
258
+ })();
259
+ }
260
+ },
261
+ /* Tracking for Google Analytics
262
+ ================================================== */
263
+ tracking = {
264
+ googlePlus: function(){},
265
+ facebook: function(){
266
+ //console.log('facebook');
267
+ fb = window.setInterval(function(){
268
+ if (typeof FB !== 'undefined') {
269
+ FB.Event.subscribe('edge.create', function(targetUrl) {
270
+ _gaq.push(['_trackSocial', 'facebook', 'like', targetUrl]);
271
+ });
272
+ FB.Event.subscribe('edge.remove', function(targetUrl) {
273
+ _gaq.push(['_trackSocial', 'facebook', 'unlike', targetUrl]);
274
+ });
275
+ FB.Event.subscribe('message.send', function(targetUrl) {
276
+ _gaq.push(['_trackSocial', 'facebook', 'send', targetUrl]);
277
+ });
278
+ //console.log('ok');
279
+ clearInterval(fb);
280
+ }
281
+ },1000);
282
+ },
283
+ twitter: function(){
284
+ //console.log('twitter');
285
+ tw = window.setInterval(function(){
286
+ if (typeof twttr !== 'undefined') {
287
+ twttr.events.bind('tweet', function(event) {
288
+ if (event) {
289
+ _gaq.push(['_trackSocial', 'twitter', 'tweet']);
290
+ }
291
+ });
292
+ //console.log('ok');
293
+ clearInterval(tw);
294
+ }
295
+ },1000);
296
+ },
297
+ digg: function(){
298
+ //if somenone find a solution, mail me !
299
+ /*$(this.element).find('.digg').on('click', function(){
300
+ _gaq.push(['_trackSocial', 'digg', 'add']);
301
+ });*/
302
+ },
303
+ delicious: function(){},
304
+ stumbleupon: function(){},
305
+ linkedin: function(){
306
+ function LinkedInShare() {
307
+ _gaq.push(['_trackSocial', 'linkedin', 'share']);
308
+ }
309
+ },
310
+ pinterest: function(){
311
+ //if somenone find a solution, mail me !
312
+ }
313
+ },
314
+ /* Popup for each social network
315
+ ================================================== */
316
+ popup = {
317
+ googlePlus: function(opt){
318
+ window.open("https://plus.google.com/share?hl="+opt.buttons.googlePlus.lang+"&url="+encodeURIComponent((opt.buttons.googlePlus.url !== '' ? opt.buttons.googlePlus.url : opt.url)), "", "toolbar=0, status=0, width=900, height=500");
319
+ },
320
+ facebook: function(opt){
321
+ window.open("http://www.facebook.com/sharer.php?u="+encodeURIComponent((opt.buttons.facebook.url !== '' ? opt.buttons.facebook.url : opt.url))+"&t="+opt.text+"", "", "toolbar=0, status=0, width=900, height=500");
322
+ },
323
+ twitter: function(opt){
324
+ window.open("https://twitter.com/intent/tweet?text="+encodeURIComponent(opt.text)+"&url="+encodeURIComponent((opt.buttons.twitter.url !== '' ? opt.buttons.twitter.url : opt.url))+(opt.buttons.twitter.via !== '' ? '&via='+opt.buttons.twitter.via : ''), "", "toolbar=0, status=0, width=650, height=360");
325
+ },
326
+ digg: function(opt){
327
+ window.open("http://digg.com/tools/diggthis/submit?url="+encodeURIComponent((opt.buttons.digg.url !== '' ? opt.buttons.digg.url : opt.url))+"&title="+opt.text+"&related=true&style=true", "", "toolbar=0, status=0, width=650, height=360");
328
+ },
329
+ delicious: function(opt){
330
+ window.open('http://www.delicious.com/save?v=5&noui&jump=close&url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url))+'&title='+opt.text, 'delicious', 'toolbar=no,width=550,height=550');
331
+ },
332
+ stumbleupon: function(opt){
333
+ window.open('http://www.stumbleupon.com/badge/?url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url)), 'stumbleupon', 'toolbar=no,width=550,height=550');
334
+ },
335
+ linkedin: function(opt){
336
+ window.open('https://www.linkedin.com/cws/share?url='+encodeURIComponent((opt.buttons.delicious.url !== '' ? opt.buttons.delicious.url : opt.url))+'&token=&isFramed=true', 'linkedin', 'toolbar=no,width=550,height=550');
337
+ },
338
+ pinterest: function(opt){
339
+ window.open('http://pinterest.com/pin/create/button/?url='+encodeURIComponent((opt.buttons.pinterest.url !== '' ? opt.buttons.pinterest.url : opt.url))+'&media='+encodeURIComponent(opt.buttons.pinterest.media)+'&description='+opt.buttons.pinterest.description, 'pinterest', 'toolbar=no,width=700,height=300');
340
+ }
341
+ };
342
+
343
+ /* Plugin constructor
344
+ ================================================== */
345
+ function Plugin( element, options ) {
346
+ this.element = element;
347
+
348
+ this.options = $.extend( true, {}, defaults, options);
349
+ this.options.share = options.share; //simple solution to allow order of buttons
350
+
351
+ this._defaults = defaults;
352
+ this._name = pluginName;
353
+
354
+ this.init();
355
+ };
356
+
357
+ /* Initialization method
358
+ ================================================== */
359
+ Plugin.prototype.init = function () {
360
+ var self = this;
361
+ if(this.options.urlCurl !== ''){
362
+ urlJson.googlePlus = this.options.urlCurl + '?url={url}&type=googlePlus'; // PHP script for GooglePlus...
363
+ urlJson.stumbleupon = this.options.urlCurl + '?url={url}&type=stumbleupon'; // PHP script for Stumbleupon...
364
+ urlJson.pinterest = this.options.urlCurl + '?url={url}&type=pinterest'; // PHP script for Pinterest...
365
+ }
366
+ $(this.element).addClass(this.options.className); //add class
367
+
368
+ //HTML5 Custom data
369
+ if(typeof $(this.element).data('title') !== 'undefined'){
370
+ this.options.title = $(this.element).attr('data-title');
371
+ }
372
+ if(typeof $(this.element).data('url') !== 'undefined'){
373
+ this.options.url = $(this.element).data('url');
374
+ }
375
+ if(typeof $(this.element).data('text') !== 'undefined'){
376
+ this.options.text = $(this.element).data('text');
377
+ }
378
+
379
+ //how many social website have been selected
380
+ $.each(this.options.share, function(name, val) {
381
+ if(val === true){
382
+ self.options.shareTotal ++;
383
+ }
384
+ });
385
+
386
+ if(self.options.enableCounter === true){ //if for some reason you don't need counter
387
+ //get count of social share that have been selected
388
+ $.each(this.options.share, function(name, val) {
389
+ if(val === true){
390
+ //self.getSocialJson(name);
391
+ try {
392
+ self.getSocialJson(name);
393
+ } catch(e){
394
+ }
395
+ }
396
+ });
397
+ }
398
+ else if(self.options.template !== ''){ //for personalized button (with template)
399
+ this.options.render(this, this.options);
400
+ }
401
+ else{ // if you want to use official button like example 3 or 5
402
+ this.loadButtons();
403
+ }
404
+
405
+ //add hover event
406
+ $(this.element).hover(function(){
407
+ //load social button if enable and 1 time
408
+ if($(this).find('.buttons').length === 0 && self.options.enableHover === true){
409
+ self.loadButtons();
410
+ }
411
+ self.options.hover(self, self.options);
412
+ }, function(){
413
+ self.options.hide(self, self.options);
414
+ });
415
+
416
+ //click event
417
+ $(this.element).click(function(){
418
+ self.options.click(self, self.options);
419
+ return false;
420
+ });
421
+ };
422
+
423
+ /* loadButtons methode
424
+ ================================================== */
425
+ Plugin.prototype.loadButtons = function () {
426
+ var self = this;
427
+ $(this.element).append('<div class="buttons"></div>');
428
+ $.each(self.options.share, function(name, val) {
429
+ if(val == true){
430
+ loadButton[name](self);
431
+ if(self.options.enableTracking === true){ //add tracking
432
+ tracking[name]();
433
+ }
434
+ }
435
+ });
436
+ };
437
+
438
+ /* getSocialJson methode
439
+ ================================================== */
440
+ Plugin.prototype.getSocialJson = function (name) {
441
+ var self = this,
442
+ count = 0,
443
+ url = urlJson[name].replace('{url}', encodeURIComponent(this.options.url));
444
+ if(this.options.buttons[name].urlCount === true && this.options.buttons[name].url !== ''){
445
+ url = urlJson[name].replace('{url}', this.options.buttons[name].url);
446
+ }
447
+ //console.log('name : ' + name + ' - url : '+url); //debug
448
+ if(url != '' && self.options.urlCurl !== ''){ //urlCurl = '' if you don't want to used PHP script but used social button
449
+ $.getJSON(url, function(json){
450
+ if(typeof json.count !== "undefined"){ //GooglePlus, Stumbleupon, Twitter and Digg
451
+ var temp = json.count + '';
452
+ temp = temp.replace('\u00c2\u00a0', ''); //remove google plus special chars
453
+ count += parseInt(temp, 10);
454
+ }
455
+ else if(typeof json.likes !== "undefined"){ //Facebook Fan page
456
+ count += parseInt(json.likes, 10); //changed shares to likes to use with fanPage url
457
+ }
458
+ else if(typeof json.shares !== "undefined"){ //Facebook
459
+ count += parseInt(json.shares, 10);
460
+ }
461
+ else if(typeof json[0] !== "undefined"){ //Delicious
462
+ count += parseInt(json[0].total_posts, 10);
463
+ }
464
+ else if(typeof json[0] !== "undefined"){ //Stumbleupon
465
+ }
466
+ self.options.count[name] = count;
467
+ self.options.total += count;
468
+ self.renderer();
469
+ self.rendererPerso();
470
+ //console.log(json); //debug
471
+ })
472
+ .error(function() {
473
+ self.options.count[name] = 0;
474
+ self.rendererPerso();
475
+ });
476
+ }
477
+ else{
478
+ self.renderer();
479
+ self.options.count[name] = 0;
480
+ self.rendererPerso();
481
+ }
482
+ };
483
+
484
+ /* launch render methode
485
+ ================================================== */
486
+ Plugin.prototype.rendererPerso = function () {
487
+ //check if this is the last social website to launch render
488
+ var shareCount = 0;
489
+ for (e in this.options.count) { shareCount++; }
490
+ if(shareCount === this.options.shareTotal){
491
+ this.options.render(this, this.options);
492
+ }
493
+ };
494
+
495
+ /* render methode
496
+ ================================================== */
497
+ Plugin.prototype.renderer = function () {
498
+ var total = this.options.total,
499
+ template = this.options.template;
500
+ if(this.options.shorterTotal === true){ //format number like 1.2k or 5M
501
+ total = this.shorterTotal(total);
502
+ }
503
+
504
+ if(template !== ''){ //if there is a template
505
+ template = template.replace('{total}', total);
506
+ $(this.element).html(template);
507
+ }
508
+ else{ //template by defaults
509
+ $(this.element).html(
510
+ '<div class="box"><a class="count" href="#">' + total + '</a>' +
511
+ (this.options.title !== '' ? '<a class="share" href="#">' + this.options.title + '</a>' : '') +
512
+ '</div>'
513
+ );
514
+ }
515
+ };
516
+
517
+ /* format total numbers like 1.2k or 5M
518
+ ================================================== */
519
+ Plugin.prototype.shorterTotal = function (num) {
520
+ if (num >= 1e6){
521
+ num = (num / 1e6).toFixed(2) + "M"
522
+ } else if (num >= 1e3){
523
+ num = (num / 1e3).toFixed(1) + "k"
524
+ }
525
+ return num;
526
+ };
527
+
528
+ /* Methode for open popup
529
+ ================================================== */
530
+ Plugin.prototype.openPopup = function (site) {
531
+ popup[site](this.options); //open
532
+ if(this.options.enableTracking === true){ //tracking!
533
+ var tracking = {
534
+ googlePlus: {site: 'Google', action: '+1'},
535
+ facebook: {site: 'facebook', action: 'like'},
536
+ twitter: {site: 'twitter', action: 'tweet'},
537
+ digg: {site: 'digg', action: 'add'},
538
+ delicious: {site: 'delicious', action: 'add'},
539
+ stumbleupon: {site: 'stumbleupon', action: 'add'},
540
+ linkedin: {site: 'linkedin', action: 'share'},
541
+ pinterest: {site: 'pinterest', action: 'pin'}
542
+ };
543
+ _gaq.push(['_trackSocial', tracking[site].site, tracking[site].action]);
544
+ }
545
+ };
546
+
547
+ /* Methode for add +1 to a counter
548
+ ================================================== */
549
+ Plugin.prototype.simulateClick = function () {
550
+ var html = $(this.element).html();
551
+ $(this.element).html(html.replace(this.options.total, this.options.total+1));
552
+ };
553
+
554
+ /* Methode for add +1 to a counter
555
+ ================================================== */
556
+ Plugin.prototype.update = function (url, text) {
557
+ if(url !== ''){
558
+ this.options.url = url;
559
+ }
560
+ if(text !== ''){
561
+ this.options.text = text;
562
+ }
563
+ };
564
+
565
+ /* A really lightweight plugin wrapper around the constructor, preventing against multiple instantiations
566
+ ================================================== */
567
+ $.fn[pluginName] = function ( options ) {
568
+ var args = arguments;
569
+ if (options === undefined || typeof options === 'object') {
570
+ return this.each(function () {
571
+ if (!$.data(this, 'plugin_' + pluginName)) {
572
+ $.data(this, 'plugin_' + pluginName, new Plugin( this, options ));
573
+ }
574
+ });
575
+ } else if (typeof options === 'string' && options[0] !== '_' && options !== 'init') {
576
+ return this.each(function () {
577
+ var instance = $.data(this, 'plugin_' + pluginName);
578
+ if (instance instanceof Plugin && typeof instance[options] === 'function') {
579
+ instance[options].apply( instance, Array.prototype.slice.call( args, 1 ) );
580
+ }
581
+ });
582
+ }
583
+ };
584
+ })(jQuery, window, document);
assets/tests/bin/phantomjs.exe DELETED
Binary file
assets/tests/bin/phantomloader DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env sh
2
- SRC_DIR="`pwd`"
3
- cd "`dirname "$0"`"
4
- cd "../../vendor/jonnyw/php-phantomjs/bin"
5
- BIN_TARGET="`pwd`/phantomloader"
6
- cd "$SRC_DIR"
7
- "$BIN_TARGET" "$@"
 
 
 
 
 
 
 
assets/tests/bin/wpcept DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env sh
2
- SRC_DIR="`pwd`"
3
- cd "`dirname "$0"`"
4
- cd "../../vendor/lucatume/wp-browser"
5
- BIN_TARGET="`pwd`/wpcept"
6
- cd "$SRC_DIR"
7
- "$BIN_TARGET" "$@"
 
 
 
 
 
 
 
assets/tests/bin/wpcept.bat DELETED
@@ -1,3 +0,0 @@
1
- @ECHO OFF
2
- SET BIN_TARGET=%~dp0/../../vendor/lucatume/wp-browser/wpcept
3
- php "%BIN_TARGET%" %*
 
 
 
assets/tests/build/php.conf DELETED
@@ -1,4 +0,0 @@
1
- <IfModule mod_php5.c>
2
- AddType application/x-httpd-php .php .phtml .php5
3
- AddType application/x-httpd-php-source .phps
4
- </IfModule>
 
 
 
 
assets/tests/build/php.load DELETED
@@ -1 +0,0 @@
1
- LoadModule php5_module /usr/lib/apache2/modules/libphp5.so
 
assets/tests/build/scratchpad.sh DELETED
@@ -1 +0,0 @@
1
- phantomjs --webdriver=4444
 
assets/tests/build/travis-ci-apache DELETED
@@ -1,23 +0,0 @@
1
- <VirtualHost *:80>
2
- ServerAdmin tests@inboundnow.com
3
- DocumentRoot /var/www/inboundtesting.dev/
4
- ServerName inboundtesting.dev
5
- ErrorLog ${APACHE_LOG_DIR}/error.log
6
- CustomLog ${APACHE_LOG_DIR}/access.log combined
7
-
8
- # Wire up Apache to use Travis CI's php-fpm.
9
- <IfModule mod_fastcgi.c>
10
- AddHandler php5-fcgi .php
11
- Action php5-fcgi /php5-fcgi
12
- Alias /php5-fcgi /usr/lib/cgi-bin/php5-fcgi
13
- FastCgiExternalServer /usr/lib/cgi-bin/php5-fcgi -host 127.0.0.1:9000 -pass-header Authorization
14
- </IfModule>
15
-
16
- <Directory "/var/www/inboundtesting.dev/">
17
- Options FollowSymLinks MultiViews ExecCGI
18
- AllowOverride All
19
- Order deny,allow
20
- Allow from all
21
- </Directory>
22
- </VirtualHost>
23
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_bootstrap.php DELETED
@@ -1,14 +0,0 @@
1
- <?php
2
-
3
-
4
- /* load wp */
5
- require '../../../wp-load.php';
6
- require '../../../wp-admin/includes/plugin.php';
7
-
8
- /* load coception addons */
9
- require LANDINGPAGES_PATH . 'vendor/lucatume/wp-browser/src/Module/WPBrowserMethods.php';
10
- require LANDINGPAGES_PATH . 'vendor/lucatume/wp-browser/src/Module/WPBrowser.php';
11
-
12
- /* Set current users */
13
- wp_set_current_user( 1 );
14
- global $wpdb;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_data/dump.sql DELETED
@@ -1 +0,0 @@
1
- /* Replace this file with actual dump of your database */
 
assets/tests/codeception/_support/AcceptanceHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class AcceptanceHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_support/FunctionalHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class FunctionalHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/_support/UnitHelper.php DELETED
@@ -1,10 +0,0 @@
1
- <?php
2
- namespace Codeception\Module;
3
-
4
- // here you can define custom actions
5
- // all public methods declared in helper class will be available in $I
6
-
7
- class UnitHelper extends \Codeception\Module
8
- {
9
-
10
- }
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance.suite.yml DELETED
@@ -1,25 +0,0 @@
1
- # Codeception Test Suite Configuration
2
-
3
- # suite for acceptance tests.
4
- # perform tests in browser using the WebDriver or PhpBrowser.
5
- # If you need both WebDriver and PHPBrowser tests - create a separate suite.
6
-
7
- class_name: AcceptanceTester
8
- modules:
9
- enabled:
10
- - PhpBrowser
11
- - Asserts
12
- - WPBrowser
13
- config:
14
- WebDriver:
15
- url: 'http://inboundtesting.dev/'
16
- browser: firefox
17
- clear_cookies: false
18
- window_size: 1024x768
19
- PhpBrowser:
20
- url: 'http://inboundsoon.dev'
21
- WPBrowser:
22
- url: 'http://inboundsoon.dev'
23
- adminUsername: 'admin'
24
- adminPassword: 'admin'
25
- adminUrl: 'wp-admin'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/tests/codeception/acceptance/AcceptanceTester.php DELETED
@@ -1,1918 +0,0 @@
1
- <?php //[STAMP] 2d5aa64986f8f94bc0f812d88aa9dee6
2
-
3
- // This class was automatically generated by build task
4
- // You should not change it manually as it will be overwritten on next build
5
- // @codingStandardsIgnoreFile
6
-
7
-
8
- use Codeception\Module\PhpBrowser;
9
- use Codeception\Module\AcceptanceHelper;
10
-
11
- /**
12
- * Inherited Methods
13
- * @method void wantToTest($text)
14
- * @method void wantTo($text)
15
- * @method void execute($callable)
16
- * @method void expectTo($prediction)
17
- * @method void expect($prediction)
18
- * @method void amGoingTo($argumentation)
19
- * @method void am($role)
20
- * @method void lookForwardTo($achieveValue)
21
- * @method void comment($description)
22
- * @method void haveFriend($name, $actorClass = null)
23
- *
24
- * @SuppressWarnings(PHPMD)
25
- */
26
- class AcceptanceTester extends \Codeception\Actor
27
- {
28
-
29
- /**
30
- * [!] Method is generated. Documentation taken from corresponding module.
31
- *
32
- * Sets the HTTP header to the passed value - which is used on
33
- * subsequent HTTP requests through PhpBrowser.
34
- *
35
- * Example:
36
- * ```php
37
- * <?php
38
- * $I->setHeader('X-Requested-With', 'Codeception');
39
- * $I->amOnPage('test-headers.php');
40
- * ?>
41
- * ```
42
- *
43
- * @param string $name the name of the request header
44
- * @param string $value the value to set it to for subsequent
45
- * requests
46
- * @see \Codeception\Module\PhpBrowser::setHeader()
47
- */
48
- public function setHeader($name, $value) {
49
- return $this->scenario->runStep(new \Codeception\Step\Action('setHeader', func_get_args()));
50
- }
51
-
52
-
53
- /**
54
- * [!] Method is generated. Documentation taken from corresponding module.
55
- *
56
- * Deletes the header with the passed name. Subsequent requests
57
- * will not have the deleted header in its request.
58
- *
59
- * Example:
60
- * ```php
61
- * <?php
62
- * $I->setHeader('X-Requested-With', 'Codeception');
63
- * $I->amOnPage('test-headers.php');
64
- * // ...
65
- * $I->deleteHeader('X-Requested-With');
66
- * $I->amOnPage('some-other-page.php');
67
- * ?>
68
- * ```
69
- *
70
- * @param string $name the name of the header to delete.
71
- * @see \Codeception\Module\PhpBrowser::deleteHeader()
72
- */
73
- public function deleteHeader($name) {
74
- return $this->scenario->runStep(new \Codeception\Step\Action('deleteHeader', func_get_args()));
75
- }
76
-
77
-
78
- /**
79
- * [!] Method is generated. Documentation taken from corresponding module.
80
- *
81
- * Authenticates user for HTTP_AUTH
82
- *
83
- * @param $username
84
- * @param $password
85
- * @see \Codeception\Module\PhpBrowser::amHttpAuthenticated()
86
- */
87
- public function amHttpAuthenticated($username, $password) {
88
- return $this->scenario->runStep(new \Codeception\Step\Condition('amHttpAuthenticated', func_get_args()));
89
- }
90
-
91
-
92
- /**
93
- * [!] Method is generated. Documentation taken from corresponding module.
94
- *
95
- * Opens the page for the given relative URI.
96
- *
97
- * ``` php
98
- * <?php
99
- * // opens front page
100
- * $I->amOnPage('/');
101
- * // opens /register page
102
- * $I->amOnPage('/register');
103
- * ?>
104
- * ```
105
- *
106
- * @param $page
107
- * @see \Codeception\Module\PhpBrowser::amOnPage()
108
- */
109
- public function amOnPage($page) {
110
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnPage', func_get_args()));
111
- }
112
-
113
-
114
- /**
115
- * [!] Method is generated. Documentation taken from corresponding module.
116
- *
117
- * Open web page at the given absolute URL and sets its hostname as the base host.
118
- *
119
- * ``` php
120
- * <?php
121
- * $I->amOnUrl('http://codeception.com');
122
- * $I->amOnPage('/quickstart'); // moves to http://codeception.com/quickstart
123
- * ?>
124
- * ```
125
- * @see \Codeception\Module\PhpBrowser::amOnUrl()
126
- */
127
- public function amOnUrl($url) {
128
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnUrl', func_get_args()));
129
- }
130
-
131
-
132
- /**
133
- * [!] Method is generated. Documentation taken from corresponding module.
134
- *
135
- * Changes the subdomain for the 'url' configuration parameter.
136
- * Does not open a page; use `amOnPage` for that.
137
- *
138
- * ``` php
139
- * <?php
140
- * // If config is: 'http://mysite.com'
141
- * // or config is: 'http://www.mysite.com'
142
- * // or config is: 'http://company.mysite.com'
143
- *
144
- * $I->amOnSubdomain('user');
145
- * $I->amOnPage('/');
146
- * // moves to http://user.mysite.com/
147
- * ?>
148
- * ```
149
- *
150
- * @param $subdomain
151
- *
152
- * @return mixed
153
- * @see \Codeception\Module\PhpBrowser::amOnSubdomain()
154
- */
155
- public function amOnSubdomain($subdomain) {
156
- return $this->scenario->runStep(new \Codeception\Step\Condition('amOnSubdomain', func_get_args()));
157
- }
158
-
159
-
160
- /**
161
- * [!] Method is generated. Documentation taken from corresponding module.
162
- *
163
- * Low-level API method.
164
- * If Codeception commands are not enough, use [Guzzle HTTP Client](http://guzzlephp.org/) methods directly
165
- *
166
- * Example:
167
- *
168
- * ``` php
169
- * <?php
170
- * $I->executeInGuzzle(function (\GuzzleHttp\Client $client) {
171
- * $client->get('/get', ['query' => ['foo' => 'bar']]);
172
- * });
173
- * ?>
174
- * ```
175
- *
176
- * It is not recommended to use this command on a regular basis.
177
- * If Codeception lacks important Guzzle Client methods, implement them and submit patches.
178
- *
179
- * @param callable $function
180
- * @see \Codeception\Module\PhpBrowser::executeInGuzzle()
181
- */
182
- public function executeInGuzzle($function) {
183
- return $this->scenario->runStep(new \Codeception\Step\Action('executeInGuzzle', func_get_args()));
184
- }
185
-
186
-
187
- /**
188
- * [!] Method is generated. Documentation taken from corresponding module.
189
- *
190
- * Perform a click on a link or a button, given by a locator.
191
- * If a fuzzy locator is given, the page will be searched for a button, link, or image matching the locator string.
192
- * For buttons, the "value" attribute, "name" attribute, and inner text are searched.
193
- * For links, the link text is searched.
194
- * For images, the "alt" attribute and inner text of any parent links are searched.
195
- *
196
- * The second parameter is a context (CSS or XPath locator) to narrow the search.
197
- *
198
- * Note that if the locator matches a button of type `submit`, the form will be submitted.
199
- *
200
- * ``` php
201
- * <?php
202
- * // simple link
203
- * $I->click('Logout');
204
- * // button of form
205
- * $I->click('Submit');
206
- * // CSS button
207
- * $I->click('#form input[type=submit]');
208
- * // XPath
209
- * $I->click('//form/*[@type=submit]');
210
- * // link in context
211
- * $I->click('Logout', '#nav');
212
- * // using strict locator
213
- * $I->click(['link' => 'Login']);
214
- * ?>
215
- * ```
216
- *
217
- * @param $link
218
- * @param $context
219
- * @see \Codeception\Lib\InnerBrowser::click()
220
- */
221
- public function click($link, $context = null) {
222
- return $this->scenario->runStep(new \Codeception\Step\Action('click', func_get_args()));
223
- }
224
-
225
-
226
- /**
227
- * [!] Method is generated. Documentation taken from corresponding module.
228
- *
229
- * Checks that the current page contains the given string.
230
- * Specify a locator as the second parameter to match a specific region.
231
- *
232
- * ``` php
233
- * <?php
234
- * $I->see('Logout'); // I can suppose user is logged in
235
- * $I->see('Sign Up','h1'); // I can suppose it's a signup page
236
- * $I->see('Sign Up','//body/h1'); // with XPath
237
- * ?>
238
- * ```
239
- *
240
- * @param $text
241
- * @param null $selector
242
- * Conditional Assertion: Test won't be stopped on fail
243
- * @see \Codeception\Lib\InnerBrowser::see()
244
- */
245
- public function canSee($text, $selector = null) {
246
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('see', func_get_args()));
247
- }
248
- /**
249
- * [!] Method is generated. Documentation taken from corresponding module.
250
- *
251
- * Checks that the current page contains the given string.
252
- * Specify a locator as the second parameter to match a specific region.
253
- *
254
- * ``` php
255
- * <?php
256
- * $I->see('Logout'); // I can suppose user is logged in
257
- * $I->see('Sign Up','h1'); // I can suppose it's a signup page
258
- * $I->see('Sign Up','//body/h1'); // with XPath
259
- * ?>
260
- * ```
261
- *
262
- * @param $text
263
- * @param null $selector
264
- * @see \Codeception\Lib\InnerBrowser::see()
265
- */
266
- public function see($text, $selector = null) {
267
- return $this->scenario->runStep(new \Codeception\Step\Assertion('see', func_get_args()));
268
- }
269
-
270
-
271
- /**
272
- * [!] Method is generated. Documentation taken from corresponding module.
273
- *
274
- * Checks that the current page doesn't contain the text specified.
275
- * Give a locator as the second parameter to match a specific region.
276
- *
277
- * ```php
278
- * <?php
279
- * $I->dontSee('Login'); // I can suppose user is already logged in
280
- * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page
281
- * $I->dontSee('Sign Up','//body/h1'); // with XPath
282
- * ?>
283
- * ```
284
- *
285
- * @param $text
286
- * @param null $selector
287
- * Conditional Assertion: Test won't be stopped on fail
288
- * @see \Codeception\Lib\InnerBrowser::dontSee()
289
- */
290
- public function cantSee($text, $selector = null) {
291
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSee', func_get_args()));
292
- }
293
- /**
294
- * [!] Method is generated. Documentation taken from corresponding module.
295
- *
296
- * Checks that the current page doesn't contain the text specified.
297
- * Give a locator as the second parameter to match a specific region.
298
- *
299
- * ```php
300
- * <?php
301
- * $I->dontSee('Login'); // I can suppose user is already logged in
302
- * $I->dontSee('Sign Up','h1'); // I can suppose it's not a signup page
303
- * $I->dontSee('Sign Up','//body/h1'); // with XPath
304
- * ?>
305
- * ```
306
- *
307
- * @param $text
308
- * @param null $selector
309
- * @see \Codeception\Lib\InnerBrowser::dontSee()
310
- */
311
- public function dontSee($text, $selector = null) {
312
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSee', func_get_args()));
313
- }
314
-
315
-
316
- /**
317
- * [!] Method is generated. Documentation taken from corresponding module.
318
- *
319
- * Checks that there's a link with the specified text.
320
- * Give a full URL as the second parameter to match links with that exact URL.
321
- *
322
- * ``` php
323
- * <?php
324
- * $I->seeLink('Logout'); // matches <a href="#">Logout</a>
325
- * $I->seeLink('Logout','/logout'); // matches <a href="/logout">Logout</a>
326
- * ?>
327
- * ```
328
- *
329
- * @param $text
330
- * @param null $url
331
- * Conditional Assertion: Test won't be stopped on fail
332
- * @see \Codeception\Lib\InnerBrowser::seeLink()
333
- */
334
- public function canSeeLink($text, $url = null) {
335
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeLink', func_get_args()));
336
- }
337
- /**
338
- * [!] Method is generated. Documentation taken from corresponding module.
339
- *
340
- * Checks that there's a link with the specified text.
341
- * Give a full URL as the second parameter to match links with that exact URL.
342
- *
343
- * ``` php
344
- * <?php
345
- * $I->seeLink('Logout'); // matches <a href="#">Logout</a>
346
- * $I->seeLink('Logout','/logout'); // matches <a href="/logout">Logout</a>
347
- * ?>
348
- * ```
349
- *
350
- * @param $text
351
- * @param null $url
352
- * @see \Codeception\Lib\InnerBrowser::seeLink()
353
- */
354
- public function seeLink($text, $url = null) {
355
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeLink', func_get_args()));
356
- }
357
-
358
-
359
- /**
360
- * [!] Method is generated. Documentation taken from corresponding module.
361
- *
362
- * Checks that the page doesn't contain a link with the given string.
363
- * If the second parameter is given, only links with a matching "href" attribute will be checked.
364
- *
365
- * ``` php
366
- * <?php
367
- * $I->dontSeeLink('Logout'); // I suppose user is not logged in
368
- * $I->dontSeeLink('Checkout now', '/store/cart.php');
369
- * ?>
370
- * ```
371
- *
372
- * @param $text
373
- * @param null $url
374
- * Conditional Assertion: Test won't be stopped on fail
375
- * @see \Codeception\Lib\InnerBrowser::dontSeeLink()
376
- */
377
- public function cantSeeLink($text, $url = null) {
378
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeLink', func_get_args()));
379
- }
380
- /**
381
- * [!] Method is generated. Documentation taken from corresponding module.
382
- *
383
- * Checks that the page doesn't contain a link with the given string.
384
- * If the second parameter is given, only links with a matching "href" attribute will be checked.
385
- *
386
- * ``` php
387
- * <?php
388
- * $I->dontSeeLink('Logout'); // I suppose user is not logged in
389
- * $I->dontSeeLink('Checkout now', '/store/cart.php');
390
- * ?>
391
- * ```
392
- *
393
- * @param $text
394
- * @param null $url
395
- * @see \Codeception\Lib\InnerBrowser::dontSeeLink()
396
- */
397
- public function dontSeeLink($text, $url = null) {
398
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeLink', func_get_args()));
399
- }
400
-
401
-
402
- /**
403
- * [!] Method is generated. Documentation taken from corresponding module.
404
- *
405
- * Checks that current URI contains the given string.
406
- *
407
- * ``` php
408
- * <?php
409
- * // to match: /home/dashboard
410
- * $I->seeInCurrentUrl('home');
411
- * // to match: /users/1
412
- * $I->seeInCurrentUrl('/users/');
413
- * ?>
414
- * ```
415
- *
416
- * @param $uri
417
- * Conditional Assertion: Test won't be stopped on fail
418
- * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl()
419
- */
420
- public function canSeeInCurrentUrl($uri) {
421
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInCurrentUrl', func_get_args()));
422
- }
423
- /**
424
- * [!] Method is generated. Documentation taken from corresponding module.
425
- *
426
- * Checks that current URI contains the given string.
427
- *
428
- * ``` php
429
- * <?php
430
- * // to match: /home/dashboard
431
- * $I->seeInCurrentUrl('home');
432
- * // to match: /users/1
433
- * $I->seeInCurrentUrl('/users/');
434
- * ?>
435
- * ```
436
- *
437
- * @param $uri
438
- * @see \Codeception\Lib\InnerBrowser::seeInCurrentUrl()
439
- */
440
- public function seeInCurrentUrl($uri) {
441
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInCurrentUrl', func_get_args()));
442
- }
443
-
444
-
445
- /**
446
- * [!] Method is generated. Documentation taken from corresponding module.
447
- *
448
- * Checks that the current URI doesn't contain the given string.
449
- *
450
- * ``` php
451
- * <?php
452
- * $I->dontSeeInCurrentUrl('/users/');
453
- * ?>
454
- * ```
455
- *
456
- * @param $uri
457
- * Conditional Assertion: Test won't be stopped on fail
458
- * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl()
459
- */
460
- public function cantSeeInCurrentUrl($uri) {
461
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInCurrentUrl', func_get_args()));
462
- }
463
- /**
464
- * [!] Method is generated. Documentation taken from corresponding module.
465
- *
466
- * Checks that the current URI doesn't contain the given string.
467
- *
468
- * ``` php
469
- * <?php
470
- * $I->dontSeeInCurrentUrl('/users/');
471
- * ?>
472
- * ```
473
- *
474
- * @param $uri
475
- * @see \Codeception\Lib\InnerBrowser::dontSeeInCurrentUrl()
476
- */
477
- public function dontSeeInCurrentUrl($uri) {
478
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInCurrentUrl', func_get_args()));
479
- }
480
-
481
-
482
- /**
483
- * [!] Method is generated. Documentation taken from corresponding module.
484
- *
485
- * Checks that the current URL is equal to the given string.
486
- * Unlike `seeInCurrentUrl`, this only matches the full URL.
487
- *
488
- * ``` php
489
- * <?php
490
- * // to match root url
491
- * $I->seeCurrentUrlEquals('/');
492
- * ?>
493
- * ```
494
- *
495
- * @param $uri
496
- * Conditional Assertion: Test won't be stopped on fail
497
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals()
498
- */
499
- public function canSeeCurrentUrlEquals($uri) {
500
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlEquals', func_get_args()));
501
- }
502
- /**
503
- * [!] Method is generated. Documentation taken from corresponding module.
504
- *
505
- * Checks that the current URL is equal to the given string.
506
- * Unlike `seeInCurrentUrl`, this only matches the full URL.
507
- *
508
- * ``` php
509
- * <?php
510
- * // to match root url
511
- * $I->seeCurrentUrlEquals('/');
512
- * ?>
513
- * ```
514
- *
515
- * @param $uri
516
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlEquals()
517
- */
518
- public function seeCurrentUrlEquals($uri) {
519
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlEquals', func_get_args()));
520
- }
521
-
522
-
523
- /**
524
- * [!] Method is generated. Documentation taken from corresponding module.
525
- *
526
- * Checks that the current URL doesn't equal the given string.
527
- * Unlike `dontSeeInCurrentUrl`, this only matches the full URL.
528
- *
529
- * ``` php
530
- * <?php
531
- * // current url is not root
532
- * $I->dontSeeCurrentUrlEquals('/');
533
- * ?>
534
- * ```
535
- *
536
- * @param $uri
537
- * Conditional Assertion: Test won't be stopped on fail
538
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals()
539
- */
540
- public function cantSeeCurrentUrlEquals($uri) {
541
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlEquals', func_get_args()));
542
- }
543
- /**
544
- * [!] Method is generated. Documentation taken from corresponding module.
545
- *
546
- * Checks that the current URL doesn't equal the given string.
547
- * Unlike `dontSeeInCurrentUrl`, this only matches the full URL.
548
- *
549
- * ``` php
550
- * <?php
551
- * // current url is not root
552
- * $I->dontSeeCurrentUrlEquals('/');
553
- * ?>
554
- * ```
555
- *
556
- * @param $uri
557
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlEquals()
558
- */
559
- public function dontSeeCurrentUrlEquals($uri) {
560
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlEquals', func_get_args()));
561
- }
562
-
563
-
564
- /**
565
- * [!] Method is generated. Documentation taken from corresponding module.
566
- *
567
- * Checks that the current URL matches the given regular expression.
568
- *
569
- * ``` php
570
- * <?php
571
- * // to match root url
572
- * $I->seeCurrentUrlMatches('~$/users/(\d+)~');
573
- * ?>
574
- * ```
575
- *
576
- * @param $uri
577
- * Conditional Assertion: Test won't be stopped on fail
578
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches()
579
- */
580
- public function canSeeCurrentUrlMatches($uri) {
581
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCurrentUrlMatches', func_get_args()));
582
- }
583
- /**
584
- * [!] Method is generated. Documentation taken from corresponding module.
585
- *
586
- * Checks that the current URL matches the given regular expression.
587
- *
588
- * ``` php
589
- * <?php
590
- * // to match root url
591
- * $I->seeCurrentUrlMatches('~$/users/(\d+)~');
592
- * ?>
593
- * ```
594
- *
595
- * @param $uri
596
- * @see \Codeception\Lib\InnerBrowser::seeCurrentUrlMatches()
597
- */
598
- public function seeCurrentUrlMatches($uri) {
599
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCurrentUrlMatches', func_get_args()));
600
- }
601
-
602
-
603
- /**
604
- * [!] Method is generated. Documentation taken from corresponding module.
605
- *
606
- * Checks that current url doesn't match the given regular expression.
607
- *
608
- * ``` php
609
- * <?php
610
- * // to match root url
611
- * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~');
612
- * ?>
613
- * ```
614
- *
615
- * @param $uri
616
- * Conditional Assertion: Test won't be stopped on fail
617
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches()
618
- */
619
- public function cantSeeCurrentUrlMatches($uri) {
620
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCurrentUrlMatches', func_get_args()));
621
- }
622
- /**
623
- * [!] Method is generated. Documentation taken from corresponding module.
624
- *
625
- * Checks that current url doesn't match the given regular expression.
626
- *
627
- * ``` php
628
- * <?php
629
- * // to match root url
630
- * $I->dontSeeCurrentUrlMatches('~$/users/(\d+)~');
631
- * ?>
632
- * ```
633
- *
634
- * @param $uri
635
- * @see \Codeception\Lib\InnerBrowser::dontSeeCurrentUrlMatches()
636
- */
637
- public function dontSeeCurrentUrlMatches($uri) {
638
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCurrentUrlMatches', func_get_args()));
639
- }
640
-
641
-
642
- /**
643
- * [!] Method is generated. Documentation taken from corresponding module.
644
- *
645
- * Executes the given regular expression against the current URI and returns the first match.
646
- * If no parameters are provided, the full URI is returned.
647
- *
648
- * ``` php
649
- * <?php
650
- * $user_id = $I->grabFromCurrentUrl('~$/user/(\d+)/~');
651
- * $uri = $I->grabFromCurrentUrl();
652
- * ?>
653
- * ```
654
- *
655
- * @param null $uri
656
- *
657
- * @internal param $url
658
- * @return mixed
659
- * @see \Codeception\Lib\InnerBrowser::grabFromCurrentUrl()
660
- */
661
- public function grabFromCurrentUrl($uri = null) {
662
- return $this->scenario->runStep(new \Codeception\Step\Action('grabFromCurrentUrl', func_get_args()));
663
- }
664
-
665
-
666
- /**
667
- * [!] Method is generated. Documentation taken from corresponding module.
668
- *
669
- * Checks that the specified checkbox is checked.
670
- *
671
- * ``` php
672
- * <?php
673
- * $I->seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
674
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form.
675
- * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]');
676
- * ?>
677
- * ```
678
- *
679
- * @param $checkbox
680
- * Conditional Assertion: Test won't be stopped on fail
681
- * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked()
682
- */
683
- public function canSeeCheckboxIsChecked($checkbox) {
684
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCheckboxIsChecked', func_get_args()));
685
- }
686
- /**
687
- * [!] Method is generated. Documentation taken from corresponding module.
688
- *
689
- * Checks that the specified checkbox is checked.
690
- *
691
- * ``` php
692
- * <?php
693
- * $I->seeCheckboxIsChecked('#agree'); // I suppose user agreed to terms
694
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user agreed to terms, If there is only one checkbox in form.
695
- * $I->seeCheckboxIsChecked('//form/input[@type=checkbox and @name=agree]');
696
- * ?>
697
- * ```
698
- *
699
- * @param $checkbox
700
- * @see \Codeception\Lib\InnerBrowser::seeCheckboxIsChecked()
701
- */
702
- public function seeCheckboxIsChecked($checkbox) {
703
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCheckboxIsChecked', func_get_args()));
704
- }
705
-
706
-
707
- /**
708
- * [!] Method is generated. Documentation taken from corresponding module.
709
- *
710
- * Check that the specified checkbox is unchecked.
711
- *
712
- * ``` php
713
- * <?php
714
- * $I->dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms
715
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form.
716
- * ?>
717
- * ```
718
- *
719
- * @param $checkbox
720
- * Conditional Assertion: Test won't be stopped on fail
721
- * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked()
722
- */
723
- public function cantSeeCheckboxIsChecked($checkbox) {
724
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCheckboxIsChecked', func_get_args()));
725
- }
726
- /**
727
- * [!] Method is generated. Documentation taken from corresponding module.
728
- *
729
- * Check that the specified checkbox is unchecked.
730
- *
731
- * ``` php
732
- * <?php
733
- * $I->dontSeeCheckboxIsChecked('#agree'); // I suppose user didn't agree to terms
734
- * $I->seeCheckboxIsChecked('#signup_form input[type=checkbox]'); // I suppose user didn't check the first checkbox in form.
735
- * ?>
736
- * ```
737
- *
738
- * @param $checkbox
739
- * @see \Codeception\Lib\InnerBrowser::dontSeeCheckboxIsChecked()
740
- */
741
- public function dontSeeCheckboxIsChecked($checkbox) {
742
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCheckboxIsChecked', func_get_args()));
743
- }
744
-
745
-
746
- /**
747
- * [!] Method is generated. Documentation taken from corresponding module.
748
- *
749
- * Checks that the given input field or textarea contains the given value.
750
- * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
751
- *
752
- * ``` php
753
- * <?php
754
- * $I->seeInField('Body','Type your comment here');
755
- * $I->seeInField('form textarea[name=body]','Type your comment here');
756
- * $I->seeInField('form input[type=hidden]','hidden_value');
757
- * $I->seeInField('#searchform input','Search');
758
- * $I->seeInField('//form/*[@name=search]','Search');
759
- * $I->seeInField(['name' => 'search'], 'Search');
760
- * ?>
761
- * ```
762
- *
763
- * @param $field
764
- * @param $value
765
- * Conditional Assertion: Test won't be stopped on fail
766
- * @see \Codeception\Lib\InnerBrowser::seeInField()
767
- */
768
- public function canSeeInField($field, $value) {
769
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInField', func_get_args()));
770
- }
771
- /**
772
- * [!] Method is generated. Documentation taken from corresponding module.
773
- *
774
- * Checks that the given input field or textarea contains the given value.
775
- * For fuzzy locators, fields are matched by label text, the "name" attribute, CSS, and XPath.
776
- *
777
- * ``` php
778
- * <?php
779
- * $I->seeInField('Body','Type your comment here');
780
- * $I->seeInField('form textarea[name=body]','Type your comment here');
781
- * $I->seeInField('form input[type=hidden]','hidden_value');
782
- * $I->seeInField('#searchform input','Search');
783
- * $I->seeInField('//form/*[@name=search]','Search');
784
- * $I->seeInField(['name' => 'search'], 'Search');
785
- * ?>
786
- * ```
787
- *
788
- * @param $field
789
- * @param $value
790
- * @see \Codeception\Lib\InnerBrowser::seeInField()
791
- */
792
- public function seeInField($field, $value) {
793
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInField', func_get_args()));
794
- }
795
-
796
-
797
- /**
798
- * [!] Method is generated. Documentation taken from corresponding module.
799
- *
800
- * Checks that an input field or textarea doesn't contain the given value.
801
- * For fuzzy locators, the field is matched by label text, CSS and XPath.
802
- *
803
- * ``` php
804
- * <?php
805
- * $I->dontSeeInField('Body','Type your comment here');
806
- * $I->dontSeeInField('form textarea[name=body]','Type your comment here');
807
- * $I->dontSeeInField('form input[type=hidden]','hidden_value');
808
- * $I->dontSeeInField('#searchform input','Search');
809
- * $I->dontSeeInField('//form/*[@name=search]','Search');
810
- * $I->dontSeeInField(['name' => 'search'], 'Search');
811
- * ?>
812
- * ```
813
- *
814
- * @param $field
815
- * @param $value
816
- * Conditional Assertion: Test won't be stopped on fail
817
- * @see \Codeception\Lib\InnerBrowser::dontSeeInField()
818
- */
819
- public function cantSeeInField($field, $value) {
820
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInField', func_get_args()));
821
- }
822
- /**
823
- * [!] Method is generated. Documentation taken from corresponding module.
824
- *
825
- * Checks that an input field or textarea doesn't contain the given value.
826
- * For fuzzy locators, the field is matched by label text, CSS and XPath.
827
- *
828
- * ``` php
829
- * <?php
830
- * $I->dontSeeInField('Body','Type your comment here');
831
- * $I->dontSeeInField('form textarea[name=body]','Type your comment here');
832
- * $I->dontSeeInField('form input[type=hidden]','hidden_value');
833
- * $I->dontSeeInField('#searchform input','Search');
834
- * $I->dontSeeInField('//form/*[@name=search]','Search');
835
- * $I->dontSeeInField(['name' => 'search'], 'Search');
836
- * ?>
837
- * ```
838
- *
839
- * @param $field
840
- * @param $value
841
- * @see \Codeception\Lib\InnerBrowser::dontSeeInField()
842
- */
843
- public function dontSeeInField($field, $value) {
844
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInField', func_get_args()));
845
- }
846
-
847
-
848
- /**
849
- * [!] Method is generated. Documentation taken from corresponding module.
850
- *
851
- * Checks if the array of form parameters (name => value) are set on the form matched with the
852
- * passed selector.
853
- *
854
- * ``` php
855
- * <?php
856
- * $I->seeInFormFields('form[name=myform]', [
857
- * 'input1' => 'value',
858
- * 'input2' => 'other value',
859
- * ]);
860
- * ?>
861
- * ```
862
- *
863
- * For multi-select elements, or to check values of multiple elements with the same name, an
864
- * array may be passed:
865
- *
866
- * ``` php
867
- * <?php
868
- * $I->seeInFormFields('.form-class', [
869
- * 'multiselect' => [
870
- * 'value1',
871
- * 'value2',
872
- * ],
873
- * 'checkbox[]' => [
874
- * 'a checked value',
875
- * 'another checked value',
876
- * ],
877
- * ]);
878
- * ?>
879
- * ```
880
- *
881
- * Additionally, checkbox values can be checked with a boolean.
882
- *
883
- * ``` php
884
- * <?php
885
- * $I->seeInFormFields('#form-id', [
886
- * 'checkbox1' => true, // passes if checked
887
- * 'checkbox2' => false, // passes if unchecked
888
- * ]);
889
- * ?>
890
- * ```
891
- *
892
- * Pair this with submitForm for quick testing magic.
893
- *
894
- * ``` php
895
- * <?php
896
- * $form = [
897
- * 'field1' => 'value',
898
- * 'field2' => 'another value',
899
- * 'checkbox1' => true,
900
- * // ...
901
- * ];
902
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
903
- * // $I->amOnPage('/path/to/form-page') may be needed
904
- * $I->seeInFormFields('//form[@id=my-form]', $form);
905
- * ?>
906
- * ```
907
- *
908
- * @param $formSelector
909
- * @param $params
910
- * Conditional Assertion: Test won't be stopped on fail
911
- * @see \Codeception\Lib\InnerBrowser::seeInFormFields()
912
- */
913
- public function canSeeInFormFields($formSelector, $params) {
914
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeInFormFields', func_get_args()));
915
- }
916
- /**
917
- * [!] Method is generated. Documentation taken from corresponding module.
918
- *
919
- * Checks if the array of form parameters (name => value) are set on the form matched with the
920
- * passed selector.
921
- *
922
- * ``` php
923
- * <?php
924
- * $I->seeInFormFields('form[name=myform]', [
925
- * 'input1' => 'value',
926
- * 'input2' => 'other value',
927
- * ]);
928
- * ?>
929
- * ```
930
- *
931
- * For multi-select elements, or to check values of multiple elements with the same name, an
932
- * array may be passed:
933
- *
934
- * ``` php
935
- * <?php
936
- * $I->seeInFormFields('.form-class', [
937
- * 'multiselect' => [
938
- * 'value1',
939
- * 'value2',
940
- * ],
941
- * 'checkbox[]' => [
942
- * 'a checked value',
943
- * 'another checked value',
944
- * ],
945
- * ]);
946
- * ?>
947
- * ```
948
- *
949
- * Additionally, checkbox values can be checked with a boolean.
950
- *
951
- * ``` php
952
- * <?php
953
- * $I->seeInFormFields('#form-id', [
954
- * 'checkbox1' => true, // passes if checked
955
- * 'checkbox2' => false, // passes if unchecked
956
- * ]);
957
- * ?>
958
- * ```
959
- *
960
- * Pair this with submitForm for quick testing magic.
961
- *
962
- * ``` php
963
- * <?php
964
- * $form = [
965
- * 'field1' => 'value',
966
- * 'field2' => 'another value',
967
- * 'checkbox1' => true,
968
- * // ...
969
- * ];
970
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
971
- * // $I->amOnPage('/path/to/form-page') may be needed
972
- * $I->seeInFormFields('//form[@id=my-form]', $form);
973
- * ?>
974
- * ```
975
- *
976
- * @param $formSelector
977
- * @param $params
978
- * @see \Codeception\Lib\InnerBrowser::seeInFormFields()
979
- */
980
- public function seeInFormFields($formSelector, $params) {
981
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeInFormFields', func_get_args()));
982
- }
983
-
984
-
985
- /**
986
- * [!] Method is generated. Documentation taken from corresponding module.
987
- *
988
- * Checks if the array of form parameters (name => value) are not set on the form matched with
989
- * the passed selector.
990
- *
991
- * ``` php
992
- * <?php
993
- * $I->dontSeeInFormFields('form[name=myform]', [
994
- * 'input1' => 'non-existent value',
995
- * 'input2' => 'other non-existent value',
996
- * ]);
997
- * ?>
998
- * ```
999
- *
1000
- * To check that an element hasn't been assigned any one of many values, an array can be passed
1001
- * as the value:
1002
- *
1003
- * ``` php
1004
- * <?php
1005
- * $I->dontSeeInFormFields('.form-class', [
1006
- * 'fieldName' => [
1007
- * 'This value shouldn\'t be set',
1008
- * 'And this value shouldn\'t be set',
1009
- * ],
1010
- * ]);
1011
- * ?>
1012
- * ```
1013
- *
1014
- * Additionally, checkbox values can be checked with a boolean.
1015
- *
1016
- * ``` php
1017
- * <?php
1018
- * $I->dontSeeInFormFields('#form-id', [
1019
- * 'checkbox1' => true, // fails if checked
1020
- * 'checkbox2' => false, // fails if unchecked
1021
- * ]);
1022
- * ?>
1023
- * ```
1024
- *
1025
- * @param $formSelector
1026
- * @param $params
1027
- * Conditional Assertion: Test won't be stopped on fail
1028
- * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields()
1029
- */
1030
- public function cantSeeInFormFields($formSelector, $params) {
1031
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeInFormFields', func_get_args()));
1032
- }
1033
- /**
1034
- * [!] Method is generated. Documentation taken from corresponding module.
1035
- *
1036
- * Checks if the array of form parameters (name => value) are not set on the form matched with
1037
- * the passed selector.
1038
- *
1039
- * ``` php
1040
- * <?php
1041
- * $I->dontSeeInFormFields('form[name=myform]', [
1042
- * 'input1' => 'non-existent value',
1043
- * 'input2' => 'other non-existent value',
1044
- * ]);
1045
- * ?>
1046
- * ```
1047
- *
1048
- * To check that an element hasn't been assigned any one of many values, an array can be passed
1049
- * as the value:
1050
- *
1051
- * ``` php
1052
- * <?php
1053
- * $I->dontSeeInFormFields('.form-class', [
1054
- * 'fieldName' => [
1055
- * 'This value shouldn\'t be set',
1056
- * 'And this value shouldn\'t be set',
1057
- * ],
1058
- * ]);
1059
- * ?>
1060
- * ```
1061
- *
1062
- * Additionally, checkbox values can be checked with a boolean.
1063
- *
1064
- * ``` php
1065
- * <?php
1066
- * $I->dontSeeInFormFields('#form-id', [
1067
- * 'checkbox1' => true, // fails if checked
1068
- * 'checkbox2' => false, // fails if unchecked
1069
- * ]);
1070
- * ?>
1071
- * ```
1072
- *
1073
- * @param $formSelector
1074
- * @param $params
1075
- * @see \Codeception\Lib\InnerBrowser::dontSeeInFormFields()
1076
- */
1077
- public function dontSeeInFormFields($formSelector, $params) {
1078
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeInFormFields', func_get_args()));
1079
- }
1080
-
1081
-
1082
- /**
1083
- * [!] Method is generated. Documentation taken from corresponding module.
1084
- *
1085
- * Submits the given form on the page, optionally with the given form values.
1086
- * Give the form fields values as an array.
1087
- *
1088
- * Skipped fields will be filled by their values from the page.
1089
- * You don't need to click the 'Submit' button afterwards.
1090
- * This command itself triggers the request to form's action.
1091
- *
1092
- * You can optionally specify what button's value to include
1093
- * in the request with the last parameter as an alternative to
1094
- * explicitly setting its value in the second parameter, as
1095
- * button values are not otherwise included in the request.
1096
- *
1097
- * Examples:
1098
- *
1099
- * ``` php
1100
- * <?php
1101
- * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456'));
1102
- * // or
1103
- * $I->submitForm('#login', array('login' => 'davert', 'password' => '123456'), 'submitButtonName');
1104
- *
1105
- * ```
1106
- *
1107
- * For example, given this sample "Sign Up" form:
1108
- *
1109
- * ``` html
1110
- * <form action="/sign_up">
1111
- * Login: <input type="text" name="user[login]" /><br/>
1112
- * Password: <input type="password" name="user[password]" /><br/>
1113
- * Do you agree to out terms? <input type="checkbox" name="user[agree]" /><br/>
1114
- * Select pricing plan <select name="plan"><option value="1">Free</option><option value="2" selected="selected">Paid</option></select>
1115
- * <input type="submit" name="submitButton" value="Submit" />
1116
- * </form>
1117
- * ```
1118
- *
1119
- * You could write the following to submit it:
1120
- *
1121
- * ``` php
1122
- * <?php
1123
- * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true)), 'submitButton');
1124
- *
1125
- * ```
1126
- * Note that "2" will be the submitted value for the "plan" field, as it is the selected option.
1127
- *
1128
- * You can also emulate a JavaScript submission by not specifying any buttons in the third parameter to submitForm.
1129
- *
1130
- * ```php
1131
- * <?php
1132
- * $I->submitForm('#userForm', array('user' => array('login' => 'Davert', 'password' => '123456', 'agree' => true)));
1133
- *
1134
- * ```
1135
- *
1136
- * Pair this with seeInFormFields for quick testing magic.
1137
- *
1138
- * ``` php
1139
- * <?php
1140
- * $form = [
1141
- * 'field1' => 'value',
1142
- * 'field2' => 'another value',
1143
- * 'checkbox1' => true,
1144
- * // ...
1145
- * ];
1146
- * $I->submitForm('//form[@id=my-form]', $form, 'submitButton');
1147
- * // $I->amOnPage('/path/to/form-page') may be needed
1148
- * $I->seeInFormFields('//form[@id=my-form]', $form);
1149
- * ?>
1150
- * ```
1151
- *
1152
- * Parameter values can be set to arrays for multiple input fields
1153
- * of the same name, or multi-select combo boxes. For checkboxes,
1154
- * either the string value can be used, or boolean values which will
1155
- * be replaced by the checkbox's value in the DOM.
1156
- *
1157
- * ``` php
1158
- * <?php
1159
- * $I->submitForm('#my-form', [
1160
- * 'field1' => 'value',
1161
- * 'checkbox' => [
1162
- * 'value of first checkbox',
1163
- * 'value of second checkbox,
1164
- * ],
1165
- * 'otherCheckboxes' => [
1166
- * true,
1167
- * false,
1168
- * false
1169
- * ],
1170
- * 'multiselect' => [
1171
- * 'first option value',
1172
- * 'second option value'
1173
- * ]
1174
- * ]);
1175
- * ?>
1176
- * ```
1177
- *
1178
- * Mixing string and boolean values for a checkbox's value is not
1179
- * supported and may produce unexpected results.
1180
- *
1181
- * @param $selector
1182
- * @param $params
1183
- * @param $button
1184
- * @see \Codeception\Lib\InnerBrowser::submitForm()
1185
- */
1186
- public function submitForm($selector, $params, $button = null) {
1187
- return $this->scenario->runStep(new \Codeception\Step\Action('submitForm', func_get_args()));
1188
- }
1189
-
1190
-
1191
- /**
1192
- * [!] Method is generated. Documentation taken from corresponding module.
1193
- *
1194
- * Fills a text field or textarea with the given string.
1195
- *
1196
- * ``` php
1197
- * <?php
1198
- * $I->fillField("//input[@type='text']", "Hello World!");
1199
- * $I->fillField(['name' => 'email'], 'jon@mail.com');
1200
- * ?>
1201
- * ```
1202
- *
1203
- * @param $field
1204
- * @param $value
1205
- * @see \Codeception\Lib\InnerBrowser::fillField()
1206
- */
1207
- public function fillField($field, $value) {
1208
- return $this->scenario->runStep(new \Codeception\Step\Action('fillField', func_get_args()));
1209
- }
1210
-
1211
-
1212
- /**
1213
- * [!] Method is generated. Documentation taken from corresponding module.
1214
- *
1215
- * Selects an option in a select tag or in radio button group.
1216
- *
1217
- * ``` php
1218
- * <?php
1219
- * $I->selectOption('form select[name=account]', 'Premium');
1220
- * $I->selectOption('form input[name=payment]', 'Monthly');
1221
- * $I->selectOption('//form/select[@name=account]', 'Monthly');
1222
- * ?>
1223
- * ```
1224
- *
1225
- * Provide an array for the second argument to select multiple options:
1226
- *
1227
- * ``` php
1228
- * <?php
1229
- * $I->selectOption('Which OS do you use?', array('Windows','Linux'));
1230
- * ?>
1231
- * ```
1232
- *
1233
- * @param $select
1234
- * @param $option
1235
- * @see \Codeception\Lib\InnerBrowser::selectOption()
1236
- */
1237
- public function selectOption($select, $option) {
1238
- return $this->scenario->runStep(new \Codeception\Step\Action('selectOption', func_get_args()));
1239
- }
1240
-
1241
-
1242
- /**
1243
- * [!] Method is generated. Documentation taken from corresponding module.
1244
- *
1245
- * Ticks a checkbox. For radio buttons, use the `selectOption` method instead.
1246
- *
1247
- * ``` php
1248
- * <?php
1249
- * $I->checkOption('#agree');
1250
- * ?>
1251
- * ```
1252
- *
1253
- * @param $option
1254
- * @see \Codeception\Lib\InnerBrowser::checkOption()
1255
- */
1256
- public function checkOption($option) {
1257
- return $this->scenario->runStep(new \Codeception\Step\Action('checkOption', func_get_args()));
1258
- }
1259
-
1260
-
1261
- /**
1262
- * [!] Method is generated. Documentation taken from corresponding module.
1263
- *
1264
- * Unticks a checkbox.
1265
- *
1266
- * ``` php
1267
- * <?php
1268
- * $I->uncheckOption('#notify');
1269
- * ?>
1270
- * ```
1271
- *
1272
- * @param $option
1273
- * @see \Codeception\Lib\InnerBrowser::uncheckOption()
1274
- */
1275
- public function uncheckOption($option) {
1276
- return $this->scenario->runStep(new \Codeception\Step\Action('uncheckOption', func_get_args()));
1277
- }
1278
-
1279
-
1280
- /**
1281
- * [!] Method is generated. Documentation taken from corresponding module.
1282
- *
1283
- * Attaches a file relative to the Codeception data directory to the given file upload field.
1284
- *
1285
- * ``` php
1286
- * <?php
1287
- * // file is stored in 'tests/_data/prices.xls'
1288
- * $I->attachFile('input[@type="file"]', 'prices.xls');
1289
- * ?>
1290
- * ```
1291
- *
1292
- * @param $field
1293
- * @param $filename
1294
- * @see \Codeception\Lib\InnerBrowser::attachFile()
1295
- */
1296
- public function attachFile($field, $filename) {
1297
- return $this->scenario->runStep(new \Codeception\Step\Action('attachFile', func_get_args()));
1298
- }
1299
-
1300
-
1301
- /**
1302
- * [!] Method is generated. Documentation taken from corresponding module.
1303
- *
1304
- * If your page triggers an ajax request, you can perform it manually.
1305
- * This action sends a GET ajax request with specified params.
1306
- *
1307
- * See ->sendAjaxPostRequest for examples.
1308
- *
1309
- * @param $uri
1310
- * @param $params
1311
- * @see \Codeception\Lib\InnerBrowser::sendAjaxGetRequest()
1312
- */
1313
- public function sendAjaxGetRequest($uri, $params = null) {
1314
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxGetRequest', func_get_args()));
1315
- }
1316
-
1317
-
1318
- /**
1319
- * [!] Method is generated. Documentation taken from corresponding module.
1320
- *
1321
- * If your page triggers an ajax request, you can perform it manually.
1322
- * This action sends a POST ajax request with specified params.
1323
- * Additional params can be passed as array.
1324
- *
1325
- * Example:
1326
- *
1327
- * Imagine that by clicking checkbox you trigger ajax request which updates user settings.
1328
- * We emulate that click by running this ajax request manually.
1329
- *
1330
- * ``` php
1331
- * <?php
1332
- * $I->sendAjaxPostRequest('/updateSettings', array('notifications' => true)); // POST
1333
- * $I->sendAjaxGetRequest('/updateSettings', array('notifications' => true)); // GET
1334
- *
1335
- * ```
1336
- *
1337
- * @param $uri
1338
- * @param $params
1339
- * @see \Codeception\Lib\InnerBrowser::sendAjaxPostRequest()
1340
- */
1341
- public function sendAjaxPostRequest($uri, $params = null) {
1342
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxPostRequest', func_get_args()));
1343
- }
1344
-
1345
-
1346
- /**
1347
- * [!] Method is generated. Documentation taken from corresponding module.
1348
- *
1349
- * If your page triggers an ajax request, you can perform it manually.
1350
- * This action sends an ajax request with specified method and params.
1351
- *
1352
- * Example:
1353
- *
1354
- * You need to perform an ajax request specifying the HTTP method.
1355
- *
1356
- * ``` php
1357
- * <?php
1358
- * $I->sendAjaxRequest('PUT', '/posts/7', array('title' => 'new title'));
1359
- *
1360
- * ```
1361
- *
1362
- * @param $method
1363
- * @param $uri
1364
- * @param $params
1365
- * @see \Codeception\Lib\InnerBrowser::sendAjaxRequest()
1366
- */
1367
- public function sendAjaxRequest($method, $uri, $params = null) {
1368
- return $this->scenario->runStep(new \Codeception\Step\Action('sendAjaxRequest', func_get_args()));
1369
- }
1370
-
1371
-
1372
- /**
1373
- * [!] Method is generated. Documentation taken from corresponding module.
1374
- *
1375
- * Finds and returns the text contents of the given element.
1376
- * If a fuzzy locator is used, the element is found using CSS, XPath, and by matching the full page source by regular expression.
1377
- *
1378
- * ``` php
1379
- * <?php
1380
- * $heading = $I->grabTextFrom('h1');
1381
- * $heading = $I->grabTextFrom('descendant-or-self::h1');
1382
- * $value = $I->grabTextFrom('~<input value=(.*?)]~sgi'); // match with a regex
1383
- * ?>
1384
- * ```
1385
- *
1386
- * @param $cssOrXPathOrRegex
1387
- *
1388
- * @return mixed
1389
- * @see \Codeception\Lib\InnerBrowser::grabTextFrom()
1390
- */
1391
- public function grabTextFrom($cssOrXPathOrRegex) {
1392
- return $this->scenario->runStep(new \Codeception\Step\Action('grabTextFrom', func_get_args()));
1393
- }
1394
-
1395
-
1396
- /**
1397
- * [!] Method is generated. Documentation taken from corresponding module.
1398
- *
1399
- * Grabs the value of the given attribute value from the given element.
1400
- * Fails if element is not found.
1401
- *
1402
- * ``` php
1403
- * <?php
1404
- * $I->grabAttributeFrom('#tooltip', 'title');
1405
- * ?>
1406
- * ```
1407
- *
1408
- *
1409
- * @param $cssOrXpath
1410
- * @param $attribute
1411
- * @internal param $element
1412
- * @return mixed
1413
- * @see \Codeception\Lib\InnerBrowser::grabAttributeFrom()
1414
- */
1415
- public function grabAttributeFrom($cssOrXpath, $attribute) {
1416
- return $this->scenario->runStep(new \Codeception\Step\Action('grabAttributeFrom', func_get_args()));
1417
- }
1418
-
1419
-
1420
- /**
1421
- * [!] Method is generated. Documentation taken from corresponding module.
1422
- *
1423
- * @param $field
1424
- *
1425
- * @return array|mixed|null|string
1426
- * @see \Codeception\Lib\InnerBrowser::grabValueFrom()
1427
- */
1428
- public function grabValueFrom($field) {
1429
- return $this->scenario->runStep(new \Codeception\Step\Action('grabValueFrom', func_get_args()));
1430
- }
1431
-
1432
-
1433
- /**
1434
- * [!] Method is generated. Documentation taken from corresponding module.
1435
- *
1436
- * Sets a cookie with the given name and value.
1437
- * You can set additional cookie params like `domain`, `path`, `expire`, `secure` in array passed as last argument.
1438
- *
1439
- * ``` php
1440
- * <?php
1441
- * $I->setCookie('PHPSESSID', 'el4ukv0kqbvoirg7nkp4dncpk3');
1442
- * ?>
1443
- * ```
1444
- *
1445
- * @param $name
1446
- * @param $val
1447
- * @param array $params
1448
- * @internal param $cookie
1449
- * @internal param $value
1450
- *
1451
- * @return mixed
1452
- * @see \Codeception\Lib\InnerBrowser::setCookie()
1453
- */
1454
- public function setCookie($name, $val, $params = null) {
1455
- return $this->scenario->runStep(new \Codeception\Step\Action('setCookie', func_get_args()));
1456
- }
1457
-
1458
-
1459
- /**
1460
- * [!] Method is generated. Documentation taken from corresponding module.
1461
- *
1462
- * Grabs a cookie value.
1463
- * You can set additional cookie params like `domain`, `path` in array passed as last argument.
1464
- *
1465
- * @param $cookie
1466
- *
1467
- * @param array $params
1468
- * @return mixed
1469
- * @see \Codeception\Lib\InnerBrowser::grabCookie()
1470
- */
1471
- public function grabCookie($name, $params = null) {
1472
- return $this->scenario->runStep(new \Codeception\Step\Action('grabCookie', func_get_args()));
1473
- }
1474
-
1475
-
1476
- /**
1477
- * [!] Method is generated. Documentation taken from corresponding module.
1478
- *
1479
- * Checks that a cookie with the given name is set.
1480
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1481
- *
1482
- * ``` php
1483
- * <?php
1484
- * $I->seeCookie('PHPSESSID');
1485
- * ?>
1486
- * ```
1487
- *
1488
- * @param $cookie
1489
- * @param array $params
1490
- * @return mixed
1491
- * Conditional Assertion: Test won't be stopped on fail
1492
- * @see \Codeception\Lib\InnerBrowser::seeCookie()
1493
- */
1494
- public function canSeeCookie($name, $params = null) {
1495
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeCookie', func_get_args()));
1496
- }
1497
- /**
1498
- * [!] Method is generated. Documentation taken from corresponding module.
1499
- *
1500
- * Checks that a cookie with the given name is set.
1501
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1502
- *
1503
- * ``` php
1504
- * <?php
1505
- * $I->seeCookie('PHPSESSID');
1506
- * ?>
1507
- * ```
1508
- *
1509
- * @param $cookie
1510
- * @param array $params
1511
- * @return mixed
1512
- * @see \Codeception\Lib\InnerBrowser::seeCookie()
1513
- */
1514
- public function seeCookie($name, $params = null) {
1515
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeCookie', func_get_args()));
1516
- }
1517
-
1518
-
1519
- /**
1520
- * [!] Method is generated. Documentation taken from corresponding module.
1521
- *
1522
- * Checks that there isn't a cookie with the given name.
1523
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1524
- *
1525
- * @param $cookie
1526
- *
1527
- * @param array $params
1528
- * @return mixed
1529
- * Conditional Assertion: Test won't be stopped on fail
1530
- * @see \Codeception\Lib\InnerBrowser::dontSeeCookie()
1531
- */
1532
- public function cantSeeCookie($name, $params = null) {
1533
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeCookie', func_get_args()));
1534
- }
1535
- /**
1536
- * [!] Method is generated. Documentation taken from corresponding module.
1537
- *
1538
- * Checks that there isn't a cookie with the given name.
1539
- * You can set additional cookie params like `domain`, `path` as array passed in last argument.
1540
- *
1541
- * @param $cookie
1542
- *
1543
- * @param array $params
1544
- * @return mixed
1545
- * @see \Codeception\Lib\InnerBrowser::dontSeeCookie()
1546
- */
1547
- public function dontSeeCookie($name, $params = null) {
1548
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeCookie', func_get_args()));
1549
- }
1550
-
1551
-
1552
- /**
1553
- * [!] Method is generated. Documentation taken from corresponding module.
1554
- *
1555
- * Unsets cookie with the given name.
1556
- * You can set additional cookie params like `domain`, `path` in array passed as last argument.
1557
- *
1558
- * @param $cookie
1559
- *
1560
- * @param array $params
1561
- * @return mixed
1562
- * @see \Codeception\Lib\InnerBrowser::resetCookie()
1563
- */
1564
- public function resetCookie($name, $params = null) {
1565
- return $this->scenario->runStep(new \Codeception\Step\Action('resetCookie', func_get_args()));
1566
- }
1567
-
1568
-
1569
- /**
1570
- * [!] Method is generated. Documentation taken from corresponding module.
1571
- *
1572
- * Checks that the given element exists on the page and is visible.
1573
- * You can also specify expected attributes of this element.
1574
- *
1575
- * ``` php
1576
- * <?php
1577
- * $I->seeElement('.error');
1578
- * $I->seeElement('//form/input[1]');
1579
- * $I->seeElement('input', ['name' => 'login']);
1580
- * $I->seeElement('input', ['value' => '123456']);
1581
- *
1582
- * // strict locator in first arg, attributes in second
1583
- * $I->seeElement(['css' => 'form input'], ['name' => 'login']);
1584
- * ?>
1585
- * ```
1586
- *
1587
- * @param $selector
1588
- * @param array $attributes
1589
- * @return
1590
- * Conditional Assertion: Test won't be stopped on fail
1591
- * @see \Codeception\Lib\InnerBrowser::seeElement()
1592
- */
1593
- public function canSeeElement($selector, $attributes = null) {
1594
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeElement', func_get_args()));
1595
- }
1596
- /**
1597
- * [!] Method is generated. Documentation taken from corresponding module.
1598
- *
1599
- * Checks that the given element exists on the page and is visible.
1600
- * You can also specify expected attributes of this element.
1601
- *
1602
- * ``` php
1603
- * <?php
1604
- * $I->seeElement('.error');
1605
- * $I->seeElement('//form/input[1]');
1606
- * $I->seeElement('input', ['name' => 'login']);
1607
- * $I->seeElement('input', ['value' => '123456']);
1608
- *
1609
- * // strict locator in first arg, attributes in second
1610
- * $I->seeElement(['css' => 'form input'], ['name' => 'login']);
1611
- * ?>
1612
- * ```
1613
- *
1614
- * @param $selector
1615
- * @param array $attributes
1616
- * @return
1617
- * @see \Codeception\Lib\InnerBrowser::seeElement()
1618
- */
1619
- public function seeElement($selector, $attributes = null) {
1620
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeElement', func_get_args()));
1621
- }
1622
-
1623
-
1624
- /**
1625
- * [!] Method is generated. Documentation taken from corresponding module.
1626
- *
1627
- * Checks that the given element is invisible or not present on the page.
1628
- * You can also specify expected attributes of this element.
1629
- *
1630
- * ``` php
1631
- * <?php
1632
- * $I->dontSeeElement('.error');
1633
- * $I->dontSeeElement('//form/input[1]');
1634
- * $I->dontSeeElement('input', ['name' => 'login']);
1635
- * $I->dontSeeElement('input', ['value' => '123456']);
1636
- * ?>
1637
- * ```
1638
- *
1639
- * @param $selector
1640
- * @param array $attributes
1641
- * Conditional Assertion: Test won't be stopped on fail
1642
- * @see \Codeception\Lib\InnerBrowser::dontSeeElement()
1643
- */
1644
- public function cantSeeElement($selector, $attributes = null) {
1645
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('dontSeeElement', func_get_args()));
1646
- }
1647
- /**
1648
- * [!] Method is generated. Documentation taken from corresponding module.
1649
- *
1650
- * Checks that the given element is invisible or not present on the page.
1651
- * You can also specify expected attributes of this element.
1652
- *
1653
- * ``` php
1654
- * <?php
1655
- * $I->dontSeeElement('.error');
1656
- * $I->dontSeeElement('//form/input[1]');
1657
- * $I->dontSeeElement('input', ['name' => 'login']);
1658
- * $I->dontSeeElement('input', ['value' => '123456']);
1659
- * ?>
1660
- * ```
1661
- *
1662
- * @param $selector
1663
- * @param array $attributes
1664
- * @see \Codeception\Lib\InnerBrowser::dontSeeElement()
1665
- */
1666
- public function dontSeeElement($selector, $attributes = null) {
1667
- return $this->scenario->runStep(new \Codeception\Step\Assertion('dontSeeElement', func_get_args()));
1668
- }
1669
-
1670
-
1671
- /**
1672
- * [!] Method is generated. Documentation taken from corresponding module.
1673
- *
1674
- * Checks that there are a certain number of elements matched by the given locator on the page.
1675
- *
1676
- * ``` php
1677
- * <?php
1678
- * $I->seeNumberOfElements('tr', 10);
1679
- * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements
1680
- * ?>
1681
- * ```
1682
- * @param $selector
1683
- * @param mixed $expected:
1684
- * - string: strict number
1685
- * - array: range of numbers [0,10]
1686
- * Conditional Assertion: Test won't be stopped on fail
1687
- * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements()
1688
- */
1689
- public function canSeeNumberOfElements($selector, $expected) {
1690
- return $this->scenario->runStep(new \Codeception\Step\ConditionalAssertion('seeNumberOfElements', func_get_args()));
1691
- }
1692
- /**
1693
- * [!] Method is generated. Documentation taken from corresponding module.
1694
- *
1695
- * Checks that there are a certain number of elements matched by the given locator on the page.
1696
- *
1697
- * ``` php
1698
- * <?php
1699
- * $I->seeNumberOfElements('tr', 10);
1700
- * $I->seeNumberOfElements('tr', [0,10]); //between 0 and 10 elements
1701
- * ?>
1702
- * ```
1703
- * @param $selector
1704
- * @param mixed $expected:
1705
- * - string: strict number
1706
- * - array: range of numbers [0,10]
1707
- * @see \Codeception\Lib\InnerBrowser::seeNumberOfElements()
1708
- */
1709
- public function seeNumberOfElements($selector, $expected) {
1710
- return $this->scenario->runStep(new \Codeception\Step\Assertion('seeNumberOfElements', func_get_args()));
1711
- }
1712
-
1713
-
1714
- /**
1715
- * [!] Method is generated. Documentation taken from corresponding module.
1716
- *
1717
- * Checks that the given opti