WP Rollback - Version 1.0

Version Description

This is the first version of this plugin. It is a tool for your convenience. Rollback at your own risk!

Download this release

Release Info

Developer dlocc
Plugin Icon 128x128 WP Rollback
Version 1.0
Comparing to
See all releases

Version 1.0

assets/css/magnific-popup.css ADDED
@@ -0,0 +1,484 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Magnific Popup CSS */
2
+ .white-popup {
3
+ position: relative;
4
+ background: #FFF;
5
+ padding: 20px;
6
+ width: auto;
7
+ max-width: 500px;
8
+ margin: 20px auto;
9
+ }
10
+
11
+ .mfp-bg {
12
+ top: 0;
13
+ left: 0;
14
+ width: 100%;
15
+ height: 100%;
16
+ z-index: 9998;
17
+ overflow: hidden;
18
+ position: fixed;
19
+ background: #0b0b0b;
20
+ opacity: 0.8;
21
+ filter: alpha(opacity=80);
22
+ }
23
+
24
+ .mfp-wrap {
25
+ top: 0;
26
+ left: 0;
27
+ width: 100%;
28
+ height: 100%;
29
+ z-index: 9999;
30
+ position: fixed;
31
+ outline: none !important;
32
+ -webkit-backface-visibility: hidden;
33
+ }
34
+
35
+ .mfp-container {
36
+ text-align: center;
37
+ position: absolute;
38
+ width: 100%;
39
+ height: 100%;
40
+ left: 0;
41
+ top: 0;
42
+ padding: 0 8px;
43
+ -webkit-box-sizing: border-box;
44
+ -moz-box-sizing: border-box;
45
+ box-sizing: border-box;
46
+ }
47
+
48
+ .mfp-container:before {
49
+ content: '';
50
+ display: inline-block;
51
+ height: 100%;
52
+ vertical-align: middle;
53
+ }
54
+
55
+ .mfp-align-top .mfp-container:before {
56
+ display: none;
57
+ }
58
+
59
+ .mfp-content {
60
+ position: relative;
61
+ display: inline-block;
62
+ vertical-align: middle;
63
+ margin: 0 auto;
64
+ text-align: left;
65
+ z-index: 1045;
66
+ }
67
+
68
+ .mfp-inline-holder .mfp-content, .mfp-ajax-holder .mfp-content {
69
+ width: 100%;
70
+ cursor: auto;
71
+ }
72
+
73
+ .mfp-ajax-cur {
74
+ cursor: progress;
75
+ }
76
+
77
+ .mfp-zoom-out-cur, .mfp-zoom-out-cur .mfp-image-holder .mfp-close {
78
+ cursor: -moz-zoom-out;
79
+ cursor: -webkit-zoom-out;
80
+ cursor: zoom-out;
81
+ }
82
+
83
+ .mfp-zoom {
84
+ cursor: pointer;
85
+ cursor: -webkit-zoom-in;
86
+ cursor: -moz-zoom-in;
87
+ cursor: zoom-in;
88
+ }
89
+
90
+ .mfp-auto-cursor .mfp-content {
91
+ cursor: auto;
92
+ }
93
+
94
+ .mfp-close, .mfp-arrow, .mfp-preloader, .mfp-counter {
95
+ -webkit-user-select: none;
96
+ -moz-user-select: none;
97
+ user-select: none;
98
+ }
99
+
100
+ .mfp-loading.mfp-figure {
101
+ display: none;
102
+ }
103
+
104
+ .mfp-hide {
105
+ display: none !important;
106
+ }
107
+
108
+ .mfp-preloader {
109
+ color: #CCC;
110
+ position: absolute;
111
+ top: 50%;
112
+ width: auto;
113
+ text-align: center;
114
+ margin-top: -0.8em;
115
+ left: 8px;
116
+ right: 8px;
117
+ z-index: 1044;
118
+ }
119
+
120
+ .mfp-preloader a {
121
+ color: #CCC;
122
+ }
123
+
124
+ .mfp-preloader a:hover {
125
+ color: #FFF;
126
+ }
127
+
128
+ .mfp-s-ready .mfp-preloader {
129
+ display: none;
130
+ }
131
+
132
+ .mfp-s-error .mfp-content {
133
+ display: none;
134
+ }
135
+
136
+ button.mfp-close, button.mfp-arrow {
137
+ overflow: visible;
138
+ cursor: pointer;
139
+ background: transparent;
140
+ border: 0;
141
+ -webkit-appearance: none;
142
+ display: block;
143
+ outline: none;
144
+ padding: 0;
145
+ z-index: 1046;
146
+ -webkit-box-shadow: none;
147
+ box-shadow: none;
148
+ }
149
+
150
+ button::-moz-focus-inner {
151
+ padding: 0;
152
+ border: 0;
153
+ }
154
+
155
+ .mfp-close {
156
+ width: 44px;
157
+ height: 44px;
158
+ line-height: 44px;
159
+ position: absolute;
160
+ right: 0;
161
+ top: 0;
162
+ text-decoration: none;
163
+ text-align: center;
164
+ opacity: 0.65;
165
+ filter: alpha(opacity=65);
166
+ padding: 0 0 18px 10px;
167
+ color: #FFF;
168
+ font-style: normal;
169
+ font-size: 28px;
170
+ font-family: Arial, Baskerville, monospace;
171
+ }
172
+
173
+ .mfp-close:hover, .mfp-close:focus {
174
+ opacity: 1;
175
+ filter: alpha(opacity=100);
176
+ }
177
+
178
+ .mfp-close:active {
179
+ top: 1px;
180
+ }
181
+
182
+ .mfp-close-btn-in .mfp-close {
183
+ color: #333;
184
+ }
185
+
186
+ .mfp-image-holder .mfp-close, .mfp-iframe-holder .mfp-close {
187
+ color: #FFF;
188
+ right: -6px;
189
+ text-align: right;
190
+ padding-right: 6px;
191
+ width: 100%;
192
+ }
193
+
194
+ .mfp-counter {
195
+ position: absolute;
196
+ top: 0;
197
+ right: 0;
198
+ color: #CCC;
199
+ font-size: 12px;
200
+ line-height: 18px;
201
+ white-space: nowrap;
202
+ }
203
+
204
+ .mfp-arrow {
205
+ position: absolute;
206
+ opacity: 0.65;
207
+ filter: alpha(opacity=65);
208
+ margin: 0;
209
+ top: 50%;
210
+ margin-top: -55px;
211
+ padding: 0;
212
+ width: 90px;
213
+ height: 110px;
214
+ -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
215
+ }
216
+
217
+ .mfp-arrow:active {
218
+ margin-top: -54px;
219
+ }
220
+
221
+ .mfp-arrow:hover, .mfp-arrow:focus {
222
+ opacity: 1;
223
+ filter: alpha(opacity=100);
224
+ }
225
+
226
+ .mfp-arrow:before, .mfp-arrow:after, .mfp-arrow .mfp-b, .mfp-arrow .mfp-a {
227
+ content: '';
228
+ display: block;
229
+ width: 0;
230
+ height: 0;
231
+ position: absolute;
232
+ left: 0;
233
+ top: 0;
234
+ margin-top: 35px;
235
+ margin-left: 35px;
236
+ border: medium inset transparent;
237
+ }
238
+
239
+ .mfp-arrow:after, .mfp-arrow .mfp-a {
240
+ border-top-width: 13px;
241
+ border-bottom-width: 13px;
242
+ top: 8px;
243
+ }
244
+
245
+ .mfp-arrow:before, .mfp-arrow .mfp-b {
246
+ border-top-width: 21px;
247
+ border-bottom-width: 21px;
248
+ opacity: 0.7;
249
+ }
250
+
251
+ .mfp-arrow-left {
252
+ left: 0;
253
+ }
254
+
255
+ .mfp-arrow-left:after, .mfp-arrow-left .mfp-a {
256
+ border-right: 17px solid #FFF;
257
+ margin-left: 31px;
258
+ }
259
+
260
+ .mfp-arrow-left:before, .mfp-arrow-left .mfp-b {
261
+ margin-left: 25px;
262
+ border-right: 27px solid #3F3F3F;
263
+ }
264
+
265
+ .mfp-arrow-right {
266
+ right: 0;
267
+ }
268
+
269
+ .mfp-arrow-right:after, .mfp-arrow-right .mfp-a {
270
+ border-left: 17px solid #FFF;
271
+ margin-left: 39px;
272
+ }
273
+
274
+ .mfp-arrow-right:before, .mfp-arrow-right .mfp-b {
275
+ border-left: 27px solid #3F3F3F;
276
+ }
277
+
278
+ .mfp-iframe-holder {
279
+ padding-top: 40px;
280
+ padding-bottom: 40px;
281
+ }
282
+
283
+ .mfp-iframe-holder .mfp-content {
284
+ line-height: 0;
285
+ width: 100%;
286
+ max-width: 900px;
287
+ }
288
+
289
+ .mfp-iframe-holder .mfp-close {
290
+ top: -40px;
291
+ }
292
+
293
+ .mfp-iframe-scaler {
294
+ width: 100%;
295
+ height: 0;
296
+ overflow: hidden;
297
+ padding-top: 56.25%;
298
+ }
299
+
300
+ .mfp-iframe-scaler iframe {
301
+ position: absolute;
302
+ display: block;
303
+ top: 0;
304
+ left: 0;
305
+ width: 100%;
306
+ height: 100%;
307
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
308
+ background: #000;
309
+ }
310
+
311
+ /* Main image in popup */
312
+ img.mfp-img {
313
+ width: auto;
314
+ max-width: 100%;
315
+ height: auto;
316
+ display: block;
317
+ line-height: 0;
318
+ -webkit-box-sizing: border-box;
319
+ -moz-box-sizing: border-box;
320
+ box-sizing: border-box;
321
+ padding: 40px 0 40px;
322
+ margin: 0 auto;
323
+ }
324
+
325
+ /* The shadow behind the image */
326
+ .mfp-figure {
327
+ line-height: 0;
328
+ }
329
+
330
+ .mfp-figure:after {
331
+ content: '';
332
+ position: absolute;
333
+ left: 0;
334
+ top: 40px;
335
+ bottom: 40px;
336
+ display: block;
337
+ right: 0;
338
+ width: auto;
339
+ height: auto;
340
+ z-index: -1;
341
+ box-shadow: 0 0 8px rgba(0, 0, 0, 0.6);
342
+ background: #444;
343
+ }
344
+
345
+ .mfp-figure small {
346
+ color: #BDBDBD;
347
+ display: block;
348
+ font-size: 12px;
349
+ line-height: 14px;
350
+ }
351
+
352
+ .mfp-figure figure {
353
+ margin: 0;
354
+ }
355
+
356
+ .mfp-bottom-bar {
357
+ margin-top: -36px;
358
+ position: absolute;
359
+ top: 100%;
360
+ left: 0;
361
+ width: 100%;
362
+ cursor: auto;
363
+ }
364
+
365
+ .mfp-title {
366
+ text-align: left;
367
+ line-height: 18px;
368
+ color: #F3F3F3;
369
+ word-wrap: break-word;
370
+ padding-right: 36px;
371
+ }
372
+
373
+ .mfp-image-holder .mfp-content {
374
+ max-width: 100%;
375
+ }
376
+
377
+ .mfp-gallery .mfp-image-holder .mfp-figure {
378
+ cursor: pointer;
379
+ }
380
+
381
+ @media screen and (max-width: 800px) and (orientation: landscape), screen and (max-height: 300px) {
382
+ /**
383
+ * Remove all paddings around the image on small screen
384
+ */
385
+ .mfp-img-mobile .mfp-image-holder {
386
+ padding-left: 0;
387
+ padding-right: 0;
388
+ }
389
+
390
+ .mfp-img-mobile img.mfp-img {
391
+ padding: 0;
392
+ }
393
+
394
+ .mfp-img-mobile .mfp-figure:after {
395
+ top: 0;
396
+ bottom: 0;
397
+ }
398
+
399
+ .mfp-img-mobile .mfp-figure small {
400
+ display: inline;
401
+ margin-left: 5px;
402
+ }
403
+
404
+ .mfp-img-mobile .mfp-bottom-bar {
405
+ background: rgba(0, 0, 0, 0.6);
406
+ bottom: 0;
407
+ margin: 0;
408
+ top: auto;
409
+ padding: 3px 5px;
410
+ position: fixed;
411
+ -webkit-box-sizing: border-box;
412
+ -moz-box-sizing: border-box;
413
+ box-sizing: border-box;
414
+ }
415
+
416
+ .mfp-img-mobile .mfp-bottom-bar:empty {
417
+ padding: 0;
418
+ }
419
+
420
+ .mfp-img-mobile .mfp-counter {
421
+ right: 5px;
422
+ top: 3px;
423
+ }
424
+
425
+ .mfp-img-mobile .mfp-close {
426
+ top: 0;
427
+ right: 0;
428
+ width: 35px;
429
+ height: 35px;
430
+ line-height: 35px;
431
+ background: rgba(0, 0, 0, 0.6);
432
+ position: fixed;
433
+ text-align: center;
434
+ padding: 0;
435
+ }
436
+ }
437
+
438
+ @media all and (max-width: 900px) {
439
+ .mfp-arrow {
440
+ -webkit-transform: scale(0.75);
441
+ transform: scale(0.75);
442
+ }
443
+
444
+ .mfp-arrow-left {
445
+ -webkit-transform-origin: 0;
446
+ transform-origin: 0;
447
+ }
448
+
449
+ .mfp-arrow-right {
450
+ -webkit-transform-origin: 100%;
451
+ transform-origin: 100%;
452
+ }
453
+
454
+ .mfp-container {
455
+ padding-left: 6px;
456
+ padding-right: 6px;
457
+ }
458
+ }
459
+
460
+ .mfp-ie7 .mfp-img {
461
+ padding: 0;
462
+ }
463
+
464
+ .mfp-ie7 .mfp-bottom-bar {
465
+ width: 600px;
466
+ left: 50%;
467
+ margin-left: -300px;
468
+ margin-top: 5px;
469
+ padding-bottom: 5px;
470
+ }
471
+
472
+ .mfp-ie7 .mfp-container {
473
+ padding: 0;
474
+ }
475
+
476
+ .mfp-ie7 .mfp-content {
477
+ padding-top: 44px;
478
+ }
479
+
480
+ .mfp-ie7 .mfp-close {
481
+ top: 0;
482
+ right: 0;
483
+ padding-top: 0;
484
+ }
assets/css/wp-rollback.css ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP Rollback Styles
3
+ */
4
+
5
+ .wpr-content-wrap {
6
+ max-width: 805px;
7
+ }
8
+
9
+ .wpr-content-wrap h2 {
10
+ padding: 0 0 15px 0;
11
+ }
12
+
13
+ .wpr-content-wrap h2 img {
14
+ width: 40px;
15
+ position: relative;
16
+ top: 11px;
17
+ margin-right: 10px;
18
+ }
19
+
20
+ .wpr-content-wrap p {
21
+ font-size: 18px;
22
+ margin-top: 10px;
23
+ }
24
+
25
+ .wpr-content-wrap p span {
26
+ font-style: italic;
27
+ color: #000;
28
+ font-weight: 600;
29
+ }
30
+
31
+ .rollback-form {
32
+
33
+ }
34
+
35
+ .wpr-versions-wrap {
36
+ padding-left: 20px;
37
+ }
38
+
39
+ .rollback-form label {
40
+ display: block;
41
+ padding: 8px 0;
42
+ font-size: 16px;
43
+ }
44
+
45
+ .rollback-form label input {
46
+ margin-right: 5px;
47
+ }
48
+
49
+ .wpr-submit-wrap {
50
+ margin: 25px 0 0;
51
+ border-top: 1px solid #DADADA;
52
+ padding: 20px 0 30px;
53
+ max-width: 765px;
54
+ }
55
+
56
+ .rollback-form .current-version {
57
+ text-transform: lowercase;
58
+ font-size: 12px;
59
+ font-style: italic;
60
+ color: #AEAEAE;
61
+ margin-left: 14px;
62
+ }
63
+
64
+ .wpr-selected {
65
+ font-weight: bold;
66
+ }
67
+
68
+ .wpr-rollback-disabled.magnific-popup.button-primary {
69
+ opacity: 0.6;
70
+ cursor: default;
71
+ background: #B5B5B5;
72
+ border-color: #6B6B6B;
73
+ -webkit-box-shadow: inset 0 1px 0 rgba(249, 251, 252, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 );
74
+ box-shadow: inset 0 1px 0 rgba(249, 251, 252, 0.5), 0 1px 0 rgba( 0, 0, 0, 0.15 );
75
+ color: #777;
76
+ text-decoration: none;
77
+ }
78
+
79
+ .button-primary {
80
+ margin-right: 5px !important;
81
+ }
82
+
83
+ /* Modal */
84
+
85
+ #wpr-modal-confirm {
86
+
87
+ }
88
+
89
+ #wpr-modal-confirm .button-primary {
90
+ margin-right: 5px;
91
+ }
92
+
93
+ .wpr-error {
94
+ background: #fff;
95
+ border-left: 4px solid #FFB800;
96
+ -webkit-box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
97
+ box-shadow: 0 1px 1px 0 rgba(0, 0, 0, 0.1);
98
+ margin: 20px 0;
99
+ padding: 1px 12px;
100
+ }
101
+
102
+ .wpr-error p {
103
+ margin: 0.5em 0;
104
+ padding: 2px;
105
+ }
106
+
107
+ .wpr-rollback-intro {
108
+ font-size: 18px;
109
+ margin-top: 0;
110
+ }
assets/images/wprb-icon-final.svg ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- Generator: Adobe Illustrator 15.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
3
+ <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
4
+ <svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
5
+ width="89.23px" height="100px" viewBox="0 0 89.23 100" enable-background="new 0 0 89.23 100" xml:space="preserve">
6
+ <path fill="#464342" d="M16.196,68.183c-1.398,0.656-1.515,0.518-3.173,1.23c1.898,4.219,4.532,7.676,7.89,10.79
7
+ c0.578-0.766,1.33-1.663,1.987-2.611C20.031,74.901,17.825,71.949,16.196,68.183z"/>
8
+ <path fill="#464342" d="M44.213,21.495c-18.771,0-34.042,15.27-34.042,34.041c0,3.354,0.573,6.886,1.481,9.949
9
+ c0.961-0.427,1.881-0.604,3.26-1.188l-1.306-8.762c-0.054-3.254,0.952-8.649,2.65-12.456l14.6,40
10
+ c-1.733-0.843-3.118-1.677-4.625-2.782c-0.73,1.015-0.974,1.19-2.079,2.646c5.1,3.763,11.132,6.162,17.822,6.579
11
+ c0-0.84,0-2.406,0-3.466c-2.259-0.155-4.31-0.544-6.409-1.161l9.184-26.683l9.406,25.773c0.063,0.151,0.139,0.289,0.22,0.422
12
+ c-2.61,0.918-5.382,1.49-8.26,1.669V86.5c0,1.084,0,1.935,0,3.022c17.887-0.991,32.139-15.855,32.139-33.987
13
+ C78.254,36.765,62.982,21.495,44.213,21.495z M48.431,41.185c1.841-0.097,3.503-0.29,3.503-0.29
14
+ c1.646-0.196,1.454-2.619-0.195-2.523c0,0-4.958,0.389-8.16,0.389c-3.006,0-8.059-0.389-8.059-0.389
15
+ c-1.651-0.096-1.845,2.425-0.194,2.523c0,0,1.561,0.193,3.21,0.29l4.768,13.065l-6.699,20.087L25.46,41.185
16
+ c1.844-0.097,3.502-0.29,3.502-0.29c1.648-0.196,1.454-2.619-0.195-2.523c0,0-4.957,0.389-8.157,0.389
17
+ c-0.574,0-1.251-0.013-1.971-0.037c5.473-8.309,14.879-13.795,25.573-13.795c7.967,0,15.222,3.047,20.666,8.036
18
+ c-0.131-0.008-0.259-0.024-0.395-0.024c-3.007,0-5.141,2.62-5.141,5.432c0,2.523,1.457,4.656,3.007,7.179
19
+ c1.163,2.038,2.523,4.656,2.523,8.44c0,2.62-1.008,5.659-2.329,9.895L59.49,74.084L48.431,41.185z M59.6,81.989l9.347-27.027
20
+ c1.746-4.366,2.329-7.858,2.329-10.962c0-1.126-0.076-2.172-0.207-3.147c2.389,4.359,3.747,9.362,3.747,14.683
21
+ C74.816,66.825,68.698,76.685,59.6,81.989z"/>
22
+ <g>
23
+ <path fill="#464342" d="M44.055,11.836V1.196L14.095,18.493l29.959,17.296V25.151c16.979,0,30.793,13.812,30.793,30.791
24
+ c0,16.28-12.701,29.641-28.713,30.716v13.34c23.36-1.089,42.027-20.431,42.027-44.056C88.162,31.622,68.376,11.836,44.055,11.836z"
25
+ />
26
+ <path fill="#464342" d="M17.346,91.01c6.901,5.272,15.396,8.559,24.628,8.99V86.659c-5.979-0.403-11.487-2.528-16.05-5.872
27
+ L17.346,91.01z"/>
28
+ <path fill="#464342" d="M15.897,68.367L3.361,72.931c2.468,5.887,6.178,11.131,10.842,15.33l8.876-10.566
29
+ C23.44,78.153,17.183,71.27,15.897,68.367z"/>
30
+ <path fill="#464342" d="M14.477,64.456c-0.597-2.065-0.989-4.216-1.137-6.434H0c0.177,3.813,0.85,7.495,1.938,10.997L14.477,64.456
31
+ z"/>
32
+ </g>
33
+ </svg>
assets/images/wprb-logo.png ADDED
Binary file
assets/js/jquery.magnific-popup.js ADDED
@@ -0,0 +1,2060 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*! Magnific Popup - v1.0.0 - 2015-01-03
2
+ * http://dimsemenov.com/plugins/magnific-popup/
3
+ * Copyright (c) 2015 Dmitry Semenov; */
4
+ ;(function (factory) {
5
+ if (typeof define === 'function' && define.amd) {
6
+ // AMD. Register as an anonymous module.
7
+ define(['jquery'], factory);
8
+ } else if (typeof exports === 'object') {
9
+ // Node/CommonJS
10
+ factory(require('jquery'));
11
+ } else {
12
+ // Browser globals
13
+ factory(window.jQuery || window.Zepto);
14
+ }
15
+ }(function($) {
16
+
17
+ /*>>core*/
18
+ /**
19
+ *
20
+ * Magnific Popup Core JS file
21
+ *
22
+ */
23
+
24
+
25
+ /**
26
+ * Private static constants
27
+ */
28
+ var CLOSE_EVENT = 'Close',
29
+ BEFORE_CLOSE_EVENT = 'BeforeClose',
30
+ AFTER_CLOSE_EVENT = 'AfterClose',
31
+ BEFORE_APPEND_EVENT = 'BeforeAppend',
32
+ MARKUP_PARSE_EVENT = 'MarkupParse',
33
+ OPEN_EVENT = 'Open',
34
+ CHANGE_EVENT = 'Change',
35
+ NS = 'mfp',
36
+ EVENT_NS = '.' + NS,
37
+ READY_CLASS = 'mfp-ready',
38
+ REMOVING_CLASS = 'mfp-removing',
39
+ PREVENT_CLOSE_CLASS = 'mfp-prevent-close';
40
+
41
+
42
+ /**
43
+ * Private vars
44
+ */
45
+ /*jshint -W079 */
46
+ var mfp, // As we have only one instance of MagnificPopup object, we define it locally to not to use 'this'
47
+ MagnificPopup = function(){},
48
+ _isJQ = !!(window.jQuery),
49
+ _prevStatus,
50
+ _window = $(window),
51
+ _document,
52
+ _prevContentType,
53
+ _wrapClasses,
54
+ _currPopupType;
55
+
56
+
57
+ /**
58
+ * Private functions
59
+ */
60
+ var _mfpOn = function(name, f) {
61
+ mfp.ev.on(NS + name + EVENT_NS, f);
62
+ },
63
+ _getEl = function(className, appendTo, html, raw) {
64
+ var el = document.createElement('div');
65
+ el.className = 'mfp-'+className;
66
+ if(html) {
67
+ el.innerHTML = html;
68
+ }
69
+ if(!raw) {
70
+ el = $(el);
71
+ if(appendTo) {
72
+ el.appendTo(appendTo);
73
+ }
74
+ } else if(appendTo) {
75
+ appendTo.appendChild(el);
76
+ }
77
+ return el;
78
+ },
79
+ _mfpTrigger = function(e, data) {
80
+ mfp.ev.triggerHandler(NS + e, data);
81
+
82
+ if(mfp.st.callbacks) {
83
+ // converts "mfpEventName" to "eventName" callback and triggers it if it's present
84
+ e = e.charAt(0).toLowerCase() + e.slice(1);
85
+ if(mfp.st.callbacks[e]) {
86
+ mfp.st.callbacks[e].apply(mfp, $.isArray(data) ? data : [data]);
87
+ }
88
+ }
89
+ },
90
+ _getCloseBtn = function(type) {
91
+ if(type !== _currPopupType || !mfp.currTemplate.closeBtn) {
92
+ mfp.currTemplate.closeBtn = $( mfp.st.closeMarkup.replace('%title%', mfp.st.tClose ) );
93
+ _currPopupType = type;
94
+ }
95
+ return mfp.currTemplate.closeBtn;
96
+ },
97
+ // Initialize Magnific Popup only when called at least once
98
+ _checkInstance = function() {
99
+ if(!$.magnificPopup.instance) {
100
+ /*jshint -W020 */
101
+ mfp = new MagnificPopup();
102
+ mfp.init();
103
+ $.magnificPopup.instance = mfp;
104
+ }
105
+ },
106
+ // CSS transition detection, http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
107
+ supportsTransitions = function() {
108
+ var s = document.createElement('p').style, // 's' for style. better to create an element if body yet to exist
109
+ v = ['ms','O','Moz','Webkit']; // 'v' for vendor
110
+
111
+ if( s['transition'] !== undefined ) {
112
+ return true;
113
+ }
114
+
115
+ while( v.length ) {
116
+ if( v.pop() + 'Transition' in s ) {
117
+ return true;
118
+ }
119
+ }
120
+
121
+ return false;
122
+ };
123
+
124
+
125
+
126
+ /**
127
+ * Public functions
128
+ */
129
+ MagnificPopup.prototype = {
130
+
131
+ constructor: MagnificPopup,
132
+
133
+ /**
134
+ * Initializes Magnific Popup plugin.
135
+ * This function is triggered only once when $.fn.magnificPopup or $.magnificPopup is executed
136
+ */
137
+ init: function() {
138
+ var appVersion = navigator.appVersion;
139
+ mfp.isIE7 = appVersion.indexOf("MSIE 7.") !== -1;
140
+ mfp.isIE8 = appVersion.indexOf("MSIE 8.") !== -1;
141
+ mfp.isLowIE = mfp.isIE7 || mfp.isIE8;
142
+ mfp.isAndroid = (/android/gi).test(appVersion);
143
+ mfp.isIOS = (/iphone|ipad|ipod/gi).test(appVersion);
144
+ mfp.supportsTransition = supportsTransitions();
145
+
146
+ // We disable fixed positioned lightbox on devices that don't handle it nicely.
147
+ // If you know a better way of detecting this - let me know.
148
+ mfp.probablyMobile = (mfp.isAndroid || mfp.isIOS || /(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent) );
149
+ _document = $(document);
150
+
151
+ mfp.popupsCache = {};
152
+ },
153
+
154
+ /**
155
+ * Opens popup
156
+ * @param data [description]
157
+ */
158
+ open: function(data) {
159
+
160
+ var i;
161
+
162
+ if(data.isObj === false) {
163
+ // convert jQuery collection to array to avoid conflicts later
164
+ mfp.items = data.items.toArray();
165
+
166
+ mfp.index = 0;
167
+ var items = data.items,
168
+ item;
169
+ for(i = 0; i < items.length; i++) {
170
+ item = items[i];
171
+ if(item.parsed) {
172
+ item = item.el[0];
173
+ }
174
+ if(item === data.el[0]) {
175
+ mfp.index = i;
176
+ break;
177
+ }
178
+ }
179
+ } else {
180
+ mfp.items = $.isArray(data.items) ? data.items : [data.items];
181
+ mfp.index = data.index || 0;
182
+ }
183
+
184
+ // if popup is already opened - we just update the content
185
+ if(mfp.isOpen) {
186
+ mfp.updateItemHTML();
187
+ return;
188
+ }
189
+
190
+ mfp.types = [];
191
+ _wrapClasses = '';
192
+ if(data.mainEl && data.mainEl.length) {
193
+ mfp.ev = data.mainEl.eq(0);
194
+ } else {
195
+ mfp.ev = _document;
196
+ }
197
+
198
+ if(data.key) {
199
+ if(!mfp.popupsCache[data.key]) {
200
+ mfp.popupsCache[data.key] = {};
201
+ }
202
+ mfp.currTemplate = mfp.popupsCache[data.key];
203
+ } else {
204
+ mfp.currTemplate = {};
205
+ }
206
+
207
+
208
+
209
+ mfp.st = $.extend(true, {}, $.magnificPopup.defaults, data );
210
+ mfp.fixedContentPos = mfp.st.fixedContentPos === 'auto' ? !mfp.probablyMobile : mfp.st.fixedContentPos;
211
+
212
+ if(mfp.st.modal) {
213
+ mfp.st.closeOnContentClick = false;
214
+ mfp.st.closeOnBgClick = false;
215
+ mfp.st.showCloseBtn = false;
216
+ mfp.st.enableEscapeKey = false;
217
+ }
218
+
219
+
220
+ // Building markup
221
+ // main containers are created only once
222
+ if(!mfp.bgOverlay) {
223
+
224
+ // Dark overlay
225
+ mfp.bgOverlay = _getEl('bg').on('click'+EVENT_NS, function() {
226
+ mfp.close();
227
+ });
228
+
229
+ mfp.wrap = _getEl('wrap').attr('tabindex', -1).on('click'+EVENT_NS, function(e) {
230
+ if(mfp._checkIfClose(e.target)) {
231
+ mfp.close();
232
+ }
233
+ });
234
+
235
+ mfp.container = _getEl('container', mfp.wrap);
236
+ }
237
+
238
+ mfp.contentContainer = _getEl('content');
239
+ if(mfp.st.preloader) {
240
+ mfp.preloader = _getEl('preloader', mfp.container, mfp.st.tLoading);
241
+ }
242
+
243
+
244
+ // Initializing modules
245
+ var modules = $.magnificPopup.modules;
246
+ for(i = 0; i < modules.length; i++) {
247
+ var n = modules[i];
248
+ n = n.charAt(0).toUpperCase() + n.slice(1);
249
+ mfp['init'+n].call(mfp);
250
+ }
251
+ _mfpTrigger('BeforeOpen');
252
+
253
+
254
+ if(mfp.st.showCloseBtn) {
255
+ // Close button
256
+ if(!mfp.st.closeBtnInside) {
257
+ mfp.wrap.append( _getCloseBtn() );
258
+ } else {
259
+ _mfpOn(MARKUP_PARSE_EVENT, function(e, template, values, item) {
260
+ values.close_replaceWith = _getCloseBtn(item.type);
261
+ });
262
+ _wrapClasses += ' mfp-close-btn-in';
263
+ }
264
+ }
265
+
266
+ if(mfp.st.alignTop) {
267
+ _wrapClasses += ' mfp-align-top';
268
+ }
269
+
270
+
271
+
272
+ if(mfp.fixedContentPos) {
273
+ mfp.wrap.css({
274
+ overflow: mfp.st.overflowY,
275
+ overflowX: 'hidden',
276
+ overflowY: mfp.st.overflowY
277
+ });
278
+ } else {
279
+ mfp.wrap.css({
280
+ top: _window.scrollTop(),
281
+ position: 'absolute'
282
+ });
283
+ }
284
+ if( mfp.st.fixedBgPos === false || (mfp.st.fixedBgPos === 'auto' && !mfp.fixedContentPos) ) {
285
+ mfp.bgOverlay.css({
286
+ height: _document.height(),
287
+ position: 'absolute'
288
+ });
289
+ }
290
+
291
+
292
+
293
+ if(mfp.st.enableEscapeKey) {
294
+ // Close on ESC key
295
+ _document.on('keyup' + EVENT_NS, function(e) {
296
+ if(e.keyCode === 27) {
297
+ mfp.close();
298
+ }
299
+ });
300
+ }
301
+
302
+ _window.on('resize' + EVENT_NS, function() {
303
+ mfp.updateSize();
304
+ });
305
+
306
+
307
+ if(!mfp.st.closeOnContentClick) {
308
+ _wrapClasses += ' mfp-auto-cursor';
309
+ }
310
+
311
+ if(_wrapClasses)
312
+ mfp.wrap.addClass(_wrapClasses);
313
+
314
+
315
+ // this triggers recalculation of layout, so we get it once to not to trigger twice
316
+ var windowHeight = mfp.wH = _window.height();
317
+
318
+
319
+ var windowStyles = {};
320
+
321
+ if( mfp.fixedContentPos ) {
322
+ if(mfp._hasScrollBar(windowHeight)){
323
+ var s = mfp._getScrollbarSize();
324
+ if(s) {
325
+ windowStyles.marginRight = s;
326
+ }
327
+ }
328
+ }
329
+
330
+ if(mfp.fixedContentPos) {
331
+ if(!mfp.isIE7) {
332
+ windowStyles.overflow = 'hidden';
333
+ } else {
334
+ // ie7 double-scroll bug
335
+ $('body, html').css('overflow', 'hidden');
336
+ }
337
+ }
338
+
339
+
340
+
341
+ var classesToadd = mfp.st.mainClass;
342
+ if(mfp.isIE7) {
343
+ classesToadd += ' mfp-ie7';
344
+ }
345
+ if(classesToadd) {
346
+ mfp._addClassToMFP( classesToadd );
347
+ }
348
+
349
+ // add content
350
+ mfp.updateItemHTML();
351
+
352
+ _mfpTrigger('BuildControls');
353
+
354
+ // remove scrollbar, add margin e.t.c
355
+ $('html').css(windowStyles);
356
+
357
+ // add everything to DOM
358
+ mfp.bgOverlay.add(mfp.wrap).prependTo( mfp.st.prependTo || $(document.body) );
359
+
360
+ // Save last focused element
361
+ mfp._lastFocusedEl = document.activeElement;
362
+
363
+ // Wait for next cycle to allow CSS transition
364
+ setTimeout(function() {
365
+
366
+ if(mfp.content) {
367
+ mfp._addClassToMFP(READY_CLASS);
368
+ mfp._setFocus();
369
+ } else {
370
+ // if content is not defined (not loaded e.t.c) we add class only for BG
371
+ mfp.bgOverlay.addClass(READY_CLASS);
372
+ }
373
+
374
+ // Trap the focus in popup
375
+ _document.on('focusin' + EVENT_NS, mfp._onFocusIn);
376
+
377
+ }, 16);
378
+
379
+ mfp.isOpen = true;
380
+ mfp.updateSize(windowHeight);
381
+ _mfpTrigger(OPEN_EVENT);
382
+
383
+ return data;
384
+ },
385
+
386
+ /**
387
+ * Closes the popup
388
+ */
389
+ close: function() {
390
+ if(!mfp.isOpen) return;
391
+ _mfpTrigger(BEFORE_CLOSE_EVENT);
392
+
393
+ mfp.isOpen = false;
394
+ // for CSS3 animation
395
+ if(mfp.st.removalDelay && !mfp.isLowIE && mfp.supportsTransition ) {
396
+ mfp._addClassToMFP(REMOVING_CLASS);
397
+ setTimeout(function() {
398
+ mfp._close();
399
+ }, mfp.st.removalDelay);
400
+ } else {
401
+ mfp._close();
402
+ }
403
+ },
404
+
405
+ /**
406
+ * Helper for close() function
407
+ */
408
+ _close: function() {
409
+ _mfpTrigger(CLOSE_EVENT);
410
+
411
+ var classesToRemove = REMOVING_CLASS + ' ' + READY_CLASS + ' ';
412
+
413
+ mfp.bgOverlay.detach();
414
+ mfp.wrap.detach();
415
+ mfp.container.empty();
416
+
417
+ if(mfp.st.mainClass) {
418
+ classesToRemove += mfp.st.mainClass + ' ';
419
+ }
420
+
421
+ mfp._removeClassFromMFP(classesToRemove);
422
+
423
+ if(mfp.fixedContentPos) {
424
+ var windowStyles = {marginRight: ''};
425
+ if(mfp.isIE7) {
426
+ $('body, html').css('overflow', '');
427
+ } else {
428
+ windowStyles.overflow = '';
429
+ }
430
+ $('html').css(windowStyles);
431
+ }
432
+
433
+ _document.off('keyup' + EVENT_NS + ' focusin' + EVENT_NS);
434
+ mfp.ev.off(EVENT_NS);
435
+
436
+ // clean up DOM elements that aren't removed
437
+ mfp.wrap.attr('class', 'mfp-wrap').removeAttr('style');
438
+ mfp.bgOverlay.attr('class', 'mfp-bg');
439
+ mfp.container.attr('class', 'mfp-container');
440
+
441
+ // remove close button from target element
442
+ if(mfp.st.showCloseBtn &&
443
+ (!mfp.st.closeBtnInside || mfp.currTemplate[mfp.currItem.type] === true)) {
444
+ if(mfp.currTemplate.closeBtn)
445
+ mfp.currTemplate.closeBtn.detach();
446
+ }
447
+
448
+
449
+ if(mfp._lastFocusedEl) {
450
+ $(mfp._lastFocusedEl).focus(); // put tab focus back
451
+ }
452
+ mfp.currItem = null;
453
+ mfp.content = null;
454
+ mfp.currTemplate = null;
455
+ mfp.prevHeight = 0;
456
+
457
+ _mfpTrigger(AFTER_CLOSE_EVENT);
458
+ },
459
+
460
+ updateSize: function(winHeight) {
461
+
462
+ if(mfp.isIOS) {
463
+ // fixes iOS nav bars https://github.com/dimsemenov/Magnific-Popup/issues/2
464
+ var zoomLevel = document.documentElement.clientWidth / window.innerWidth;
465
+ var height = window.innerHeight * zoomLevel;
466
+ mfp.wrap.css('height', height);
467
+ mfp.wH = height;
468
+ } else {
469
+ mfp.wH = winHeight || _window.height();
470
+ }
471
+ // Fixes #84: popup incorrectly positioned with position:relative on body
472
+ if(!mfp.fixedContentPos) {
473
+ mfp.wrap.css('height', mfp.wH);
474
+ }
475
+
476
+ _mfpTrigger('Resize');
477
+
478
+ },
479
+
480
+ /**
481
+ * Set content of popup based on current index
482
+ */
483
+ updateItemHTML: function() {
484
+ var item = mfp.items[mfp.index];
485
+
486
+ // Detach and perform modifications
487
+ mfp.contentContainer.detach();
488
+
489
+ if(mfp.content)
490
+ mfp.content.detach();
491
+
492
+ if(!item.parsed) {
493
+ item = mfp.parseEl( mfp.index );
494
+ }
495
+
496
+ var type = item.type;
497
+
498
+ _mfpTrigger('BeforeChange', [mfp.currItem ? mfp.currItem.type : '', type]);
499
+ // BeforeChange event works like so:
500
+ // _mfpOn('BeforeChange', function(e, prevType, newType) { });
501
+
502
+ mfp.currItem = item;
503
+
504
+
505
+
506
+
507
+
508
+ if(!mfp.currTemplate[type]) {
509
+ var markup = mfp.st[type] ? mfp.st[type].markup : false;
510
+
511
+ // allows to modify markup
512
+ _mfpTrigger('FirstMarkupParse', markup);
513
+
514
+ if(markup) {
515
+ mfp.currTemplate[type] = $(markup);
516
+ } else {
517
+ // if there is no markup found we just define that template is parsed
518
+ mfp.currTemplate[type] = true;
519
+ }
520
+ }
521
+
522
+ if(_prevContentType && _prevContentType !== item.type) {
523
+ mfp.container.removeClass('mfp-'+_prevContentType+'-holder');
524
+ }
525
+
526
+ var newContent = mfp['get' + type.charAt(0).toUpperCase() + type.slice(1)](item, mfp.currTemplate[type]);
527
+ mfp.appendContent(newContent, type);
528
+
529
+ item.preloaded = true;
530
+
531
+ _mfpTrigger(CHANGE_EVENT, item);
532
+ _prevContentType = item.type;
533
+
534
+ // Append container back after its content changed
535
+ mfp.container.prepend(mfp.contentContainer);
536
+
537
+ _mfpTrigger('AfterChange');
538
+ },
539
+
540
+
541
+ /**
542
+ * Set HTML content of popup
543
+ */
544
+ appendContent: function(newContent, type) {
545
+ mfp.content = newContent;
546
+
547
+ if(newContent) {
548
+ if(mfp.st.showCloseBtn && mfp.st.closeBtnInside &&
549
+ mfp.currTemplate[type] === true) {
550
+ // if there is no markup, we just append close button element inside
551
+ if(!mfp.content.find('.mfp-close').length) {
552
+ mfp.content.append(_getCloseBtn());
553
+ }
554
+ } else {
555
+ mfp.content = newContent;
556
+ }
557
+ } else {
558
+ mfp.content = '';
559
+ }
560
+
561
+ _mfpTrigger(BEFORE_APPEND_EVENT);
562
+ mfp.container.addClass('mfp-'+type+'-holder');
563
+
564
+ mfp.contentContainer.append(mfp.content);
565
+ },
566
+
567
+
568
+
569
+
570
+ /**
571
+ * Creates Magnific Popup data object based on given data
572
+ * @param {int} index Index of item to parse
573
+ */
574
+ parseEl: function(index) {
575
+ var item = mfp.items[index],
576
+ type;
577
+
578
+ if(item.tagName) {
579
+ item = { el: $(item) };
580
+ } else {
581
+ type = item.type;
582
+ item = { data: item, src: item.src };
583
+ }
584
+
585
+ if(item.el) {
586
+ var types = mfp.types;
587
+
588
+ // check for 'mfp-TYPE' class
589
+ for(var i = 0; i < types.length; i++) {
590
+ if( item.el.hasClass('mfp-'+types[i]) ) {
591
+ type = types[i];
592
+ break;
593
+ }
594
+ }
595
+
596
+ item.src = item.el.attr('data-mfp-src');
597
+ if(!item.src) {
598
+ item.src = item.el.attr('href');
599
+ }
600
+ }
601
+
602
+ item.type = type || mfp.st.type || 'inline';
603
+ item.index = index;
604
+ item.parsed = true;
605
+ mfp.items[index] = item;
606
+ _mfpTrigger('ElementParse', item);
607
+
608
+ return mfp.items[index];
609
+ },
610
+
611
+
612
+ /**
613
+ * Initializes single popup or a group of popups
614
+ */
615
+ addGroup: function(el, options) {
616
+ var eHandler = function(e) {
617
+ e.mfpEl = this;
618
+ mfp._openClick(e, el, options);
619
+ };
620
+
621
+ if(!options) {
622
+ options = {};
623
+ }
624
+
625
+ var eName = 'click.magnificPopup';
626
+ options.mainEl = el;
627
+
628
+ if(options.items) {
629
+ options.isObj = true;
630
+ el.off(eName).on(eName, eHandler);
631
+ } else {
632
+ options.isObj = false;
633
+ if(options.delegate) {
634
+ el.off(eName).on(eName, options.delegate , eHandler);
635
+ } else {
636
+ options.items = el;
637
+ el.off(eName).on(eName, eHandler);
638
+ }
639
+ }
640
+ },
641
+ _openClick: function(e, el, options) {
642
+ var midClick = options.midClick !== undefined ? options.midClick : $.magnificPopup.defaults.midClick;
643
+
644
+
645
+ if(!midClick && ( e.which === 2 || e.ctrlKey || e.metaKey ) ) {
646
+ return;
647
+ }
648
+
649
+ var disableOn = options.disableOn !== undefined ? options.disableOn : $.magnificPopup.defaults.disableOn;
650
+
651
+ if(disableOn) {
652
+ if($.isFunction(disableOn)) {
653
+ if( !disableOn.call(mfp) ) {
654
+ return true;
655
+ }
656
+ } else { // else it's number
657
+ if( _window.width() < disableOn ) {
658
+ return true;
659
+ }
660
+ }
661
+ }
662
+
663
+ if(e.type) {
664
+ e.preventDefault();
665
+
666
+ // This will prevent popup from closing if element is inside and popup is already opened
667
+ if(mfp.isOpen) {
668
+ e.stopPropagation();
669
+ }
670
+ }
671
+
672
+
673
+ options.el = $(e.mfpEl);
674
+ if(options.delegate) {
675
+ options.items = el.find(options.delegate);
676
+ }
677
+ mfp.open(options);
678
+ },
679
+
680
+
681
+ /**
682
+ * Updates text on preloader
683
+ */
684
+ updateStatus: function(status, text) {
685
+
686
+ if(mfp.preloader) {
687
+ if(_prevStatus !== status) {
688
+ mfp.container.removeClass('mfp-s-'+_prevStatus);
689
+ }
690
+
691
+ if(!text && status === 'loading') {
692
+ text = mfp.st.tLoading;
693
+ }
694
+
695
+ var data = {
696
+ status: status,
697
+ text: text
698
+ };
699
+ // allows to modify status
700
+ _mfpTrigger('UpdateStatus', data);
701
+
702
+ status = data.status;
703
+ text = data.text;
704
+
705
+ mfp.preloader.html(text);
706
+
707
+ mfp.preloader.find('a').on('click', function(e) {
708
+ e.stopImmediatePropagation();
709
+ });
710
+
711
+ mfp.container.addClass('mfp-s-'+status);
712
+ _prevStatus = status;
713
+ }
714
+ },
715
+
716
+
717
+ /*
718
+ "Private" helpers that aren't private at all
719
+ */
720
+ // Check to close popup or not
721
+ // "target" is an element that was clicked
722
+ _checkIfClose: function(target) {
723
+
724
+ if($(target).hasClass(PREVENT_CLOSE_CLASS)) {
725
+ return;
726
+ }
727
+
728
+ var closeOnContent = mfp.st.closeOnContentClick;
729
+ var closeOnBg = mfp.st.closeOnBgClick;
730
+
731
+ if(closeOnContent && closeOnBg) {
732
+ return true;
733
+ } else {
734
+
735
+ // We close the popup if click is on close button or on preloader. Or if there is no content.
736
+ if(!mfp.content || $(target).hasClass('mfp-close') || (mfp.preloader && target === mfp.preloader[0]) ) {
737
+ return true;
738
+ }
739
+
740
+ // if click is outside the content
741
+ if( (target !== mfp.content[0] && !$.contains(mfp.content[0], target)) ) {
742
+ if(closeOnBg) {
743
+ // last check, if the clicked element is in DOM, (in case it's removed onclick)
744
+ if( $.contains(document, target) ) {
745
+ return true;
746
+ }
747
+ }
748
+ } else if(closeOnContent) {
749
+ return true;
750
+ }
751
+
752
+ }
753
+ return false;
754
+ },
755
+ _addClassToMFP: function(cName) {
756
+ mfp.bgOverlay.addClass(cName);
757
+ mfp.wrap.addClass(cName);
758
+ },
759
+ _removeClassFromMFP: function(cName) {
760
+ this.bgOverlay.removeClass(cName);
761
+ mfp.wrap.removeClass(cName);
762
+ },
763
+ _hasScrollBar: function(winHeight) {
764
+ return ( (mfp.isIE7 ? _document.height() : document.body.scrollHeight) > (winHeight || _window.height()) );
765
+ },
766
+ _setFocus: function() {
767
+ (mfp.st.focus ? mfp.content.find(mfp.st.focus).eq(0) : mfp.wrap).focus();
768
+ },
769
+ _onFocusIn: function(e) {
770
+ if( e.target !== mfp.wrap[0] && !$.contains(mfp.wrap[0], e.target) ) {
771
+ mfp._setFocus();
772
+ return false;
773
+ }
774
+ },
775
+ _parseMarkup: function(template, values, item) {
776
+ var arr;
777
+ if(item.data) {
778
+ values = $.extend(item.data, values);
779
+ }
780
+ _mfpTrigger(MARKUP_PARSE_EVENT, [template, values, item] );
781
+
782
+ $.each(values, function(key, value) {
783
+ if(value === undefined || value === false) {
784
+ return true;
785
+ }
786
+ arr = key.split('_');
787
+ if(arr.length > 1) {
788
+ var el = template.find(EVENT_NS + '-'+arr[0]);
789
+
790
+ if(el.length > 0) {
791
+ var attr = arr[1];
792
+ if(attr === 'replaceWith') {
793
+ if(el[0] !== value[0]) {
794
+ el.replaceWith(value);
795
+ }
796
+ } else if(attr === 'img') {
797
+ if(el.is('img')) {
798
+ el.attr('src', value);
799
+ } else {
800
+ el.replaceWith( '<img src="'+value+'" class="' + el.attr('class') + '" />' );
801
+ }
802
+ } else {
803
+ el.attr(arr[1], value);
804
+ }
805
+ }
806
+
807
+ } else {
808
+ template.find(EVENT_NS + '-'+key).html(value);
809
+ }
810
+ });
811
+ },
812
+
813
+ _getScrollbarSize: function() {
814
+ // thx David
815
+ if(mfp.scrollbarSize === undefined) {
816
+ var scrollDiv = document.createElement("div");
817
+ scrollDiv.style.cssText = 'width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;';
818
+ document.body.appendChild(scrollDiv);
819
+ mfp.scrollbarSize = scrollDiv.offsetWidth - scrollDiv.clientWidth;
820
+ document.body.removeChild(scrollDiv);
821
+ }
822
+ return mfp.scrollbarSize;
823
+ }
824
+
825
+ }; /* MagnificPopup core prototype end */
826
+
827
+
828
+
829
+
830
+ /**
831
+ * Public static functions
832
+ */
833
+ $.magnificPopup = {
834
+ instance: null,
835
+ proto: MagnificPopup.prototype,
836
+ modules: [],
837
+
838
+ open: function(options, index) {
839
+ _checkInstance();
840
+
841
+ if(!options) {
842
+ options = {};
843
+ } else {
844
+ options = $.extend(true, {}, options);
845
+ }
846
+
847
+
848
+ options.isObj = true;
849
+ options.index = index || 0;
850
+ return this.instance.open(options);
851
+ },
852
+
853
+ close: function() {
854
+ return $.magnificPopup.instance && $.magnificPopup.instance.close();
855
+ },
856
+
857
+ registerModule: function(name, module) {
858
+ if(module.options) {
859
+ $.magnificPopup.defaults[name] = module.options;
860
+ }
861
+ $.extend(this.proto, module.proto);
862
+ this.modules.push(name);
863
+ },
864
+
865
+ defaults: {
866
+
867
+ // Info about options is in docs:
868
+ // http://dimsemenov.com/plugins/magnific-popup/documentation.html#options
869
+
870
+ disableOn: 0,
871
+
872
+ key: null,
873
+
874
+ midClick: false,
875
+
876
+ mainClass: '',
877
+
878
+ preloader: true,
879
+
880
+ focus: '', // CSS selector of input to focus after popup is opened
881
+
882
+ closeOnContentClick: false,
883
+
884
+ closeOnBgClick: true,
885
+
886
+ closeBtnInside: true,
887
+
888
+ showCloseBtn: true,
889
+
890
+ enableEscapeKey: true,
891
+
892
+ modal: false,
893
+
894
+ alignTop: false,
895
+
896
+ removalDelay: 0,
897
+
898
+ prependTo: null,
899
+
900
+ fixedContentPos: 'auto',
901
+
902
+ fixedBgPos: 'auto',
903
+
904
+ overflowY: 'auto',
905
+
906
+ closeMarkup: '<button title="%title%" type="button" class="mfp-close">&times;</button>',
907
+
908
+ tClose: 'Close (Esc)',
909
+
910
+ tLoading: 'Loading...'
911
+
912
+ }
913
+ };
914
+
915
+
916
+
917
+ $.fn.magnificPopup = function(options) {
918
+ _checkInstance();
919
+
920
+ var jqEl = $(this);
921
+
922
+ // We call some API method of first param is a string
923
+ if (typeof options === "string" ) {
924
+
925
+ if(options === 'open') {
926
+ var items,
927
+ itemOpts = _isJQ ? jqEl.data('magnificPopup') : jqEl[0].magnificPopup,
928
+ index = parseInt(arguments[1], 10) || 0;
929
+
930
+ if(itemOpts.items) {
931
+ items = itemOpts.items[index];
932
+ } else {
933
+ items = jqEl;
934
+ if(itemOpts.delegate) {
935
+ items = items.find(itemOpts.delegate);
936
+ }
937
+ items = items.eq( index );
938
+ }
939
+ mfp._openClick({mfpEl:items}, jqEl, itemOpts);
940
+ } else {
941
+ if(mfp.isOpen)
942
+ mfp[options].apply(mfp, Array.prototype.slice.call(arguments, 1));
943
+ }
944
+
945
+ } else {
946
+ // clone options obj
947
+ options = $.extend(true, {}, options);
948
+
949
+ /*
950
+ * As Zepto doesn't support .data() method for objects
951
+ * and it works only in normal browsers
952
+ * we assign "options" object directly to the DOM element. FTW!
953
+ */
954
+ if(_isJQ) {
955
+ jqEl.data('magnificPopup', options);
956
+ } else {
957
+ jqEl[0].magnificPopup = options;
958
+ }
959
+
960
+ mfp.addGroup(jqEl, options);
961
+
962
+ }
963
+ return jqEl;
964
+ };
965
+
966
+
967
+ //Quick benchmark
968
+ /*
969
+ var start = performance.now(),
970
+ i,
971
+ rounds = 1000;
972
+
973
+ for(i = 0; i < rounds; i++) {
974
+
975
+ }
976
+ console.log('Test #1:', performance.now() - start);
977
+
978
+ start = performance.now();
979
+ for(i = 0; i < rounds; i++) {
980
+
981
+ }
982
+ console.log('Test #2:', performance.now() - start);
983
+ */
984
+
985
+
986
+ /*>>core*/
987
+
988
+ /*>>inline*/
989
+
990
+ var INLINE_NS = 'inline',
991
+ _hiddenClass,
992
+ _inlinePlaceholder,
993
+ _lastInlineElement,
994
+ _putInlineElementsBack = function() {
995
+ if(_lastInlineElement) {
996
+ _inlinePlaceholder.after( _lastInlineElement.addClass(_hiddenClass) ).detach();
997
+ _lastInlineElement = null;
998
+ }
999
+ };
1000
+
1001
+ $.magnificPopup.registerModule(INLINE_NS, {
1002
+ options: {
1003
+ hiddenClass: 'hide', // will be appended with `mfp-` prefix
1004
+ markup: '',
1005
+ tNotFound: 'Content not found'
1006
+ },
1007
+ proto: {
1008
+
1009
+ initInline: function() {
1010
+ mfp.types.push(INLINE_NS);
1011
+
1012
+ _mfpOn(CLOSE_EVENT+'.'+INLINE_NS, function() {
1013
+ _putInlineElementsBack();
1014
+ });
1015
+ },
1016
+
1017
+ getInline: function(item, template) {
1018
+
1019
+ _putInlineElementsBack();
1020
+
1021
+ if(item.src) {
1022
+ var inlineSt = mfp.st.inline,
1023
+ el = $(item.src);
1024
+
1025
+ if(el.length) {
1026
+
1027
+ // If target element has parent - we replace it with placeholder and put it back after popup is closed
1028
+ var parent = el[0].parentNode;
1029
+ if(parent && parent.tagName) {
1030
+ if(!_inlinePlaceholder) {
1031
+ _hiddenClass = inlineSt.hiddenClass;
1032
+ _inlinePlaceholder = _getEl(_hiddenClass);
1033
+ _hiddenClass = 'mfp-'+_hiddenClass;
1034
+ }
1035
+ // replace target inline element with placeholder
1036
+ _lastInlineElement = el.after(_inlinePlaceholder).detach().removeClass(_hiddenClass);
1037
+ }
1038
+
1039
+ mfp.updateStatus('ready');
1040
+ } else {
1041
+ mfp.updateStatus('error', inlineSt.tNotFound);
1042
+ el = $('<div>');
1043
+ }
1044
+
1045
+ item.inlineElement = el;
1046
+ return el;
1047
+ }
1048
+
1049
+ mfp.updateStatus('ready');
1050
+ mfp._parseMarkup(template, {}, item);
1051
+ return template;
1052
+ }
1053
+ }
1054
+ });
1055
+
1056
+ /*>>inline*/
1057
+
1058
+ /*>>ajax*/
1059
+ var AJAX_NS = 'ajax',
1060
+ _ajaxCur,
1061
+ _removeAjaxCursor = function() {
1062
+ if(_ajaxCur) {
1063
+ $(document.body).removeClass(_ajaxCur);
1064
+ }
1065
+ },
1066
+ _destroyAjaxRequest = function() {
1067
+ _removeAjaxCursor();
1068
+ if(mfp.req) {
1069
+ mfp.req.abort();
1070
+ }
1071
+ };
1072
+
1073
+ $.magnificPopup.registerModule(AJAX_NS, {
1074
+
1075
+ options: {
1076
+ settings: null,
1077
+ cursor: 'mfp-ajax-cur',
1078
+ tError: '<a href="%url%">The content</a> could not be loaded.'
1079
+ },
1080
+
1081
+ proto: {
1082
+ initAjax: function() {
1083
+ mfp.types.push(AJAX_NS);
1084
+ _ajaxCur = mfp.st.ajax.cursor;
1085
+
1086
+ _mfpOn(CLOSE_EVENT+'.'+AJAX_NS, _destroyAjaxRequest);
1087
+ _mfpOn('BeforeChange.' + AJAX_NS, _destroyAjaxRequest);
1088
+ },
1089
+ getAjax: function(item) {
1090
+
1091
+ if(_ajaxCur) {
1092
+ $(document.body).addClass(_ajaxCur);
1093
+ }
1094
+
1095
+ mfp.updateStatus('loading');
1096
+
1097
+ var opts = $.extend({
1098
+ url: item.src,
1099
+ success: function(data, textStatus, jqXHR) {
1100
+ var temp = {
1101
+ data:data,
1102
+ xhr:jqXHR
1103
+ };
1104
+
1105
+ _mfpTrigger('ParseAjax', temp);
1106
+
1107
+ mfp.appendContent( $(temp.data), AJAX_NS );
1108
+
1109
+ item.finished = true;
1110
+
1111
+ _removeAjaxCursor();
1112
+
1113
+ mfp._setFocus();
1114
+
1115
+ setTimeout(function() {
1116
+ mfp.wrap.addClass(READY_CLASS);
1117
+ }, 16);
1118
+
1119
+ mfp.updateStatus('ready');
1120
+
1121
+ _mfpTrigger('AjaxContentAdded');
1122
+ },
1123
+ error: function() {
1124
+ _removeAjaxCursor();
1125
+ item.finished = item.loadError = true;
1126
+ mfp.updateStatus('error', mfp.st.ajax.tError.replace('%url%', item.src));
1127
+ }
1128
+ }, mfp.st.ajax.settings);
1129
+
1130
+ mfp.req = $.ajax(opts);
1131
+
1132
+ return '';
1133
+ }
1134
+ }
1135
+ });
1136
+
1137
+
1138
+
1139
+
1140
+
1141
+
1142
+
1143
+ /*>>ajax*/
1144
+
1145
+ /*>>image*/
1146
+ var _imgInterval,
1147
+ _getTitle = function(item) {
1148
+ if(item.data && item.data.title !== undefined)
1149
+ return item.data.title;
1150
+
1151
+ var src = mfp.st.image.titleSrc;
1152
+
1153
+ if(src) {
1154
+ if($.isFunction(src)) {
1155
+ return src.call(mfp, item);
1156
+ } else if(item.el) {
1157
+ return item.el.attr(src) || '';
1158
+ }
1159
+ }
1160
+ return '';
1161
+ };
1162
+
1163
+ $.magnificPopup.registerModule('image', {
1164
+
1165
+ options: {
1166
+ markup: '<div class="mfp-figure">'+
1167
+ '<div class="mfp-close"></div>'+
1168
+ '<figure>'+
1169
+ '<div class="mfp-img"></div>'+
1170
+ '<figcaption>'+
1171
+ '<div class="mfp-bottom-bar">'+
1172
+ '<div class="mfp-title"></div>'+
1173
+ '<div class="mfp-counter"></div>'+
1174
+ '</div>'+
1175
+ '</figcaption>'+
1176
+ '</figure>'+
1177
+ '</div>',
1178
+ cursor: 'mfp-zoom-out-cur',
1179
+ titleSrc: 'title',
1180
+ verticalFit: true,
1181
+ tError: '<a href="%url%">The image</a> could not be loaded.'
1182
+ },
1183
+
1184
+ proto: {
1185
+ initImage: function() {
1186
+ var imgSt = mfp.st.image,
1187
+ ns = '.image';
1188
+
1189
+ mfp.types.push('image');
1190
+
1191
+ _mfpOn(OPEN_EVENT+ns, function() {
1192
+ if(mfp.currItem.type === 'image' && imgSt.cursor) {
1193
+ $(document.body).addClass(imgSt.cursor);
1194
+ }
1195
+ });
1196
+
1197
+ _mfpOn(CLOSE_EVENT+ns, function() {
1198
+ if(imgSt.cursor) {
1199
+ $(document.body).removeClass(imgSt.cursor);
1200
+ }
1201
+ _window.off('resize' + EVENT_NS);
1202
+ });
1203
+
1204
+ _mfpOn('Resize'+ns, mfp.resizeImage);
1205
+ if(mfp.isLowIE) {
1206
+ _mfpOn('AfterChange', mfp.resizeImage);
1207
+ }
1208
+ },
1209
+ resizeImage: function() {
1210
+ var item = mfp.currItem;
1211
+ if(!item || !item.img) return;
1212
+
1213
+ if(mfp.st.image.verticalFit) {
1214
+ var decr = 0;
1215
+ // fix box-sizing in ie7/8
1216
+ if(mfp.isLowIE) {
1217
+ decr = parseInt(item.img.css('padding-top'), 10) + parseInt(item.img.css('padding-bottom'),10);
1218
+ }
1219
+ item.img.css('max-height', mfp.wH-decr);
1220
+ }
1221
+ },
1222
+ _onImageHasSize: function(item) {
1223
+ if(item.img) {
1224
+
1225
+ item.hasSize = true;
1226
+
1227
+ if(_imgInterval) {
1228
+ clearInterval(_imgInterval);
1229
+ }
1230
+
1231
+ item.isCheckingImgSize = false;
1232
+
1233
+ _mfpTrigger('ImageHasSize', item);
1234
+
1235
+ if(item.imgHidden) {
1236
+ if(mfp.content)
1237
+ mfp.content.removeClass('mfp-loading');
1238
+
1239
+ item.imgHidden = false;
1240
+ }
1241
+
1242
+ }
1243
+ },
1244
+
1245
+ /**
1246
+ * Function that loops until the image has size to display elements that rely on it asap
1247
+ */
1248
+ findImageSize: function(item) {
1249
+
1250
+ var counter = 0,
1251
+ img = item.img[0],
1252
+ mfpSetInterval = function(delay) {
1253
+
1254
+ if(_imgInterval) {
1255
+ clearInterval(_imgInterval);
1256
+ }
1257
+ // decelerating interval that checks for size of an image
1258
+ _imgInterval = setInterval(function() {
1259
+ if(img.naturalWidth > 0) {
1260
+ mfp._onImageHasSize(item);
1261
+ return;
1262
+ }
1263
+
1264
+ if(counter > 200) {
1265
+ clearInterval(_imgInterval);
1266
+ }
1267
+
1268
+ counter++;
1269
+ if(counter === 3) {
1270
+ mfpSetInterval(10);
1271
+ } else if(counter === 40) {
1272
+ mfpSetInterval(50);
1273
+ } else if(counter === 100) {
1274
+ mfpSetInterval(500);
1275
+ }
1276
+ }, delay);
1277
+ };
1278
+
1279
+ mfpSetInterval(1);
1280
+ },
1281
+
1282
+ getImage: function(item, template) {
1283
+
1284
+ var guard = 0,
1285
+
1286
+ // image load complete handler
1287
+ onLoadComplete = function() {
1288
+ if(item) {
1289
+ if (item.img[0].complete) {
1290
+ item.img.off('.mfploader');
1291
+
1292
+ if(item === mfp.currItem){
1293
+ mfp._onImageHasSize(item);
1294
+
1295
+ mfp.updateStatus('ready');
1296
+ }
1297
+
1298
+ item.hasSize = true;
1299
+ item.loaded = true;
1300
+
1301
+ _mfpTrigger('ImageLoadComplete');
1302
+
1303
+ }
1304
+ else {
1305
+ // if image complete check fails 200 times (20 sec), we assume that there was an error.
1306
+ guard++;
1307
+ if(guard < 200) {
1308
+ setTimeout(onLoadComplete,100);
1309
+ } else {
1310
+ onLoadError();
1311
+ }
1312
+ }
1313
+ }
1314
+ },
1315
+
1316
+ // image error handler
1317
+ onLoadError = function() {
1318
+ if(item) {
1319
+ item.img.off('.mfploader');
1320
+ if(item === mfp.currItem){
1321
+ mfp._onImageHasSize(item);
1322
+ mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
1323
+ }
1324
+
1325
+ item.hasSize = true;
1326
+ item.loaded = true;
1327
+ item.loadError = true;
1328
+ }
1329
+ },
1330
+ imgSt = mfp.st.image;
1331
+
1332
+
1333
+ var el = template.find('.mfp-img');
1334
+ if(el.length) {
1335
+ var img = document.createElement('img');
1336
+ img.className = 'mfp-img';
1337
+ if(item.el && item.el.find('img').length) {
1338
+ img.alt = item.el.find('img').attr('alt');
1339
+ }
1340
+ item.img = $(img).on('load.mfploader', onLoadComplete).on('error.mfploader', onLoadError);
1341
+ img.src = item.src;
1342
+
1343
+ // without clone() "error" event is not firing when IMG is replaced by new IMG
1344
+ // TODO: find a way to avoid such cloning
1345
+ if(el.is('img')) {
1346
+ item.img = item.img.clone();
1347
+ }
1348
+
1349
+ img = item.img[0];
1350
+ if(img.naturalWidth > 0) {
1351
+ item.hasSize = true;
1352
+ } else if(!img.width) {
1353
+ item.hasSize = false;
1354
+ }
1355
+ }
1356
+
1357
+ mfp._parseMarkup(template, {
1358
+ title: _getTitle(item),
1359
+ img_replaceWith: item.img
1360
+ }, item);
1361
+
1362
+ mfp.resizeImage();
1363
+
1364
+ if(item.hasSize) {
1365
+ if(_imgInterval) clearInterval(_imgInterval);
1366
+
1367
+ if(item.loadError) {
1368
+ template.addClass('mfp-loading');
1369
+ mfp.updateStatus('error', imgSt.tError.replace('%url%', item.src) );
1370
+ } else {
1371
+ template.removeClass('mfp-loading');
1372
+ mfp.updateStatus('ready');
1373
+ }
1374
+ return template;
1375
+ }
1376
+
1377
+ mfp.updateStatus('loading');
1378
+ item.loading = true;
1379
+
1380
+ if(!item.hasSize) {
1381
+ item.imgHidden = true;
1382
+ template.addClass('mfp-loading');
1383
+ mfp.findImageSize(item);
1384
+ }
1385
+
1386
+ return template;
1387
+ }
1388
+ }
1389
+ });
1390
+
1391
+
1392
+
1393
+ /*>>image*/
1394
+
1395
+ /*>>zoom*/
1396
+ var hasMozTransform,
1397
+ getHasMozTransform = function() {
1398
+ if(hasMozTransform === undefined) {
1399
+ hasMozTransform = document.createElement('p').style.MozTransform !== undefined;
1400
+ }
1401
+ return hasMozTransform;
1402
+ };
1403
+
1404
+ $.magnificPopup.registerModule('zoom', {
1405
+
1406
+ options: {
1407
+ enabled: false,
1408
+ easing: 'ease-in-out',
1409
+ duration: 300,
1410
+ opener: function(element) {
1411
+ return element.is('img') ? element : element.find('img');
1412
+ }
1413
+ },
1414
+
1415
+ proto: {
1416
+
1417
+ initZoom: function() {
1418
+ var zoomSt = mfp.st.zoom,
1419
+ ns = '.zoom',
1420
+ image;
1421
+
1422
+ if(!zoomSt.enabled || !mfp.supportsTransition) {
1423
+ return;
1424
+ }
1425
+
1426
+ var duration = zoomSt.duration,
1427
+ getElToAnimate = function(image) {
1428
+ var newImg = image.clone().removeAttr('style').removeAttr('class').addClass('mfp-animated-image'),
1429
+ transition = 'all '+(zoomSt.duration/1000)+'s ' + zoomSt.easing,
1430
+ cssObj = {
1431
+ position: 'fixed',
1432
+ zIndex: 9999,
1433
+ left: 0,
1434
+ top: 0,
1435
+ '-webkit-backface-visibility': 'hidden'
1436
+ },
1437
+ t = 'transition';
1438
+
1439
+ cssObj['-webkit-'+t] = cssObj['-moz-'+t] = cssObj['-o-'+t] = cssObj[t] = transition;
1440
+
1441
+ newImg.css(cssObj);
1442
+ return newImg;
1443
+ },
1444
+ showMainContent = function() {
1445
+ mfp.content.css('visibility', 'visible');
1446
+ },
1447
+ openTimeout,
1448
+ animatedImg;
1449
+
1450
+ _mfpOn('BuildControls'+ns, function() {
1451
+ if(mfp._allowZoom()) {
1452
+
1453
+ clearTimeout(openTimeout);
1454
+ mfp.content.css('visibility', 'hidden');
1455
+
1456
+ // Basically, all code below does is clones existing image, puts in on top of the current one and animated it
1457
+
1458
+ image = mfp._getItemToZoom();
1459
+
1460
+ if(!image) {
1461
+ showMainContent();
1462
+ return;
1463
+ }
1464
+
1465
+ animatedImg = getElToAnimate(image);
1466
+
1467
+ animatedImg.css( mfp._getOffset() );
1468
+
1469
+ mfp.wrap.append(animatedImg);
1470
+
1471
+ openTimeout = setTimeout(function() {
1472
+ animatedImg.css( mfp._getOffset( true ) );
1473
+ openTimeout = setTimeout(function() {
1474
+
1475
+ showMainContent();
1476
+
1477
+ setTimeout(function() {
1478
+ animatedImg.remove();
1479
+ image = animatedImg = null;
1480
+ _mfpTrigger('ZoomAnimationEnded');
1481
+ }, 16); // avoid blink when switching images
1482
+
1483
+ }, duration); // this timeout equals animation duration
1484
+
1485
+ }, 16); // by adding this timeout we avoid short glitch at the beginning of animation
1486
+
1487
+
1488
+ // Lots of timeouts...
1489
+ }
1490
+ });
1491
+ _mfpOn(BEFORE_CLOSE_EVENT+ns, function() {
1492
+ if(mfp._allowZoom()) {
1493
+
1494
+ clearTimeout(openTimeout);
1495
+
1496
+ mfp.st.removalDelay = duration;
1497
+
1498
+ if(!image) {
1499
+ image = mfp._getItemToZoom();
1500
+ if(!image) {
1501
+ return;
1502
+ }
1503
+ animatedImg = getElToAnimate(image);
1504
+ }
1505
+
1506
+
1507
+ animatedImg.css( mfp._getOffset(true) );
1508
+ mfp.wrap.append(animatedImg);
1509
+ mfp.content.css('visibility', 'hidden');
1510
+
1511
+ setTimeout(function() {
1512
+ animatedImg.css( mfp._getOffset() );
1513
+ }, 16);
1514
+ }
1515
+
1516
+ });
1517
+
1518
+ _mfpOn(CLOSE_EVENT+ns, function() {
1519
+ if(mfp._allowZoom()) {
1520
+ showMainContent();
1521
+ if(animatedImg) {
1522
+ animatedImg.remove();
1523
+ }
1524
+ image = null;
1525
+ }
1526
+ });
1527
+ },
1528
+
1529
+ _allowZoom: function() {
1530
+ return mfp.currItem.type === 'image';
1531
+ },
1532
+
1533
+ _getItemToZoom: function() {
1534
+ if(mfp.currItem.hasSize) {
1535
+ return mfp.currItem.img;
1536
+ } else {
1537
+ return false;
1538
+ }
1539
+ },
1540
+
1541
+ // Get element postion relative to viewport
1542
+ _getOffset: function(isLarge) {
1543
+ var el;
1544
+ if(isLarge) {
1545
+ el = mfp.currItem.img;
1546
+ } else {
1547
+ el = mfp.st.zoom.opener(mfp.currItem.el || mfp.currItem);
1548
+ }
1549
+
1550
+ var offset = el.offset();
1551
+ var paddingTop = parseInt(el.css('padding-top'),10);
1552
+ var paddingBottom = parseInt(el.css('padding-bottom'),10);
1553
+ offset.top -= ( $(window).scrollTop() - paddingTop );
1554
+
1555
+
1556
+ /*
1557
+
1558
+ Animating left + top + width/height looks glitchy in Firefox, but perfect in Chrome. And vice-versa.
1559
+
1560
+ */
1561
+ var obj = {
1562
+ width: el.width(),
1563
+ // fix Zepto height+padding issue
1564
+ height: (_isJQ ? el.innerHeight() : el[0].offsetHeight) - paddingBottom - paddingTop
1565
+ };
1566
+
1567
+ // I hate to do this, but there is no another option
1568
+ if( getHasMozTransform() ) {
1569
+ obj['-moz-transform'] = obj['transform'] = 'translate(' + offset.left + 'px,' + offset.top + 'px)';
1570
+ } else {
1571
+ obj.left = offset.left;
1572
+ obj.top = offset.top;
1573
+ }
1574
+ return obj;
1575
+ }
1576
+
1577
+ }
1578
+ });
1579
+
1580
+
1581
+
1582
+ /*>>zoom*/
1583
+
1584
+ /*>>iframe*/
1585
+
1586
+ var IFRAME_NS = 'iframe',
1587
+ _emptyPage = '//about:blank',
1588
+
1589
+ _fixIframeBugs = function(isShowing) {
1590
+ if(mfp.currTemplate[IFRAME_NS]) {
1591
+ var el = mfp.currTemplate[IFRAME_NS].find('iframe');
1592
+ if(el.length) {
1593
+ // reset src after the popup is closed to avoid "video keeps playing after popup is closed" bug
1594
+ if(!isShowing) {
1595
+ el[0].src = _emptyPage;
1596
+ }
1597
+
1598
+ // IE8 black screen bug fix
1599
+ if(mfp.isIE8) {
1600
+ el.css('display', isShowing ? 'block' : 'none');
1601
+ }
1602
+ }
1603
+ }
1604
+ };
1605
+
1606
+ $.magnificPopup.registerModule(IFRAME_NS, {
1607
+
1608
+ options: {
1609
+ markup: '<div class="mfp-iframe-scaler">'+
1610
+ '<div class="mfp-close"></div>'+
1611
+ '<iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe>'+
1612
+ '</div>',
1613
+
1614
+ srcAction: 'iframe_src',
1615
+
1616
+ // we don't care and support only one default type of URL by default
1617
+ patterns: {
1618
+ youtube: {
1619
+ index: 'youtube.com',
1620
+ id: 'v=',
1621
+ src: '//www.youtube.com/embed/%id%?autoplay=1'
1622
+ },
1623
+ vimeo: {
1624
+ index: 'vimeo.com/',
1625
+ id: '/',
1626
+ src: '//player.vimeo.com/video/%id%?autoplay=1'
1627
+ },
1628
+ gmaps: {
1629
+ index: '//maps.google.',
1630
+ src: '%id%&output=embed'
1631
+ }
1632
+ }
1633
+ },
1634
+
1635
+ proto: {
1636
+ initIframe: function() {
1637
+ mfp.types.push(IFRAME_NS);
1638
+
1639
+ _mfpOn('BeforeChange', function(e, prevType, newType) {
1640
+ if(prevType !== newType) {
1641
+ if(prevType === IFRAME_NS) {
1642
+ _fixIframeBugs(); // iframe if removed
1643
+ } else if(newType === IFRAME_NS) {
1644
+ _fixIframeBugs(true); // iframe is showing
1645
+ }
1646
+ }// else {
1647
+ // iframe source is switched, don't do anything
1648
+ //}
1649
+ });
1650
+
1651
+ _mfpOn(CLOSE_EVENT + '.' + IFRAME_NS, function() {
1652
+ _fixIframeBugs();
1653
+ });
1654
+ },
1655
+
1656
+ getIframe: function(item, template) {
1657
+ var embedSrc = item.src;
1658
+ var iframeSt = mfp.st.iframe;
1659
+
1660
+ $.each(iframeSt.patterns, function() {
1661
+ if(embedSrc.indexOf( this.index ) > -1) {
1662
+ if(this.id) {
1663
+ if(typeof this.id === 'string') {
1664
+ embedSrc = embedSrc.substr(embedSrc.lastIndexOf(this.id)+this.id.length, embedSrc.length);
1665
+ } else {
1666
+ embedSrc = this.id.call( this, embedSrc );
1667
+ }
1668
+ }
1669
+ embedSrc = this.src.replace('%id%', embedSrc );
1670
+ return false; // break;
1671
+ }
1672
+ });
1673
+
1674
+ var dataObj = {};
1675
+ if(iframeSt.srcAction) {
1676
+ dataObj[iframeSt.srcAction] = embedSrc;
1677
+ }
1678
+ mfp._parseMarkup(template, dataObj, item);
1679
+
1680
+ mfp.updateStatus('ready');
1681
+
1682
+ return template;
1683
+ }
1684
+ }
1685
+ });
1686
+
1687
+
1688
+
1689
+ /*>>iframe*/
1690
+
1691
+ /*>>gallery*/
1692
+ /**
1693
+ * Get looped index depending on number of slides
1694
+ */
1695
+ var _getLoopedId = function(index) {
1696
+ var numSlides = mfp.items.length;
1697
+ if(index > numSlides - 1) {
1698
+ return index - numSlides;
1699
+ } else if(index < 0) {
1700
+ return numSlides + index;
1701
+ }
1702
+ return index;
1703
+ },
1704
+ _replaceCurrTotal = function(text, curr, total) {
1705
+ return text.replace(/%curr%/gi, curr + 1).replace(/%total%/gi, total);
1706
+ };
1707
+
1708
+ $.magnificPopup.registerModule('gallery', {
1709
+
1710
+ options: {
1711
+ enabled: false,
1712
+ arrowMarkup: '<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',
1713
+ preload: [0,2],
1714
+ navigateByImgClick: true,
1715
+ arrows: true,
1716
+
1717
+ tPrev: 'Previous (Left arrow key)',
1718
+ tNext: 'Next (Right arrow key)',
1719
+ tCounter: '%curr% of %total%'
1720
+ },
1721
+
1722
+ proto: {
1723
+ initGallery: function() {
1724
+
1725
+ var gSt = mfp.st.gallery,
1726
+ ns = '.mfp-gallery',
1727
+ supportsFastClick = Boolean($.fn.mfpFastClick);
1728
+
1729
+ mfp.direction = true; // true - next, false - prev
1730
+
1731
+ if(!gSt || !gSt.enabled ) return false;
1732
+
1733
+ _wrapClasses += ' mfp-gallery';
1734
+
1735
+ _mfpOn(OPEN_EVENT+ns, function() {
1736
+
1737
+ if(gSt.navigateByImgClick) {
1738
+ mfp.wrap.on('click'+ns, '.mfp-img', function() {
1739
+ if(mfp.items.length > 1) {
1740
+ mfp.next();
1741
+ return false;
1742
+ }
1743
+ });
1744
+ }
1745
+
1746
+ _document.on('keydown'+ns, function(e) {
1747
+ if (e.keyCode === 37) {
1748
+ mfp.prev();
1749
+ } else if (e.keyCode === 39) {
1750
+ mfp.next();
1751
+ }
1752
+ });
1753
+ });
1754
+
1755
+ _mfpOn('UpdateStatus'+ns, function(e, data) {
1756
+ if(data.text) {
1757
+ data.text = _replaceCurrTotal(data.text, mfp.currItem.index, mfp.items.length);
1758
+ }
1759
+ });
1760
+
1761
+ _mfpOn(MARKUP_PARSE_EVENT+ns, function(e, element, values, item) {
1762
+ var l = mfp.items.length;
1763
+ values.counter = l > 1 ? _replaceCurrTotal(gSt.tCounter, item.index, l) : '';
1764
+ });
1765
+
1766
+ _mfpOn('BuildControls' + ns, function() {
1767
+ if(mfp.items.length > 1 && gSt.arrows && !mfp.arrowLeft) {
1768
+ var markup = gSt.arrowMarkup,
1769
+ arrowLeft = mfp.arrowLeft = $( markup.replace(/%title%/gi, gSt.tPrev).replace(/%dir%/gi, 'left') ).addClass(PREVENT_CLOSE_CLASS),
1770
+ arrowRight = mfp.arrowRight = $( markup.replace(/%title%/gi, gSt.tNext).replace(/%dir%/gi, 'right') ).addClass(PREVENT_CLOSE_CLASS);
1771
+
1772
+ var eName = supportsFastClick ? 'mfpFastClick' : 'click';
1773
+ arrowLeft[eName](function() {
1774
+ mfp.prev();
1775
+ });
1776
+ arrowRight[eName](function() {
1777
+ mfp.next();
1778
+ });
1779
+
1780
+ // Polyfill for :before and :after (adds elements with classes mfp-a and mfp-b)
1781
+ if(mfp.isIE7) {
1782
+ _getEl('b', arrowLeft[0], false, true);
1783
+ _getEl('a', arrowLeft[0], false, true);
1784
+ _getEl('b', arrowRight[0], false, true);
1785
+ _getEl('a', arrowRight[0], false, true);
1786
+ }
1787
+
1788
+ mfp.container.append(arrowLeft.add(arrowRight));
1789
+ }
1790
+ });
1791
+
1792
+ _mfpOn(CHANGE_EVENT+ns, function() {
1793
+ if(mfp._preloadTimeout) clearTimeout(mfp._preloadTimeout);
1794
+
1795
+ mfp._preloadTimeout = setTimeout(function() {
1796
+ mfp.preloadNearbyImages();
1797
+ mfp._preloadTimeout = null;
1798
+ }, 16);
1799
+ });
1800
+
1801
+
1802
+ _mfpOn(CLOSE_EVENT+ns, function() {
1803
+ _document.off(ns);
1804
+ mfp.wrap.off('click'+ns);
1805
+
1806
+ if(mfp.arrowLeft && supportsFastClick) {
1807
+ mfp.arrowLeft.add(mfp.arrowRight).destroyMfpFastClick();
1808
+ }
1809
+ mfp.arrowRight = mfp.arrowLeft = null;
1810
+ });
1811
+
1812
+ },
1813
+ next: function() {
1814
+ mfp.direction = true;
1815
+ mfp.index = _getLoopedId(mfp.index + 1);
1816
+ mfp.updateItemHTML();
1817
+ },
1818
+ prev: function() {
1819
+ mfp.direction = false;
1820
+ mfp.index = _getLoopedId(mfp.index - 1);
1821
+ mfp.updateItemHTML();
1822
+ },
1823
+ goTo: function(newIndex) {
1824
+ mfp.direction = (newIndex >= mfp.index);
1825
+ mfp.index = newIndex;
1826
+ mfp.updateItemHTML();
1827
+ },
1828
+ preloadNearbyImages: function() {
1829
+ var p = mfp.st.gallery.preload,
1830
+ preloadBefore = Math.min(p[0], mfp.items.length),
1831
+ preloadAfter = Math.min(p[1], mfp.items.length),
1832
+ i;
1833
+
1834
+ for(i = 1; i <= (mfp.direction ? preloadAfter : preloadBefore); i++) {
1835
+ mfp._preloadItem(mfp.index+i);
1836
+ }
1837
+ for(i = 1; i <= (mfp.direction ? preloadBefore : preloadAfter); i++) {
1838
+ mfp._preloadItem(mfp.index-i);
1839
+ }
1840
+ },
1841
+ _preloadItem: function(index) {
1842
+ index = _getLoopedId(index);
1843
+
1844
+ if(mfp.items[index].preloaded) {
1845
+ return;
1846
+ }
1847
+
1848
+ var item = mfp.items[index];
1849
+ if(!item.parsed) {
1850
+ item = mfp.parseEl( index );
1851
+ }
1852
+
1853
+ _mfpTrigger('LazyLoad', item);
1854
+
1855
+ if(item.type === 'image') {
1856
+ item.img = $('<img class="mfp-img" />').on('load.mfploader', function() {
1857
+ item.hasSize = true;
1858
+ }).on('error.mfploader', function() {
1859
+ item.hasSize = true;
1860
+ item.loadError = true;
1861
+ _mfpTrigger('LazyLoadError', item);
1862
+ }).attr('src', item.src);
1863
+ }
1864
+
1865
+
1866
+ item.preloaded = true;
1867
+ }
1868
+ }
1869
+ });
1870
+
1871
+ /*
1872
+ Touch Support that might be implemented some day
1873
+
1874
+ addSwipeGesture: function() {
1875
+ var startX,
1876
+ moved,
1877
+ multipleTouches;
1878
+
1879
+ return;
1880
+
1881
+ var namespace = '.mfp',
1882
+ addEventNames = function(pref, down, move, up, cancel) {
1883
+ mfp._tStart = pref + down + namespace;
1884
+ mfp._tMove = pref + move + namespace;
1885
+ mfp._tEnd = pref + up + namespace;
1886
+ mfp._tCancel = pref + cancel + namespace;
1887
+ };
1888
+
1889
+ if(window.navigator.msPointerEnabled) {
1890
+ addEventNames('MSPointer', 'Down', 'Move', 'Up', 'Cancel');
1891
+ } else if('ontouchstart' in window) {
1892
+ addEventNames('touch', 'start', 'move', 'end', 'cancel');
1893
+ } else {
1894
+ return;
1895
+ }
1896
+ _window.on(mfp._tStart, function(e) {
1897
+ var oE = e.originalEvent;
1898
+ multipleTouches = moved = false;
1899
+ startX = oE.pageX || oE.changedTouches[0].pageX;
1900
+ }).on(mfp._tMove, function(e) {
1901
+ if(e.originalEvent.touches.length > 1) {
1902
+ multipleTouches = e.originalEvent.touches.length;
1903
+ } else {
1904
+ //e.preventDefault();
1905
+ moved = true;
1906
+ }
1907
+ }).on(mfp._tEnd + ' ' + mfp._tCancel, function(e) {
1908
+ if(moved && !multipleTouches) {
1909
+ var oE = e.originalEvent,
1910
+ diff = startX - (oE.pageX || oE.changedTouches[0].pageX);
1911
+
1912
+ if(diff > 20) {
1913
+ mfp.next();
1914
+ } else if(diff < -20) {
1915
+ mfp.prev();
1916
+ }
1917
+ }
1918
+ });
1919
+ },
1920
+ */
1921
+
1922
+
1923
+ /*>>gallery*/
1924
+
1925
+ /*>>retina*/
1926
+
1927
+ var RETINA_NS = 'retina';
1928
+
1929
+ $.magnificPopup.registerModule(RETINA_NS, {
1930
+ options: {
1931
+ replaceSrc: function(item) {
1932
+ return item.src.replace(/\.\w+$/, function(m) { return '@2x' + m; });
1933
+ },
1934
+ ratio: 1 // Function or number. Set to 1 to disable.
1935
+ },
1936
+ proto: {
1937
+ initRetina: function() {
1938
+ if(window.devicePixelRatio > 1) {
1939
+
1940
+ var st = mfp.st.retina,
1941
+ ratio = st.ratio;
1942
+
1943
+ ratio = !isNaN(ratio) ? ratio : ratio();
1944
+
1945
+ if(ratio > 1) {
1946
+ _mfpOn('ImageHasSize' + '.' + RETINA_NS, function(e, item) {
1947
+ item.img.css({
1948
+ 'max-width': item.img[0].naturalWidth / ratio,
1949
+ 'width': '100%'
1950
+ });
1951
+ });
1952
+ _mfpOn('ElementParse' + '.' + RETINA_NS, function(e, item) {
1953
+ item.src = st.replaceSrc(item, ratio);
1954
+ });
1955
+ }
1956
+ }
1957
+
1958
+ }
1959
+ }
1960
+ });
1961
+
1962
+ /*>>retina*/
1963
+
1964
+ /*>>fastclick*/
1965
+ /**
1966
+ * FastClick event implementation. (removes 300ms delay on touch devices)
1967
+ * Based on https://developers.google.com/mobile/articles/fast_buttons
1968
+ *
1969
+ * You may use it outside the Magnific Popup by calling just:
1970
+ *
1971
+ * $('.your-el').mfpFastClick(function() {
1972
+ * console.log('Clicked!');
1973
+ * });
1974
+ *
1975
+ * To unbind:
1976
+ * $('.your-el').destroyMfpFastClick();
1977
+ *
1978
+ *
1979
+ * Note that it's a very basic and simple implementation, it blocks ghost click on the same element where it was bound.
1980
+ * If you need something more advanced, use plugin by FT Labs https://github.com/ftlabs/fastclick
1981
+ *
1982
+ */
1983
+
1984
+ (function() {
1985
+ var ghostClickDelay = 1000,
1986
+ supportsTouch = 'ontouchstart' in window,
1987
+ unbindTouchMove = function() {
1988
+ _window.off('touchmove'+ns+' touchend'+ns);
1989
+ },
1990
+ eName = 'mfpFastClick',
1991
+ ns = '.'+eName;
1992
+
1993
+
1994
+ // As Zepto.js doesn't have an easy way to add custom events (like jQuery), so we implement it in this way
1995
+ $.fn.mfpFastClick = function(callback) {
1996
+
1997
+ return $(this).each(function() {
1998
+
1999
+ var elem = $(this),
2000
+ lock;
2001
+
2002
+ if( supportsTouch ) {
2003
+
2004
+ var timeout,
2005
+ startX,
2006
+ startY,
2007
+ pointerMoved,
2008
+ point,
2009
+ numPointers;
2010
+
2011
+ elem.on('touchstart' + ns, function(e) {
2012
+ pointerMoved = false;
2013
+ numPointers = 1;
2014
+
2015
+ point = e.originalEvent ? e.originalEvent.touches[0] : e.touches[0];
2016
+ startX = point.clientX;
2017
+ startY = point.clientY;
2018
+
2019
+ _window.on('touchmove'+ns, function(e) {
2020
+ point = e.originalEvent ? e.originalEvent.touches : e.touches;
2021
+ numPointers = point.length;
2022
+ point = point[0];
2023
+ if (Math.abs(point.clientX - startX) > 10 ||
2024
+ Math.abs(point.clientY - startY) > 10) {
2025
+ pointerMoved = true;
2026
+ unbindTouchMove();
2027
+ }
2028
+ }).on('touchend'+ns, function(e) {
2029
+ unbindTouchMove();
2030
+ if(pointerMoved || numPointers > 1) {
2031
+ return;
2032
+ }
2033
+ lock = true;
2034
+ e.preventDefault();
2035
+ clearTimeout(timeout);
2036
+ timeout = setTimeout(function() {
2037
+ lock = false;
2038
+ }, ghostClickDelay);
2039
+ callback();
2040
+ });
2041
+ });
2042
+
2043
+ }
2044
+
2045
+ elem.on('click' + ns, function() {
2046
+ if(!lock) {
2047
+ callback();
2048
+ }
2049
+ });
2050
+ });
2051
+ };
2052
+
2053
+ $.fn.destroyMfpFastClick = function() {
2054
+ $(this).off('touchstart' + ns + ' click' + ns);
2055
+ if(supportsTouch) _window.off('touchmove'+ns+' touchend'+ns);
2056
+ };
2057
+ })();
2058
+
2059
+ /*>>fastclick*/
2060
+ _checkInstance(); }));
assets/js/jquery.magnific-popup.min.js ADDED
@@ -0,0 +1,4 @@
 
 
 
 
1
+ /*! Magnific Popup - v1.0.0 - 2015-01-03
2
+ * http://dimsemenov.com/plugins/magnific-popup/
3
+ * Copyright (c) 2015 Dmitry Semenov; */
4
+ !function(a){"function"==typeof define&&define.amd?define(["jquery"],a):a("object"==typeof exports?require("jquery"):window.jQuery||window.Zepto)}(function(a){var b,c,d,e,f,g,h="Close",i="BeforeClose",j="AfterClose",k="BeforeAppend",l="MarkupParse",m="Open",n="Change",o="mfp",p="."+o,q="mfp-ready",r="mfp-removing",s="mfp-prevent-close",t=function(){},u=!!window.jQuery,v=a(window),w=function(a,c){b.ev.on(o+a+p,c)},x=function(b,c,d,e){var f=document.createElement("div");return f.className="mfp-"+b,d&&(f.innerHTML=d),e?c&&c.appendChild(f):(f=a(f),c&&f.appendTo(c)),f},y=function(c,d){b.ev.triggerHandler(o+c,d),b.st.callbacks&&(c=c.charAt(0).toLowerCase()+c.slice(1),b.st.callbacks[c]&&b.st.callbacks[c].apply(b,a.isArray(d)?d:[d]))},z=function(c){return c===g&&b.currTemplate.closeBtn||(b.currTemplate.closeBtn=a(b.st.closeMarkup.replace("%title%",b.st.tClose)),g=c),b.currTemplate.closeBtn},A=function(){a.magnificPopup.instance||(b=new t,b.init(),a.magnificPopup.instance=b)},B=function(){var a=document.createElement("p").style,b=["ms","O","Moz","Webkit"];if(void 0!==a.transition)return!0;for(;b.length;)if(b.pop()+"Transition"in a)return!0;return!1};t.prototype={constructor:t,init:function(){var c=navigator.appVersion;b.isIE7=-1!==c.indexOf("MSIE 7."),b.isIE8=-1!==c.indexOf("MSIE 8."),b.isLowIE=b.isIE7||b.isIE8,b.isAndroid=/android/gi.test(c),b.isIOS=/iphone|ipad|ipod/gi.test(c),b.supportsTransition=B(),b.probablyMobile=b.isAndroid||b.isIOS||/(Opera Mini)|Kindle|webOS|BlackBerry|(Opera Mobi)|(Windows Phone)|IEMobile/i.test(navigator.userAgent),d=a(document),b.popupsCache={}},open:function(c){var e;if(c.isObj===!1){b.items=c.items.toArray(),b.index=0;var g,h=c.items;for(e=0;e<h.length;e++)if(g=h[e],g.parsed&&(g=g.el[0]),g===c.el[0]){b.index=e;break}}else b.items=a.isArray(c.items)?c.items:[c.items],b.index=c.index||0;if(b.isOpen)return void b.updateItemHTML();b.types=[],f="",b.ev=c.mainEl&&c.mainEl.length?c.mainEl.eq(0):d,c.key?(b.popupsCache[c.key]||(b.popupsCache[c.key]={}),b.currTemplate=b.popupsCache[c.key]):b.currTemplate={},b.st=a.extend(!0,{},a.magnificPopup.defaults,c),b.fixedContentPos="auto"===b.st.fixedContentPos?!b.probablyMobile:b.st.fixedContentPos,b.st.modal&&(b.st.closeOnContentClick=!1,b.st.closeOnBgClick=!1,b.st.showCloseBtn=!1,b.st.enableEscapeKey=!1),b.bgOverlay||(b.bgOverlay=x("bg").on("click"+p,function(){b.close()}),b.wrap=x("wrap").attr("tabindex",-1).on("click"+p,function(a){b._checkIfClose(a.target)&&b.close()}),b.container=x("container",b.wrap)),b.contentContainer=x("content"),b.st.preloader&&(b.preloader=x("preloader",b.container,b.st.tLoading));var i=a.magnificPopup.modules;for(e=0;e<i.length;e++){var j=i[e];j=j.charAt(0).toUpperCase()+j.slice(1),b["init"+j].call(b)}y("BeforeOpen"),b.st.showCloseBtn&&(b.st.closeBtnInside?(w(l,function(a,b,c,d){c.close_replaceWith=z(d.type)}),f+=" mfp-close-btn-in"):b.wrap.append(z())),b.st.alignTop&&(f+=" mfp-align-top"),b.wrap.css(b.fixedContentPos?{overflow:b.st.overflowY,overflowX:"hidden",overflowY:b.st.overflowY}:{top:v.scrollTop(),position:"absolute"}),(b.st.fixedBgPos===!1||"auto"===b.st.fixedBgPos&&!b.fixedContentPos)&&b.bgOverlay.css({height:d.height(),position:"absolute"}),b.st.enableEscapeKey&&d.on("keyup"+p,function(a){27===a.keyCode&&b.close()}),v.on("resize"+p,function(){b.updateSize()}),b.st.closeOnContentClick||(f+=" mfp-auto-cursor"),f&&b.wrap.addClass(f);var k=b.wH=v.height(),n={};if(b.fixedContentPos&&b._hasScrollBar(k)){var o=b._getScrollbarSize();o&&(n.marginRight=o)}b.fixedContentPos&&(b.isIE7?a("body, html").css("overflow","hidden"):n.overflow="hidden");var r=b.st.mainClass;return b.isIE7&&(r+=" mfp-ie7"),r&&b._addClassToMFP(r),b.updateItemHTML(),y("BuildControls"),a("html").css(n),b.bgOverlay.add(b.wrap).prependTo(b.st.prependTo||a(document.body)),b._lastFocusedEl=document.activeElement,setTimeout(function(){b.content?(b._addClassToMFP(q),b._setFocus()):b.bgOverlay.addClass(q),d.on("focusin"+p,b._onFocusIn)},16),b.isOpen=!0,b.updateSize(k),y(m),c},close:function(){b.isOpen&&(y(i),b.isOpen=!1,b.st.removalDelay&&!b.isLowIE&&b.supportsTransition?(b._addClassToMFP(r),setTimeout(function(){b._close()},b.st.removalDelay)):b._close())},_close:function(){y(h);var c=r+" "+q+" ";if(b.bgOverlay.detach(),b.wrap.detach(),b.container.empty(),b.st.mainClass&&(c+=b.st.mainClass+" "),b._removeClassFromMFP(c),b.fixedContentPos){var e={marginRight:""};b.isIE7?a("body, html").css("overflow",""):e.overflow="",a("html").css(e)}d.off("keyup"+p+" focusin"+p),b.ev.off(p),b.wrap.attr("class","mfp-wrap").removeAttr("style"),b.bgOverlay.attr("class","mfp-bg"),b.container.attr("class","mfp-container"),!b.st.showCloseBtn||b.st.closeBtnInside&&b.currTemplate[b.currItem.type]!==!0||b.currTemplate.closeBtn&&b.currTemplate.closeBtn.detach(),b._lastFocusedEl&&a(b._lastFocusedEl).focus(),b.currItem=null,b.content=null,b.currTemplate=null,b.prevHeight=0,y(j)},updateSize:function(a){if(b.isIOS){var c=document.documentElement.clientWidth/window.innerWidth,d=window.innerHeight*c;b.wrap.css("height",d),b.wH=d}else b.wH=a||v.height();b.fixedContentPos||b.wrap.css("height",b.wH),y("Resize")},updateItemHTML:function(){var c=b.items[b.index];b.contentContainer.detach(),b.content&&b.content.detach(),c.parsed||(c=b.parseEl(b.index));var d=c.type;if(y("BeforeChange",[b.currItem?b.currItem.type:"",d]),b.currItem=c,!b.currTemplate[d]){var f=b.st[d]?b.st[d].markup:!1;y("FirstMarkupParse",f),b.currTemplate[d]=f?a(f):!0}e&&e!==c.type&&b.container.removeClass("mfp-"+e+"-holder");var g=b["get"+d.charAt(0).toUpperCase()+d.slice(1)](c,b.currTemplate[d]);b.appendContent(g,d),c.preloaded=!0,y(n,c),e=c.type,b.container.prepend(b.contentContainer),y("AfterChange")},appendContent:function(a,c){b.content=a,a?b.st.showCloseBtn&&b.st.closeBtnInside&&b.currTemplate[c]===!0?b.content.find(".mfp-close").length||b.content.append(z()):b.content=a:b.content="",y(k),b.container.addClass("mfp-"+c+"-holder"),b.contentContainer.append(b.content)},parseEl:function(c){var d,e=b.items[c];if(e.tagName?e={el:a(e)}:(d=e.type,e={data:e,src:e.src}),e.el){for(var f=b.types,g=0;g<f.length;g++)if(e.el.hasClass("mfp-"+f[g])){d=f[g];break}e.src=e.el.attr("data-mfp-src"),e.src||(e.src=e.el.attr("href"))}return e.type=d||b.st.type||"inline",e.index=c,e.parsed=!0,b.items[c]=e,y("ElementParse",e),b.items[c]},addGroup:function(a,c){var d=function(d){d.mfpEl=this,b._openClick(d,a,c)};c||(c={});var e="click.magnificPopup";c.mainEl=a,c.items?(c.isObj=!0,a.off(e).on(e,d)):(c.isObj=!1,c.delegate?a.off(e).on(e,c.delegate,d):(c.items=a,a.off(e).on(e,d)))},_openClick:function(c,d,e){var f=void 0!==e.midClick?e.midClick:a.magnificPopup.defaults.midClick;if(f||2!==c.which&&!c.ctrlKey&&!c.metaKey){var g=void 0!==e.disableOn?e.disableOn:a.magnificPopup.defaults.disableOn;if(g)if(a.isFunction(g)){if(!g.call(b))return!0}else if(v.width()<g)return!0;c.type&&(c.preventDefault(),b.isOpen&&c.stopPropagation()),e.el=a(c.mfpEl),e.delegate&&(e.items=d.find(e.delegate)),b.open(e)}},updateStatus:function(a,d){if(b.preloader){c!==a&&b.container.removeClass("mfp-s-"+c),d||"loading"!==a||(d=b.st.tLoading);var e={status:a,text:d};y("UpdateStatus",e),a=e.status,d=e.text,b.preloader.html(d),b.preloader.find("a").on("click",function(a){a.stopImmediatePropagation()}),b.container.addClass("mfp-s-"+a),c=a}},_checkIfClose:function(c){if(!a(c).hasClass(s)){var d=b.st.closeOnContentClick,e=b.st.closeOnBgClick;if(d&&e)return!0;if(!b.content||a(c).hasClass("mfp-close")||b.preloader&&c===b.preloader[0])return!0;if(c===b.content[0]||a.contains(b.content[0],c)){if(d)return!0}else if(e&&a.contains(document,c))return!0;return!1}},_addClassToMFP:function(a){b.bgOverlay.addClass(a),b.wrap.addClass(a)},_removeClassFromMFP:function(a){this.bgOverlay.removeClass(a),b.wrap.removeClass(a)},_hasScrollBar:function(a){return(b.isIE7?d.height():document.body.scrollHeight)>(a||v.height())},_setFocus:function(){(b.st.focus?b.content.find(b.st.focus).eq(0):b.wrap).focus()},_onFocusIn:function(c){return c.target===b.wrap[0]||a.contains(b.wrap[0],c.target)?void 0:(b._setFocus(),!1)},_parseMarkup:function(b,c,d){var e;d.data&&(c=a.extend(d.data,c)),y(l,[b,c,d]),a.each(c,function(a,c){if(void 0===c||c===!1)return!0;if(e=a.split("_"),e.length>1){var d=b.find(p+"-"+e[0]);if(d.length>0){var f=e[1];"replaceWith"===f?d[0]!==c[0]&&d.replaceWith(c):"img"===f?d.is("img")?d.attr("src",c):d.replaceWith('<img src="'+c+'" class="'+d.attr("class")+'" />'):d.attr(e[1],c)}}else b.find(p+"-"+a).html(c)})},_getScrollbarSize:function(){if(void 0===b.scrollbarSize){var a=document.createElement("div");a.style.cssText="width: 99px; height: 99px; overflow: scroll; position: absolute; top: -9999px;",document.body.appendChild(a),b.scrollbarSize=a.offsetWidth-a.clientWidth,document.body.removeChild(a)}return b.scrollbarSize}},a.magnificPopup={instance:null,proto:t.prototype,modules:[],open:function(b,c){return A(),b=b?a.extend(!0,{},b):{},b.isObj=!0,b.index=c||0,this.instance.open(b)},close:function(){return a.magnificPopup.instance&&a.magnificPopup.instance.close()},registerModule:function(b,c){c.options&&(a.magnificPopup.defaults[b]=c.options),a.extend(this.proto,c.proto),this.modules.push(b)},defaults:{disableOn:0,key:null,midClick:!1,mainClass:"",preloader:!0,focus:"",closeOnContentClick:!1,closeOnBgClick:!0,closeBtnInside:!0,showCloseBtn:!0,enableEscapeKey:!0,modal:!1,alignTop:!1,removalDelay:0,prependTo:null,fixedContentPos:"auto",fixedBgPos:"auto",overflowY:"auto",closeMarkup:'<button title="%title%" type="button" class="mfp-close">&times;</button>',tClose:"Close (Esc)",tLoading:"Loading..."}},a.fn.magnificPopup=function(c){A();var d=a(this);if("string"==typeof c)if("open"===c){var e,f=u?d.data("magnificPopup"):d[0].magnificPopup,g=parseInt(arguments[1],10)||0;f.items?e=f.items[g]:(e=d,f.delegate&&(e=e.find(f.delegate)),e=e.eq(g)),b._openClick({mfpEl:e},d,f)}else b.isOpen&&b[c].apply(b,Array.prototype.slice.call(arguments,1));else c=a.extend(!0,{},c),u?d.data("magnificPopup",c):d[0].magnificPopup=c,b.addGroup(d,c);return d};var C,D,E,F="inline",G=function(){E&&(D.after(E.addClass(C)).detach(),E=null)};a.magnificPopup.registerModule(F,{options:{hiddenClass:"hide",markup:"",tNotFound:"Content not found"},proto:{initInline:function(){b.types.push(F),w(h+"."+F,function(){G()})},getInline:function(c,d){if(G(),c.src){var e=b.st.inline,f=a(c.src);if(f.length){var g=f[0].parentNode;g&&g.tagName&&(D||(C=e.hiddenClass,D=x(C),C="mfp-"+C),E=f.after(D).detach().removeClass(C)),b.updateStatus("ready")}else b.updateStatus("error",e.tNotFound),f=a("<div>");return c.inlineElement=f,f}return b.updateStatus("ready"),b._parseMarkup(d,{},c),d}}});var H,I="ajax",J=function(){H&&a(document.body).removeClass(H)},K=function(){J(),b.req&&b.req.abort()};a.magnificPopup.registerModule(I,{options:{settings:null,cursor:"mfp-ajax-cur",tError:'<a href="%url%">The content</a> could not be loaded.'},proto:{initAjax:function(){b.types.push(I),H=b.st.ajax.cursor,w(h+"."+I,K),w("BeforeChange."+I,K)},getAjax:function(c){H&&a(document.body).addClass(H),b.updateStatus("loading");var d=a.extend({url:c.src,success:function(d,e,f){var g={data:d,xhr:f};y("ParseAjax",g),b.appendContent(a(g.data),I),c.finished=!0,J(),b._setFocus(),setTimeout(function(){b.wrap.addClass(q)},16),b.updateStatus("ready"),y("AjaxContentAdded")},error:function(){J(),c.finished=c.loadError=!0,b.updateStatus("error",b.st.ajax.tError.replace("%url%",c.src))}},b.st.ajax.settings);return b.req=a.ajax(d),""}}});var L,M=function(c){if(c.data&&void 0!==c.data.title)return c.data.title;var d=b.st.image.titleSrc;if(d){if(a.isFunction(d))return d.call(b,c);if(c.el)return c.el.attr(d)||""}return""};a.magnificPopup.registerModule("image",{options:{markup:'<div class="mfp-figure"><div class="mfp-close"></div><figure><div class="mfp-img"></div><figcaption><div class="mfp-bottom-bar"><div class="mfp-title"></div><div class="mfp-counter"></div></div></figcaption></figure></div>',cursor:"mfp-zoom-out-cur",titleSrc:"title",verticalFit:!0,tError:'<a href="%url%">The image</a> could not be loaded.'},proto:{initImage:function(){var c=b.st.image,d=".image";b.types.push("image"),w(m+d,function(){"image"===b.currItem.type&&c.cursor&&a(document.body).addClass(c.cursor)}),w(h+d,function(){c.cursor&&a(document.body).removeClass(c.cursor),v.off("resize"+p)}),w("Resize"+d,b.resizeImage),b.isLowIE&&w("AfterChange",b.resizeImage)},resizeImage:function(){var a=b.currItem;if(a&&a.img&&b.st.image.verticalFit){var c=0;b.isLowIE&&(c=parseInt(a.img.css("padding-top"),10)+parseInt(a.img.css("padding-bottom"),10)),a.img.css("max-height",b.wH-c)}},_onImageHasSize:function(a){a.img&&(a.hasSize=!0,L&&clearInterval(L),a.isCheckingImgSize=!1,y("ImageHasSize",a),a.imgHidden&&(b.content&&b.content.removeClass("mfp-loading"),a.imgHidden=!1))},findImageSize:function(a){var c=0,d=a.img[0],e=function(f){L&&clearInterval(L),L=setInterval(function(){return d.naturalWidth>0?void b._onImageHasSize(a):(c>200&&clearInterval(L),c++,void(3===c?e(10):40===c?e(50):100===c&&e(500)))},f)};e(1)},getImage:function(c,d){var e=0,f=function(){c&&(c.img[0].complete?(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("ready")),c.hasSize=!0,c.loaded=!0,y("ImageLoadComplete")):(e++,200>e?setTimeout(f,100):g()))},g=function(){c&&(c.img.off(".mfploader"),c===b.currItem&&(b._onImageHasSize(c),b.updateStatus("error",h.tError.replace("%url%",c.src))),c.hasSize=!0,c.loaded=!0,c.loadError=!0)},h=b.st.image,i=d.find(".mfp-img");if(i.length){var j=document.createElement("img");j.className="mfp-img",c.el&&c.el.find("img").length&&(j.alt=c.el.find("img").attr("alt")),c.img=a(j).on("load.mfploader",f).on("error.mfploader",g),j.src=c.src,i.is("img")&&(c.img=c.img.clone()),j=c.img[0],j.naturalWidth>0?c.hasSize=!0:j.width||(c.hasSize=!1)}return b._parseMarkup(d,{title:M(c),img_replaceWith:c.img},c),b.resizeImage(),c.hasSize?(L&&clearInterval(L),c.loadError?(d.addClass("mfp-loading"),b.updateStatus("error",h.tError.replace("%url%",c.src))):(d.removeClass("mfp-loading"),b.updateStatus("ready")),d):(b.updateStatus("loading"),c.loading=!0,c.hasSize||(c.imgHidden=!0,d.addClass("mfp-loading"),b.findImageSize(c)),d)}}});var N,O=function(){return void 0===N&&(N=void 0!==document.createElement("p").style.MozTransform),N};a.magnificPopup.registerModule("zoom",{options:{enabled:!1,easing:"ease-in-out",duration:300,opener:function(a){return a.is("img")?a:a.find("img")}},proto:{initZoom:function(){var a,c=b.st.zoom,d=".zoom";if(c.enabled&&b.supportsTransition){var e,f,g=c.duration,j=function(a){var b=a.clone().removeAttr("style").removeAttr("class").addClass("mfp-animated-image"),d="all "+c.duration/1e3+"s "+c.easing,e={position:"fixed",zIndex:9999,left:0,top:0,"-webkit-backface-visibility":"hidden"},f="transition";return e["-webkit-"+f]=e["-moz-"+f]=e["-o-"+f]=e[f]=d,b.css(e),b},k=function(){b.content.css("visibility","visible")};w("BuildControls"+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.content.css("visibility","hidden"),a=b._getItemToZoom(),!a)return void k();f=j(a),f.css(b._getOffset()),b.wrap.append(f),e=setTimeout(function(){f.css(b._getOffset(!0)),e=setTimeout(function(){k(),setTimeout(function(){f.remove(),a=f=null,y("ZoomAnimationEnded")},16)},g)},16)}}),w(i+d,function(){if(b._allowZoom()){if(clearTimeout(e),b.st.removalDelay=g,!a){if(a=b._getItemToZoom(),!a)return;f=j(a)}f.css(b._getOffset(!0)),b.wrap.append(f),b.content.css("visibility","hidden"),setTimeout(function(){f.css(b._getOffset())},16)}}),w(h+d,function(){b._allowZoom()&&(k(),f&&f.remove(),a=null)})}},_allowZoom:function(){return"image"===b.currItem.type},_getItemToZoom:function(){return b.currItem.hasSize?b.currItem.img:!1},_getOffset:function(c){var d;d=c?b.currItem.img:b.st.zoom.opener(b.currItem.el||b.currItem);var e=d.offset(),f=parseInt(d.css("padding-top"),10),g=parseInt(d.css("padding-bottom"),10);e.top-=a(window).scrollTop()-f;var h={width:d.width(),height:(u?d.innerHeight():d[0].offsetHeight)-g-f};return O()?h["-moz-transform"]=h.transform="translate("+e.left+"px,"+e.top+"px)":(h.left=e.left,h.top=e.top),h}}});var P="iframe",Q="//about:blank",R=function(a){if(b.currTemplate[P]){var c=b.currTemplate[P].find("iframe");c.length&&(a||(c[0].src=Q),b.isIE8&&c.css("display",a?"block":"none"))}};a.magnificPopup.registerModule(P,{options:{markup:'<div class="mfp-iframe-scaler"><div class="mfp-close"></div><iframe class="mfp-iframe" src="//about:blank" frameborder="0" allowfullscreen></iframe></div>',srcAction:"iframe_src",patterns:{youtube:{index:"youtube.com",id:"v=",src:"//www.youtube.com/embed/%id%?autoplay=1"},vimeo:{index:"vimeo.com/",id:"/",src:"//player.vimeo.com/video/%id%?autoplay=1"},gmaps:{index:"//maps.google.",src:"%id%&output=embed"}}},proto:{initIframe:function(){b.types.push(P),w("BeforeChange",function(a,b,c){b!==c&&(b===P?R():c===P&&R(!0))}),w(h+"."+P,function(){R()})},getIframe:function(c,d){var e=c.src,f=b.st.iframe;a.each(f.patterns,function(){return e.indexOf(this.index)>-1?(this.id&&(e="string"==typeof this.id?e.substr(e.lastIndexOf(this.id)+this.id.length,e.length):this.id.call(this,e)),e=this.src.replace("%id%",e),!1):void 0});var g={};return f.srcAction&&(g[f.srcAction]=e),b._parseMarkup(d,g,c),b.updateStatus("ready"),d}}});var S=function(a){var c=b.items.length;return a>c-1?a-c:0>a?c+a:a},T=function(a,b,c){return a.replace(/%curr%/gi,b+1).replace(/%total%/gi,c)};a.magnificPopup.registerModule("gallery",{options:{enabled:!1,arrowMarkup:'<button title="%title%" type="button" class="mfp-arrow mfp-arrow-%dir%"></button>',preload:[0,2],navigateByImgClick:!0,arrows:!0,tPrev:"Previous (Left arrow key)",tNext:"Next (Right arrow key)",tCounter:"%curr% of %total%"},proto:{initGallery:function(){var c=b.st.gallery,e=".mfp-gallery",g=Boolean(a.fn.mfpFastClick);return b.direction=!0,c&&c.enabled?(f+=" mfp-gallery",w(m+e,function(){c.navigateByImgClick&&b.wrap.on("click"+e,".mfp-img",function(){return b.items.length>1?(b.next(),!1):void 0}),d.on("keydown"+e,function(a){37===a.keyCode?b.prev():39===a.keyCode&&b.next()})}),w("UpdateStatus"+e,function(a,c){c.text&&(c.text=T(c.text,b.currItem.index,b.items.length))}),w(l+e,function(a,d,e,f){var g=b.items.length;e.counter=g>1?T(c.tCounter,f.index,g):""}),w("BuildControls"+e,function(){if(b.items.length>1&&c.arrows&&!b.arrowLeft){var d=c.arrowMarkup,e=b.arrowLeft=a(d.replace(/%title%/gi,c.tPrev).replace(/%dir%/gi,"left")).addClass(s),f=b.arrowRight=a(d.replace(/%title%/gi,c.tNext).replace(/%dir%/gi,"right")).addClass(s),h=g?"mfpFastClick":"click";e[h](function(){b.prev()}),f[h](function(){b.next()}),b.isIE7&&(x("b",e[0],!1,!0),x("a",e[0],!1,!0),x("b",f[0],!1,!0),x("a",f[0],!1,!0)),b.container.append(e.add(f))}}),w(n+e,function(){b._preloadTimeout&&clearTimeout(b._preloadTimeout),b._preloadTimeout=setTimeout(function(){b.preloadNearbyImages(),b._preloadTimeout=null},16)}),void w(h+e,function(){d.off(e),b.wrap.off("click"+e),b.arrowLeft&&g&&b.arrowLeft.add(b.arrowRight).destroyMfpFastClick(),b.arrowRight=b.arrowLeft=null})):!1},next:function(){b.direction=!0,b.index=S(b.index+1),b.updateItemHTML()},prev:function(){b.direction=!1,b.index=S(b.index-1),b.updateItemHTML()},goTo:function(a){b.direction=a>=b.index,b.index=a,b.updateItemHTML()},preloadNearbyImages:function(){var a,c=b.st.gallery.preload,d=Math.min(c[0],b.items.length),e=Math.min(c[1],b.items.length);for(a=1;a<=(b.direction?e:d);a++)b._preloadItem(b.index+a);for(a=1;a<=(b.direction?d:e);a++)b._preloadItem(b.index-a)},_preloadItem:function(c){if(c=S(c),!b.items[c].preloaded){var d=b.items[c];d.parsed||(d=b.parseEl(c)),y("LazyLoad",d),"image"===d.type&&(d.img=a('<img class="mfp-img" />').on("load.mfploader",function(){d.hasSize=!0}).on("error.mfploader",function(){d.hasSize=!0,d.loadError=!0,y("LazyLoadError",d)}).attr("src",d.src)),d.preloaded=!0}}}});var U="retina";a.magnificPopup.registerModule(U,{options:{replaceSrc:function(a){return a.src.replace(/\.\w+$/,function(a){return"@2x"+a})},ratio:1},proto:{initRetina:function(){if(window.devicePixelRatio>1){var a=b.st.retina,c=a.ratio;c=isNaN(c)?c():c,c>1&&(w("ImageHasSize."+U,function(a,b){b.img.css({"max-width":b.img[0].naturalWidth/c,width:"100%"})}),w("ElementParse."+U,function(b,d){d.src=a.replaceSrc(d,c)}))}}}}),function(){var b=1e3,c="ontouchstart"in window,d=function(){v.off("touchmove"+f+" touchend"+f)},e="mfpFastClick",f="."+e;a.fn.mfpFastClick=function(e){return a(this).each(function(){var g,h=a(this);if(c){var i,j,k,l,m,n;h.on("touchstart"+f,function(a){l=!1,n=1,m=a.originalEvent?a.originalEvent.touches[0]:a.touches[0],j=m.clientX,k=m.clientY,v.on("touchmove"+f,function(a){m=a.originalEvent?a.originalEvent.touches:a.touches,n=m.length,m=m[0],(Math.abs(m.clientX-j)>10||Math.abs(m.clientY-k)>10)&&(l=!0,d())}).on("touchend"+f,function(a){d(),l||n>1||(g=!0,a.preventDefault(),clearTimeout(i),i=setTimeout(function(){g=!1},b),e())})})}h.on("click"+f,function(){g||e()})})},a.fn.destroyMfpFastClick=function(){a(this).off("touchstart"+f+" click"+f),c&&v.off("touchmove"+f+" touchend"+f)}}(),A()});
assets/js/themes-wp-rollback.js ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Theme Specific WP Rollback
3
+ *
4
+ * @description: Adds a rollback option to themes
5
+ * @copyright: http://opensource.org/licenses/gpl-2.0.php GNU Public License
6
+ */
7
+ var wpr_vars;
8
+ jQuery.noConflict();
9
+
10
+ (function ( $ ) {
11
+
12
+ /**
13
+ * Content Change DOM Event Listenter
14
+ *
15
+ * @see: http://stackoverflow.com/questions/3233991/jquery-watch-div/3234646#3234646
16
+ * @param callback
17
+ * @returns {*}
18
+ */
19
+ jQuery.fn.contentChange = function ( callback ) {
20
+ var elms = jQuery( this );
21
+ elms.each(
22
+ function ( i ) {
23
+ var elm = jQuery( this );
24
+ elm.data( "lastContents", elm.html() );
25
+ window.watchContentChange = window.watchContentChange ? window.watchContentChange : [];
26
+ window.watchContentChange.push( {"element": elm, "callback": callback} );
27
+ }
28
+ );
29
+ return elms;
30
+ };
31
+ setInterval( function () {
32
+ if ( window.watchContentChange ) {
33
+ for ( i in window.watchContentChange ) {
34
+ if ( window.watchContentChange[i].element.data( "lastContents" ) != window.watchContentChange[i].element.html() ) {
35
+ window.watchContentChange[i].callback.apply( window.watchContentChange[i].element );
36
+ window.watchContentChange[i].element.data( "lastContents", window.watchContentChange[i].element.html() )
37
+ }
38
+
39
+ }
40
+ }
41
+ }, 150 );
42
+
43
+ //On DOM Ready
44
+ $( function () {
45
+ var themes;
46
+ themes = wp.themes = wp.themes || {};
47
+ themes.data = _wpThemeSettings;
48
+
49
+ //On clicking a theme template
50
+ $( '.theme-overlay' ).contentChange( function ( e ) {
51
+
52
+ //get theme name that was clicked
53
+ var clicked_theme = wpr_get_parameter_by_name( 'theme' );
54
+
55
+ //check that rollback button hasn't been placed
56
+ if ( is_rollback_btn_there() ) {
57
+ //button is there, bail
58
+ return false;
59
+ }
60
+
61
+ //pass off to rollback function
62
+ wpr_theme_rollback( clicked_theme );
63
+
64
+
65
+ } );
66
+
67
+
68
+ /**
69
+ * Check to see if Rollback button is in place
70
+ *
71
+ * @returns {boolean}
72
+ */
73
+ function is_rollback_btn_there() {
74
+
75
+ if ( $( '.wpr-theme-rollback' ).length > 0 ) {
76
+ return true;
77
+ }
78
+ return false;
79
+
80
+
81
+ }
82
+
83
+ /**
84
+ * Is Theme WordPress.org?
85
+ *
86
+ * @description Rollback only supports WordPress.org themes
87
+ */
88
+ function wpr_theme_rollback( theme ) {
89
+
90
+ var theme_data = wpr_get_theme_data( theme );
91
+
92
+ //ensure this theme can be rolled back (not premium, etc)
93
+ if ( theme_data !== null && typeof theme_data.hasRollback !== 'undefined' && theme_data.hasRollback !== false ) {
94
+
95
+ var active_theme = $( '.theme-overlay' ).hasClass( 'active' );
96
+
97
+
98
+ var rollback_btn_html = '<a href="' + encodeURI( 'index.php?page=wp-rollback&type=theme&theme_file=' + theme + '&current_version=' + theme_data.version + '&rollback_name=' + theme_data.name + '' ) + '" style="position:absolute;right: ' + (active_theme === true ? '5px' : '80px') + '; bottom: 5px;" class="button wpr-theme-rollback">' + wpr_vars.text_rollback_label + '</a>';
99
+
100
+ $( '.theme-wrap' ).find( '.theme-actions' ).append( rollback_btn_html );
101
+
102
+ } else {
103
+ //Can't roll back this theme, display the
104
+ $( '.theme-wrap' ).find( '.theme-actions' ).append( '<span class="no-rollback" style="position: absolute;left: 23px;bottom: 16px;font-size: 12px;font-style: italic;color: rgb(181, 181, 181);">' + wpr_vars.text_not_rollbackable + '</span>' );
105
+ }
106
+
107
+ }
108
+
109
+ /**
110
+ * Get Theme Data
111
+ *
112
+ * @description Loops through the wp.themes.data.themes object, finds a match, and returns the data
113
+ * @param theme
114
+ * @returns {*}
115
+ */
116
+ function wpr_get_theme_data( theme ) {
117
+ var theme_data = wp.themes.data.themes;
118
+
119
+ //Loop through complete theme data to find this current theme's data
120
+ for ( var i = 0, len = theme_data.length; i < len; i++ ) {
121
+ if ( theme_data[i].id === theme ) {
122
+ return theme_data[i]; // Return as soon as the object is found
123
+ }
124
+ }
125
+ return null; // The object was not found
126
+ }
127
+
128
+ /**
129
+ * JS Ready Query String (Helper)
130
+ *
131
+ * Kinda dirty but whatever...
132
+ *
133
+ * @see: http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
134
+ * @param name
135
+ * @returns {string}
136
+ */
137
+ function wpr_get_parameter_by_name( name ) {
138
+ name = name.replace( /[\[]/, "\\[" ).replace( /[\]]/, "\\]" );
139
+ var regex = new RegExp( "[\\?&]" + name + "=([^&#]*)" ),
140
+ results = regex.exec( location.search );
141
+ return results === null ? "" : decodeURIComponent( results[1].replace( /\+/g, " " ) );
142
+ }
143
+
144
+ /**
145
+ * Get Parameter from Focused Theme
146
+ *
147
+ * @returns {*}
148
+ */
149
+ function wpr_get_parameter_from_focused_theme() {
150
+ var focused_theme = wp.themes.focusedTheme;
151
+ var name = $( focused_theme ).find( '.theme-name' ).attr( 'id' );
152
+
153
+ if ( typeof name !== 'undefined' ) {
154
+ name = name.replace( '-name', '' );
155
+ } else {
156
+ return false;
157
+ }
158
+
159
+ return name;
160
+ }
161
+
162
+
163
+ /**
164
+ * Theme Rollback Button Clicked
165
+ *
166
+ * @description Send them over to
167
+ */
168
+ $( 'body' ).on( 'click', '.wpr-theme-rollback', function ( e ) {
169
+
170
+ window.location = $( this ).attr( 'href' );
171
+
172
+ } );
173
+
174
+
175
+
176
+ } );
177
+
178
+
179
+ })( jQuery );
assets/js/wp-rollback.js ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * WP Rollback Scripts
3
+ *
4
+ * @description:
5
+ * @copyright: http://opensource.org/licenses/gpl-2.0.php GNU Public License
6
+ */
7
+
8
+ var wpr_vars;
9
+
10
+ jQuery.noConflict();
11
+ (function ( $ ) {
12
+
13
+ //On DOM Ready
14
+ $( function () {
15
+
16
+ var form = $( '.rollback-form' );
17
+ var form_labels = $( 'label', form.get( 0 ) );
18
+ var form_submit_btn = $( '.magnific-popup' );
19
+
20
+ //On Element Click
21
+ form_labels.on( 'click', function () {
22
+
23
+ //add a selected class
24
+ form_labels.removeClass( 'wpr-selected' );
25
+ form_submit_btn.removeClass( 'wpr-rollback-disabled' );
26
+ $( this ).addClass( 'wpr-selected' );
27
+
28
+ //ensure the radio button always gets clicked
29
+ $( this ).find( 'input' ).prop( 'checked', true );
30
+
31
+ } );
32
+
33
+ //Modal
34
+ form_submit_btn.on( 'click', function () {
35
+
36
+ var rollback_form_vals = form.serializeArray();
37
+
38
+ var rollback_version = form.find( 'input[name="plugin_version"]:checked' ).val();
39
+ if ( !rollback_version ) {
40
+ rollback_version = form.find( 'input[name="theme_version"]:checked' ).val();
41
+ }
42
+ var installed_version = form.find( 'input[name="installed_version"]' ).val();
43
+ var new_version = form.find( 'input[name="new_version"]' ).val();
44
+ var rollback_name = form.find( 'input[name="rollback_name"]' ).val();
45
+
46
+ //Ensure a version is selected
47
+ if ( !rollback_version ) {
48
+
49
+ alert( wpr_vars.version_missing );
50
+ $.magnificPopup.close(); //just for good measure
51
+
52
+ } else {
53
+
54
+ //Passed
55
+ $.magnificPopup.open( {
56
+ items : {
57
+ src : $( '#wpr-modal-confirm' ), // can be a HTML string, jQuery object, or CSS selector
58
+ type: 'inline'
59
+ },
60
+ closeBtnInside: false,
61
+ callbacks : {
62
+ open: function () {
63
+
64
+ $( 'span.wpr-plugin-name' ).text( rollback_name );
65
+ $( 'span.wpr-installed-version' ).text( installed_version );
66
+ $( 'span.wpr-new-version' ).text( rollback_version );
67
+
68
+ }
69
+ }
70
+ } );
71
+
72
+
73
+ }
74
+
75
+ } );
76
+
77
+ //Modal Close
78
+ $( '.wpr-close' ).on( 'click', function ( e ) {
79
+ e.preventDefault();
80
+ $.magnificPopup.close();
81
+ } );
82
+ //Modal Confirm (GO! GO! GO!)
83
+ $( '.wpr-go' ).on( 'click', function ( e ) {
84
+ //submit form
85
+ form.submit();
86
+ } );
87
+
88
+ } );
89
+
90
+
91
+ })( jQuery );
includes/class-rollback-plugin-upgrader.php ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WP Rollback Plugin Upgrader
4
+ *
5
+ * @description: Class that extends the WP Core Plugin_Upgrader found in core to do rollbacks
6
+ * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
7
+ * @since : 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ //kick things off
16
+ class WP_Rollback_Plugin_Upgrader extends Plugin_Upgrader {
17
+
18
+ public function rollback( $plugin, $args = array() ) {
19
+
20
+ $defaults = array(
21
+ 'clear_update_cache' => true,
22
+ );
23
+ $parsed_args = wp_parse_args( $args, $defaults );
24
+
25
+ $this->init();
26
+ $this->upgrade_strings();
27
+
28
+ // TODO: Add final check to make sure plugin exists
29
+ if ( 0 ) {
30
+ $this->skin->before();
31
+ $this->skin->set_result( false );
32
+ $this->skin->error( 'up_to_date' );
33
+ $this->skin->after();
34
+
35
+ return false;
36
+ }
37
+
38
+ $plugin_slug = $this->skin->plugin;
39
+
40
+ $plugin_version = $this->skin->options['version'];
41
+
42
+ $download_endpoint = 'https://downloads.wordpress.org/plugin/';
43
+
44
+ $url = $download_endpoint . $plugin_slug . '.' . $plugin_version . '.zip';
45
+
46
+ add_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ), 10, 2 );
47
+ add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ), 10, 4 );
48
+
49
+ $this->run( array(
50
+ 'package' => $url,
51
+ 'destination' => WP_PLUGIN_DIR,
52
+ 'clear_destination' => true,
53
+ 'clear_working' => true,
54
+ 'hook_extra' => array(
55
+ 'plugin' => $plugin,
56
+ 'type' => 'plugin',
57
+ 'action' => 'update',
58
+ ),
59
+ ) );
60
+
61
+ // Cleanup our hooks, in case something else does a upgrade on this connection.
62
+ remove_filter( 'upgrader_pre_install', array( $this, 'deactivate_plugin_before_upgrade' ) );
63
+ remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_plugin' ) );
64
+
65
+ if ( ! $this->result || is_wp_error( $this->result ) ) {
66
+ return $this->result;
67
+ }
68
+
69
+ // Force refresh of plugin update information
70
+ wp_clean_plugins_cache( $parsed_args['clear_update_cache'] );
71
+
72
+ return true;
73
+ }
74
+
75
+ }
includes/class-rollback-theme-upgrader.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * WP Rollback Theme Upgrader
4
+ *
5
+ * @description: Class that extends the WP Core Theme_Upgrader found in core to do rollbacks
6
+ * @copyright : http://opensource.org/licenses/gpl-2.0.php GNU Public License
7
+ * @since : 1.0.0
8
+ */
9
+
10
+ // Exit if accessed directly
11
+ if ( ! defined( 'ABSPATH' ) ) {
12
+ exit;
13
+ }
14
+
15
+ //kick things off
16
+ class WP_Rollback_Theme_Upgrader extends Theme_Upgrader {
17
+
18
+ public function rollback( $theme, $args = array() ) {
19
+
20
+ $defaults = array(
21
+ 'clear_update_cache' => true,
22
+ );
23
+ $parsed_args = wp_parse_args( $args, $defaults );
24
+
25
+ $this->init();
26
+ $this->upgrade_strings();
27
+
28
+ if ( 0 ) {
29
+ $this->skin->before();
30
+ $this->skin->set_result( false );
31
+ $this->skin->error( 'up_to_date' );
32
+ $this->skin->after();
33
+
34
+ return false;
35
+ }
36
+
37
+ $theme_slug = $this->skin->theme;
38
+
39
+ $theme_version = $this->skin->options['version'];
40
+
41
+ $download_endpoint = 'https://downloads.wordpress.org/theme/';
42
+
43
+ $url = $download_endpoint . $theme_slug . '.' . $theme_version . '.zip';
44
+
45
+
46
+ add_filter( 'upgrader_pre_install', array( $this, 'current_before' ), 10, 2 );
47
+ add_filter( 'upgrader_post_install', array( $this, 'current_after' ), 10, 2 );
48
+ add_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ), 10, 4 );
49
+
50
+ //'source_selection' => array($this, 'source_selection'), //there's a trac ticket to move up the directory for zip's which are made a bit differently, useful for non-.org plugins.
51
+
52
+ $this->run( array(
53
+ 'package' => $url,
54
+ 'destination' => get_theme_root(),
55
+ 'clear_destination' => true,
56
+ 'clear_working' => true,
57
+ 'hook_extra' => array(
58
+ 'theme' => $theme,
59
+ 'type' => 'theme',
60
+ 'action' => 'update',
61
+ ),
62
+ ) );
63
+
64
+
65
+ remove_filter( 'upgrader_pre_install', array( $this, 'current_before' ) );
66
+ remove_filter( 'upgrader_post_install', array( $this, 'current_after' ) );
67
+ remove_filter( 'upgrader_clear_destination', array( $this, 'delete_old_theme' ) );
68
+
69
+ if ( ! $this->result || is_wp_error( $this->result ) ) {
70
+ return $this->result;
71
+ }
72
+
73
+ // Force refresh of theme update information
74
+ wp_clean_themes_cache( $parsed_args['clear_update_cache'] );
75
+
76
+ return true;
77
+
78
+ }
79
+
80
+ }
includes/rollback-action.php ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Rollback Action
4
+ */
5
+
6
+ // Exit if accessed directly
7
+ if ( ! defined( 'ABSPATH' ) ) {
8
+ exit;
9
+ }
10
+
11
+ $nonce = 'upgrade-plugin_' . $this->plugin_slug;
12
+ $url = 'index.php?page=wp-rollback&plugin_file=' . $args['plugin_file'] . 'action=upgrade-plugin';
13
+ $plugin = $this->plugin_slug;
14
+ $version = $args['plugin_version'];
15
+
16
+ //Theme rollback
17
+ if ( isset( $_GET['theme_file'] ) ) {
18
+
19
+ //theme specific vars
20
+ $nonce = 'upgrade-theme_' . $_GET['theme_file'];
21
+ $url = 'index.php?page=wp-rollback&theme_file=' . $args['theme_file'] . 'action=upgrade-theme';
22
+ $version = $_GET['theme_version'];
23
+ $theme = $_GET['theme_file'];
24
+
25
+ $upgrader = new WP_Rollback_Theme_Upgrader( new Theme_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'theme', 'version' ) ) );
26
+
27
+ $upgrader->rollback( $_GET['theme_file'] );
28
+
29
+ } elseif ( isset( $_GET['plugin_file'] ) ) {
30
+ //This is a plugin rollback
31
+ $upgrader = new WP_Rollback_Plugin_Upgrader( new Plugin_Upgrader_Skin( compact( 'title', 'nonce', 'url', 'plugin', 'version' ) ) );
32
+
33
+ $upgrader->rollback( $this->plugin_file );
34
+ } else {
35
+ _e( 'This rollback request is missing proper query string. Please contact support.', 'wpr' );
36
+ }
37
+
includes/rollback-menu.php ADDED
@@ -0,0 +1,129 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Rollback Menu
4
+ *
5
+ * @description Provides the rollback screen view with releases and
6
+ *
7
+ */
8
+
9
+
10
+ //Ensure we have our necessary query strings
11
+ if ( ( ! isset( $_GET['type'] ) && ! isset( $_GET['theme'] ) ) || ( ! isset( $_GET['type'] ) && ! isset( $_GET['plugin_file'] ) ) ) {
12
+ wp_die( __( 'WP Rollback is missing necessary parameters to continue. Please contact support.', 'wpr' ) );
13
+ }
14
+
15
+ $theme_rollback = $_GET['type'] == 'theme' ? true : false;
16
+ $plugin_rollback = $_GET['type'] == 'plugin' ? true : false;
17
+ $plugins = get_plugins();
18
+ ?>
19
+ <div class="wrap">
20
+
21
+ <div class="wpr-content-wrap">
22
+
23
+ <h2><img src="<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-icon-final.svg" onerror="this.onerror=null; this.src='<?php echo WP_ROLLBACK_PLUGIN_URL; ?>/assets/images/wprb-logo.png'"><?php _e( 'WP Rollback', 'wpr' ); ?></h2>
24
+
25
+ <p><?php echo apply_filters( 'wpr_rollback_description', sprintf( __( 'Please select which %1$s version you would like to rollback to from the releases listed below. You currently have version %2$s installed of %3$s.', 'wpr' ), '<span class="type">' . ( $theme_rollback == true ? 'theme' : 'plugin' ) . '</span>', '<span class="current-version">' . $args['current_version'] . '</span>', '<span class="rollback-name">' . $args['rollback_name'] . '</span>' ) ); ?></p>
26
+
27
+ </div>
28
+
29
+ <?php if ( isset( $args['plugin_file'] ) && in_array( $args['plugin_file'], array_keys( $plugins ) ) ) {
30
+ $versions = WP_Rollback()->versions_select( 'plugin' );
31
+ } elseif ( $theme_rollback == true && isset( $_GET['theme_file'] ) ) {
32
+ //theme rollback: set up our theme vars
33
+ $svn_tags = WP_Rollback()->get_svn_tags( 'theme', $_GET['theme_file'] );
34
+ $this->set_svn_versions_data( $svn_tags );
35
+ $this->current_version = $_GET['current_version'];
36
+ $versions = WP_Rollback()->versions_select( 'theme' );
37
+
38
+ } else {
39
+ //Fallback check
40
+ wp_die( 'Oh no! We\'re missing required rollback query strings. Please contact support so we can check this bug out and squash it!', 'wpr' );
41
+ }
42
+ ?>
43
+
44
+ <form name="check_for_rollbacks" class="rollback-form" action="<?php echo admin_url( '/index.php' ); ?>">
45
+ <?php
46
+ //Output Versions
47
+ if ( ! empty( $versions ) ) { ?>
48
+
49
+ <div class="wpr-versions-wrap">
50
+
51
+ <?php
52
+ do_action( 'wpr_pre_versions' );
53
+
54
+ echo apply_filters( 'wpr_versions_output', $versions );
55
+
56
+ do_action( 'wpr_post_version' ); ?>
57
+
58
+ </div>
59
+
60
+ <?php } ?>
61
+
62
+ <div class="wpr-submit-wrap">
63
+ <a href="#wpr-modal-confirm" class="magnific-popup button-primary wpr-rollback-disabled"><?php _e( 'Rollback', 'wpr' ); ?></a>
64
+ <input type="submit" value="<?php _e( 'Cancel', 'wpr' ); ?>" class="button" />
65
+ </div>
66
+ <?php do_action( 'wpr_hidden_fields' ); ?>
67
+ <input type="hidden" name="page" value="wp-rollback">
68
+ <?php
69
+ //Important: We need the appropriate file to perform a rollback
70
+ if ( $plugin_rollback == true ) { ?>
71
+ <input type="hidden" name="plugin_file" value="<?php echo $args['plugin_file']; ?>">
72
+ <?php } else { ?>
73
+ <input type="hidden" name="theme_file" value="<?php echo $_GET['theme_file']; ?>">
74
+ <?php } ?>
75
+ <input type="hidden" name="rollback_name" value="<?php echo $args['rollback_name']; ?>">
76
+ <input type="hidden" name="installed_version" value="<?php echo $args['current_version']; ?>">
77
+
78
+
79
+ <div id="wpr-modal-confirm" class="white-popup mfp-hide">
80
+ <div class="wpr-modal-inner">
81
+ <p class="wpr-rollback-intro"><?php
82
+ _e( 'Are you sure you want to perform the following rollback?', 'wpr' );
83
+ ?></p>
84
+
85
+ <div class="rollback-details">
86
+ <table class="widefat">
87
+ <tbody>
88
+ <?php do_action( 'wpr_pre_rollback_table' ); ?>
89
+ <tr>
90
+ <td class="row-title">
91
+ <label for="tablecell"><?php if ( $plugin_rollback == true ) {
92
+ _e( 'Plugin Name:', 'wpr' );
93
+ } else {
94
+ _e( 'Theme Name:', 'wpr' );
95
+ } ?></label>
96
+ </td>
97
+ <td><span class="wpr-plugin-name"></span></td>
98
+ </tr>
99
+ <tr class="alternate">
100
+ <td class="row-title">
101
+ <label for="tablecell"><?php _e( 'Installed Version:', 'wpr' ); ?></label>
102
+ </td>
103
+ <td><span class="wpr-installed-version"></span></td>
104
+ </tr>
105
+ <tr>
106
+ <td class="row-title">
107
+ <label for="tablecell"><?php _e( 'New Version:', 'wpr' ); ?></label>
108
+ </td>
109
+ <td><span class="wpr-new-version"></span></td>
110
+ </tr>
111
+ <?php do_action( 'wpr_post_rollback_table' ); ?>
112
+ </tbody>
113
+ </table>
114
+ </div>
115
+ <div class="wpr-error">
116
+ <p><?php
117
+ _e( '<strong>Notice:</strong> We strongly recommend you perform a test rollback on a staging site and create a complete backup of your WordPress files and database prior to performing a rollback. We are not responsible for any misuse, deletions, white screens, fatal errors, or any other issue arising from using this plugin.', 'wpr' );
118
+ ?></p>
119
+ </div>
120
+ <?php do_action( 'wpr_pre_rollback_buttons' ); ?>
121
+ <input type="submit" value="<?php _e( 'Rollback', 'wpr' ); ?>" class="button-primary wpr-go" />
122
+ <a href="#" class="button wpr-close"><?php _e( 'Cancel', 'wpr' ); ?></a>
123
+ <?php do_action( 'wpr_post_rollback_buttons' ); ?>
124
+ </div>
125
+ </div>
126
+
127
+ </form>
128
+
129
+ </div>
languages/wp-rollback.pot ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: WP Rollback\n"
4
+ "POT-Creation-Date: 2015-05-12 14:31-0800\n"
5
+ "PO-Revision-Date: 2015-05-12 14:32-0800\n"
6
+ "Last-Translator: \n"
7
+ "Language-Team: WordImpress <info@wordimpress.com>\n"
8
+ "MIME-Version: 1.0\n"
9
+ "Content-Type: text/plain; charset=UTF-8\n"
10
+ "Content-Transfer-Encoding: 8bit\n"
11
+ "X-Generator: Poedit 1.7.6\n"
12
+ "X-Poedit-Basepath: ..\n"
13
+ "X-Poedit-SourceCharset: UTF-8\n"
14
+ "X-Poedit-KeywordsList: __;_e;_n:1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;esc_attr__;"
15
+ "esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c;_n_noop:1,2;"
16
+ "_nx_noop:3c,1,2;__ngettext_noop:1,2\n"
17
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
18
+ "X-Poedit-SearchPath-0: .\n"
19
+ "X-Poedit-SearchPathExcluded-0: *.js\n"
20
+
21
+ #: includes/rollback-action.php:35
22
+ msgid ""
23
+ "This rollback request is missing proper query string. Please contact support."
24
+ msgstr ""
25
+
26
+ #: includes/rollback-menu.php:12
27
+ msgid ""
28
+ "WP Rollback is missing necessary parameters to continue. Please contact "
29
+ "support."
30
+ msgstr ""
31
+
32
+ #: includes/rollback-menu.php:23
33
+ msgid "WP Rollback"
34
+ msgstr ""
35
+
36
+ #: includes/rollback-menu.php:25
37
+ #, php-format
38
+ msgid ""
39
+ "Please select which %1$s version you would like to rollback to from the "
40
+ "releases listed below. You currently have version %2$s installed of %3$s."
41
+ msgstr ""
42
+
43
+ #: includes/rollback-menu.php:63 includes/rollback-menu.php:121
44
+ #: wp-rollback.php:211 wp-rollback.php:455 wp-rollback.php:515
45
+ msgid "Rollback"
46
+ msgstr ""
47
+
48
+ #: includes/rollback-menu.php:64 includes/rollback-menu.php:122
49
+ msgid "Cancel"
50
+ msgstr ""
51
+
52
+ #: includes/rollback-menu.php:82
53
+ msgid "Are you sure you want to perform the following rollback?"
54
+ msgstr ""
55
+
56
+ #: includes/rollback-menu.php:92
57
+ msgid "Plugin Name:"
58
+ msgstr ""
59
+
60
+ #: includes/rollback-menu.php:94
61
+ msgid "Theme Name:"
62
+ msgstr ""
63
+
64
+ #: includes/rollback-menu.php:101
65
+ msgid "Installed Version:"
66
+ msgstr ""
67
+
68
+ #: includes/rollback-menu.php:107
69
+ msgid "New Version:"
70
+ msgstr ""
71
+
72
+ #: includes/rollback-menu.php:117
73
+ msgid ""
74
+ "<strong>Notice:</strong> We strongly recommend you perform a test rollback "
75
+ "on a staging site and create a complete backup of your WordPress files and "
76
+ "database prior to performing a rollback. We are not responsible for any "
77
+ "misuse, deletions, white screens, fatal errors, or any other issue arising "
78
+ "from using this plugin."
79
+ msgstr ""
80
+
81
+ #: wp-rollback.php:122 wp-rollback.php:134
82
+ msgid "Cheatin&#8217; huh?"
83
+ msgstr ""
84
+
85
+ #: wp-rollback.php:212
86
+ msgid "No Rollback Available: This is a non-WordPress.org theme."
87
+ msgstr ""
88
+
89
+ #: wp-rollback.php:213
90
+ msgid "Loading..."
91
+ msgstr ""
92
+
93
+ #: wp-rollback.php:232
94
+ msgid "Please select a version number to perform a rollback."
95
+ msgstr ""
96
+
97
+ #: wp-rollback.php:280
98
+ msgid ""
99
+ "You do not have sufficient permissions to perform rollbacks for this site."
100
+ msgstr ""
101
+
102
+ #: wp-rollback.php:402
103
+ msgid "Installed Version"
104
+ msgstr ""
105
+
106
+ #: wp-rollback.php:627
107
+ msgid ""
108
+ "An unexpected error occurred. Something may be wrong with WordPress.org or "
109
+ "this server&#8217;s configuration. If you continue to have problems, please "
110
+ "try the <a href=\"https://wordpress.org/support/\">support forums</a>."
111
+ msgstr ""
112
+
113
+ #: wp-rollback.php:627
114
+ msgid ""
115
+ "(WordPress could not establish a secure connection to WordPress.org. Please "
116
+ "contact your server administrator.)"
117
+ msgstr ""
license.txt ADDED
@@ -0,0 +1,281 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ GNU GENERAL PUBLIC LICENSE
2
+ Version 2, June 1991
3
+
4
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
5
+ 51 Franklin St, Fifth Floor, Boston, MA 02110, USA
6
+
7
+ Everyone is permitted to copy and distribute verbatim copies
8
+ of this license document, but changing it is not allowed.
9
+
10
+ Preamble
11
+
12
+ The licenses for most software are designed to take away your
13
+ freedom to share and change it. By contrast, the GNU General Public
14
+ License is intended to guarantee your freedom to share and change free
15
+ software--to make sure the software is free for all its users. This
16
+ General Public License applies to most of the Free Software
17
+ Foundation's software and to any other program whose authors commit to
18
+ using it. (Some other Free Software Foundation software is covered by
19
+ the GNU Library General Public License instead.) You can apply it to
20
+ your programs, too.
21
+
22
+ When we speak of free software, we are referring to freedom, not
23
+ price. Our General Public Licenses are designed to make sure that you
24
+ have the freedom to distribute copies of free software (and charge for
25
+ this service if you wish), that you receive source code or can get it
26
+ if you want it, that you can change the software or use pieces of it
27
+ in new free programs; and that you know you can do these things.
28
+
29
+ To protect your rights, we need to make restrictions that forbid
30
+ anyone to deny you these rights or to ask you to surrender the rights.
31
+ These restrictions translate to certain responsibilities for you if you
32
+ distribute copies of the software, or if you modify it.
33
+
34
+ For example, if you distribute copies of such a program, whether
35
+ gratis or for a fee, you must give the recipients all the rights that
36
+ you have. You must make sure that they, too, receive or can get the
37
+ source code. And you must show them these terms so they know their
38
+ rights.
39
+
40
+ We protect your rights with two steps: (1) copyright the software, and
41
+ (2) offer you this license which gives you legal permission to copy,
42
+ distribute and/or modify the software.
43
+
44
+ Also, for each author's protection and ours, we want to make certain
45
+ that everyone understands that there is no warranty for this free
46
+ software. If the software is modified by someone else and passed on, we
47
+ want its recipients to know that what they have is not the original, so
48
+ that any problems introduced by others will not reflect on the original
49
+ authors' reputations.
50
+
51
+ Finally, any free program is threatened constantly by software
52
+ patents. We wish to avoid the danger that redistributors of a free
53
+ program will individually obtain patent licenses, in effect making the
54
+ program proprietary. To prevent this, we have made it clear that any
55
+ patent must be licensed for everyone's free use or not licensed at all.
56
+
57
+ The precise terms and conditions for copying, distribution and
58
+ modification follow.
59
+
60
+ GNU GENERAL PUBLIC LICENSE
61
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
62
+
63
+ 0. This License applies to any program or other work which contains
64
+ a notice placed by the copyright holder saying it may be distributed
65
+ under the terms of this General Public License. The "Program", below,
66
+ refers to any such program or work, and a "work based on the Program"
67
+ means either the Program or any derivative work under copyright law:
68
+ that is to say, a work containing the Program or a portion of it,
69
+ either verbatim or with modifications and/or translated into another
70
+ language. (Hereinafter, translation is included without limitation in
71
+ the term "modification".) Each licensee is addressed as "you".
72
+
73
+ Activities other than copying, distribution and modification are not
74
+ covered by this License; they are outside its scope. The act of
75
+ running the Program is not restricted, and the output from the Program
76
+ is covered only if its contents constitute a work based on the
77
+ Program (independent of having been made by running the Program).
78
+ Whether that is true depends on what the Program does.
79
+
80
+ 1. You may copy and distribute verbatim copies of the Program's
81
+ source code as you receive it, in any medium, provided that you
82
+ conspicuously and appropriately publish on each copy an appropriate
83
+ copyright notice and disclaimer of warranty; keep intact all the
84
+ notices that refer to this License and to the absence of any warranty;
85
+ and give any other recipients of the Program a copy of this License
86
+ along with the Program.
87
+
88
+ You may charge a fee for the physical act of transferring a copy, and
89
+ you may at your option offer warranty protection in exchange for a fee.
90
+
91
+ 2. You may modify your copy or copies of the Program or any portion
92
+ of it, thus forming a work based on the Program, and copy and
93
+ distribute such modifications or work under the terms of Section 1
94
+ above, provided that you also meet all of these conditions:
95
+
96
+ a) You must cause the modified files to carry prominent notices
97
+ stating that you changed the files and the date of any change.
98
+
99
+ b) You must cause any work that you distribute or publish, that in
100
+ whole or in part contains or is derived from the Program or any
101
+ part thereof, to be licensed as a whole at no charge to all third
102
+ parties under the terms of this License.
103
+
104
+ c) If the modified program normally reads commands interactively
105
+ when run, you must cause it, when started running for such
106
+ interactive use in the most ordinary way, to print or display an
107
+ announcement including an appropriate copyright notice and a
108
+ notice that there is no warranty (or else, saying that you provide
109
+ a warranty) and that users may redistribute the program under
110
+ these conditions, and telling the user how to view a copy of this
111
+ License. (Exception: if the Program itself is interactive but
112
+ does not normally print such an announcement, your work based on
113
+ the Program is not required to print an announcement.)
114
+
115
+ These requirements apply to the modified work as a whole. If
116
+ identifiable sections of that work are not derived from the Program,
117
+ and can be reasonably considered independent and separate works in
118
+ themselves, then this License, and its terms, do not apply to those
119
+ sections when you distribute them as separate works. But when you
120
+ distribute the same sections as part of a whole which is a work based
121
+ on the Program, the distribution of the whole must be on the terms of
122
+ this License, whose permissions for other licensees extend to the
123
+ entire whole, and thus to each and every part regardless of who wrote it.
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
+
readme.txt ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Rollback ===
2
+ Contributors: wordimpress, dlocc, drrobotnik, webdevmattcrom
3
+ Tags: rollback, revert, downgrade, version, plugins, themes, version, versions, backup, backups, revision, revisions
4
+ Requires at least: 3.8
5
+ Donate Link: https://wordimpress.com
6
+ Tested up to: 4.2.2
7
+ Stable tag: 1.0
8
+ License: GPLv3
9
+ License URI: http://www.gnu.org/licenses/gpl-3.0.html
10
+
11
+ Rollback (or forward) any WordPress.org plugin or theme like a boss.
12
+
13
+ == Description ==
14
+
15
+ Quickly and easily rollback any theme or plugin from WordPress.org to any previous (or newer) version without any of the manual fuss. Works just like the plugin updater, except you're rolling back (or forward) to a specific version. No need for manually downloading and FTPing the files or learning Subversion. This plugin takes care of the trouble for you.
16
+
17
+ = Rollback WordPress.org Plugins and Themes =
18
+
19
+ While it's considered best practice to always keep your WordPress plugins and themes updated, we understand there are times you may need to quickly revert to a previous version. This plugin makes that process as easy as a few mouse clicks. Simply select the version of the plugin or theme that you'd like to rollback to, confirm, and in a few moments you'll be using the version requested. No more fumbling to find the version, downloading, unzipping, FTPing, learning Subversion or hair pulling.
20
+
21
+ = Muy Importante (Very Important): Always Test and Backup =
22
+
23
+ **Important Disclaimer:** This plugin is not intended to be used without first taking the proper precautions to ensure zero data loss or site downtime. Always be sure you have first tested the rollback on a staging or development site prior to using WP Rollback on a live site.
24
+
25
+ We provide no (zero) assurances, guarantees, or warrenties that the plugin, theme, or WordPress version you are downgrading to will work as you expect. Use this plugin at your own risk.
26
+
27
+ = Translation Ready =
28
+
29
+ Do you speak another language? Want to contribute in a meaninful way to WP Rollback? There's no better way than to help us translate the plugin. This plugin is translation ready. Simply use the wp-rollback.pot file and your favorite translation tool. Once finished, please reach out to us on the WordPress.org forums or better yet, submit a pull request on the [Github Repo](https://github.com/WordImpress/WP-Rollback/).
30
+
31
+ = Support and Documentation =
32
+
33
+ We answer all support requests [on the WordPress.org support forum](https://wordpress.org/support/plugin/wp-rollback).
34
+
35
+ WP Rollback was created to be as intuitive to the natural WordPress experience as possible. There are is no dedicated setting page or option panels. We believe that once you activate WP Rollback, you'll quickly discover exactly how it works without question.
36
+
37
+ **BUT!!**
38
+
39
+ We do have documentation on the plugin [Github Wiki](https://github.com/WordImpress/WP-Rollback/wiki).
40
+
41
+ == Installation ==
42
+
43
+ = Minimum Requirements =
44
+
45
+ * WordPress 3.8 or greater
46
+ * PHP version 5.3 or greater
47
+ * MySQL version 5.0 or greater
48
+
49
+ = Automatic installation =
50
+
51
+ Automatic installation is the easiest option as WordPress handles the file transfers itself and you don't need to leave your web browser. To do an automatic install of WP Rollback, log in to your WordPress dashboard, navigate to the Plugins menu and click Add New.
52
+
53
+ In the search field type "WP Rollback" and click Search Plugins. Once you have found the plugin you can view details about it such as the the point release, rating and description. Most importantly of course, you can install it by simply clicking "Install Now".
54
+
55
+ = Manual installation =
56
+
57
+ The manual installation method involves downloading our donation plugin and uploading it to your server via your favorite FTP application. The WordPress codex contains [instructions on how to do this here](http://codex.wordpress.org/Managing_Plugins#Manual_Plugin_Installation).
58
+
59
+ = Updating =
60
+
61
+ Automatic updates should work like a charm; as always though, ensure you backup your site just in case.
62
+
63
+ == Frequently Asked Questions ==
64
+
65
+ = Is this plugin safe to use? =
66
+ Short answer = Yes. Longer answer = It depends on how you use it.
67
+
68
+ WP Rollback is completely safe because all it does is take publicly available versions of the plugins you already have on your site and install the version that you designate. There is no other kinds of trickery or fancy offsite calls or anything.
69
+
70
+ **BUT!!!**
71
+
72
+ It depends on you. We absolutely do NOT recommend rolling back any plugins or themes on a live site. Test the rollback locally first, have backups, use all the best practice tools available to you. This is intended to make rolling back easier, that's all.
73
+
74
+ = Why isn't there a rollback button next to X plugin or theme? =
75
+
76
+ WP Rollback only works with plugins or themes installed from the WordPress Repository. If you don't see the rollback link, then most likely that plugin or theme is not found on WordPress.org. This plugin does not support plugins from Github, ThemeForest, or other sources other than the WordPress.org Repo.
77
+
78
+ = I rolled my [insert plugin name] back to version X.X and now my site is broken. This is your fault. =
79
+
80
+ Nope. We warned you in **bold** print several times in many places. And our plugin delivered exactly what it said it would do. May the gods of the Internet pity your broken site's soul.
81
+
82
+ = Where is the complete documentation located? =
83
+
84
+ The documentation for this plugin is located on our [Github Wiki](https://github.com/WordImpress/WP-Rollback/wiki). This is where we make regular updates.
85
+
86
+ = Can this plugin be translated? =
87
+
88
+ Yes! All strings are internationalized and ready to be translated. Simply use the languages/wp-rollback.pot file and your favorite translation tool. Once finished, please reach out to us on the WordPress.org forums or better yet, submit a pull request on the [Github Repo](https://github.com/WordImpress/WP-Rollback/).
89
+
90
+ == Screenshots ==
91
+
92
+ 1. The Rollback link on the Plugins page.
93
+
94
+ 2. The Rollback Versions page for a plugin.
95
+
96
+ 3. The Update plugin screen.
97
+
98
+ 4. The Rollback button on the Theme Modal popup.
99
+
100
+ 5. The Rollback Versions page for a theme.
101
+
102
+ 6. The Rollback modal confirmation popup.
103
+
104
+ 7. The Update theme screen.
105
+
106
+ == Upgrade Notice ==
107
+
108
+ This is the first version of this plugin. It is a tool for your convenience. Rollback at your own risk!
109
+
110
+ == Changelog ==
111
+
112
+ = 1.0 =
113
+
114
+ * Initial plugin release. Yippee!
115
+ * Adds "Rollback" link to all plugins from the WordPress repo on the plugin screen.
116
+ * Adds "Rollback" link to all themes from the WordPress repo inside the modal details screen.
117
+ * The "Rollback" page allows you to choose which version you want to rollback to.
wp-rollback.php ADDED
@@ -0,0 +1,695 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Plugin Name: WP Rollback
4
+ * Plugin URI: http://wordimpress.com
5
+ * Description: Rollback any WordPress.org plugin or theme like a boss.
6
+ * Author: WordImpress
7
+ * Author URI: http://wordimpress.com
8
+ * Version: 1.0
9
+ * Text Domain: wpr
10
+ * Domain Path: languages
11
+ *
12
+ * WP Rollback is free software: you can redistribute it and/or modify
13
+ * it under the terms of the GNU General Public License as published by
14
+ * the Free Software Foundation, either version 2 of the License, or
15
+ * any later version.
16
+ *
17
+ * WP Rollback is distributed in the hope that it will be useful,
18
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
19
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20
+ * GNU General Public License for more details.
21
+ *
22
+ * You should have received a copy of the GNU General Public License
23
+ * along with WP Rollback. If not, see <http://www.gnu.org/licenses/>.
24
+ *
25
+ */
26
+
27
+ // Exit if accessed directly
28
+ if ( ! defined( 'ABSPATH' ) ) {
29
+ exit;
30
+ }
31
+
32
+ if ( ! class_exists( 'WP Rollback' ) ) : /**
33
+ * Main WP Rollback Class
34
+ *
35
+ * @since 1.0
36
+ */ {
37
+ final class WP_Rollback {
38
+ /** Singleton *************************************************************/
39
+
40
+ /**
41
+ * @var WP_Rollback The one and only
42
+ * @since 1.0
43
+ */
44
+ private static $instance;
45
+
46
+ /**
47
+ * WP_Rollback Settings Object
48
+ *
49
+ * @var object
50
+ * @since 1.0
51
+ */
52
+ public $wpr_settings;
53
+
54
+ public $plugins_repo = 'http://plugins.svn.wordpress.org';
55
+
56
+ public $themes_repo = 'http://themes.svn.wordpress.org';
57
+
58
+ public $plugin_file;
59
+
60
+ public $plugin_slug;
61
+
62
+ public $versions;
63
+
64
+ public $current_version;
65
+
66
+
67
+ /**
68
+ * Main WP_Rollback Instance
69
+ *
70
+ * Insures that only one instance of WP Rollback exists in memory at any one
71
+ * time. Also prevents needing to define globals all over the place.
72
+ *
73
+ * @since 1.0
74
+ * @static
75
+ * @staticvar array $instance
76
+ * @uses WP_Rollback::setup_constants() Setup the constants needed
77
+ * @uses WP_Rollback::includes() Include the required files
78
+ * @uses WP_Rollback::load_textdomain() load the language files
79
+ * @see WP_Rollback()
80
+ * @return WP_Rollback
81
+ */
82
+ public static function instance() {
83
+ if ( ! isset( self::$instance ) && ! ( self::$instance instanceof WP_Rollback ) && is_admin() ) {
84
+ self::$instance = new WP_Rollback;
85
+ self::$instance->setup_constants();
86
+ self::$instance->setup_vars();
87
+
88
+ //i18n
89
+ add_action( 'plugins_loaded', array( self::$instance, 'load_textdomain' ) );
90
+ //Admin
91
+ add_action( 'admin_enqueue_scripts', array( self::$instance, 'scripts' ) );
92
+ add_action( 'admin_menu', array( self::$instance, 'admin_menu' ), 20 );
93
+ add_action( 'pre_current_active_plugins', array(
94
+ self::$instance,
95
+ 'pre_current_active_plugins'
96
+ ), 20, 1 );
97
+ add_action( 'wp_ajax_is_wordpress_theme', array( self::$instance, 'is_wordpress_theme' ) );
98
+ add_action( 'set_site_transient_update_themes', array( self::$instance, 'wpr_theme_updates_list' ) );
99
+
100
+ add_filter( 'wp_prepare_themes_for_js', array( self::$instance, 'wpr_prepare_themes_js' ) );
101
+ add_filter( 'plugin_action_links', array( self::$instance, 'plugin_action_links' ), 20, 4 );
102
+
103
+ self::$instance->includes();
104
+
105
+ }
106
+
107
+ return self::$instance;
108
+ }
109
+
110
+ /**
111
+ * Throw error on object clone
112
+ *
113
+ * The whole idea of the singleton design pattern is that there is a single
114
+ * object therefore, we don't want the object to be cloned.
115
+ *
116
+ * @since 1.0
117
+ * @access protected
118
+ * @return void
119
+ */
120
+ public function __clone() {
121
+ // Cloning instances of the class is forbidden
122
+ _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'wpr' ), '1.0' );
123
+ }
124
+
125
+ /**
126
+ * Disable unserializing of the class
127
+ *
128
+ * @since 1.0
129
+ * @access protected
130
+ * @return void
131
+ */
132
+ public function __wakeup() {
133
+ // Unserializing instances of the class is forbidden
134
+ _doing_it_wrong( __FUNCTION__, __( 'Cheatin&#8217; huh?', 'wpr' ), '1.0' );
135
+ }
136
+
137
+ /**
138
+ * Setup plugin constants
139
+ *
140
+ * @access private
141
+ * @since 1.0
142
+ * @return void
143
+ */
144
+ private function setup_constants() {
145
+
146
+ // Plugin version
147
+ if ( ! defined( 'WP_ROLLBACK_VERSION' ) ) {
148
+ define( 'WP_ROLLBACK_VERSION', '1.0' );
149
+ }
150
+
151
+ // Plugin Folder Path
152
+ if ( ! defined( 'WP_ROLLBACK_PLUGIN_DIR' ) ) {
153
+ define( 'WP_ROLLBACK_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
154
+ }
155
+
156
+ // Plugin Folder URL
157
+ if ( ! defined( 'WP_ROLLBACK_PLUGIN_URL' ) ) {
158
+ define( 'WP_ROLLBACK_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
159
+ }
160
+
161
+ // Plugin Root File
162
+ if ( ! defined( 'WP_ROLLBACK_PLUGIN_FILE' ) ) {
163
+ define( 'WP_ROLLBACK_PLUGIN_FILE', __FILE__ );
164
+ }
165
+
166
+ }
167
+
168
+ /**
169
+ * Setup Variables
170
+ *
171
+ * @access private
172
+ * @description:
173
+ */
174
+ private function setup_vars() {
175
+ $this->set_plugin_slug();
176
+
177
+ $svn_tags = $this->get_svn_tags( 'plugin', $this->plugin_slug );
178
+ $this->set_svn_versions_data( $svn_tags );
179
+ }
180
+
181
+ /**
182
+ * Include required files
183
+ *
184
+ * @access private
185
+ * @since 1.0
186
+ * @return void
187
+ */
188
+ private function includes() {
189
+
190
+ }
191
+
192
+ /**
193
+ * Enqueue Admin Scripts
194
+ *
195
+ * @access private
196
+ * @since 1.0
197
+ *
198
+ * @param $hook
199
+ *
200
+ * @return void
201
+ *
202
+ */
203
+ public function scripts( $hook ) {
204
+
205
+ if ( $hook === 'themes.php' ) {
206
+ wp_enqueue_script( 'wp_rollback_themes_script', plugin_dir_url( __FILE__ ) . 'assets/js/themes-wp-rollback.js', array( 'jquery' ), false, true );
207
+ //Localize for i18n
208
+ wp_localize_script( 'wp_rollback_themes_script', 'wpr_vars', array(
209
+ 'ajaxurl' => admin_url(),
210
+ 'ajax_loader' => admin_url( 'images/spinner.gif' ),
211
+ 'text_rollback_label' => __( 'Rollback', 'wpr' ),
212
+ 'text_not_rollbackable' => __( 'No Rollback Available: This is a non-WordPress.org theme.', 'wpr' ),
213
+ 'text_loading_rollback' => __( 'Loading...', 'wpr' ),
214
+ ) );
215
+ }
216
+
217
+ if ( $hook !== 'dashboard_page_wp-rollback' ) {
218
+ return;
219
+ }
220
+
221
+ $suffix = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? '' : '.min';
222
+
223
+ wp_enqueue_style( 'wp_rollback_css', plugin_dir_url( __FILE__ ) . 'assets/css/wp-rollback.css' );
224
+ wp_enqueue_style( 'wp_rollback_modal_css', plugin_dir_url( __FILE__ ) . 'assets/css/magnific-popup.css' );
225
+ wp_enqueue_script( 'wp_rollback_modal', plugin_dir_url( __FILE__ ) . 'assets/js/jquery.magnific-popup' . $suffix . '.js', array( 'jquery' ) );
226
+ wp_enqueue_script( 'wp_rollback_script', plugin_dir_url( __FILE__ ) . 'assets/js/wp-rollback.js', array( 'jquery' ) );
227
+ wp_enqueue_script( 'updates' );
228
+
229
+ //Localize for i18n
230
+ wp_localize_script( 'wp_rollback_script', 'wpr_vars', array(
231
+ 'ajaxurl' => admin_url(),
232
+ 'version_missing' => __( 'Please select a version number to perform a rollback.', 'wpr' ),
233
+ ) );
234
+
235
+
236
+ }
237
+
238
+ /**
239
+ * Loads the plugin language files
240
+ *
241
+ * @access public
242
+ * @since 1.0
243
+ * @return void
244
+ */
245
+ public function load_textdomain() {
246
+
247
+ // Set filter for plugin's languages directory
248
+ $wpr_lang_dir = dirname( plugin_basename( WP_ROLLBACK_PLUGIN_FILE ) ) . '/languages/';
249
+ $wpr_lang_dir = apply_filters( 'wpr_languages_directory', $wpr_lang_dir );
250
+
251
+ // Traditional WordPress plugin locale filter
252
+ $locale = apply_filters( 'plugin_locale', get_locale(), 'wpr' );
253
+ $mofile = sprintf( '%1$s-%2$s.mo', 'wpr', $locale );
254
+
255
+ // Setup paths to current locale file
256
+ $mofile_local = $wpr_lang_dir . $mofile;
257
+ $mofile_global = WP_LANG_DIR . '/wp-rollback/' . $mofile;
258
+
259
+ if ( file_exists( $mofile_global ) ) {
260
+ // Look in global /wp-content/languages/wpr folder
261
+ load_textdomain( 'wpr', $mofile_global );
262
+ } elseif ( file_exists( $mofile_local ) ) {
263
+ // Look in local /wp-content/plugins/wpr/languages/ folder
264
+ load_textdomain( 'wpr', $mofile_local );
265
+ } else {
266
+ // Load the default language files
267
+ load_plugin_textdomain( 'wpr', false, $wpr_lang_dir );
268
+ }
269
+ }
270
+
271
+ /**
272
+ * HTML
273
+ *
274
+ * @description: FILL ME IN
275
+ *
276
+ */
277
+ public function html() {
278
+
279
+ if ( ! current_user_can( 'update_plugins' ) ) {
280
+ wp_die( __( 'You do not have sufficient permissions to perform rollbacks for this site.', 'wpr' ) );
281
+ }
282
+
283
+ //Get the necessary class
284
+ include_once( ABSPATH . 'wp-admin/includes/class-wp-upgrader.php' );
285
+
286
+ $defaults = apply_filters( 'wpr_rollback_html_args', array(
287
+ 'page' => 'wp-rollback',
288
+ 'plugin_file' => '',
289
+ 'action' => '',
290
+ 'plugin_version' => '',
291
+ 'plugin' => ''
292
+ ) );
293
+
294
+ $args = wp_parse_args( $_GET, $defaults );
295
+
296
+ if ( ! empty( $args['plugin_version'] ) ) {
297
+ //Plugin: rolling back
298
+ include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-plugin-upgrader.php';
299
+ include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
300
+ } elseif ( ! empty( $args['theme_version'] ) ) {
301
+ //Theme: rolling back
302
+ include WP_ROLLBACK_PLUGIN_DIR . '/includes/class-rollback-theme-upgrader.php';
303
+ include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-action.php';
304
+ } else {
305
+ //This is the menu
306
+ include WP_ROLLBACK_PLUGIN_DIR . '/includes/rollback-menu.php';
307
+ }
308
+
309
+ }
310
+
311
+
312
+ /**
313
+ * Get Subversion Tags
314
+ *
315
+ * @description cURLs wp.org repo to get the proper tags
316
+ *
317
+ * @param $type
318
+ * @param $slug
319
+ *
320
+ * @return null|string
321
+ */
322
+ private function get_svn_tags( $type, $slug ) {
323
+
324
+ $url = $this->plugins_repo . '/' . $this->plugin_slug . '/tags/';
325
+
326
+ //is this a theme svn request?
327
+ if ( $type == 'theme' ) {
328
+ $url = $this->themes_repo . '/' . $slug;
329
+ }
330
+
331
+ $response = wp_remote_get( $url );
332
+
333
+ //Do we have an error?
334
+ if ( wp_remote_retrieve_response_code( $response ) !== 200 ) {
335
+ return null;
336
+ }
337
+
338
+ //Nope: Return that bad boy
339
+ return wp_remote_retrieve_body( $response );
340
+
341
+ }
342
+
343
+ /**
344
+ * Set SVN Version Data
345
+ *
346
+ * @description FILL ME IN
347
+ *
348
+ * @param $html
349
+ *
350
+ * @return array|bool
351
+ */
352
+ private function set_svn_versions_data( $html ) {
353
+
354
+ if ( ! $html ) {
355
+ return false;
356
+ }
357
+
358
+ $DOM = new DOMDocument;
359
+ $DOM->loadHTML( $html );
360
+
361
+ $versions = array();
362
+
363
+ $items = $DOM->getElementsByTagName( 'a' );
364
+
365
+ foreach ( $items as $item ) {
366
+ $href = str_replace( '/', '', $item->getAttribute( 'href' ) ); //Remove trailing slash
367
+
368
+ if ( strpos( $href, 'http' ) === false && $href !== '..' ) {
369
+ $versions[] = $href;
370
+ }
371
+ }
372
+
373
+ $this->versions = array_reverse( $versions );
374
+
375
+ return $versions;
376
+ }
377
+
378
+ /**
379
+ * Versions Select
380
+ *
381
+ * @description Outputs the version radio buttons to select a rollback; types = 'plugin' or 'theme'
382
+ *
383
+ * @param $type
384
+ *
385
+ * @return bool|string
386
+ */
387
+ public function versions_select( $type ) {
388
+
389
+ if ( ! $this->versions ) {
390
+ return false;
391
+ }
392
+
393
+ $versions_html = '';
394
+
395
+ //Loop through versions and output in a radio list
396
+ foreach ( $this->versions as $version ) {
397
+
398
+ $versions_html .= '<label><input type="radio" value="' . $version . '" name="' . $type . '_version">' . $version;
399
+
400
+ //Is this the current version?
401
+ if ( $version === $this->current_version ) {
402
+ $versions_html .= '<span class="current-version">' . __( 'Installed Version', 'wpr' ) . '</span>';
403
+ }
404
+
405
+ $versions_html .= '</label>';
406
+
407
+
408
+ }
409
+
410
+ return $versions_html;
411
+ }
412
+
413
+ /**
414
+ * Set Plugin Slug
415
+ *
416
+ * @return bool
417
+ */
418
+ private function set_plugin_slug() {
419
+
420
+ if ( ! isset( $_GET['plugin_file'] ) ) {
421
+ return false;
422
+ }
423
+
424
+ if ( isset( $_GET['current_version'] ) ) {
425
+ $curr_version = explode( ' ', $_GET['current_version'] );
426
+ $this->current_version = apply_filters( 'wpr_current_version', $curr_version[0] );
427
+ }
428
+
429
+ include_once ABSPATH . 'wp-admin/includes/plugin.php';
430
+
431
+ $plugin_file = WP_PLUGIN_DIR . '/' . $_GET['plugin_file'];
432
+
433
+ $plugin_data = get_plugin_data( $plugin_file, false, false );
434
+
435
+ //the plugin slug is the base directory name without the path to the main file
436
+ $plugin_slug = explode( '/', plugin_basename( $plugin_file ) );
437
+
438
+ $this->plugin_file = apply_filters( 'wpr_plugin_file', $plugin_file );
439
+ $this->plugin_slug = apply_filters( 'wpr_plugin_slug', $plugin_slug[0] );
440
+
441
+ return $plugin_slug;
442
+
443
+ }
444
+
445
+ /**
446
+ * Admin Menu
447
+ *
448
+ * @description: Adds a 'hidden' menu item that is activated when the user elects to rollback
449
+ */
450
+ public function admin_menu() {
451
+
452
+ //Only show menu item when necessary (user is interacting with plugin, ie rolling back something)
453
+ if ( isset( $_GET['page'] ) && $_GET['page'] == 'wp-rollback' ) {
454
+ //Add it in a native WP way, like WP updates do... (a dashboard page)
455
+ add_dashboard_page( __( 'Rollback', 'wpr' ), __( 'Rollback', 'wpr' ), 'update_plugins', 'wp-rollback', array(
456
+ self::$instance,
457
+ 'html'
458
+ ) );
459
+ }
460
+
461
+ }
462
+
463
+ /**
464
+ * Pre-Current Active Plugins
465
+ *
466
+ * @param $plugins
467
+ *
468
+ * @return mixed
469
+ */
470
+ public function pre_current_active_plugins( $plugins ) {
471
+ $updated = $plugins;
472
+ foreach ( $updated as $key => $value ) {
473
+ $updated[ $key ] = $value;
474
+ $updated[ $key ]['rollback'] = true;
475
+ }
476
+
477
+ return $updated;
478
+ }
479
+
480
+
481
+ /**
482
+ * Plugin Action Links
483
+ *
484
+ * @description Adds a "rollback" link into the plugins listing page w/ appropriate query strings
485
+ *
486
+ * @param $actions
487
+ * @param $plugin_file
488
+ * @param $plugin_data
489
+ * @param $context
490
+ *
491
+ * @return array $actions
492
+ */
493
+ public function plugin_action_links( $actions, $plugin_file, $plugin_data, $context ) {
494
+
495
+ //Base rollback URL
496
+ $rollback_url = 'index.php?page=wp-rollback&type=plugin&plugin_file=' . $plugin_file;
497
+
498
+ //Filter for other devs
499
+ $plugin_data = apply_filters( 'wpr_plugin_data', $plugin_data );
500
+
501
+ //If plugin is missing package data do not output Rollback option
502
+ if ( ! isset( $plugin_data['package'] ) ) {
503
+ return $actions;
504
+ }
505
+
506
+ //Add in the current version for later reference
507
+ if ( isset( $plugin_data['Version'] ) ) {
508
+ $rollback_url = add_query_arg( apply_filters( 'wpr_plugin_query_args', array(
509
+ 'current_version' => urlencode( $plugin_data['Version'] ),
510
+ 'rollback_name' => urlencode( $plugin_data['Name'] ),
511
+ ) ), $rollback_url );
512
+ }
513
+
514
+ //Final Output
515
+ $actions['rollback'] = apply_filters( 'wpr_plugin_markup', '<a href="' . esc_url( $rollback_url ) . '">' . __( 'Rollback', 'wpr' ) . '</a>' );
516
+
517
+ return apply_filters( 'wpr_plugin_action_links', $actions );
518
+
519
+ }
520
+
521
+
522
+ /**
523
+ * Is WordPress Theme?
524
+ *
525
+ * @description Queryies the WordPress.org API via theme's slug to see if this theme is on WordPress
526
+ * @return bool
527
+ * @TODO Set transient here to speed up future checks?
528
+ */
529
+ public function is_wordpress_theme() {
530
+
531
+ $url = add_query_arg( 'request[slug]', $_POST['theme'], 'https://api.wordpress.org/themes/info/1.1/?action=theme_information' );
532
+ $wp_api = wp_remote_get( $url );
533
+
534
+ if ( ! is_wp_error( $wp_api ) ) {
535
+ if ( isset( $wp_api['body'] ) && strlen( $wp_api['body'] ) > 0 && $wp_api['body'] !== 'false' ) {
536
+ echo 'wp';
537
+ } else {
538
+ echo 'non-wp';
539
+ }
540
+ } else {
541
+ echo 'error';
542
+ }
543
+
544
+ wp_die(); // this is required to terminate immediately and return a proper response
545
+
546
+ }
547
+
548
+
549
+ /**
550
+ * Plugin Row Meta
551
+ *
552
+ * @param $plugin_meta
553
+ * @param $plugin_file
554
+ * @param $plugin_data
555
+ * @param $status
556
+ *
557
+ * @return mixed
558
+ */
559
+ public function plugin_row_meta( $plugin_meta, $plugin_file, $plugin_data, $status ) {
560
+ return $plugin_meta;
561
+ }
562
+
563
+
564
+ /**
565
+ * Updates theme list
566
+ *
567
+ * @description
568
+ *
569
+ * @return bool
570
+ */
571
+ function wpr_theme_updates_list() {
572
+
573
+ include( ABSPATH . WPINC . '/version.php' ); // include an unmodified $wp_version
574
+
575
+ //Bounce out if improperly called
576
+ if ( defined( 'WP_INSTALLING' ) || ! is_admin() ) {
577
+ return false;
578
+ }
579
+
580
+
581
+ $expiration = 12 * HOUR_IN_SECONDS;
582
+ $installed_themes = wp_get_themes();
583
+
584
+ $last_update = get_site_transient( 'update_themes' );
585
+ if ( ! is_object( $last_update ) ) {
586
+ set_site_transient( 'rollback_themes', time(), $expiration );
587
+ }
588
+
589
+ $themes = $checked = $request = array();
590
+
591
+ // Put slug of current theme into request.
592
+ $request['active'] = get_option( 'stylesheet' );
593
+
594
+ foreach ( $installed_themes as $theme ) {
595
+ $checked[ $theme->get_stylesheet() ] = $theme->get( 'Version' );
596
+
597
+ $themes[ $theme->get_stylesheet() ] = array(
598
+ 'Name' => $theme->get( 'Name' ),
599
+ 'Title' => $theme->get( 'Name' ),
600
+ 'Version' => '0.0.0.0.0.0',
601
+ 'Author' => $theme->get( 'Author' ),
602
+ 'Author URI' => $theme->get( 'AuthorURI' ),
603
+ 'Template' => $theme->get_template(),
604
+ 'Stylesheet' => $theme->get_stylesheet(),
605
+ );
606
+ }
607
+
608
+ $request['themes'] = $themes;
609
+
610
+ $timeout = 3 + (int) ( count( $themes ) / 10 );
611
+
612
+ $options = array(
613
+ 'timeout' => $timeout,
614
+ 'body' => array(
615
+ 'themes' => wp_json_encode( $request ),
616
+ ),
617
+ 'user-agent' => 'WordPress/' . $wp_version . '; ' . get_bloginfo( 'url' )
618
+ );
619
+
620
+ $url = $http_url = 'http://api.wordpress.org/themes/update-check/1.1/';
621
+ if ( $ssl = wp_http_supports( array( 'ssl' ) ) ) {
622
+ $url = set_url_scheme( $url, 'https' );
623
+ }
624
+
625
+ $raw_response = wp_remote_post( $url, $options );
626
+ if ( $ssl && is_wp_error( $raw_response ) ) {
627
+ trigger_error( __( 'An unexpected error occurred. Something may be wrong with WordPress.org or this server&#8217;s configuration. If you continue to have problems, please try the <a href="https://wordpress.org/support/">support forums</a>.' ) . ' ' . __( '(WordPress could not establish a secure connection to WordPress.org. Please contact your server administrator.)' ), headers_sent() || WP_DEBUG ? E_USER_WARNING : E_USER_NOTICE );
628
+ $raw_response = wp_remote_post( $http_url, $options );
629
+ }
630
+
631
+ set_site_transient( 'rollback_themes', time(), $expiration );
632
+
633
+ if ( is_wp_error( $raw_response ) || 200 != wp_remote_retrieve_response_code( $raw_response ) ) {
634
+ return false;
635
+ }
636
+
637
+ $new_update = new stdClass;
638
+ $new_update->last_checked = time();
639
+ $new_update->checked = $checked;
640
+
641
+ $response = json_decode( wp_remote_retrieve_body( $raw_response ), true );
642
+
643
+ if ( is_array( $response ) ) {
644
+ $new_update->response = $response['themes'];
645
+ }
646
+
647
+ set_site_transient( 'rollback_themes', $new_update );
648
+ }
649
+
650
+
651
+ /**
652
+ * Prepare Themes JS
653
+ *
654
+ * @param $prepared_themes
655
+ *
656
+ * @return array
657
+ */
658
+ function wpr_prepare_themes_js( $prepared_themes ) {
659
+ $themes = array();
660
+ $wp_themes = get_site_transient( 'rollback_themes' );
661
+ $rollbacks = $wp_themes->response;
662
+
663
+ foreach ( $prepared_themes as $key => $value ) {
664
+ $themes[ $key ] = $prepared_themes[ $key ];
665
+ $themes[ $key ]['hasRollback'] = isset( $rollbacks[ $key ] );
666
+ }
667
+
668
+ return $themes;
669
+ }
670
+
671
+
672
+ }
673
+ }
674
+
675
+ endif; // End if class_exists check
676
+
677
+
678
+ /**
679
+ * The main function responsible for returning the one true WP Rollback
680
+ * Instance to functions everywhere.
681
+ *
682
+ * Use this function like you would a global variable, except without needing
683
+ * to declare the global.
684
+ *
685
+ * Example: <?php $wp_rollback = WP_Rollback(); ?>
686
+ *
687
+ * @since 1.0
688
+ * @return object The one true WP Rollback Instance
689
+ */
690
+ function WP_Rollback() {
691
+ return WP_Rollback::instance();
692
+ }
693
+
694
+ // Get WP Rollback Running
695
+ WP_Rollback();