WP SEO Structured Data Schema - Version 1.1

Version Description

  • Layout change
  • Fix some coding bug
Download this release

Release Info

Developer kcseopro
Plugin Icon 128x128 WP SEO Structured Data Schema
Version 1.1
Comparing to
See all releases

Code changes from version 1.0 to 1.1

assets/css/admin.css CHANGED
@@ -27,6 +27,76 @@ div#tabs-kcseo-container .field-container input[type=number]{
27
  div#tabs-kcseo-container .field-container input.kcseo-date{
28
  width: auto;
29
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  li.ui-tabs-active a:focus{
31
  -webkit-box-shadow: none;
32
  box-shadow: none;
27
  div#tabs-kcseo-container .field-container input.kcseo-date{
28
  width: auto;
29
  }
30
+
31
+ /* tab */
32
+ .ui-widget {
33
+ font-size: inherit;
34
+ }
35
+
36
+
37
+ #tabs-kcseo-container ul.tabs-menu{
38
+ clear: both;
39
+ margin-top: 0;
40
+ padding: 0;
41
+ display:inline-block;
42
+ width: 100%;
43
+ margin-bottom: -4px;
44
+ }
45
+ #tabs-kcseo-container ul.tabs-menu li{
46
+ background: none repeat scroll 0 0 rgb(230, 230, 230);
47
+ cursor: pointer;
48
+ display: inline-block;
49
+ float: left;
50
+ margin: 0;
51
+ }
52
+ #tabs-kcseo-container ul.tabs-menu li:focus{
53
+ outline: none;
54
+ }
55
+ #tabs-kcseo-container ul.tabs-menu li.ui-tabs-active a , #tabs-kcseo-container ul.tabs-menu li a:hover{
56
+ background: #8cc63e;
57
+ }
58
+ #tabs-kcseo-container ul.tabs-menu li.ui-tabs-active a:after , #tabs-kcseo-container ul.tabs-menu li a:hover:after{
59
+ width: 0;
60
+ height: 0;
61
+ border-left: 10px solid transparent;
62
+ border-right: 10px solid transparent;
63
+ border-top: 5px solid #8cc63e;
64
+ position: absolute;
65
+ bottom: -5px;
66
+ left: 0;
67
+ right: 0;
68
+ text-align: center;
69
+ margin: auto;
70
+ content: "";
71
+ }
72
+ #tabs-kcseo-container ul.tabs-menu li a{
73
+ text-decoration: none;
74
+ display: block;
75
+ padding: 8px 10px;
76
+ border-right: 1px solid;
77
+ background: #0071bd;
78
+ color: #fff;
79
+ position: relative;
80
+ }
81
+ #tabs-kcseo-container ul.tabs-menu li:last-child a{
82
+ border-right: 0;
83
+ }
84
+ #tabs-kcseo-container ul.tabs-menu li a:focus{
85
+ -webkit-box-shadow: none;
86
+ box-shadow: none;
87
+ }
88
+ .kcseo-tab-container{
89
+ display: none;
90
+ border: 1px solid #e7e7e7;
91
+ padding: 15px;
92
+ }
93
+ #kcseo-settings #tabs-kcseo-container{
94
+ margin-top: 25px;
95
+ background: #fff;
96
+ }
97
+
98
+
99
+
100
  li.ui-tabs-active a:focus{
101
  -webkit-box-shadow: none;
102
  box-shadow: none;
assets/css/jquery.qtip.css ADDED
@@ -0,0 +1,617 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * qTip2 - Pretty powerful tooltips - v2.2.1
3
+ * http://qtip2.com
4
+ *
5
+ * Copyright (c) 2014
6
+ * Released under the MIT licenses
7
+ * http://jquery.org/license
8
+ *
9
+ * Date: Sat Sep 6 2014 06:25 EDT-0400
10
+ * Plugins: tips viewport imagemap svg modal ie6
11
+ * Styles: core basic css3
12
+ */
13
+ .qtip{
14
+ position: absolute;
15
+ left: -28000px;
16
+ top: -28000px;
17
+ display: none;
18
+
19
+ max-width: 280px;
20
+ min-width: 50px;
21
+
22
+ font-size: 10.5px;
23
+ line-height: 12px;
24
+
25
+ direction: ltr;
26
+
27
+ box-shadow: none;
28
+ padding: 0;
29
+ }
30
+
31
+ .qtip-content{
32
+ position: relative;
33
+ padding: 5px 9px;
34
+ overflow: hidden;
35
+
36
+ text-align: left;
37
+ word-wrap: break-word;
38
+ }
39
+
40
+ .qtip-titlebar{
41
+ position: relative;
42
+ padding: 5px 35px 5px 10px;
43
+ overflow: hidden;
44
+
45
+ border-width: 0 0 1px;
46
+ font-weight: bold;
47
+ }
48
+
49
+ .qtip-titlebar + .qtip-content{ border-top-width: 0 !important; }
50
+
51
+ /* Default close button class */
52
+ .qtip-close{
53
+ position: absolute;
54
+ right: -9px; top: -9px;
55
+ z-index: 11; /* Overlap .qtip-tip */
56
+
57
+ cursor: pointer;
58
+ outline: medium none;
59
+
60
+ border: 1px solid transparent;
61
+ }
62
+
63
+ .qtip-titlebar .qtip-close{
64
+ right: 4px; top: 50%;
65
+ margin-top: -9px;
66
+ }
67
+
68
+ * html .qtip-titlebar .qtip-close{ top: 16px; } /* IE fix */
69
+
70
+ .qtip-titlebar .ui-icon,
71
+ .qtip-icon .ui-icon{
72
+ display: block;
73
+ text-indent: -1000em;
74
+ direction: ltr;
75
+ }
76
+
77
+ .qtip-icon, .qtip-icon .ui-icon{
78
+ -moz-border-radius: 3px;
79
+ -webkit-border-radius: 3px;
80
+ border-radius: 3px;
81
+ text-decoration: none;
82
+ }
83
+
84
+ .qtip-icon .ui-icon{
85
+ width: 18px;
86
+ height: 14px;
87
+
88
+ line-height: 14px;
89
+ text-align: center;
90
+ text-indent: 0;
91
+ font: normal bold 10px/13px Tahoma,sans-serif;
92
+
93
+ color: inherit;
94
+ background: transparent none no-repeat -100em -100em;
95
+ }
96
+
97
+ /* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
98
+ .qtip-focus{}
99
+
100
+ /* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
101
+ .qtip-hover{}
102
+
103
+ /* Default tooltip style */
104
+ .qtip-default{
105
+ border: 1px solid #F1D031;
106
+
107
+ background-color: #FFFFA3;
108
+ color: #555;
109
+ }
110
+
111
+ .qtip-default .qtip-titlebar{
112
+ background-color: #FFEF93;
113
+ }
114
+
115
+ .qtip-default .qtip-icon{
116
+ border-color: #CCC;
117
+ background: #F1F1F1;
118
+ color: #777;
119
+ }
120
+
121
+ .qtip-default .qtip-titlebar .qtip-close{
122
+ border-color: #AAA;
123
+ color: #111;
124
+ }
125
+
126
+
127
+ /*! Light tooltip style */
128
+ .qtip-light{
129
+ background-color: white;
130
+ border-color: #E2E2E2;
131
+ color: #454545;
132
+ }
133
+
134
+ .qtip-light .qtip-titlebar{
135
+ background-color: #f1f1f1;
136
+ }
137
+
138
+
139
+ /*! Dark tooltip style */
140
+ .qtip-dark{
141
+ background-color: #505050;
142
+ border-color: #303030;
143
+ color: #f3f3f3;
144
+ }
145
+
146
+ .qtip-dark .qtip-titlebar{
147
+ background-color: #404040;
148
+ }
149
+
150
+ .qtip-dark .qtip-icon{
151
+ border-color: #444;
152
+ }
153
+
154
+ .qtip-dark .qtip-titlebar .ui-state-hover{
155
+ border-color: #303030;
156
+ }
157
+
158
+
159
+ /*! Cream tooltip style */
160
+ .qtip-cream{
161
+ background-color: #FBF7AA;
162
+ border-color: #F9E98E;
163
+ color: #A27D35;
164
+ }
165
+
166
+ .qtip-cream .qtip-titlebar{
167
+ background-color: #F0DE7D;
168
+ }
169
+
170
+ .qtip-cream .qtip-close .qtip-icon{
171
+ background-position: -82px 0;
172
+ }
173
+
174
+
175
+ /*! Red tooltip style */
176
+ .qtip-red{
177
+ background-color: #F78B83;
178
+ border-color: #D95252;
179
+ color: #912323;
180
+ }
181
+
182
+ .qtip-red .qtip-titlebar{
183
+ background-color: #F06D65;
184
+ }
185
+
186
+ .qtip-red .qtip-close .qtip-icon{
187
+ background-position: -102px 0;
188
+ }
189
+
190
+ .qtip-red .qtip-icon{
191
+ border-color: #D95252;
192
+ }
193
+
194
+ .qtip-red .qtip-titlebar .ui-state-hover{
195
+ border-color: #D95252;
196
+ }
197
+
198
+
199
+ /*! Green tooltip style */
200
+ .qtip-green{
201
+ background-color: #CAED9E;
202
+ border-color: #90D93F;
203
+ color: #3F6219;
204
+ }
205
+
206
+ .qtip-green .qtip-titlebar{
207
+ background-color: #B0DE78;
208
+ }
209
+
210
+ .qtip-green .qtip-close .qtip-icon{
211
+ background-position: -42px 0;
212
+ }
213
+
214
+
215
+ /*! Blue tooltip style */
216
+ .qtip-blue{
217
+ background-color: #E5F6FE;
218
+ border-color: #ADD9ED;
219
+ color: #5E99BD;
220
+ }
221
+
222
+ .qtip-blue .qtip-titlebar{
223
+ background-color: #D0E9F5;
224
+ }
225
+
226
+ .qtip-blue .qtip-close .qtip-icon{
227
+ background-position: -2px 0;
228
+ }
229
+
230
+
231
+ .qtip-shadow{
232
+ -webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
233
+ -moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
234
+ box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
235
+ }
236
+
237
+ /* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
238
+ .qtip-rounded,
239
+ .qtip-tipsy,
240
+ .qtip-bootstrap{
241
+ -moz-border-radius: 5px;
242
+ -webkit-border-radius: 5px;
243
+ border-radius: 5px;
244
+ }
245
+
246
+ .qtip-rounded .qtip-titlebar{
247
+ -moz-border-radius: 4px 4px 0 0;
248
+ -webkit-border-radius: 4px 4px 0 0;
249
+ border-radius: 4px 4px 0 0;
250
+ }
251
+
252
+ /* Youtube tooltip style */
253
+ .qtip-youtube{
254
+ -moz-border-radius: 2px;
255
+ -webkit-border-radius: 2px;
256
+ border-radius: 2px;
257
+
258
+ -webkit-box-shadow: 0 0 3px #333;
259
+ -moz-box-shadow: 0 0 3px #333;
260
+ box-shadow: 0 0 3px #333;
261
+
262
+ color: white;
263
+ border: 0 solid transparent;
264
+
265
+ background: #4A4A4A;
266
+ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
267
+ background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
268
+ background-image: -moz-linear-gradient(top,#4A4A4A 0,black 100%);
269
+ background-image: -ms-linear-gradient(top,#4A4A4A 0,black 100%);
270
+ background-image: -o-linear-gradient(top,#4A4A4A 0,black 100%);
271
+ }
272
+
273
+ .qtip-youtube .qtip-titlebar{
274
+ background-color: #4A4A4A;
275
+ background-color: rgba(0,0,0,0);
276
+ }
277
+
278
+ .qtip-youtube .qtip-content{
279
+ padding: .75em;
280
+ font: 12px arial,sans-serif;
281
+
282
+ filter: progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);
283
+ -ms-filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);";
284
+ }
285
+
286
+ .qtip-youtube .qtip-icon{
287
+ border-color: #222;
288
+ }
289
+
290
+ .qtip-youtube .qtip-titlebar .ui-state-hover{
291
+ border-color: #303030;
292
+ }
293
+
294
+
295
+ /* jQuery TOOLS Tooltip style */
296
+ .qtip-jtools{
297
+ background: #232323;
298
+ background: rgba(0, 0, 0, 0.7);
299
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323));
300
+ background-image: -moz-linear-gradient(top, #717171, #232323);
301
+ background-image: -webkit-linear-gradient(top, #717171, #232323);
302
+ background-image: -ms-linear-gradient(top, #717171, #232323);
303
+ background-image: -o-linear-gradient(top, #717171, #232323);
304
+
305
+ border: 2px solid #ddd;
306
+ border: 2px solid rgba(241,241,241,1);
307
+
308
+ -moz-border-radius: 2px;
309
+ -webkit-border-radius: 2px;
310
+ border-radius: 2px;
311
+
312
+ -webkit-box-shadow: 0 0 12px #333;
313
+ -moz-box-shadow: 0 0 12px #333;
314
+ box-shadow: 0 0 12px #333;
315
+ }
316
+
317
+ /* IE Specific */
318
+ .qtip-jtools .qtip-titlebar{
319
+ background-color: transparent;
320
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);
321
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";
322
+ }
323
+ .qtip-jtools .qtip-content{
324
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);
325
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";
326
+ }
327
+
328
+ .qtip-jtools .qtip-titlebar,
329
+ .qtip-jtools .qtip-content{
330
+ background: transparent;
331
+ color: white;
332
+ border: 0 dashed transparent;
333
+ }
334
+
335
+ .qtip-jtools .qtip-icon{
336
+ border-color: #555;
337
+ }
338
+
339
+ .qtip-jtools .qtip-titlebar .ui-state-hover{
340
+ border-color: #333;
341
+ }
342
+
343
+
344
+ /* Cluetip style */
345
+ .qtip-cluetip{
346
+ -webkit-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
347
+ -moz-box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
348
+ box-shadow: 4px 4px 5px rgba(0, 0, 0, 0.4);
349
+
350
+ background-color: #D9D9C2;
351
+ color: #111;
352
+ border: 0 dashed transparent;
353
+ }
354
+
355
+ .qtip-cluetip .qtip-titlebar{
356
+ background-color: #87876A;
357
+ color: white;
358
+ border: 0 dashed transparent;
359
+ }
360
+
361
+ .qtip-cluetip .qtip-icon{
362
+ border-color: #808064;
363
+ }
364
+
365
+ .qtip-cluetip .qtip-titlebar .ui-state-hover{
366
+ border-color: #696952;
367
+ color: #696952;
368
+ }
369
+
370
+
371
+ /* Tipsy style */
372
+ .qtip-tipsy{
373
+ background: black;
374
+ background: rgba(0, 0, 0, .87);
375
+
376
+ color: white;
377
+ border: 0 solid transparent;
378
+
379
+ font-size: 11px;
380
+ font-family: 'Lucida Grande', sans-serif;
381
+ font-weight: bold;
382
+ line-height: 16px;
383
+ text-shadow: 0 1px black;
384
+ }
385
+
386
+ .qtip-tipsy .qtip-titlebar{
387
+ padding: 6px 35px 0 10px;
388
+ background-color: transparent;
389
+ }
390
+
391
+ .qtip-tipsy .qtip-content{
392
+ padding: 6px 10px;
393
+ }
394
+
395
+ .qtip-tipsy .qtip-icon{
396
+ border-color: #222;
397
+ text-shadow: none;
398
+ }
399
+
400
+ .qtip-tipsy .qtip-titlebar .ui-state-hover{
401
+ border-color: #303030;
402
+ }
403
+
404
+
405
+ /* Tipped style */
406
+ .qtip-tipped{
407
+ border: 3px solid #959FA9;
408
+
409
+ -moz-border-radius: 3px;
410
+ -webkit-border-radius: 3px;
411
+ border-radius: 3px;
412
+
413
+ background-color: #F9F9F9;
414
+ color: #454545;
415
+
416
+ font-weight: normal;
417
+ font-family: serif;
418
+ }
419
+
420
+ .qtip-tipped .qtip-titlebar{
421
+ border-bottom-width: 0;
422
+
423
+ color: white;
424
+ background: #3A79B8;
425
+ background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D));
426
+ background-image: -webkit-linear-gradient(top, #3A79B8, #2E629D);
427
+ background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
428
+ background-image: -ms-linear-gradient(top, #3A79B8, #2E629D);
429
+ background-image: -o-linear-gradient(top, #3A79B8, #2E629D);
430
+ filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);
431
+ -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";
432
+ }
433
+
434
+ .qtip-tipped .qtip-icon{
435
+ border: 2px solid #285589;
436
+ background: #285589;
437
+ }
438
+
439
+ .qtip-tipped .qtip-icon .ui-icon{
440
+ background-color: #FBFBFB;
441
+ color: #555;
442
+ }
443
+
444
+
445
+ /**
446
+ * Twitter Bootstrap style.
447
+ *
448
+ * Tested with IE 8, IE 9, Chrome 18, Firefox 9, Opera 11.
449
+ * Does not work with IE 7.
450
+ */
451
+ .qtip-bootstrap{
452
+ /** Taken from Bootstrap body */
453
+ font-size: 14px;
454
+ line-height: 20px;
455
+ color: #333333;
456
+
457
+ /** Taken from Bootstrap .popover */
458
+ padding: 1px;
459
+ background-color: #ffffff;
460
+ border: 1px solid #ccc;
461
+ border: 1px solid rgba(0, 0, 0, 0.2);
462
+ -webkit-border-radius: 6px;
463
+ -moz-border-radius: 6px;
464
+ border-radius: 6px;
465
+ -webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
466
+ -moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
467
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
468
+ -webkit-background-clip: padding-box;
469
+ -moz-background-clip: padding;
470
+ background-clip: padding-box;
471
+ }
472
+
473
+ .qtip-bootstrap .qtip-titlebar{
474
+ /** Taken from Bootstrap .popover-title */
475
+ padding: 8px 14px;
476
+ margin: 0;
477
+ font-size: 14px;
478
+ font-weight: normal;
479
+ line-height: 18px;
480
+ background-color: #f7f7f7;
481
+ border-bottom: 1px solid #ebebeb;
482
+ -webkit-border-radius: 5px 5px 0 0;
483
+ -moz-border-radius: 5px 5px 0 0;
484
+ border-radius: 5px 5px 0 0;
485
+ }
486
+
487
+ .qtip-bootstrap .qtip-titlebar .qtip-close{
488
+ /**
489
+ * Overrides qTip2:
490
+ * .qtip-titlebar .qtip-close{
491
+ * [...]
492
+ * right: 4px;
493
+ * top: 50%;
494
+ * [...]
495
+ * border-style: solid;
496
+ * }
497
+ */
498
+ right: 11px;
499
+ top: 45%;
500
+ border-style: none;
501
+ }
502
+
503
+ .qtip-bootstrap .qtip-content{
504
+ /** Taken from Bootstrap .popover-content */
505
+ padding: 9px 14px;
506
+ }
507
+
508
+ .qtip-bootstrap .qtip-icon{
509
+ /**
510
+ * Overrides qTip2:
511
+ * .qtip-default .qtip-icon {
512
+ * border-color: #CCC;
513
+ * background: #F1F1F1;
514
+ * color: #777;
515
+ * }
516
+ */
517
+ background: transparent;
518
+ }
519
+
520
+ .qtip-bootstrap .qtip-icon .ui-icon{
521
+ /**
522
+ * Overrides qTip2:
523
+ * .qtip-icon .ui-icon{
524
+ * width: 18px;
525
+ * height: 14px;
526
+ * }
527
+ */
528
+ width: auto;
529
+ height: auto;
530
+
531
+ /* Taken from Bootstrap .close */
532
+ float: right;
533
+ font-size: 20px;
534
+ font-weight: bold;
535
+ line-height: 18px;
536
+ color: #000000;
537
+ text-shadow: 0 1px 0 #ffffff;
538
+ opacity: 0.2;
539
+ filter: alpha(opacity=20);
540
+ }
541
+
542
+ .qtip-bootstrap .qtip-icon .ui-icon:hover{
543
+ /* Taken from Bootstrap .close:hover */
544
+ color: #000000;
545
+ text-decoration: none;
546
+ cursor: pointer;
547
+ opacity: 0.4;
548
+ filter: alpha(opacity=40);
549
+ }
550
+
551
+
552
+ /* IE9 fix - removes all filters */
553
+ .qtip:not(.ie9haxors) div.qtip-content,
554
+ .qtip:not(.ie9haxors) div.qtip-titlebar{
555
+ filter: none;
556
+ -ms-filter: none;
557
+ }
558
+
559
+
560
+ .qtip .qtip-tip{
561
+ margin: 0 auto;
562
+ overflow: hidden;
563
+ z-index: 10;
564
+
565
+ }
566
+
567
+ /* Opera bug #357 - Incorrect tip position
568
+ https://github.com/Craga89/qTip2/issues/367 */
569
+ x:-o-prefocus, .qtip .qtip-tip{
570
+ visibility: hidden;
571
+ }
572
+
573
+ .qtip .qtip-tip,
574
+ .qtip .qtip-tip .qtip-vml,
575
+ .qtip .qtip-tip canvas{
576
+ position: absolute;
577
+
578
+ color: #123456;
579
+ background: transparent;
580
+ border: 0 dashed transparent;
581
+ }
582
+
583
+ .qtip .qtip-tip canvas{ top: 0; left: 0; }
584
+
585
+ .qtip .qtip-tip .qtip-vml{
586
+ behavior: url(#default#VML);
587
+ display: inline-block;
588
+ visibility: visible;
589
+ }
590
+
591
+
592
+ #qtip-overlay{
593
+ position: fixed;
594
+ left: 0; top: 0;
595
+ width: 100%; height: 100%;
596
+ }
597
+
598
+ /* Applied to modals with show.modal.blur set to true */
599
+ #qtip-overlay.blurs{ cursor: pointer; }
600
+
601
+ /* Change opacity of overlay here */
602
+ #qtip-overlay div{
603
+ position: absolute;
604
+ left: 0; top: 0;
605
+ width: 100%; height: 100%;
606
+
607
+ background-color: black;
608
+
609
+ opacity: 0.7;
610
+ filter:alpha(opacity=70);
611
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
612
+ }
613
+
614
+
615
+ .qtipmodal-ie6fix{
616
+ position: absolute !important;
617
+ }
assets/js/jquery.qtip.js ADDED
@@ -0,0 +1,3451 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * qTip2 - Pretty powerful tooltips - v2.2.1
3
+ * http://qtip2.com
4
+ *
5
+ * Copyright (c) 2014
6
+ * Released under the MIT licenses
7
+ * http://jquery.org/license
8
+ *
9
+ * Date: Sat Sep 6 2014 06:25 EDT-0400
10
+ * Plugins: tips viewport imagemap svg modal ie6
11
+ * Styles: core basic css3
12
+ */
13
+ /*global window: false, jQuery: false, console: false, define: false */
14
+
15
+ /* Cache window, document, undefined */
16
+ (function( window, document, undefined ) {
17
+
18
+ // Uses AMD or browser globals to create a jQuery plugin.
19
+ (function( factory ) {
20
+ "use strict";
21
+ if(typeof define === 'function' && define.amd) {
22
+ define(['jquery'], factory);
23
+ }
24
+ else if(jQuery && !jQuery.fn.qtip) {
25
+ factory(jQuery);
26
+ }
27
+ }
28
+ (function($) {
29
+ "use strict"; // Enable ECMAScript "strict" operation for this function. See more: http://ejohn.org/blog/ecmascript-5-strict-mode-json-and-more/
30
+ ;// Munge the primitives - Paul Irish tip
31
+ var TRUE = true,
32
+ FALSE = false,
33
+ NULL = null,
34
+
35
+ // Common variables
36
+ X = 'x', Y = 'y',
37
+ WIDTH = 'width',
38
+ HEIGHT = 'height',
39
+
40
+ // Positioning sides
41
+ TOP = 'top',
42
+ LEFT = 'left',
43
+ BOTTOM = 'bottom',
44
+ RIGHT = 'right',
45
+ CENTER = 'center',
46
+
47
+ // Position adjustment types
48
+ FLIP = 'flip',
49
+ FLIPINVERT = 'flipinvert',
50
+ SHIFT = 'shift',
51
+
52
+ // Shortcut vars
53
+ QTIP, PROTOTYPE, CORNER, CHECKS,
54
+ PLUGINS = {},
55
+ NAMESPACE = 'qtip',
56
+ ATTR_HAS = 'data-hasqtip',
57
+ ATTR_ID = 'data-qtip-id',
58
+ WIDGET = ['ui-widget', 'ui-tooltip'],
59
+ SELECTOR = '.'+NAMESPACE,
60
+ INACTIVE_EVENTS = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' '),
61
+
62
+ CLASS_FIXED = NAMESPACE+'-fixed',
63
+ CLASS_DEFAULT = NAMESPACE + '-default',
64
+ CLASS_FOCUS = NAMESPACE + '-focus',
65
+ CLASS_HOVER = NAMESPACE + '-hover',
66
+ CLASS_DISABLED = NAMESPACE+'-disabled',
67
+
68
+ replaceSuffix = '_replacedByqTip',
69
+ oldtitle = 'oldtitle',
70
+ trackingBound,
71
+
72
+ // Browser detection
73
+ BROWSER = {
74
+ /*
75
+ * IE version detection
76
+ *
77
+ * Adapted from: http://ajaxian.com/archives/attack-of-the-ie-conditional-comment
78
+ * Credit to James Padolsey for the original implemntation!
79
+ */
80
+ ie: (function(){
81
+ for (
82
+ var v = 4, i = document.createElement("div");
83
+ (i.innerHTML = "<!--[if gt IE " + v + "]><i></i><![endif]-->") && i.getElementsByTagName("i")[0];
84
+ v+=1
85
+ ) {}
86
+ return v > 4 ? v : NaN;
87
+ }()),
88
+
89
+ /*
90
+ * iOS version detection
91
+ */
92
+ iOS: parseFloat(
93
+ ('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
94
+ .replace('undefined', '3_2').replace('_', '.').replace('_', '')
95
+ ) || FALSE
96
+ };
97
+ ;function QTip(target, options, id, attr) {
98
+ // Elements and ID
99
+ this.id = id;
100
+ this.target = target;
101
+ this.tooltip = NULL;
102
+ this.elements = { target: target };
103
+
104
+ // Internal constructs
105
+ this._id = NAMESPACE + '-' + id;
106
+ this.timers = { img: {} };
107
+ this.options = options;
108
+ this.plugins = {};
109
+
110
+ // Cache object
111
+ this.cache = {
112
+ event: {},
113
+ target: $(),
114
+ disabled: FALSE,
115
+ attr: attr,
116
+ onTooltip: FALSE,
117
+ lastClass: ''
118
+ };
119
+
120
+ // Set the initial flags
121
+ this.rendered = this.destroyed = this.disabled = this.waiting =
122
+ this.hiddenDuringWait = this.positioning = this.triggering = FALSE;
123
+ }
124
+ PROTOTYPE = QTip.prototype;
125
+
126
+ PROTOTYPE._when = function(deferreds) {
127
+ return $.when.apply($, deferreds);
128
+ };
129
+
130
+ PROTOTYPE.render = function(show) {
131
+ if(this.rendered || this.destroyed) { return this; } // If tooltip has already been rendered, exit
132
+
133
+ var self = this,
134
+ options = this.options,
135
+ cache = this.cache,
136
+ elements = this.elements,
137
+ text = options.content.text,
138
+ title = options.content.title,
139
+ button = options.content.button,
140
+ posOptions = options.position,
141
+ namespace = '.'+this._id+' ',
142
+ deferreds = [],
143
+ tooltip;
144
+
145
+ // Add ARIA attributes to target
146
+ $.attr(this.target[0], 'aria-describedby', this._id);
147
+
148
+ // Create public position object that tracks current position corners
149
+ cache.posClass = this._createPosClass(
150
+ (this.position = { my: posOptions.my, at: posOptions.at }).my
151
+ );
152
+
153
+ // Create tooltip element
154
+ this.tooltip = elements.tooltip = tooltip = $('<div/>', {
155
+ 'id': this._id,
156
+ 'class': [ NAMESPACE, CLASS_DEFAULT, options.style.classes, cache.posClass ].join(' '),
157
+ 'width': options.style.width || '',
158
+ 'height': options.style.height || '',
159
+ 'tracking': posOptions.target === 'mouse' && posOptions.adjust.mouse,
160
+
161
+ /* ARIA specific attributes */
162
+ 'role': 'alert',
163
+ 'aria-live': 'polite',
164
+ 'aria-atomic': FALSE,
165
+ 'aria-describedby': this._id + '-content',
166
+ 'aria-hidden': TRUE
167
+ })
168
+ .toggleClass(CLASS_DISABLED, this.disabled)
169
+ .attr(ATTR_ID, this.id)
170
+ .data(NAMESPACE, this)
171
+ .appendTo(posOptions.container)
172
+ .append(
173
+ // Create content element
174
+ elements.content = $('<div />', {
175
+ 'class': NAMESPACE + '-content',
176
+ 'id': this._id + '-content',
177
+ 'aria-atomic': TRUE
178
+ })
179
+ );
180
+
181
+ // Set rendered flag and prevent redundant reposition calls for now
182
+ this.rendered = -1;
183
+ this.positioning = TRUE;
184
+
185
+ // Create title...
186
+ if(title) {
187
+ this._createTitle();
188
+
189
+ // Update title only if its not a callback (called in toggle if so)
190
+ if(!$.isFunction(title)) {
191
+ deferreds.push( this._updateTitle(title, FALSE) );
192
+ }
193
+ }
194
+
195
+ // Create button
196
+ if(button) { this._createButton(); }
197
+
198
+ // Set proper rendered flag and update content if not a callback function (called in toggle)
199
+ if(!$.isFunction(text)) {
200
+ deferreds.push( this._updateContent(text, FALSE) );
201
+ }
202
+ this.rendered = TRUE;
203
+
204
+ // Setup widget classes
205
+ this._setWidget();
206
+
207
+ // Initialize 'render' plugins
208
+ $.each(PLUGINS, function(name) {
209
+ var instance;
210
+ if(this.initialize === 'render' && (instance = this(self))) {
211
+ self.plugins[name] = instance;
212
+ }
213
+ });
214
+
215
+ // Unassign initial events and assign proper events
216
+ this._unassignEvents();
217
+ this._assignEvents();
218
+
219
+ // When deferreds have completed
220
+ this._when(deferreds).then(function() {
221
+ // tooltiprender event
222
+ self._trigger('render');
223
+
224
+ // Reset flags
225
+ self.positioning = FALSE;
226
+
227
+ // Show tooltip if not hidden during wait period
228
+ if(!self.hiddenDuringWait && (options.show.ready || show)) {
229
+ self.toggle(TRUE, cache.event, FALSE);
230
+ }
231
+ self.hiddenDuringWait = FALSE;
232
+ });
233
+
234
+ // Expose API
235
+ QTIP.api[this.id] = this;
236
+
237
+ return this;
238
+ };
239
+
240
+ PROTOTYPE.destroy = function(immediate) {
241
+ // Set flag the signify destroy is taking place to plugins
242
+ // and ensure it only gets destroyed once!
243
+ if(this.destroyed) { return this.target; }
244
+
245
+ function process() {
246
+ if(this.destroyed) { return; }
247
+ this.destroyed = TRUE;
248
+
249
+ var target = this.target,
250
+ title = target.attr(oldtitle),
251
+ timer;
252
+
253
+ // Destroy tooltip if rendered
254
+ if(this.rendered) {
255
+ this.tooltip.stop(1,0).find('*').remove().end().remove();
256
+ }
257
+
258
+ // Destroy all plugins
259
+ $.each(this.plugins, function(name) {
260
+ this.destroy && this.destroy();
261
+ });
262
+
263
+ // Clear timers
264
+ for(timer in this.timers) {
265
+ clearTimeout(this.timers[timer]);
266
+ }
267
+
268
+ // Remove api object and ARIA attributes
269
+ target.removeData(NAMESPACE)
270
+ .removeAttr(ATTR_ID)
271
+ .removeAttr(ATTR_HAS)
272
+ .removeAttr('aria-describedby');
273
+
274
+ // Reset old title attribute if removed
275
+ if(this.options.suppress && title) {
276
+ target.attr('title', title).removeAttr(oldtitle);
277
+ }
278
+
279
+ // Remove qTip events associated with this API
280
+ this._unassignEvents();
281
+
282
+ // Remove ID from used id objects, and delete object references
283
+ // for better garbage collection and leak protection
284
+ this.options = this.elements = this.cache = this.timers =
285
+ this.plugins = this.mouse = NULL;
286
+
287
+ // Delete epoxsed API object
288
+ delete QTIP.api[this.id];
289
+ }
290
+
291
+ // If an immediate destory is needed
292
+ if((immediate !== TRUE || this.triggering === 'hide') && this.rendered) {
293
+ this.tooltip.one('tooltiphidden', $.proxy(process, this));
294
+ !this.triggering && this.hide();
295
+ }
296
+
297
+ // If we're not in the process of hiding... process
298
+ else { process.call(this); }
299
+
300
+ return this.target;
301
+ };
302
+ ;function invalidOpt(a) {
303
+ return a === NULL || $.type(a) !== 'object';
304
+ }
305
+
306
+ function invalidContent(c) {
307
+ return !( $.isFunction(c) || (c && c.attr) || c.length || ($.type(c) === 'object' && (c.jquery || c.then) ));
308
+ }
309
+
310
+ // Option object sanitizer
311
+ function sanitizeOptions(opts) {
312
+ var content, text, ajax, once;
313
+
314
+ if(invalidOpt(opts)) { return FALSE; }
315
+
316
+ if(invalidOpt(opts.metadata)) {
317
+ opts.metadata = { type: opts.metadata };
318
+ }
319
+
320
+ if('content' in opts) {
321
+ content = opts.content;
322
+
323
+ if(invalidOpt(content) || content.jquery || content.done) {
324
+ content = opts.content = {
325
+ text: (text = invalidContent(content) ? FALSE : content)
326
+ };
327
+ }
328
+ else { text = content.text; }
329
+
330
+ // DEPRECATED - Old content.ajax plugin functionality
331
+ // Converts it into the proper Deferred syntax
332
+ if('ajax' in content) {
333
+ ajax = content.ajax;
334
+ once = ajax && ajax.once !== FALSE;
335
+ delete content.ajax;
336
+
337
+ content.text = function(event, api) {
338
+ var loading = text || $(this).attr(api.options.content.attr) || 'Loading...',
339
+
340
+ deferred = $.ajax(
341
+ $.extend({}, ajax, { context: api })
342
+ )
343
+ .then(ajax.success, NULL, ajax.error)
344
+ .then(function(content) {
345
+ if(content && once) { api.set('content.text', content); }
346
+ return content;
347
+ },
348
+ function(xhr, status, error) {
349
+ if(api.destroyed || xhr.status === 0) { return; }
350
+ api.set('content.text', status + ': ' + error);
351
+ });
352
+
353
+ return !once ? (api.set('content.text', loading), deferred) : loading;
354
+ };
355
+ }
356
+
357
+ if('title' in content) {
358
+ if($.isPlainObject(content.title)) {
359
+ content.button = content.title.button;
360
+ content.title = content.title.text;
361
+ }
362
+
363
+ if(invalidContent(content.title || FALSE)) {
364
+ content.title = FALSE;
365
+ }
366
+ }
367
+ }
368
+
369
+ if('position' in opts && invalidOpt(opts.position)) {
370
+ opts.position = { my: opts.position, at: opts.position };
371
+ }
372
+
373
+ if('show' in opts && invalidOpt(opts.show)) {
374
+ opts.show = opts.show.jquery ? { target: opts.show } :
375
+ opts.show === TRUE ? { ready: TRUE } : { event: opts.show };
376
+ }
377
+
378
+ if('hide' in opts && invalidOpt(opts.hide)) {
379
+ opts.hide = opts.hide.jquery ? { target: opts.hide } : { event: opts.hide };
380
+ }
381
+
382
+ if('style' in opts && invalidOpt(opts.style)) {
383
+ opts.style = { classes: opts.style };
384
+ }
385
+
386
+ // Sanitize plugin options
387
+ $.each(PLUGINS, function() {
388
+ this.sanitize && this.sanitize(opts);
389
+ });
390
+
391
+ return opts;
392
+ }
393
+
394
+ // Setup builtin .set() option checks
395
+ CHECKS = PROTOTYPE.checks = {
396
+ builtin: {
397
+ // Core checks
398
+ '^id$': function(obj, o, v, prev) {
399
+ var id = v === TRUE ? QTIP.nextid : v,
400
+ new_id = NAMESPACE + '-' + id;
401
+
402
+ if(id !== FALSE && id.length > 0 && !$('#'+new_id).length) {
403
+ this._id = new_id;
404
+
405
+ if(this.rendered) {
406
+ this.tooltip[0].id = this._id;
407
+ this.elements.content[0].id = this._id + '-content';
408
+ this.elements.title[0].id = this._id + '-title';
409
+ }
410
+ }
411
+ else { obj[o] = prev; }
412
+ },
413
+ '^prerender': function(obj, o, v) {
414
+ v && !this.rendered && this.render(this.options.show.ready);
415
+ },
416
+
417
+ // Content checks
418
+ '^content.text$': function(obj, o, v) {
419
+ this._updateContent(v);
420
+ },
421
+ '^content.attr$': function(obj, o, v, prev) {
422
+ if(this.options.content.text === this.target.attr(prev)) {
423
+ this._updateContent( this.target.attr(v) );
424
+ }
425
+ },
426
+ '^content.title$': function(obj, o, v) {
427
+ // Remove title if content is null
428
+ if(!v) { return this._removeTitle(); }
429
+
430
+ // If title isn't already created, create it now and update
431
+ v && !this.elements.title && this._createTitle();
432
+ this._updateTitle(v);
433
+ },
434
+ '^content.button$': function(obj, o, v) {
435
+ this._updateButton(v);
436
+ },
437
+ '^content.title.(text|button)$': function(obj, o, v) {
438
+ this.set('content.'+o, v); // Backwards title.text/button compat
439
+ },
440
+
441
+ // Position checks
442
+ '^position.(my|at)$': function(obj, o, v){
443
+ 'string' === typeof v && (this.position[o] = obj[o] = new CORNER(v, o === 'at'));
444
+ },
445
+ '^position.container$': function(obj, o, v){
446
+ this.rendered && this.tooltip.appendTo(v);
447
+ },
448
+
449
+ // Show checks
450
+ '^show.ready$': function(obj, o, v) {
451
+ v && (!this.rendered && this.render(TRUE) || this.toggle(TRUE));
452
+ },
453
+
454
+ // Style checks
455
+ '^style.classes$': function(obj, o, v, p) {
456
+ this.rendered && this.tooltip.removeClass(p).addClass(v);
457
+ },
458
+ '^style.(width|height)': function(obj, o, v) {
459
+ this.rendered && this.tooltip.css(o, v);
460
+ },
461
+ '^style.widget|content.title': function() {
462
+ this.rendered && this._setWidget();
463
+ },
464
+ '^style.def': function(obj, o, v) {
465
+ this.rendered && this.tooltip.toggleClass(CLASS_DEFAULT, !!v);
466
+ },
467
+
468
+ // Events check
469
+ '^events.(render|show|move|hide|focus|blur)$': function(obj, o, v) {
470
+ this.rendered && this.tooltip[($.isFunction(v) ? '' : 'un') + 'bind']('tooltip'+o, v);
471
+ },
472
+
473
+ // Properties which require event reassignment
474
+ '^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)': function() {
475
+ if(!this.rendered) { return; }
476
+
477
+ // Set tracking flag
478
+ var posOptions = this.options.position;
479
+ this.tooltip.attr('tracking', posOptions.target === 'mouse' && posOptions.adjust.mouse);
480
+
481
+ // Reassign events
482
+ this._unassignEvents();
483
+ this._assignEvents();
484
+ }
485
+ }
486
+ };
487
+
488
+ // Dot notation converter
489
+ function convertNotation(options, notation) {
490
+ var i = 0, obj, option = options,
491
+
492
+ // Split notation into array
493
+ levels = notation.split('.');
494
+
495
+ // Loop through
496
+ while( option = option[ levels[i++] ] ) {
497
+ if(i < levels.length) { obj = option; }
498
+ }
499
+
500
+ return [obj || options, levels.pop()];
501
+ }
502
+
503
+ PROTOTYPE.get = function(notation) {
504
+ if(this.destroyed) { return this; }
505
+
506
+ var o = convertNotation(this.options, notation.toLowerCase()),
507
+ result = o[0][ o[1] ];
508
+
509
+ return result.precedance ? result.string() : result;
510
+ };
511
+
512
+ function setCallback(notation, args) {
513
+ var category, rule, match;
514
+
515
+ for(category in this.checks) {
516
+ for(rule in this.checks[category]) {
517
+ if(match = (new RegExp(rule, 'i')).exec(notation)) {
518
+ args.push(match);
519
+
520
+ if(category === 'builtin' || this.plugins[category]) {
521
+ this.checks[category][rule].apply(
522
+ this.plugins[category] || this, args
523
+ );
524
+ }
525
+ }
526
+ }
527
+ }
528
+ }
529
+
530
+ var rmove = /^position\.(my|at|adjust|target|container|viewport)|style|content|show\.ready/i,
531
+ rrender = /^prerender|show\.ready/i;
532
+
533
+ PROTOTYPE.set = function(option, value) {
534
+ if(this.destroyed) { return this; }
535
+
536
+ var rendered = this.rendered,
537
+ reposition = FALSE,
538
+ options = this.options,
539
+ checks = this.checks,
540
+ name;
541
+
542
+ // Convert singular option/value pair into object form
543
+ if('string' === typeof option) {
544
+ name = option; option = {}; option[name] = value;
545
+ }
546
+ else { option = $.extend({}, option); }
547
+
548
+ // Set all of the defined options to their new values
549
+ $.each(option, function(notation, value) {
550
+ if(rendered && rrender.test(notation)) {
551
+ delete option[notation]; return;
552
+ }
553
+
554
+ // Set new obj value
555
+ var obj = convertNotation(options, notation.toLowerCase()), previous;
556
+ previous = obj[0][ obj[1] ];
557
+ obj[0][ obj[1] ] = value && value.nodeType ? $(value) : value;
558
+
559
+ // Also check if we need to reposition
560
+ reposition = rmove.test(notation) || reposition;
561
+
562
+ // Set the new params for the callback
563
+ option[notation] = [obj[0], obj[1], value, previous];
564
+ });
565
+
566
+ // Re-sanitize options
567
+ sanitizeOptions(options);
568
+
569
+ /*
570
+ * Execute any valid callbacks for the set options
571
+ * Also set positioning flag so we don't get loads of redundant repositioning calls.
572
+ */
573
+ this.positioning = TRUE;
574
+ $.each(option, $.proxy(setCallback, this));
575
+ this.positioning = FALSE;
576
+
577
+ // Update position if needed
578
+ if(this.rendered && this.tooltip[0].offsetWidth > 0 && reposition) {
579
+ this.reposition( options.position.target === 'mouse' ? NULL : this.cache.event );
580
+ }
581
+
582
+ return this;
583
+ };
584
+ ;PROTOTYPE._update = function(content, element, reposition) {
585
+ var self = this,
586
+ cache = this.cache;
587
+
588
+ // Make sure tooltip is rendered and content is defined. If not return
589
+ if(!this.rendered || !content) { return FALSE; }
590
+
591
+ // Use function to parse content
592
+ if($.isFunction(content)) {
593
+ content = content.call(this.elements.target, cache.event, this) || '';
594
+ }
595
+
596
+ // Handle deferred content
597
+ if($.isFunction(content.then)) {
598
+ cache.waiting = TRUE;
599
+ return content.then(function(c) {
600
+ cache.waiting = FALSE;
601
+ return self._update(c, element);
602
+ }, NULL, function(e) {
603
+ return self._update(e, element);
604
+ });
605
+ }
606
+
607
+ // If content is null... return false
608
+ if(content === FALSE || (!content && content !== '')) { return FALSE; }
609
+
610
+ // Append new content if its a DOM array and show it if hidden
611
+ if(content.jquery && content.length > 0) {
612
+ element.empty().append(
613
+ content.css({ display: 'block', visibility: 'visible' })
614
+ );
615
+ }
616
+
617
+ // Content is a regular string, insert the new content
618
+ else { element.html(content); }
619
+
620
+ // Wait for content to be loaded, and reposition
621
+ return this._waitForContent(element).then(function(images) {
622
+ if(self.rendered && self.tooltip[0].offsetWidth > 0) {
623
+ self.reposition(cache.event, !images.length);
624
+ }
625
+ });
626
+ };
627
+
628
+ PROTOTYPE._waitForContent = function(element) {
629
+ var cache = this.cache;
630
+
631
+ // Set flag
632
+ cache.waiting = TRUE;
633
+
634
+ // If imagesLoaded is included, ensure images have loaded and return promise
635
+ return ( $.fn.imagesLoaded ? element.imagesLoaded() : $.Deferred().resolve([]) )
636
+ .done(function() { cache.waiting = FALSE; })
637
+ .promise();
638
+ };
639
+
640
+ PROTOTYPE._updateContent = function(content, reposition) {
641
+ this._update(content, this.elements.content, reposition);
642
+ };
643
+
644
+ PROTOTYPE._updateTitle = function(content, reposition) {
645
+ if(this._update(content, this.elements.title, reposition) === FALSE) {
646
+ this._removeTitle(FALSE);
647
+ }
648
+ };
649
+
650
+ PROTOTYPE._createTitle = function()
651
+ {
652
+ var elements = this.elements,
653
+ id = this._id+'-title';
654
+
655
+ // Destroy previous title element, if present
656
+ if(elements.titlebar) { this._removeTitle(); }
657
+
658
+ // Create title bar and title elements
659
+ elements.titlebar = $('<div />', {
660
+ 'class': NAMESPACE + '-titlebar ' + (this.options.style.widget ? createWidgetClass('header') : '')
661
+ })
662
+ .append(
663
+ elements.title = $('<div />', {
664
+ 'id': id,
665
+ 'class': NAMESPACE + '-title',
666
+ 'aria-atomic': TRUE
667
+ })
668
+ )
669
+ .insertBefore(elements.content)
670
+
671
+ // Button-specific events
672
+ .delegate('.qtip-close', 'mousedown keydown mouseup keyup mouseout', function(event) {
673
+ $(this).toggleClass('ui-state-active ui-state-focus', event.type.substr(-4) === 'down');
674
+ })
675
+ .delegate('.qtip-close', 'mouseover mouseout', function(event){
676
+ $(this).toggleClass('ui-state-hover', event.type === 'mouseover');
677
+ });
678
+
679
+ // Create button if enabled
680
+ if(this.options.content.button) { this._createButton(); }
681
+ };
682
+
683
+ PROTOTYPE._removeTitle = function(reposition)
684
+ {
685
+ var elements = this.elements;
686
+
687
+ if(elements.title) {
688
+ elements.titlebar.remove();
689
+ elements.titlebar = elements.title = elements.button = NULL;
690
+
691
+ // Reposition if enabled
692
+ if(reposition !== FALSE) { this.reposition(); }
693
+ }
694
+ };
695
+ ;PROTOTYPE._createPosClass = function(my) {
696
+ return NAMESPACE + '-pos-' + (my || this.options.position.my).abbrev();
697
+ };
698
+
699
+ PROTOTYPE.reposition = function(event, effect) {
700
+ if(!this.rendered || this.positioning || this.destroyed) { return this; }
701
+
702
+ // Set positioning flag
703
+ this.positioning = TRUE;
704
+
705
+ var cache = this.cache,
706
+ tooltip = this.tooltip,
707
+ posOptions = this.options.position,
708
+ target = posOptions.target,
709
+ my = posOptions.my,
710
+ at = posOptions.at,
711
+ viewport = posOptions.viewport,
712
+ container = posOptions.container,
713
+ adjust = posOptions.adjust,
714
+ method = adjust.method.split(' '),
715
+ tooltipWidth = tooltip.outerWidth(FALSE),
716
+ tooltipHeight = tooltip.outerHeight(FALSE),
717
+ targetWidth = 0,
718
+ targetHeight = 0,
719
+ type = tooltip.css('position'),
720
+ position = { left: 0, top: 0 },
721
+ visible = tooltip[0].offsetWidth > 0,
722
+ isScroll = event && event.type === 'scroll',
723
+ win = $(window),
724
+ doc = container[0].ownerDocument,
725
+ mouse = this.mouse,
726
+ pluginCalculations, offset, adjusted, newClass;
727
+
728
+ // Check if absolute position was passed
729
+ if($.isArray(target) && target.length === 2) {
730
+ // Force left top and set position
731
+ at = { x: LEFT, y: TOP };
732
+ position = { left: target[0], top: target[1] };
733
+ }
734
+
735
+ // Check if mouse was the target
736
+ else if(target === 'mouse') {
737
+ // Force left top to allow flipping
738
+ at = { x: LEFT, y: TOP };
739
+
740
+ // Use the mouse origin that caused the show event, if distance hiding is enabled
741
+ if((!adjust.mouse || this.options.hide.distance) && cache.origin && cache.origin.pageX) {
742
+ event = cache.origin;
743
+ }
744
+
745
+ // Use cached event for resize/scroll events
746
+ else if(!event || (event && (event.type === 'resize' || event.type === 'scroll'))) {
747
+ event = cache.event;
748
+ }
749
+
750
+ // Otherwise, use the cached mouse coordinates if available
751
+ else if(mouse && mouse.pageX) {
752
+ event = mouse;
753
+ }
754
+
755
+ // Calculate body and container offset and take them into account below
756
+ if(type !== 'static') { position = container.offset(); }
757
+ if(doc.body.offsetWidth !== (window.innerWidth || doc.documentElement.clientWidth)) {
758
+ offset = $(document.body).offset();
759
+ }
760
+
761
+ // Use event coordinates for position
762
+ position = {
763
+ left: event.pageX - position.left + (offset && offset.left || 0),
764
+ top: event.pageY - position.top + (offset && offset.top || 0)
765
+ };
766
+
767
+ // Scroll events are a pain, some browsers
768
+ if(adjust.mouse && isScroll && mouse) {
769
+ position.left -= (mouse.scrollX || 0) - win.scrollLeft();
770
+ position.top -= (mouse.scrollY || 0) - win.scrollTop();
771
+ }
772
+ }
773
+
774
+ // Target wasn't mouse or absolute...
775
+ else {
776
+ // Check if event targetting is being used
777
+ if(target === 'event') {
778
+ if(event && event.target && event.type !== 'scroll' && event.type !== 'resize') {
779
+ cache.target = $(event.target);
780
+ }
781
+ else if(!event.target) {
782
+ cache.target = this.elements.target;
783
+ }
784
+ }
785
+ else if(target !== 'event'){
786
+ cache.target = $(target.jquery ? target : this.elements.target);
787
+ }
788
+ target = cache.target;
789
+
790
+ // Parse the target into a jQuery object and make sure there's an element present
791
+ target = $(target).eq(0);
792
+ if(target.length === 0) { return this; }
793
+
794
+ // Check if window or document is the target
795
+ else if(target[0] === document || target[0] === window) {
796
+ targetWidth = BROWSER.iOS ? window.innerWidth : target.width();
797
+ targetHeight = BROWSER.iOS ? window.innerHeight : target.height();
798
+
799
+ if(target[0] === window) {
800
+ position = {
801
+ top: (viewport || target).scrollTop(),
802
+ left: (viewport || target).scrollLeft()
803
+ };
804
+ }
805
+ }
806
+
807
+ // Check if the target is an <AREA> element
808
+ else if(PLUGINS.imagemap && target.is('area')) {
809
+ pluginCalculations = PLUGINS.imagemap(this, target, at, PLUGINS.viewport ? method : FALSE);
810
+ }
811
+
812
+ // Check if the target is an SVG element
813
+ else if(PLUGINS.svg && target && target[0].ownerSVGElement) {
814
+ pluginCalculations = PLUGINS.svg(this, target, at, PLUGINS.viewport ? method : FALSE);
815
+ }
816
+
817
+ // Otherwise use regular jQuery methods
818
+ else {
819
+ targetWidth = target.outerWidth(FALSE);
820
+ targetHeight = target.outerHeight(FALSE);
821
+ position = target.offset();
822
+ }
823
+
824
+ // Parse returned plugin values into proper variables
825
+ if(pluginCalculations) {
826
+ targetWidth = pluginCalculations.width;
827
+ targetHeight = pluginCalculations.height;
828
+ offset = pluginCalculations.offset;
829
+ position = pluginCalculations.position;
830
+ }
831
+
832
+ // Adjust position to take into account offset parents
833
+ position = this.reposition.offset(target, position, container);
834
+
835
+ // Adjust for position.fixed tooltips (and also iOS scroll bug in v3.2-4.0 & v4.3-4.3.2)
836
+ if((BROWSER.iOS > 3.1 && BROWSER.iOS < 4.1) ||
837
+ (BROWSER.iOS >= 4.3 && BROWSER.iOS < 4.33) ||
838
+ (!BROWSER.iOS && type === 'fixed')
839
+ ){
840
+ position.left -= win.scrollLeft();
841
+ position.top -= win.scrollTop();
842
+ }
843
+
844
+ // Adjust position relative to target
845
+ if(!pluginCalculations || (pluginCalculations && pluginCalculations.adjustable !== FALSE)) {
846
+ position.left += at.x === RIGHT ? targetWidth : at.x === CENTER ? targetWidth / 2 : 0;
847
+ position.top += at.y === BOTTOM ? targetHeight : at.y === CENTER ? targetHeight / 2 : 0;
848
+ }
849
+ }
850
+
851
+ // Adjust position relative to tooltip
852
+ position.left += adjust.x + (my.x === RIGHT ? -tooltipWidth : my.x === CENTER ? -tooltipWidth / 2 : 0);
853
+ position.top += adjust.y + (my.y === BOTTOM ? -tooltipHeight : my.y === CENTER ? -tooltipHeight / 2 : 0);
854
+
855
+ // Use viewport adjustment plugin if enabled
856
+ if(PLUGINS.viewport) {
857
+ adjusted = position.adjusted = PLUGINS.viewport(
858
+ this, position, posOptions, targetWidth, targetHeight, tooltipWidth, tooltipHeight
859
+ );
860
+
861
+ // Apply offsets supplied by positioning plugin (if used)
862
+ if(offset && adjusted.left) { position.left += offset.left; }
863
+ if(offset && adjusted.top) { position.top += offset.top; }
864
+
865
+ // Apply any new 'my' position
866
+ if(adjusted.my) { this.position.my = adjusted.my; }
867
+ }
868
+
869
+ // Viewport adjustment is disabled, set values to zero
870
+ else { position.adjusted = { left: 0, top: 0 }; }
871
+
872
+ // Set tooltip position class if it's changed
873
+ if(cache.posClass !== (newClass = this._createPosClass(this.position.my))) {
874
+ tooltip.removeClass(cache.posClass).addClass( (cache.posClass = newClass) );
875
+ }
876
+
877
+ // tooltipmove event
878
+ if(!this._trigger('move', [position, viewport.elem || viewport], event)) { return this; }
879
+ delete position.adjusted;
880
+
881
+ // If effect is disabled, target it mouse, no animation is defined or positioning gives NaN out, set CSS directly
882
+ if(effect === FALSE || !visible || isNaN(position.left) || isNaN(position.top) || target === 'mouse' || !$.isFunction(posOptions.effect)) {
883
+ tooltip.css(position);
884
+ }
885
+
886
+ // Use custom function if provided
887
+ else if($.isFunction(posOptions.effect)) {
888
+ posOptions.effect.call(tooltip, this, $.extend({}, position));
889
+ tooltip.queue(function(next) {
890
+ // Reset attributes to avoid cross-browser rendering bugs
891
+ $(this).css({ opacity: '', height: '' });
892
+ if(BROWSER.ie) { this.style.removeAttribute('filter'); }
893
+
894
+ next();
895
+ });
896
+ }
897
+
898
+ // Set positioning flag
899
+ this.positioning = FALSE;
900
+
901
+ return this;
902
+ };
903
+
904
+ // Custom (more correct for qTip!) offset calculator
905
+ PROTOTYPE.reposition.offset = function(elem, pos, container) {
906
+ if(!container[0]) { return pos; }
907
+
908
+ var ownerDocument = $(elem[0].ownerDocument),
909
+ quirks = !!BROWSER.ie && document.compatMode !== 'CSS1Compat',
910
+ parent = container[0],
911
+ scrolled, position, parentOffset, overflow;
912
+
913
+ function scroll(e, i) {
914
+ pos.left += i * e.scrollLeft();
915
+ pos.top += i * e.scrollTop();
916
+ }
917
+
918
+ // Compensate for non-static containers offset
919
+ do {
920
+ if((position = $.css(parent, 'position')) !== 'static') {
921
+ if(position === 'fixed') {
922
+ parentOffset = parent.getBoundingClientRect();
923
+ scroll(ownerDocument, -1);
924
+ }
925
+ else {
926
+ parentOffset = $(parent).position();
927
+ parentOffset.left += (parseFloat($.css(parent, 'borderLeftWidth')) || 0);
928
+ parentOffset.top += (parseFloat($.css(parent, 'borderTopWidth')) || 0);
929
+ }
930
+
931
+ pos.left -= parentOffset.left + (parseFloat($.css(parent, 'marginLeft')) || 0);
932
+ pos.top -= parentOffset.top + (parseFloat($.css(parent, 'marginTop')) || 0);
933
+
934
+ // If this is the first parent element with an overflow of "scroll" or "auto", store it
935
+ if(!scrolled && (overflow = $.css(parent, 'overflow')) !== 'hidden' && overflow !== 'visible') { scrolled = $(parent); }
936
+ }
937
+ }
938
+ while((parent = parent.offsetParent));
939
+
940
+ // Compensate for containers scroll if it also has an offsetParent (or in IE quirks mode)
941
+ if(scrolled && (scrolled[0] !== ownerDocument[0] || quirks)) {
942
+ scroll(scrolled, 1);
943
+ }
944
+
945
+ return pos;
946
+ };
947
+
948
+ // Corner class
949
+ var C = (CORNER = PROTOTYPE.reposition.Corner = function(corner, forceY) {
950
+ corner = ('' + corner).replace(/([A-Z])/, ' $1').replace(/middle/gi, CENTER).toLowerCase();
951
+ this.x = (corner.match(/left|right/i) || corner.match(/center/) || ['inherit'])[0].toLowerCase();
952
+ this.y = (corner.match(/top|bottom|center/i) || ['inherit'])[0].toLowerCase();
953
+ this.forceY = !!forceY;
954
+
955
+ var f = corner.charAt(0);
956
+ this.precedance = (f === 't' || f === 'b' ? Y : X);
957
+ }).prototype;
958
+
959
+ C.invert = function(z, center) {
960
+ this[z] = this[z] === LEFT ? RIGHT : this[z] === RIGHT ? LEFT : center || this[z];
961
+ };
962
+
963
+ C.string = function(join) {
964
+ var x = this.x, y = this.y;
965
+
966
+ var result = x !== y ?
967
+ (x === 'center' || y !== 'center' && (this.precedance === Y || this.forceY) ?
968
+ [y,x] : [x,y]
969
+ ) :
970
+ [x];
971
+
972
+ return join !== false ? result.join(' ') : result;
973
+ };
974
+
975
+ C.abbrev = function() {
976
+ var result = this.string(false);
977
+ return result[0].charAt(0) + (result[1] && result[1].charAt(0) || '');
978
+ };
979
+
980
+ C.clone = function() {
981
+ return new CORNER( this.string(), this.forceY );
982
+ };
983
+
984
+ ;
985
+ PROTOTYPE.toggle = function(state, event) {
986
+ var cache = this.cache,
987
+ options = this.options,
988
+ tooltip = this.tooltip;
989
+
990
+ // Try to prevent flickering when tooltip overlaps show element
991
+ if(event) {
992
+ if((/over|enter/).test(event.type) && cache.event && (/out|leave/).test(cache.event.type) &&
993
+ options.show.target.add(event.target).length === options.show.target.length &&
994
+ tooltip.has(event.relatedTarget).length) {
995
+ return this;
996
+ }
997
+
998
+ // Cache event
999
+ cache.event = $.event.fix(event);
1000
+ }
1001
+
1002
+ // If we're currently waiting and we've just hidden... stop it
1003
+ this.waiting && !state && (this.hiddenDuringWait = TRUE);
1004
+
1005
+ // Render the tooltip if showing and it isn't already
1006
+ if(!this.rendered) { return state ? this.render(1) : this; }
1007
+ else if(this.destroyed || this.disabled) { return this; }
1008
+
1009
+ var type = state ? 'show' : 'hide',
1010
+ opts = this.options[type],
1011
+ otherOpts = this.options[ !state ? 'show' : 'hide' ],
1012
+ posOptions = this.options.position,
1013
+ contentOptions = this.options.content,
1014
+ width = this.tooltip.css('width'),
1015
+ visible = this.tooltip.is(':visible'),
1016
+ animate = state || opts.target.length === 1,
1017
+ sameTarget = !event || opts.target.length < 2 || cache.target[0] === event.target,
1018
+ identicalState, allow, showEvent, delay, after;
1019
+
1020
+ // Detect state if valid one isn't provided
1021
+ if((typeof state).search('boolean|number')) { state = !visible; }
1022
+
1023
+ // Check if the tooltip is in an identical state to the new would-be state
1024
+ identicalState = !tooltip.is(':animated') && visible === state && sameTarget;
1025
+
1026
+ // Fire tooltip(show/hide) event and check if destroyed
1027
+ allow = !identicalState ? !!this._trigger(type, [90]) : NULL;
1028
+
1029
+ // Check to make sure the tooltip wasn't destroyed in the callback
1030
+ if(this.destroyed) { return this; }
1031
+
1032
+ // If the user didn't stop the method prematurely and we're showing the tooltip, focus it
1033
+ if(allow !== FALSE && state) { this.focus(event); }
1034
+
1035
+ // If the state hasn't changed or the user stopped it, return early
1036
+ if(!allow || identicalState) { return this; }
1037
+
1038
+ // Set ARIA hidden attribute
1039
+ $.attr(tooltip[0], 'aria-hidden', !!!state);
1040
+
1041
+ // Execute state specific properties
1042
+ if(state) {
1043
+ // Store show origin coordinates
1044
+ this.mouse && (cache.origin = $.event.fix(this.mouse));
1045
+
1046
+ // Update tooltip content & title if it's a dynamic function
1047
+ if($.isFunction(contentOptions.text)) { this._updateContent(contentOptions.text, FALSE); }
1048
+ if($.isFunction(contentOptions.title)) { this._updateTitle(contentOptions.title, FALSE); }
1049
+
1050
+ // Cache mousemove events for positioning purposes (if not already tracking)
1051
+ if(!trackingBound && posOptions.target === 'mouse' && posOptions.adjust.mouse) {
1052
+ $(document).bind('mousemove.'+NAMESPACE, this._storeMouse);
1053
+ trackingBound = TRUE;
1054
+ }
1055
+
1056
+ // Update the tooltip position (set width first to prevent viewport/max-width issues)
1057
+ if(!width) { tooltip.css('width', tooltip.outerWidth(FALSE)); }
1058
+ this.reposition(event, arguments[2]);
1059
+ if(!width) { tooltip.css('width', ''); }
1060
+
1061
+ // Hide other tooltips if tooltip is solo
1062
+ if(!!opts.solo) {
1063
+ (typeof opts.solo === 'string' ? $(opts.solo) : $(SELECTOR, opts.solo))
1064
+ .not(tooltip).not(opts.target).qtip('hide', $.Event('tooltipsolo'));
1065
+ }
1066
+ }
1067
+ else {
1068
+ // Clear show timer if we're hiding
1069
+ clearTimeout(this.timers.show);
1070
+
1071
+ // Remove cached origin on hide
1072
+ delete cache.origin;
1073
+
1074
+ // Remove mouse tracking event if not needed (all tracking qTips are hidden)
1075
+ if(trackingBound && !$(SELECTOR+'[tracking="true"]:visible', opts.solo).not(tooltip).length) {
1076
+ $(document).unbind('mousemove.'+NAMESPACE);
1077
+ trackingBound = FALSE;
1078
+ }
1079
+
1080
+ // Blur the tooltip
1081
+ this.blur(event);
1082
+ }
1083
+
1084
+ // Define post-animation, state specific properties
1085
+ after = $.proxy(function() {
1086
+ if(state) {
1087
+ // Prevent antialias from disappearing in IE by removing filter
1088
+ if(BROWSER.ie) { tooltip[0].style.removeAttribute('filter'); }
1089
+
1090
+ // Remove overflow setting to prevent tip bugs
1091
+ tooltip.css('overflow', '');
1092
+
1093
+ // Autofocus elements if enabled
1094
+ if('string' === typeof opts.autofocus) {
1095
+ $(this.options.show.autofocus, tooltip).focus();
1096
+ }
1097
+
1098
+ // If set, hide tooltip when inactive for delay period
1099
+ this.options.show.target.trigger('qtip-'+this.id+'-inactive');
1100
+ }
1101
+ else {
1102
+ // Reset CSS states
1103
+ tooltip.css({
1104
+ display: '',
1105
+ visibility: '',
1106
+ opacity: '',
1107
+ left: '',
1108
+ top: ''
1109
+ });
1110
+ }
1111
+
1112
+ // tooltipvisible/tooltiphidden events
1113
+ this._trigger(state ? 'visible' : 'hidden');
1114
+ }, this);
1115
+
1116
+ // If no effect type is supplied, use a simple toggle
1117
+ if(opts.effect === FALSE || animate === FALSE) {
1118
+ tooltip[ type ]();
1119
+ after();
1120
+ }
1121
+
1122
+ // Use custom function if provided
1123
+ else if($.isFunction(opts.effect)) {
1124
+ tooltip.stop(1, 1);
1125
+ opts.effect.call(tooltip, this);
1126
+ tooltip.queue('fx', function(n) {
1127
+ after(); n();
1128
+ });
1129
+ }
1130
+
1131
+ // Use basic fade function by default
1132
+ else { tooltip.fadeTo(90, state ? 1 : 0, after); }
1133
+
1134
+ // If inactive hide method is set, active it
1135
+ if(state) { opts.target.trigger('qtip-'+this.id+'-inactive'); }
1136
+
1137
+ return this;
1138
+ };
1139
+
1140
+ PROTOTYPE.show = function(event) { return this.toggle(TRUE, event); };
1141
+
1142
+ PROTOTYPE.hide = function(event) { return this.toggle(FALSE, event); };
1143
+ ;PROTOTYPE.focus = function(event) {
1144
+ if(!this.rendered || this.destroyed) { return this; }
1145
+
1146
+ var qtips = $(SELECTOR),
1147
+ tooltip = this.tooltip,
1148
+ curIndex = parseInt(tooltip[0].style.zIndex, 10),
1149
+ newIndex = QTIP.zindex + qtips.length,
1150
+ focusedElem;
1151
+
1152
+ // Only update the z-index if it has changed and tooltip is not already focused
1153
+ if(!tooltip.hasClass(CLASS_FOCUS)) {
1154
+ // tooltipfocus event
1155
+ if(this._trigger('focus', [newIndex], event)) {
1156
+ // Only update z-index's if they've changed
1157
+ if(curIndex !== newIndex) {
1158
+ // Reduce our z-index's and keep them properly ordered
1159
+ qtips.each(function() {
1160
+ if(this.style.zIndex > curIndex) {
1161
+ this.style.zIndex = this.style.zIndex - 1;
1162
+ }
1163
+ });
1164
+
1165
+ // Fire blur event for focused tooltip
1166
+ qtips.filter('.' + CLASS_FOCUS).qtip('blur', event);
1167
+ }
1168
+
1169
+ // Set the new z-index
1170
+ tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
1171
+ }
1172
+ }
1173
+
1174
+ return this;
1175
+ };
1176
+
1177
+ PROTOTYPE.blur = function(event) {
1178
+ if(!this.rendered || this.destroyed) { return this; }
1179
+
1180
+ // Set focused status to FALSE
1181
+ this.tooltip.removeClass(CLASS_FOCUS);
1182
+
1183
+ // tooltipblur event
1184
+ this._trigger('blur', [ this.tooltip.css('zIndex') ], event);
1185
+
1186
+ return this;
1187
+ };
1188
+ ;PROTOTYPE.disable = function(state) {
1189
+ if(this.destroyed) { return this; }
1190
+
1191
+ // If 'toggle' is passed, toggle the current state
1192
+ if(state === 'toggle') {
1193
+ state = !(this.rendered ? this.tooltip.hasClass(CLASS_DISABLED) : this.disabled);
1194
+ }
1195
+
1196
+ // Disable if no state passed
1197
+ else if('boolean' !== typeof state) {
1198
+ state = TRUE;
1199
+ }
1200
+
1201
+ if(this.rendered) {
1202
+ this.tooltip.toggleClass(CLASS_DISABLED, state)
1203
+ .attr('aria-disabled', state);
1204
+ }
1205
+
1206
+ this.disabled = !!state;
1207
+
1208
+ return this;
1209
+ };
1210
+
1211
+ PROTOTYPE.enable = function() { return this.disable(FALSE); };
1212
+ ;PROTOTYPE._createButton = function()
1213
+ {
1214
+ var self = this,
1215
+ elements = this.elements,
1216
+ tooltip = elements.tooltip,
1217
+ button = this.options.content.button,
1218
+ isString = typeof button === 'string',
1219
+ close = isString ? button : 'Close tooltip';
1220
+
1221
+ if(elements.button) { elements.button.remove(); }
1222
+
1223
+ // Use custom button if one was supplied by user, else use default
1224
+ if(button.jquery) {
1225
+ elements.button = button;
1226
+ }
1227
+ else {
1228
+ elements.button = $('<a />', {
1229
+ 'class': 'qtip-close ' + (this.options.style.widget ? '' : NAMESPACE+'-icon'),
1230
+ 'title': close,
1231
+ 'aria-label': close
1232
+ })
1233
+ .prepend(
1234
+ $('<span />', {
1235
+ 'class': 'ui-icon ui-icon-close',
1236
+ 'html': '&times;'
1237
+ })
1238
+ );
1239
+ }
1240
+
1241
+ // Create button and setup attributes
1242
+ elements.button.appendTo(elements.titlebar || tooltip)
1243
+ .attr('role', 'button')
1244
+ .click(function(event) {
1245
+ if(!tooltip.hasClass(CLASS_DISABLED)) { self.hide(event); }
1246
+ return FALSE;
1247
+ });
1248
+ };
1249
+
1250
+ PROTOTYPE._updateButton = function(button)
1251
+ {
1252
+ // Make sure tooltip is rendered and if not, return
1253
+ if(!this.rendered) { return FALSE; }
1254
+
1255
+ var elem = this.elements.button;
1256
+ if(button) { this._createButton(); }
1257
+ else { elem.remove(); }
1258
+ };
1259
+ ;// Widget class creator
1260
+ function createWidgetClass(cls) {
1261
+ return WIDGET.concat('').join(cls ? '-'+cls+' ' : ' ');
1262
+ }
1263
+
1264
+ // Widget class setter method
1265
+ PROTOTYPE._setWidget = function()
1266
+ {
1267
+ var on = this.options.style.widget,
1268
+ elements = this.elements,
1269
+ tooltip = elements.tooltip,
1270
+ disabled = tooltip.hasClass(CLASS_DISABLED);
1271
+
1272
+ tooltip.removeClass(CLASS_DISABLED);
1273
+ CLASS_DISABLED = on ? 'ui-state-disabled' : 'qtip-disabled';
1274
+ tooltip.toggleClass(CLASS_DISABLED, disabled);
1275
+
1276
+ tooltip.toggleClass('ui-helper-reset '+createWidgetClass(), on).toggleClass(CLASS_DEFAULT, this.options.style.def && !on);
1277
+
1278
+ if(elements.content) {
1279
+ elements.content.toggleClass( createWidgetClass('content'), on);
1280
+ }
1281
+ if(elements.titlebar) {
1282
+ elements.titlebar.toggleClass( createWidgetClass('header'), on);
1283
+ }
1284
+ if(elements.button) {
1285
+ elements.button.toggleClass(NAMESPACE+'-icon', !on);
1286
+ }
1287
+ };
1288
+ ;function delay(callback, duration) {
1289
+ // If tooltip has displayed, start hide timer
1290
+ if(duration > 0) {
1291
+ return setTimeout(
1292
+ $.proxy(callback, this), duration
1293
+ );
1294
+ }
1295
+ else{ callback.call(this); }
1296
+ }
1297
+
1298
+ function showMethod(event) {
1299
+ if(this.tooltip.hasClass(CLASS_DISABLED)) { return; }
1300
+
1301
+ // Clear hide timers
1302
+ clearTimeout(this.timers.show);
1303
+ clearTimeout(this.timers.hide);
1304
+
1305
+ // Start show timer
1306
+ this.timers.show = delay.call(this,
1307
+ function() { this.toggle(TRUE, event); },
1308
+ this.options.show.delay
1309
+ );
1310
+ }
1311
+
1312
+ function hideMethod(event) {
1313
+ if(this.tooltip.hasClass(CLASS_DISABLED) || this.destroyed) { return; }
1314
+
1315
+ // Check if new target was actually the tooltip element
1316
+ var relatedTarget = $(event.relatedTarget),
1317
+ ontoTooltip = relatedTarget.closest(SELECTOR)[0] === this.tooltip[0],
1318
+ ontoTarget = relatedTarget[0] === this.options.show.target[0];
1319
+
1320
+ // Clear timers and stop animation queue
1321
+ clearTimeout(this.timers.show);
1322
+ clearTimeout(this.timers.hide);
1323
+
1324
+ // Prevent hiding if tooltip is fixed and event target is the tooltip.
1325
+ // Or if mouse positioning is enabled and cursor momentarily overlaps
1326
+ if(this !== relatedTarget[0] &&
1327
+ (this.options.position.target === 'mouse' && ontoTooltip) ||
1328
+ (this.options.hide.fixed && (
1329
+ (/mouse(out|leave|move)/).test(event.type) && (ontoTooltip || ontoTarget))
1330
+ ))
1331
+ {
1332
+ try {
1333
+ event.preventDefault();
1334
+ event.stopImmediatePropagation();
1335
+ } catch(e) {}
1336
+
1337
+ return;
1338
+ }
1339
+
1340
+ // If tooltip has displayed, start hide timer
1341
+ this.timers.hide = delay.call(this,
1342
+ function() { this.toggle(FALSE, event); },
1343
+ this.options.hide.delay,
1344
+ this
1345
+ );
1346
+ }
1347
+
1348
+ function inactiveMethod(event) {
1349
+ if(this.tooltip.hasClass(CLASS_DISABLED) || !this.options.hide.inactive) { return; }
1350
+
1351
+ // Clear timer
1352
+ clearTimeout(this.timers.inactive);
1353
+
1354
+ this.timers.inactive = delay.call(this,
1355
+ function(){ this.hide(event); },
1356
+ this.options.hide.inactive
1357
+ );
1358
+ }
1359
+
1360
+ function repositionMethod(event) {
1361
+ if(this.rendered && this.tooltip[0].offsetWidth > 0) { this.reposition(event); }
1362
+ }
1363
+
1364
+ // Store mouse coordinates
1365
+ PROTOTYPE._storeMouse = function(event) {
1366
+ (this.mouse = $.event.fix(event)).type = 'mousemove';
1367
+ return this;
1368
+ };
1369
+
1370
+ // Bind events
1371
+ PROTOTYPE._bind = function(targets, events, method, suffix, context) {
1372
+ if(!targets || !method || !events.length) { return; }
1373
+ var ns = '.' + this._id + (suffix ? '-'+suffix : '');
1374
+ $(targets).bind(
1375
+ (events.split ? events : events.join(ns + ' ')) + ns,
1376
+ $.proxy(method, context || this)
1377
+ );
1378
+ return this;
1379
+ };
1380
+ PROTOTYPE._unbind = function(targets, suffix) {
1381
+ targets && $(targets).unbind('.' + this._id + (suffix ? '-'+suffix : ''));
1382
+ return this;
1383
+ };
1384
+
1385
+ // Global delegation helper
1386
+ function delegate(selector, events, method) {
1387
+ $(document.body).delegate(selector,
1388
+ (events.split ? events : events.join('.'+NAMESPACE + ' ')) + '.'+NAMESPACE,
1389
+ function() {
1390
+ var api = QTIP.api[ $.attr(this, ATTR_ID) ];
1391
+ api && !api.disabled && method.apply(api, arguments);
1392
+ }
1393
+ );
1394
+ }
1395
+ // Event trigger
1396
+ PROTOTYPE._trigger = function(type, args, event) {
1397
+ var callback = $.Event('tooltip'+type);
1398
+ callback.originalEvent = (event && $.extend({}, event)) || this.cache.event || NULL;
1399
+
1400
+ this.triggering = type;
1401
+ this.tooltip.trigger(callback, [this].concat(args || []));
1402
+ this.triggering = FALSE;
1403
+
1404
+ return !callback.isDefaultPrevented();
1405
+ };
1406
+
1407
+ PROTOTYPE._bindEvents = function(showEvents, hideEvents, showTargets, hideTargets, showMethod, hideMethod) {
1408
+ // Get tasrgets that lye within both
1409
+ var similarTargets = showTargets.filter( hideTargets ).add( hideTargets.filter(showTargets) ),
1410
+ toggleEvents = [];
1411
+
1412
+ // If hide and show targets are the same...
1413
+ if(similarTargets.length) {
1414
+
1415
+ // Filter identical show/hide events
1416
+ $.each(hideEvents, function(i, type) {
1417
+ var showIndex = $.inArray(type, showEvents);
1418
+
1419
+ // Both events are identical, remove from both hide and show events
1420
+ // and append to toggleEvents
1421
+ showIndex > -1 && toggleEvents.push( showEvents.splice( showIndex, 1 )[0] );
1422
+ });
1423
+
1424
+ // Toggle events are special case of identical show/hide events, which happen in sequence
1425
+ if(toggleEvents.length) {
1426
+ // Bind toggle events to the similar targets
1427
+ this._bind(similarTargets, toggleEvents, function(event) {
1428
+ var state = this.rendered ? this.tooltip[0].offsetWidth > 0 : false;
1429
+ (state ? hideMethod : showMethod).call(this, event);
1430
+ });
1431
+
1432
+ // Remove the similar targets from the regular show/hide bindings
1433
+ showTargets = showTargets.not(similarTargets);
1434
+ hideTargets = hideTargets.not(similarTargets);
1435
+ }
1436
+ }
1437
+
1438
+ // Apply show/hide/toggle events
1439
+ this._bind(showTargets, showEvents, showMethod);
1440
+ this._bind(hideTargets, hideEvents, hideMethod);
1441
+ };
1442
+
1443
+ PROTOTYPE._assignInitialEvents = function(event) {
1444
+ var options = this.options,
1445
+ showTarget = options.show.target,
1446
+ hideTarget = options.hide.target,
1447
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
1448
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
1449
+
1450
+ // Catch remove/removeqtip events on target element to destroy redundant tooltips
1451
+ this._bind(this.elements.target, ['remove', 'removeqtip'], function(event) {
1452
+ this.destroy(true);
1453
+ }, 'destroy');
1454
+
1455
+ /*
1456
+ * Make sure hoverIntent functions properly by using mouseleave as a hide event if
1457
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
1458
+ */
1459
+ if(/mouse(over|enter)/i.test(options.show.event) && !/mouse(out|leave)/i.test(options.hide.event)) {
1460
+ hideEvents.push('mouseleave');
1461
+ }
1462
+
1463
+ /*
1464
+ * Also make sure initial mouse targetting works correctly by caching mousemove coords
1465
+ * on show targets before the tooltip has rendered. Also set onTarget when triggered to
1466
+ * keep mouse tracking working.
1467
+ */
1468
+ this._bind(showTarget, 'mousemove', function(event) {
1469
+ this._storeMouse(event);
1470
+ this.cache.onTarget = TRUE;
1471
+ });
1472
+
1473
+ // Define hoverIntent function
1474
+ function hoverIntent(event) {
1475
+ // Only continue if tooltip isn't disabled
1476
+ if(this.disabled || this.destroyed) { return FALSE; }
1477
+
1478
+ // Cache the event data
1479
+ this.cache.event = event && $.event.fix(event);
1480
+ this.cache.target = event && $(event.target);
1481
+
1482
+ // Start the event sequence
1483
+ clearTimeout(this.timers.show);
1484
+ this.timers.show = delay.call(this,
1485
+ function() { this.render(typeof event === 'object' || options.show.ready); },
1486
+ options.prerender ? 0 : options.show.delay
1487
+ );
1488
+ }
1489
+
1490
+ // Filter and bind events
1491
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, hoverIntent, function() {
1492
+ if(!this.timers) { return FALSE; }
1493
+ clearTimeout(this.timers.show);
1494
+ });
1495
+
1496
+ // Prerendering is enabled, create tooltip now
1497
+ if(options.show.ready || options.prerender) { hoverIntent.call(this, event); }
1498
+ };
1499
+
1500
+ // Event assignment method
1501
+ PROTOTYPE._assignEvents = function() {
1502
+ var self = this,
1503
+ options = this.options,
1504
+ posOptions = options.position,
1505
+
1506
+ tooltip = this.tooltip,
1507
+ showTarget = options.show.target,
1508
+ hideTarget = options.hide.target,
1509
+ containerTarget = posOptions.container,
1510
+ viewportTarget = posOptions.viewport,
1511
+ documentTarget = $(document),
1512
+ bodyTarget = $(document.body),
1513
+ windowTarget = $(window),
1514
+
1515
+ showEvents = options.show.event ? $.trim('' + options.show.event).split(' ') : [],
1516
+ hideEvents = options.hide.event ? $.trim('' + options.hide.event).split(' ') : [];
1517
+
1518
+
1519
+ // Assign passed event callbacks
1520
+ $.each(options.events, function(name, callback) {
1521
+ self._bind(tooltip, name === 'toggle' ? ['tooltipshow','tooltiphide'] : ['tooltip'+name], callback, null, tooltip);
1522
+ });
1523
+
1524
+ // Hide tooltips when leaving current window/frame (but not select/option elements)
1525
+ if(/mouse(out|leave)/i.test(options.hide.event) && options.hide.leave === 'window') {
1526
+ this._bind(documentTarget, ['mouseout', 'blur'], function(event) {
1527
+ if(!/select|option/.test(event.target.nodeName) && !event.relatedTarget) {
1528
+ this.hide(event);
1529
+ }
1530
+ });
1531
+ }
1532
+
1533
+ // Enable hide.fixed by adding appropriate class
1534
+ if(options.hide.fixed) {
1535
+ hideTarget = hideTarget.add( tooltip.addClass(CLASS_FIXED) );
1536
+ }
1537
+
1538
+ /*
1539
+ * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
1540
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
1541
+ */
1542
+ else if(/mouse(over|enter)/i.test(options.show.event)) {
1543
+ this._bind(hideTarget, 'mouseleave', function() {
1544
+ clearTimeout(this.timers.show);
1545
+ });
1546
+ }
1547
+
1548
+ // Hide tooltip on document mousedown if unfocus events are enabled
1549
+ if(('' + options.hide.event).indexOf('unfocus') > -1) {
1550
+ this._bind(containerTarget.closest('html'), ['mousedown', 'touchstart'], function(event) {
1551
+ var elem = $(event.target),
1552
+ enabled = this.rendered && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0,
1553
+ isAncestor = elem.parents(SELECTOR).filter(this.tooltip[0]).length > 0;
1554
+
1555
+ if(elem[0] !== this.target[0] && elem[0] !== this.tooltip[0] && !isAncestor &&
1556
+ !this.target.has(elem[0]).length && enabled
1557
+ ) {
1558
+ this.hide(event);
1559
+ }
1560
+ });
1561
+ }
1562
+
1563
+ // Check if the tooltip hides when inactive
1564
+ if('number' === typeof options.hide.inactive) {
1565
+ // Bind inactive method to show target(s) as a custom event
1566
+ this._bind(showTarget, 'qtip-'+this.id+'-inactive', inactiveMethod, 'inactive');
1567
+
1568
+ // Define events which reset the 'inactive' event handler
1569
+ this._bind(hideTarget.add(tooltip), QTIP.inactiveEvents, inactiveMethod);
1570
+ }
1571
+
1572
+ // Filter and bind events
1573
+ this._bindEvents(showEvents, hideEvents, showTarget, hideTarget, showMethod, hideMethod);
1574
+
1575
+ // Mouse movement bindings
1576
+ this._bind(showTarget.add(tooltip), 'mousemove', function(event) {
1577
+ // Check if the tooltip hides when mouse is moved a certain distance
1578
+ if('number' === typeof options.hide.distance) {
1579
+ var origin = this.cache.origin || {},
1580
+ limit = this.options.hide.distance,
1581
+ abs = Math.abs;
1582
+
1583
+ // Check if the movement has gone beyond the limit, and hide it if so
1584
+ if(abs(event.pageX - origin.pageX) >= limit || abs(event.pageY - origin.pageY) >= limit) {
1585
+ this.hide(event);
1586
+ }
1587
+ }
1588
+
1589
+ // Cache mousemove coords on show targets
1590
+ this._storeMouse(event);
1591
+ });
1592
+
1593
+ // Mouse positioning events
1594
+ if(posOptions.target === 'mouse') {
1595
+ // If mouse adjustment is on...
1596
+ if(posOptions.adjust.mouse) {
1597
+ // Apply a mouseleave event so we don't get problems with overlapping
1598
+ if(options.hide.event) {
1599
+ // Track if we're on the target or not
1600
+ this._bind(showTarget, ['mouseenter', 'mouseleave'], function(event) {
1601
+ if(!this.cache) {return FALSE; }
1602
+ this.cache.onTarget = event.type === 'mouseenter';
1603
+ });
1604
+ }
1605
+
1606
+ // Update tooltip position on mousemove
1607
+ this._bind(documentTarget, 'mousemove', function(event) {
1608
+ // Update the tooltip position only if the tooltip is visible and adjustment is enabled
1609
+ if(this.rendered && this.cache.onTarget && !this.tooltip.hasClass(CLASS_DISABLED) && this.tooltip[0].offsetWidth > 0) {
1610
+ this.reposition(event);
1611
+ }
1612
+ });
1613
+ }
1614
+ }
1615
+
1616
+ // Adjust positions of the tooltip on window resize if enabled
1617
+ if(posOptions.adjust.resize || viewportTarget.length) {
1618
+ this._bind( $.event.special.resize ? viewportTarget : windowTarget, 'resize', repositionMethod );
1619
+ }
1620
+
1621
+ // Adjust tooltip position on scroll of the window or viewport element if present
1622
+ if(posOptions.adjust.scroll) {
1623
+ this._bind( windowTarget.add(posOptions.container), 'scroll', repositionMethod );
1624
+ }
1625
+ };
1626
+
1627
+ // Un-assignment method
1628
+ PROTOTYPE._unassignEvents = function() {
1629
+ var options = this.options,
1630
+ showTargets = options.show.target,
1631
+ hideTargets = options.hide.target,
1632
+ targets = $.grep([
1633
+ this.elements.target[0],
1634
+ this.rendered && this.tooltip[0],
1635
+ options.position.container[0],
1636
+ options.position.viewport[0],
1637
+ options.position.container.closest('html')[0], // unfocus
1638
+ window,
1639
+ document
1640
+ ], function(i) {
1641
+ return typeof i === 'object';
1642
+ });
1643
+
1644
+ // Add show and hide targets if they're valid
1645
+ if(showTargets && showTargets.toArray) {
1646
+ targets = targets.concat(showTargets.toArray());
1647
+ }
1648
+ if(hideTargets && hideTargets.toArray) {
1649
+ targets = targets.concat(hideTargets.toArray());
1650
+ }
1651
+
1652
+ // Unbind the events
1653
+ this._unbind(targets)
1654
+ ._unbind(targets, 'destroy')
1655
+ ._unbind(targets, 'inactive');
1656
+ };
1657
+
1658
+ // Apply common event handlers using delegate (avoids excessive .bind calls!)
1659
+ $(function() {
1660
+ delegate(SELECTOR, ['mouseenter', 'mouseleave'], function(event) {
1661
+ var state = event.type === 'mouseenter',
1662
+ tooltip = $(event.currentTarget),
1663
+ target = $(event.relatedTarget || event.target),
1664
+ options = this.options;
1665
+
1666
+ // On mouseenter...
1667
+ if(state) {
1668
+ // Focus the tooltip on mouseenter (z-index stacking)
1669
+ this.focus(event);
1670
+
1671
+ // Clear hide timer on tooltip hover to prevent it from closing
1672
+ tooltip.hasClass(CLASS_FIXED) && !tooltip.hasClass(CLASS_DISABLED) && clearTimeout(this.timers.hide);
1673
+ }
1674
+
1675
+ // On mouseleave...
1676
+ else {
1677
+ // When mouse tracking is enabled, hide when we leave the tooltip and not onto the show target (if a hide event is set)
1678
+ if(options.position.target === 'mouse' && options.position.adjust.mouse &&
1679
+ options.hide.event && options.show.target && !target.closest(options.show.target[0]).length) {
1680
+ this.hide(event);
1681
+ }
1682
+ }
1683
+
1684
+ // Add hover class
1685
+ tooltip.toggleClass(CLASS_HOVER, state);
1686
+ });
1687
+
1688
+ // Define events which reset the 'inactive' event handler
1689
+ delegate('['+ATTR_ID+']', INACTIVE_EVENTS, inactiveMethod);
1690
+ });
1691
+ ;// Initialization method
1692
+ function init(elem, id, opts) {
1693
+ var obj, posOptions, attr, config, title,
1694
+
1695
+ // Setup element references
1696
+ docBody = $(document.body),
1697
+
1698
+ // Use document body instead of document element if needed
1699
+ newTarget = elem[0] === document ? docBody : elem,
1700
+
1701
+ // Grab metadata from element if plugin is present
1702
+ metadata = (elem.metadata) ? elem.metadata(opts.metadata) : NULL,
1703
+
1704
+ // If metadata type if HTML5, grab 'name' from the object instead, or use the regular data object otherwise
1705
+ metadata5 = opts.metadata.type === 'html5' && metadata ? metadata[opts.metadata.name] : NULL,
1706
+
1707
+ // Grab data from metadata.name (or data-qtipopts as fallback) using .data() method,
1708
+ html5 = elem.data(opts.metadata.name || 'qtipopts');
1709
+
1710
+ // If we don't get an object returned attempt to parse it manualyl without parseJSON
1711
+ try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; } catch(e) {}
1712
+
1713
+ // Merge in and sanitize metadata
1714
+ config = $.extend(TRUE, {}, QTIP.defaults, opts,
1715
+ typeof html5 === 'object' ? sanitizeOptions(html5) : NULL,
1716
+ sanitizeOptions(metadata5 || metadata));
1717
+
1718
+ // Re-grab our positioning options now we've merged our metadata and set id to passed value
1719
+ posOptions = config.position;
1720
+ config.id = id;
1721
+
1722
+ // Setup missing content if none is detected
1723
+ if('boolean' === typeof config.content.text) {
1724
+ attr = elem.attr(config.content.attr);
1725
+
1726
+ // Grab from supplied attribute if available
1727
+ if(config.content.attr !== FALSE && attr) { config.content.text = attr; }
1728
+
1729
+ // No valid content was found, abort render
1730
+ else { return FALSE; }
1731
+ }
1732
+
1733
+ // Setup target options
1734
+ if(!posOptions.container.length) { posOptions.container = docBody; }
1735
+ if(posOptions.target === FALSE) { posOptions.target = newTarget; }
1736
+ if(config.show.target === FALSE) { config.show.target = newTarget; }
1737
+ if(config.show.solo === TRUE) { config.show.solo = posOptions.container.closest('body'); }
1738
+ if(config.hide.target === FALSE) { config.hide.target = newTarget; }
1739
+ if(config.position.viewport === TRUE) { config.position.viewport = posOptions.container; }
1740
+
1741
+ // Ensure we only use a single container
1742
+ posOptions.container = posOptions.container.eq(0);
1743
+
1744
+ // Convert position corner values into x and y strings
1745
+ posOptions.at = new CORNER(posOptions.at, TRUE);
1746
+ posOptions.my = new CORNER(posOptions.my);
1747
+
1748
+ // Destroy previous tooltip if overwrite is enabled, or skip element if not
1749
+ if(elem.data(NAMESPACE)) {
1750
+ if(config.overwrite) {
1751
+ elem.qtip('destroy', true);
1752
+ }
1753
+ else if(config.overwrite === FALSE) {
1754
+ return FALSE;
1755
+ }
1756
+ }
1757
+
1758
+ // Add has-qtip attribute
1759
+ elem.attr(ATTR_HAS, id);
1760
+
1761
+ // Remove title attribute and store it if present
1762
+ if(config.suppress && (title = elem.attr('title'))) {
1763
+ // Final attr call fixes event delegatiom and IE default tooltip showing problem
1764
+ elem.removeAttr('title').attr(oldtitle, title).attr('title', '');
1765
+ }
1766
+
1767
+ // Initialize the tooltip and add API reference
1768
+ obj = new QTip(elem, config, id, !!attr);
1769
+ elem.data(NAMESPACE, obj);
1770
+
1771
+ return obj;
1772
+ }
1773
+
1774
+ // jQuery $.fn extension method
1775
+ QTIP = $.fn.qtip = function(options, notation, newValue)
1776
+ {
1777
+ var command = ('' + options).toLowerCase(), // Parse command
1778
+ returned = NULL,
1779
+ args = $.makeArray(arguments).slice(1),
1780
+ event = args[args.length - 1],
1781
+ opts = this[0] ? $.data(this[0], NAMESPACE) : NULL;
1782
+
1783
+ // Check for API request
1784
+ if((!arguments.length && opts) || command === 'api') {
1785
+ return opts;
1786
+ }
1787
+
1788
+ // Execute API command if present
1789
+ else if('string' === typeof options) {
1790
+ this.each(function() {
1791
+ var api = $.data(this, NAMESPACE);
1792
+ if(!api) { return TRUE; }
1793
+
1794
+ // Cache the event if possible
1795
+ if(event && event.timeStamp) { api.cache.event = event; }
1796
+
1797
+ // Check for specific API commands
1798
+ if(notation && (command === 'option' || command === 'options')) {
1799
+ if(newValue !== undefined || $.isPlainObject(notation)) {
1800
+ api.set(notation, newValue);
1801
+ }
1802
+ else {
1803
+ returned = api.get(notation);
1804
+ return FALSE;
1805
+ }
1806
+ }
1807
+
1808
+ // Execute API command
1809
+ else if(api[command]) {
1810
+ api[command].apply(api, args);
1811
+ }
1812
+ });
1813
+
1814
+ return returned !== NULL ? returned : this;
1815
+ }
1816
+
1817
+ // No API commands. validate provided options and setup qTips
1818
+ else if('object' === typeof options || !arguments.length) {
1819
+ // Sanitize options first
1820
+ opts = sanitizeOptions($.extend(TRUE, {}, options));
1821
+
1822
+ return this.each(function(i) {
1823
+ var api, id;
1824
+
1825
+ // Find next available ID, or use custom ID if provided
1826
+ id = $.isArray(opts.id) ? opts.id[i] : opts.id;
1827
+ id = !id || id === FALSE || id.length < 1 || QTIP.api[id] ? QTIP.nextid++ : id;
1828
+
1829
+ // Initialize the qTip and re-grab newly sanitized options
1830
+ api = init($(this), id, opts);
1831
+ if(api === FALSE) { return TRUE; }
1832
+ else { QTIP.api[id] = api; }
1833
+
1834
+ // Initialize plugins
1835
+ $.each(PLUGINS, function() {
1836
+ if(this.initialize === 'initialize') { this(api); }
1837
+ });
1838
+
1839
+ // Assign initial pre-render events
1840
+ api._assignInitialEvents(event);
1841
+ });
1842
+ }
1843
+ };
1844
+
1845
+ // Expose class
1846
+ $.qtip = QTip;
1847
+
1848
+ // Populated in render method
1849
+ QTIP.api = {};
1850
+ ;$.each({
1851
+ /* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
1852
+ attr: function(attr, val) {
1853
+ if(this.length) {
1854
+ var self = this[0],
1855
+ title = 'title',
1856
+ api = $.data(self, 'qtip');
1857
+
1858
+ if(attr === title && api && 'object' === typeof api && api.options.suppress) {
1859
+ if(arguments.length < 2) {
1860
+ return $.attr(self, oldtitle);
1861
+ }
1862
+
1863
+ // If qTip is rendered and title was originally used as content, update it
1864
+ if(api && api.options.content.attr === title && api.cache.attr) {
1865
+ api.set('content.text', val);
1866
+ }
1867
+
1868
+ // Use the regular attr method to set, then cache the result
1869
+ return this.attr(oldtitle, val);
1870
+ }
1871
+ }
1872
+
1873
+ return $.fn['attr'+replaceSuffix].apply(this, arguments);
1874
+ },
1875
+
1876
+ /* Allow clone to correctly retrieve cached title attributes */
1877
+ clone: function(keepData) {
1878
+ var titles = $([]), title = 'title',
1879
+
1880
+ // Clone our element using the real clone method
1881
+ elems = $.fn['clone'+replaceSuffix].apply(this, arguments);
1882
+
1883
+ // Grab all elements with an oldtitle set, and change it to regular title attribute, if keepData is false
1884
+ if(!keepData) {
1885
+ elems.filter('['+oldtitle+']').attr('title', function() {
1886
+ return $.attr(this, oldtitle);
1887
+ })
1888
+ .removeAttr(oldtitle);
1889
+ }
1890
+
1891
+ return elems;
1892
+ }
1893
+ }, function(name, func) {
1894
+ if(!func || $.fn[name+replaceSuffix]) { return TRUE; }
1895
+
1896
+ var old = $.fn[name+replaceSuffix] = $.fn[name];
1897
+ $.fn[name] = function() {
1898
+ return func.apply(this, arguments) || old.apply(this, arguments);
1899
+ };
1900
+ });
1901
+
1902
+ /* Fire off 'removeqtip' handler in $.cleanData if jQuery UI not present (it already does similar).
1903
+ * This snippet is taken directly from jQuery UI source code found here:
1904
+ * http://code.jquery.com/ui/jquery-ui-git.js
1905
+ */
1906
+ if(!$.ui) {
1907
+ $['cleanData'+replaceSuffix] = $.cleanData;
1908
+ $.cleanData = function( elems ) {
1909
+ for(var i = 0, elem; (elem = $( elems[i] )).length; i++) {
1910
+ if(elem.attr(ATTR_HAS)) {
1911
+ try { elem.triggerHandler('removeqtip'); }
1912
+ catch( e ) {}
1913
+ }
1914
+ }
1915
+ $['cleanData'+replaceSuffix].apply(this, arguments);
1916
+ };
1917
+ }
1918
+ ;// qTip version
1919
+ QTIP.version = '2.2.1';
1920
+
1921
+ // Base ID for all qTips
1922
+ QTIP.nextid = 0;
1923
+
1924
+ // Inactive events array
1925
+ QTIP.inactiveEvents = INACTIVE_EVENTS;
1926
+
1927
+ // Base z-index for all qTips
1928
+ QTIP.zindex = 15000;
1929
+
1930
+ // Define configuration defaults
1931
+ QTIP.defaults = {
1932
+ prerender: FALSE,
1933
+ id: FALSE,
1934
+ overwrite: TRUE,
1935
+ suppress: TRUE,
1936
+ content: {
1937
+ text: TRUE,
1938
+ attr: 'title',
1939
+ title: FALSE,
1940
+ button: FALSE
1941
+ },
1942
+ position: {
1943
+ my: 'top left',
1944
+ at: 'bottom right',
1945
+ target: FALSE,
1946
+ container: FALSE,
1947
+ viewport: FALSE,
1948
+ adjust: {
1949
+ x: 0, y: 0,
1950
+ mouse: TRUE,
1951
+ scroll: TRUE,
1952
+ resize: TRUE,
1953
+ method: 'flipinvert flipinvert'
1954
+ },
1955
+ effect: function(api, pos, viewport) {
1956
+ $(this).animate(pos, {
1957
+ duration: 200,
1958
+ queue: FALSE
1959
+ });
1960
+ }
1961
+ },
1962
+ show: {
1963
+ target: FALSE,
1964
+ event: 'mouseenter',
1965
+ effect: TRUE,
1966
+ delay: 90,
1967
+ solo: FALSE,
1968
+ ready: FALSE,
1969
+ autofocus: FALSE
1970
+ },
1971
+ hide: {
1972
+ target: FALSE,
1973
+ event: 'mouseleave',
1974
+ effect: TRUE,
1975
+ delay: 0,
1976
+ fixed: FALSE,
1977
+ inactive: FALSE,
1978
+ leave: 'window',
1979
+ distance: FALSE
1980
+ },
1981
+ style: {
1982
+ classes: '',
1983
+ widget: FALSE,
1984
+ width: FALSE,
1985
+ height: FALSE,
1986
+ def: TRUE
1987
+ },
1988
+ events: {
1989
+ render: NULL,
1990
+ move: NULL,
1991
+ show: NULL,
1992
+ hide: NULL,
1993
+ toggle: NULL,
1994
+ visible: NULL,
1995
+ hidden: NULL,
1996
+ focus: NULL,
1997
+ blur: NULL
1998
+ }
1999
+ };
2000
+ ;var TIP,
2001
+
2002
+ // .bind()/.on() namespace
2003
+ TIPNS = '.qtip-tip',
2004
+
2005
+ // Common CSS strings
2006
+ MARGIN = 'margin',
2007
+ BORDER = 'border',
2008
+ COLOR = 'color',
2009
+ BG_COLOR = 'background-color',
2010
+ TRANSPARENT = 'transparent',
2011
+ IMPORTANT = ' !important',
2012
+
2013
+ // Check if the browser supports <canvas/> elements
2014
+ HASCANVAS = !!document.createElement('canvas').getContext,
2015
+
2016
+ // Invalid colour values used in parseColours()
2017
+ INVALID = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i;
2018
+
2019
+ // Camel-case method, taken from jQuery source
2020
+ // http://code.jquery.com/jquery-1.8.0.js
2021
+ function camel(s) { return s.charAt(0).toUpperCase() + s.slice(1); }
2022
+
2023
+ /*
2024
+ * Modified from Modernizr's testPropsAll()
2025
+ * http://modernizr.com/downloads/modernizr-latest.js
2026
+ */
2027
+ var cssProps = {}, cssPrefixes = ["Webkit", "O", "Moz", "ms"];
2028
+ function vendorCss(elem, prop) {
2029
+ var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
2030
+ props = (prop + ' ' + cssPrefixes.join(ucProp + ' ') + ucProp).split(' '),
2031
+ cur, val, i = 0;
2032
+
2033
+ // If the property has already been mapped...
2034
+ if(cssProps[prop]) { return elem.css(cssProps[prop]); }
2035
+
2036
+ while((cur = props[i++])) {
2037
+ if((val = elem.css(cur)) !== undefined) {
2038
+ return cssProps[prop] = cur, val;
2039
+ }
2040
+ }
2041
+ }
2042
+
2043
+ // Parse a given elements CSS property into an int
2044
+ function intCss(elem, prop) {
2045
+ return Math.ceil(parseFloat(vendorCss(elem, prop)));
2046
+ }
2047
+
2048
+
2049
+ // VML creation (for IE only)
2050
+ if(!HASCANVAS) {
2051
+ var createVML = function(tag, props, style) {
2052
+ return '<qtipvml:'+tag+' xmlns="urn:schemas-microsoft.com:vml" class="qtip-vml" '+(props||'')+
2053
+ ' style="behavior: url(#default#VML); '+(style||'')+ '" />';
2054
+ };
2055
+ }
2056
+
2057
+ // Canvas only definitions
2058
+ else {
2059
+ var PIXEL_RATIO = window.devicePixelRatio || 1,
2060
+ BACKING_STORE_RATIO = (function() {
2061
+ var context = document.createElement('canvas').getContext('2d');
2062
+ return context.backingStorePixelRatio || context.webkitBackingStorePixelRatio || context.mozBackingStorePixelRatio ||
2063
+ context.msBackingStorePixelRatio || context.oBackingStorePixelRatio || 1;
2064
+ }()),
2065
+ SCALE = PIXEL_RATIO / BACKING_STORE_RATIO;
2066
+ }
2067
+
2068
+
2069
+ function Tip(qtip, options) {
2070
+ this._ns = 'tip';
2071
+ this.options = options;
2072
+ this.offset = options.offset;
2073
+ this.size = [ options.width, options.height ];
2074
+
2075
+ // Initialize
2076
+ this.init( (this.qtip = qtip) );
2077
+ }
2078
+
2079
+ $.extend(Tip.prototype, {
2080
+ init: function(qtip) {
2081
+ var context, tip;
2082
+
2083
+ // Create tip element and prepend to the tooltip
2084
+ tip = this.element = qtip.elements.tip = $('<div />', { 'class': NAMESPACE+'-tip' }).prependTo(qtip.tooltip);
2085
+
2086
+ // Create tip drawing element(s)
2087
+ if(HASCANVAS) {
2088
+ // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
2089
+ context = $('<canvas />').appendTo(this.element)[0].getContext('2d');
2090
+
2091
+ // Setup constant parameters
2092
+ context.lineJoin = 'miter';
2093
+ context.miterLimit = 100000;
2094
+ context.save();
2095
+ }
2096
+ else {
2097
+ context = createVML('shape', 'coordorigin="0,0"', 'position:absolute;');
2098
+ this.element.html(context + context);
2099
+
2100
+ // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML
2101
+ qtip._bind( $('*', tip).add(tip), ['click', 'mousedown'], function(event) { event.stopPropagation(); }, this._ns);
2102
+ }
2103
+
2104
+ // Bind update events
2105
+ qtip._bind(qtip.tooltip, 'tooltipmove', this.reposition, this._ns, this);
2106
+
2107
+ // Create it
2108
+ this.create();
2109
+ },
2110
+
2111
+ _swapDimensions: function() {
2112
+ this.size[0] = this.options.height;
2113
+ this.size[1] = this.options.width;
2114
+ },
2115
+ _resetDimensions: function() {
2116
+ this.size[0] = this.options.width;
2117
+ this.size[1] = this.options.height;
2118
+ },
2119
+
2120
+ _useTitle: function(corner) {
2121
+ var titlebar = this.qtip.elements.titlebar;
2122
+ return titlebar && (
2123
+ corner.y === TOP || (corner.y === CENTER && this.element.position().top + (this.size[1] / 2) + this.options.offset < titlebar.outerHeight(TRUE))
2124
+ );
2125
+ },
2126
+
2127
+ _parseCorner: function(corner) {
2128
+ var my = this.qtip.options.position.my;
2129
+
2130
+ // Detect corner and mimic properties
2131
+ if(corner === FALSE || my === FALSE) {
2132
+ corner = FALSE;
2133
+ }
2134
+ else if(corner === TRUE) {
2135
+ corner = new CORNER( my.string() );
2136
+ }
2137
+ else if(!corner.string) {
2138
+ corner = new CORNER(corner);
2139
+ corner.fixed = TRUE;
2140
+ }
2141
+
2142
+ return corner;
2143
+ },
2144
+
2145
+ _parseWidth: function(corner, side, use) {
2146
+ var elements = this.qtip.elements,
2147
+ prop = BORDER + camel(side) + 'Width';
2148
+
2149
+ return (use ? intCss(use, prop) : (
2150
+ intCss(elements.content, prop) ||
2151
+ intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) ||
2152
+ intCss(elements.tooltip, prop)
2153
+ )) || 0;
2154
+ },
2155
+
2156
+ _parseRadius: function(corner) {
2157
+ var elements = this.qtip.elements,
2158
+ prop = BORDER + camel(corner.y) + camel(corner.x) + 'Radius';
2159
+
2160
+ return BROWSER.ie < 9 ? 0 :
2161
+ intCss(this._useTitle(corner) && elements.titlebar || elements.content, prop) ||
2162
+ intCss(elements.tooltip, prop) || 0;
2163
+ },
2164
+
2165
+ _invalidColour: function(elem, prop, compare) {
2166
+ var val = elem.css(prop);
2167
+ return !val || (compare && val === elem.css(compare)) || INVALID.test(val) ? FALSE : val;
2168
+ },
2169
+
2170
+ _parseColours: function(corner) {
2171
+ var elements = this.qtip.elements,
2172
+ tip = this.element.css('cssText', ''),
2173
+ borderSide = BORDER + camel(corner[ corner.precedance ]) + camel(COLOR),
2174
+ colorElem = this._useTitle(corner) && elements.titlebar || elements.content,
2175
+ css = this._invalidColour, color = [];
2176
+
2177
+ // Attempt to detect the background colour from various elements, left-to-right precedance
2178
+ color[0] = css(tip, BG_COLOR) || css(colorElem, BG_COLOR) || css(elements.content, BG_COLOR) ||
2179
+ css(elements.tooltip, BG_COLOR) || tip.css(BG_COLOR);
2180
+
2181
+ // Attempt to detect the correct border side colour from various elements, left-to-right precedance
2182
+ color[1] = css(tip, borderSide, COLOR) || css(colorElem, borderSide, COLOR) ||
2183
+ css(elements.content, borderSide, COLOR) || css(elements.tooltip, borderSide, COLOR) || elements.tooltip.css(borderSide);
2184
+
2185
+ // Reset background and border colours
2186
+ $('*', tip).add(tip).css('cssText', BG_COLOR+':'+TRANSPARENT+IMPORTANT+';'+BORDER+':0'+IMPORTANT+';');
2187
+
2188
+ return color;
2189
+ },
2190
+
2191
+ _calculateSize: function(corner) {
2192
+ var y = corner.precedance === Y,
2193
+ width = this.options['width'],
2194
+ height = this.options['height'],
2195
+ isCenter = corner.abbrev() === 'c',
2196
+ base = (y ? width: height) * (isCenter ? 0.5 : 1),
2197
+ pow = Math.pow,
2198
+ round = Math.round,
2199
+ bigHyp, ratio, result,
2200
+
2201
+ smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
2202
+ hyp = [ (this.border / base) * smallHyp, (this.border / height) * smallHyp ];
2203
+
2204
+ hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(this.border, 2) );
2205
+ hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(this.border, 2) );
2206
+
2207
+ bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
2208
+ ratio = bigHyp / smallHyp;
2209
+
2210
+ result = [ round(ratio * width), round(ratio * height) ];
2211
+ return y ? result : result.reverse();
2212
+ },
2213
+
2214
+ // Tip coordinates calculator
2215
+ _calculateTip: function(corner, size, scale) {
2216
+ scale = scale || 1;
2217
+ size = size || this.size;
2218
+
2219
+ var width = size[0] * scale,
2220
+ height = size[1] * scale,
2221
+ width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
2222
+
2223
+ // Define tip coordinates in terms of height and width values
2224
+ tips = {
2225
+ br: [0,0, width,height, width,0],
2226
+ bl: [0,0, width,0, 0,height],
2227
+ tr: [0,height, width,0, width,height],
2228
+ tl: [0,0, 0,height, width,height],
2229
+ tc: [0,height, width2,0, width,height],
2230
+ bc: [0,0, width,0, width2,height],
2231
+ rc: [0,0, width,height2, 0,height],
2232
+ lc: [width,0, width,height, 0,height2]
2233
+ };
2234
+
2235
+ // Set common side shapes
2236
+ tips.lt = tips.br; tips.rt = tips.bl;
2237
+ tips.lb = tips.tr; tips.rb = tips.tl;
2238
+
2239
+ return tips[ corner.abbrev() ];
2240
+ },
2241
+
2242
+ // Tip coordinates drawer (canvas)
2243
+ _drawCoords: function(context, coords) {
2244
+ context.beginPath();
2245
+ context.moveTo(coords[0], coords[1]);
2246
+ context.lineTo(coords[2], coords[3]);
2247
+ context.lineTo(coords[4], coords[5]);
2248
+ context.closePath();
2249
+ },
2250
+
2251
+ create: function() {
2252
+ // Determine tip corner
2253
+ var c = this.corner = (HASCANVAS || BROWSER.ie) && this._parseCorner(this.options.corner);
2254
+
2255
+ // If we have a tip corner...
2256
+ if( (this.enabled = !!this.corner && this.corner.abbrev() !== 'c') ) {
2257
+ // Cache it
2258
+ this.qtip.cache.corner = c.clone();
2259
+
2260
+ // Create it
2261
+ this.update();
2262
+ }
2263
+
2264
+ // Toggle tip element
2265
+ this.element.toggle(this.enabled);
2266
+
2267
+ return this.corner;
2268
+ },
2269
+
2270
+ update: function(corner, position) {
2271
+ if(!this.enabled) { return this; }
2272
+
2273
+ var elements = this.qtip.elements,
2274
+ tip = this.element,
2275
+ inner = tip.children(),
2276
+ options = this.options,
2277
+ curSize = this.size,
2278
+ mimic = options.mimic,
2279
+ round = Math.round,
2280
+ color, precedance, context,
2281
+ coords, bigCoords, translate, newSize, border, BACKING_STORE_RATIO;
2282
+
2283
+ // Re-determine tip if not already set
2284
+ if(!corner) { corner = this.qtip.cache.corner || this.corner; }
2285
+
2286
+ // Use corner property if we detect an invalid mimic value
2287
+ if(mimic === FALSE) { mimic = corner; }
2288
+
2289
+ // Otherwise inherit mimic properties from the corner object as necessary
2290
+ else {
2291
+ mimic = new CORNER(mimic);
2292
+ mimic.precedance = corner.precedance;
2293
+
2294
+ if(mimic.x === 'inherit') { mimic.x = corner.x; }
2295
+ else if(mimic.y === 'inherit') { mimic.y = corner.y; }
2296
+ else if(mimic.x === mimic.y) {
2297
+ mimic[ corner.precedance ] = corner[ corner.precedance ];
2298
+ }
2299
+ }
2300
+ precedance = mimic.precedance;
2301
+
2302
+ // Ensure the tip width.height are relative to the tip position
2303
+ if(corner.precedance === X) { this._swapDimensions(); }
2304
+ else { this._resetDimensions(); }
2305
+
2306
+ // Update our colours
2307
+ color = this.color = this._parseColours(corner);
2308
+
2309
+ // Detect border width, taking into account colours
2310
+ if(color[1] !== TRANSPARENT) {
2311
+ // Grab border width
2312
+ border = this.border = this._parseWidth(corner, corner[corner.precedance]);
2313
+
2314
+ // If border width isn't zero, use border color as fill if it's not invalid (1.0 style tips)
2315
+ if(options.border && border < 1 && !INVALID.test(color[1])) { color[0] = color[1]; }
2316
+
2317
+ // Set border width (use detected border width if options.border is true)
2318
+ this.border = border = options.border !== TRUE ? options.border : border;
2319
+ }
2320
+
2321
+ // Border colour was invalid, set border to zero
2322
+ else { this.border = border = 0; }
2323
+
2324
+ // Determine tip size
2325
+ newSize = this.size = this._calculateSize(corner);
2326
+ tip.css({
2327
+ width: newSize[0],
2328
+ height: newSize[1],
2329
+ lineHeight: newSize[1]+'px'
2330
+ });
2331
+
2332
+ // Calculate tip translation
2333
+ if(corner.precedance === Y) {
2334
+ translate = [
2335
+ round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize[0] - curSize[0] - border : (newSize[0] - curSize[0]) / 2),
2336
+ round(mimic.y === TOP ? newSize[1] - curSize[1] : 0)
2337
+ ];
2338
+ }
2339
+ else {
2340
+ translate = [
2341
+ round(mimic.x === LEFT ? newSize[0] - curSize[0] : 0),
2342
+ round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize[1] - curSize[1] - border : (newSize[1] - curSize[1]) / 2)
2343
+ ];
2344
+ }
2345
+
2346
+ // Canvas drawing implementation
2347
+ if(HASCANVAS) {
2348
+ // Grab canvas context and clear/save it
2349
+ context = inner[0].getContext('2d');
2350
+ context.restore(); context.save();
2351
+ context.clearRect(0,0,6000,6000);
2352
+
2353
+ // Calculate coordinates
2354
+ coords = this._calculateTip(mimic, curSize, SCALE);
2355
+ bigCoords = this._calculateTip(mimic, this.size, SCALE);
2356
+
2357
+ // Set the canvas size using calculated size
2358
+ inner.attr(WIDTH, newSize[0] * SCALE).attr(HEIGHT, newSize[1] * SCALE);
2359
+ inner.css(WIDTH, newSize[0]).css(HEIGHT, newSize[1]);
2360
+
2361
+ // Draw the outer-stroke tip
2362
+ this._drawCoords(context, bigCoords);
2363
+ context.fillStyle = color[1];
2364
+ context.fill();
2365
+
2366
+ // Draw the actual tip
2367
+ context.translate(translate[0] * SCALE, translate[1] * SCALE);
2368
+ this._drawCoords(context, coords);
2369
+ context.fillStyle = color[0];
2370
+ context.fill();
2371
+ }
2372
+
2373
+ // VML (IE Proprietary implementation)
2374
+ else {
2375
+ // Calculate coordinates
2376
+ coords = this._calculateTip(mimic);
2377
+
2378
+ // Setup coordinates string
2379
+ coords = 'm' + coords[0] + ',' + coords[1] + ' l' + coords[2] +
2380
+ ',' + coords[3] + ' ' + coords[4] + ',' + coords[5] + ' xe';
2381
+
2382
+ // Setup VML-specific offset for pixel-perfection
2383
+ translate[2] = border && /^(r|b)/i.test(corner.string()) ?
2384
+ BROWSER.ie === 8 ? 2 : 1 : 0;
2385
+
2386
+ // Set initial CSS
2387
+ inner.css({
2388
+ coordsize: (newSize[0]+border) + ' ' + (newSize[1]+border),
2389
+ antialias: ''+(mimic.string().indexOf(CENTER) > -1),
2390
+ left: translate[0] - (translate[2] * Number(precedance === X)),
2391
+ top: translate[1] - (translate[2] * Number(precedance === Y)),
2392
+ width: newSize[0] + border,
2393
+ height: newSize[1] + border
2394
+ })
2395
+ .each(function(i) {
2396
+ var $this = $(this);
2397
+
2398
+ // Set shape specific attributes
2399
+ $this[ $this.prop ? 'prop' : 'attr' ]({
2400
+ coordsize: (newSize[0]+border) + ' ' + (newSize[1]+border),
2401
+ path: coords,
2402
+ fillcolor: color[0],
2403
+ filled: !!i,
2404
+ stroked: !i
2405
+ })
2406
+ .toggle(!!(border || i));
2407
+
2408
+ // Check if border is enabled and add stroke element
2409
+ !i && $this.html( createVML(
2410
+ 'stroke', 'weight="'+(border*2)+'px" color="'+color[1]+'" miterlimit="1000" joinstyle="miter"'
2411
+ ) );
2412
+ });
2413
+ }
2414
+
2415
+ // Opera bug #357 - Incorrect tip position
2416
+ // https://github.com/Craga89/qTip2/issues/367
2417
+ window.opera && setTimeout(function() {
2418
+ elements.tip.css({
2419
+ display: 'inline-block',
2420
+ visibility: 'visible'
2421
+ });
2422
+ }, 1);
2423
+
2424
+ // Position if needed
2425
+ if(position !== FALSE) { this.calculate(corner, newSize); }
2426
+ },
2427
+
2428
+ calculate: function(corner, size) {
2429
+ if(!this.enabled) { return FALSE; }
2430
+
2431
+ var self = this,
2432
+ elements = this.qtip.elements,
2433
+ tip = this.element,
2434
+ userOffset = this.options.offset,
2435
+ isWidget = elements.tooltip.hasClass('ui-widget'),
2436
+ position = { },
2437
+ precedance, corners;
2438
+
2439
+ // Inherit corner if not provided
2440
+ corner = corner || this.corner;
2441
+ precedance = corner.precedance;
2442
+
2443
+ // Determine which tip dimension to use for adjustment
2444
+ size = size || this._calculateSize(corner);
2445
+
2446
+ // Setup corners and offset array
2447
+ corners = [ corner.x, corner.y ];
2448
+ if(precedance === X) { corners.reverse(); }
2449
+
2450
+ // Calculate tip position
2451
+ $.each(corners, function(i, side) {
2452
+ var b, bc, br;
2453
+
2454
+ if(side === CENTER) {
2455
+ b = precedance === Y ? LEFT : TOP;
2456
+ position[ b ] = '50%';
2457
+ position[MARGIN+'-' + b] = -Math.round(size[ precedance === Y ? 0 : 1 ] / 2) + userOffset;
2458
+ }
2459
+ else {
2460
+ b = self._parseWidth(corner, side, elements.tooltip);
2461
+ bc = self._parseWidth(corner, side, elements.content);
2462
+ br = self._parseRadius(corner);
2463
+
2464
+ position[ side ] = Math.max(-self.border, i ? bc : (userOffset + (br > b ? br : -b)));
2465
+ }
2466
+ });
2467
+
2468
+ // Adjust for tip size
2469
+ position[ corner[precedance] ] -= size[ precedance === X ? 0 : 1 ];
2470
+
2471
+ // Set and return new position
2472
+ tip.css({ margin: '', top: '', bottom: '', left: '', right: '' }).css(position);
2473
+ return position;
2474
+ },
2475
+
2476
+ reposition: function(event, api, pos, viewport) {
2477
+ if(!this.enabled) { return; }
2478
+
2479
+ var cache = api.cache,
2480
+ newCorner = this.corner.clone(),
2481
+ adjust = pos.adjusted,
2482
+ method = api.options.position.adjust.method.split(' '),
2483
+ horizontal = method[0],
2484
+ vertical = method[1] || method[0],
2485
+ shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
2486
+ offset, css = {}, props;
2487
+
2488
+ function shiftflip(direction, precedance, popposite, side, opposite) {
2489
+ // Horizontal - Shift or flip method
2490
+ if(direction === SHIFT && newCorner.precedance === precedance && adjust[side] && newCorner[popposite] !== CENTER) {
2491
+ newCorner.precedance = newCorner.precedance === X ? Y : X;
2492
+ }
2493
+ else if(direction !== SHIFT && adjust[side]){
2494
+ newCorner[precedance] = newCorner[precedance] === CENTER ?
2495
+ (adjust[side] > 0 ? side : opposite) : (newCorner[precedance] === side ? opposite : side);
2496
+ }
2497
+ }
2498
+
2499
+ function shiftonly(xy, side, opposite) {
2500
+ if(newCorner[xy] === CENTER) {
2501
+ css[MARGIN+'-'+side] = shift[xy] = offset[MARGIN+'-'+side] - adjust[side];
2502
+ }
2503
+ else {
2504
+ props = offset[opposite] !== undefined ?
2505
+ [ adjust[side], -offset[side] ] : [ -adjust[side], offset[side] ];
2506
+
2507
+ if( (shift[xy] = Math.max(props[0], props[1])) > props[0] ) {
2508
+ pos[side] -= adjust[side];
2509
+ shift[side] = FALSE;
2510
+ }
2511
+
2512
+ css[ offset[opposite] !== undefined ? opposite : side ] = shift[xy];
2513
+ }
2514
+ }
2515
+
2516
+ // If our tip position isn't fixed e.g. doesn't adjust with viewport...
2517
+ if(this.corner.fixed !== TRUE) {
2518
+ // Perform shift/flip adjustments
2519
+ shiftflip(horizontal, X, Y, LEFT, RIGHT);
2520
+ shiftflip(vertical, Y, X, TOP, BOTTOM);
2521
+
2522
+ // Update and redraw the tip if needed (check cached details of last drawn tip)
2523
+ if(newCorner.string() !== cache.corner.string() || cache.cornerTop !== adjust.top || cache.cornerLeft !== adjust.left) {
2524
+ this.update(newCorner, FALSE);
2525
+ }
2526
+ }
2527
+
2528
+ // Setup tip offset properties
2529
+ offset = this.calculate(newCorner);
2530
+
2531
+ // Readjust offset object to make it left/top
2532
+ if(offset.right !== undefined) { offset.left = -offset.right; }
2533
+ if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
2534
+ offset.user = this.offset;
2535
+
2536
+ // Perform shift adjustments
2537
+ if(shift.left = (horizontal === SHIFT && !!adjust.left)) { shiftonly(X, LEFT, RIGHT); }
2538
+ if(shift.top = (vertical === SHIFT && !!adjust.top)) { shiftonly(Y, TOP, BOTTOM); }
2539
+
2540
+ /*
2541
+ * If the tip is adjusted in both dimensions, or in a
2542
+ * direction that would cause it to be anywhere but the
2543
+ * outer border, hide it!
2544
+ */
2545
+ this.element.css(css).toggle(
2546
+ !((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x))
2547
+ );
2548
+
2549
+ // Adjust position to accomodate tip dimensions
2550
+ pos.left -= offset.left.charAt ? offset.user :
2551
+ horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left + this.border : 0;
2552
+ pos.top -= offset.top.charAt ? offset.user :
2553
+ vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top + this.border : 0;
2554
+
2555
+ // Cache details
2556
+ cache.cornerLeft = adjust.left; cache.cornerTop = adjust.top;
2557
+ cache.corner = newCorner.clone();
2558
+ },
2559
+
2560
+ destroy: function() {
2561
+ // Unbind events
2562
+ this.qtip._unbind(this.qtip.tooltip, this._ns);
2563
+
2564
+ // Remove the tip element(s)
2565
+ if(this.qtip.elements.tip) {
2566
+ this.qtip.elements.tip.find('*')
2567
+ .remove().end().remove();
2568
+ }
2569
+ }
2570
+ });
2571
+
2572
+ TIP = PLUGINS.tip = function(api) {
2573
+ return new Tip(api, api.options.style.tip);
2574
+ };
2575
+
2576
+ // Initialize tip on render
2577
+ TIP.initialize = 'render';
2578
+
2579
+ // Setup plugin sanitization options
2580
+ TIP.sanitize = function(options) {
2581
+ if(options.style && 'tip' in options.style) {
2582
+ var opts = options.style.tip;
2583
+ if(typeof opts !== 'object') { opts = options.style.tip = { corner: opts }; }
2584
+ if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
2585
+ }
2586
+ };
2587
+
2588
+ // Add new option checks for the plugin
2589
+ CHECKS.tip = {
2590
+ '^position.my|style.tip.(corner|mimic|border)$': function() {
2591
+ // Make sure a tip can be drawn
2592
+ this.create();
2593
+
2594
+ // Reposition the tooltip
2595
+ this.qtip.reposition();
2596
+ },
2597
+ '^style.tip.(height|width)$': function(obj) {
2598
+ // Re-set dimensions and redraw the tip
2599
+ this.size = [ obj.width, obj.height ];
2600
+ this.update();
2601
+
2602
+ // Reposition the tooltip
2603
+ this.qtip.reposition();
2604
+ },
2605
+ '^content.title|style.(classes|widget)$': function() {
2606
+ this.update();
2607
+ }
2608
+ };
2609
+
2610
+ // Extend original qTip defaults
2611
+ $.extend(TRUE, QTIP.defaults, {
2612
+ style: {
2613
+ tip: {
2614
+ corner: TRUE,
2615
+ mimic: FALSE,
2616
+ width: 6,
2617
+ height: 6,
2618
+ border: TRUE,
2619
+ offset: 0
2620
+ }
2621
+ }
2622
+ });
2623
+ ;PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
2624
+ {
2625
+ var target = posOptions.target,
2626
+ tooltip = api.elements.tooltip,
2627
+ my = posOptions.my,
2628
+ at = posOptions.at,
2629
+ adjust = posOptions.adjust,
2630
+ method = adjust.method.split(' '),
2631
+ methodX = method[0],
2632
+ methodY = method[1] || method[0],
2633
+ viewport = posOptions.viewport,
2634
+ container = posOptions.container,
2635
+ cache = api.cache,
2636
+ adjusted = { left: 0, top: 0 },
2637
+ fixed, newMy, containerOffset, containerStatic,
2638
+ viewportWidth, viewportHeight, viewportScroll, viewportOffset;
2639
+
2640
+ // If viewport is not a jQuery element, or it's the window/document, or no adjustment method is used... return
2641
+ if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') {
2642
+ return adjusted;
2643
+ }
2644
+
2645
+ // Cach container details
2646
+ containerOffset = container.offset() || adjusted;
2647
+ containerStatic = container.css('position') === 'static';
2648
+
2649
+ // Cache our viewport details
2650
+ fixed = tooltip.css('position') === 'fixed';
2651
+ viewportWidth = viewport[0] === window ? viewport.width() : viewport.outerWidth(FALSE);
2652
+ viewportHeight = viewport[0] === window ? viewport.height() : viewport.outerHeight(FALSE);
2653
+ viewportScroll = { left: fixed ? 0 : viewport.scrollLeft(), top: fixed ? 0 : viewport.scrollTop() };
2654
+ viewportOffset = viewport.offset() || adjusted;
2655
+
2656
+ // Generic calculation method
2657
+ function calculate(side, otherSide, type, adjust, side1, side2, lengthName, targetLength, elemLength) {
2658
+ var initialPos = position[side1],
2659
+ mySide = my[side],
2660
+ atSide = at[side],
2661
+ isShift = type === SHIFT,
2662
+ myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2,
2663
+ atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2,
2664
+ sideOffset = viewportScroll[side1] + viewportOffset[side1] - (containerStatic ? 0 : containerOffset[side1]),
2665
+ overflow1 = sideOffset - initialPos,
2666
+ overflow2 = initialPos + elemLength - (lengthName === WIDTH ? viewportWidth : viewportHeight) - sideOffset,
2667
+ offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0);
2668
+
2669
+ // shift
2670
+ if(isShift) {
2671
+ offset = (mySide === side1 ? 1 : -1) * myLength;
2672
+
2673
+ // Adjust position but keep it within viewport dimensions
2674
+ position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0;
2675
+ position[side1] = Math.max(
2676
+ -containerOffset[side1] + viewportOffset[side1],
2677
+ initialPos - offset,
2678
+ Math.min(
2679
+ Math.max(
2680
+ -containerOffset[side1] + viewportOffset[side1] + (lengthName === WIDTH ? viewportWidth : viewportHeight),
2681
+ initialPos + offset
2682
+ ),
2683
+ position[side1],
2684
+
2685
+ // Make sure we don't adjust complete off the element when using 'center'
2686
+ mySide === 'center' ? initialPos - myLength : 1E9
2687
+ )
2688
+ );
2689
+
2690
+ }
2691
+
2692
+ // flip/flipinvert
2693
+ else {
2694
+ // Update adjustment amount depending on if using flipinvert or flip
2695
+ adjust *= (type === FLIPINVERT ? 2 : 0);
2696
+
2697
+ // Check for overflow on the left/top
2698
+ if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) {
2699
+ position[side1] -= offset + adjust;
2700
+ newMy.invert(side, side1);
2701
+ }
2702
+
2703
+ // Check for overflow on the bottom/right
2704
+ else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) {
2705
+ position[side1] -= (mySide === CENTER ? -offset : offset) + adjust;
2706
+ newMy.invert(side, side2);
2707
+ }
2708
+
2709
+ // Make sure we haven't made things worse with the adjustment and reset if so
2710
+ if(position[side1] < viewportScroll && -position[side1] > overflow2) {
2711
+ position[side1] = initialPos; newMy = my.clone();
2712
+ }
2713
+ }
2714
+
2715
+ return position[side1] - initialPos;
2716
+ }
2717
+
2718
+ // Set newMy if using flip or flipinvert methods
2719
+ if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); }
2720
+
2721
+ // Adjust position based onviewport and adjustment options
2722
+ adjusted = {
2723
+ left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0,
2724
+ top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0,
2725
+ my: newMy
2726
+ };
2727
+
2728
+ return adjusted;
2729
+ };
2730
+ ;PLUGINS.polys = {
2731
+ // POLY area coordinate calculator
2732
+ // Special thanks to Ed Cradock for helping out with this.
2733
+ // Uses a binary search algorithm to find suitable coordinates.
2734
+ polygon: function(baseCoords, corner) {
2735
+ var result = {
2736
+ width: 0, height: 0,
2737
+ position: {
2738
+ top: 1e10, right: 0,
2739
+ bottom: 0, left: 1e10
2740
+ },
2741
+ adjustable: FALSE
2742
+ },
2743
+ i = 0, next,
2744
+ coords = [],
2745
+ compareX = 1, compareY = 1,
2746
+ realX = 0, realY = 0,
2747
+ newWidth, newHeight;
2748
+
2749
+ // First pass, sanitize coords and determine outer edges
2750
+ i = baseCoords.length; while(i--) {
2751
+ next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
2752
+
2753
+ if(next[0] > result.position.right){ result.position.right = next[0]; }
2754
+ if(next[0] < result.position.left){ result.position.left = next[0]; }
2755
+ if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
2756
+ if(next[1] < result.position.top){ result.position.top = next[1]; }
2757
+
2758
+ coords.push(next);
2759
+ }
2760
+
2761
+ // Calculate height and width from outer edges
2762
+ newWidth = result.width = Math.abs(result.position.right - result.position.left);
2763
+ newHeight = result.height = Math.abs(result.position.bottom - result.position.top);
2764
+
2765
+ // If it's the center corner...
2766
+ if(corner.abbrev() === 'c') {
2767
+ result.position = {
2768
+ left: result.position.left + (result.width / 2),
2769
+ top: result.position.top + (result.height / 2)
2770
+ };
2771
+ }
2772
+ else {
2773
+ // Second pass, use a binary search algorithm to locate most suitable coordinate
2774
+ while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
2775
+ {
2776
+ newWidth = Math.floor(newWidth / 2);
2777
+ newHeight = Math.floor(newHeight / 2);
2778
+
2779
+ if(corner.x === LEFT){ compareX = newWidth; }
2780
+ else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
2781
+ else{ compareX += Math.floor(newWidth / 2); }
2782
+
2783
+ if(corner.y === TOP){ compareY = newHeight; }
2784
+ else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
2785
+ else{ compareY += Math.floor(newHeight / 2); }
2786
+
2787
+ i = coords.length; while(i--)
2788
+ {
2789
+ if(coords.length < 2){ break; }
2790
+
2791
+ realX = coords[i][0] - result.position.left;
2792
+ realY = coords[i][1] - result.position.top;
2793
+
2794
+ if((corner.x === LEFT && realX >= compareX) ||
2795
+ (corner.x === RIGHT && realX <= compareX) ||
2796
+ (corner.x === CENTER && (realX < compareX || realX > (result.width - compareX))) ||
2797
+ (corner.y === TOP && realY >= compareY) ||
2798
+ (corner.y === BOTTOM && realY <= compareY) ||
2799
+ (corner.y === CENTER && (realY < compareY || realY > (result.height - compareY)))) {
2800
+ coords.splice(i, 1);
2801
+ }
2802
+ }
2803
+ }
2804
+ result.position = { left: coords[0][0], top: coords[0][1] };
2805
+ }
2806
+
2807
+ return result;
2808
+ },
2809
+
2810
+ rect: function(ax, ay, bx, by) {
2811
+ return {
2812
+ width: Math.abs(bx - ax),
2813
+ height: Math.abs(by - ay),
2814
+ position: {
2815
+ left: Math.min(ax, bx),
2816
+ top: Math.min(ay, by)
2817
+ }
2818
+ };
2819
+ },
2820
+
2821
+ _angles: {
2822
+ tc: 3 / 2, tr: 7 / 4, tl: 5 / 4,
2823
+ bc: 1 / 2, br: 1 / 4, bl: 3 / 4,
2824
+ rc: 2, lc: 1, c: 0
2825
+ },
2826
+ ellipse: function(cx, cy, rx, ry, corner) {
2827
+ var c = PLUGINS.polys._angles[ corner.abbrev() ],
2828
+ rxc = c === 0 ? 0 : rx * Math.cos( c * Math.PI ),
2829
+ rys = ry * Math.sin( c * Math.PI );
2830
+
2831
+ return {
2832
+ width: (rx * 2) - Math.abs(rxc),
2833
+ height: (ry * 2) - Math.abs(rys),
2834
+ position: {
2835
+ left: cx + rxc,
2836
+ top: cy + rys
2837
+ },
2838
+ adjustable: FALSE
2839
+ };
2840
+ },
2841
+ circle: function(cx, cy, r, corner) {
2842
+ return PLUGINS.polys.ellipse(cx, cy, r, r, corner);
2843
+ }
2844
+ };
2845
+ ;PLUGINS.imagemap = function(api, area, corner, adjustMethod)
2846
+ {
2847
+ if(!area.jquery) { area = $(area); }
2848
+
2849
+ var shape = (area.attr('shape') || 'rect').toLowerCase().replace('poly', 'polygon'),
2850
+ image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
2851
+ coordsString = $.trim(area.attr('coords')),
2852
+ coordsArray = coordsString.replace(/,$/, '').split(','),
2853
+ imageOffset, coords, i, next, result, len;
2854
+
2855
+ // If we can't find the image using the map...
2856
+ if(!image.length) { return FALSE; }
2857
+
2858
+ // Pass coordinates string if polygon
2859
+ if(shape === 'polygon') {
2860
+ result = PLUGINS.polys.polygon(coordsArray, corner);
2861
+ }
2862
+
2863
+ // Otherwise parse the coordinates and pass them as arguments
2864
+ else if(PLUGINS.polys[shape]) {
2865
+ for(i = -1, len = coordsArray.length, coords = []; ++i < len;) {
2866
+ coords.push( parseInt(coordsArray[i], 10) );
2867
+ }
2868
+
2869
+ result = PLUGINS.polys[shape].apply(
2870
+ this, coords.concat(corner)
2871
+ );
2872
+ }
2873
+
2874
+ // If no shapre calculation method was found, return false
2875
+ else { return FALSE; }
2876
+
2877
+ // Make sure we account for padding and borders on the image
2878
+ imageOffset = image.offset();
2879
+ imageOffset.left += Math.ceil((image.outerWidth(FALSE) - image.width()) / 2);
2880
+ imageOffset.top += Math.ceil((image.outerHeight(FALSE) - image.height()) / 2);
2881
+
2882
+ // Add image position to offset coordinates
2883
+ result.position.left += imageOffset.left;
2884
+ result.position.top += imageOffset.top;
2885
+
2886
+ return result;
2887
+ };
2888
+ ;PLUGINS.svg = function(api, svg, corner)
2889
+ {
2890
+ var doc = $(document),
2891
+ elem = svg[0],
2892
+ root = $(elem.ownerSVGElement),
2893
+ ownerDocument = elem.ownerDocument,
2894
+ strokeWidth2 = (parseInt(svg.css('stroke-width'), 10) || 0) / 2,
2895
+ frameOffset, mtx, transformed, viewBox,
2896
+ len, next, i, points,
2897
+ result, position, dimensions;
2898
+
2899
+ // Ascend the parentNode chain until we find an element with getBBox()
2900
+ while(!elem.getBBox) { elem = elem.parentNode; }
2901
+ if(!elem.getBBox || !elem.parentNode) { return FALSE; }
2902
+
2903
+ // Determine which shape calculation to use
2904
+ switch(elem.nodeName) {
2905
+ case 'ellipse':
2906
+ case 'circle':
2907
+ result = PLUGINS.polys.ellipse(
2908
+ elem.cx.baseVal.value,
2909
+ elem.cy.baseVal.value,
2910
+ (elem.rx || elem.r).baseVal.value + strokeWidth2,
2911
+ (elem.ry || elem.r).baseVal.value + strokeWidth2,
2912
+ corner
2913
+ );
2914
+ break;
2915
+
2916
+ case 'line':
2917
+ case 'polygon':
2918
+ case 'polyline':
2919
+ // Determine points object (line has none, so mimic using array)
2920
+ points = elem.points || [
2921
+ { x: elem.x1.baseVal.value, y: elem.y1.baseVal.value },
2922
+ { x: elem.x2.baseVal.value, y: elem.y2.baseVal.value }
2923
+ ];
2924
+
2925
+ for(result = [], i = -1, len = points.numberOfItems || points.length; ++i < len;) {
2926
+ next = points.getItem ? points.getItem(i) : points[i];
2927
+ result.push.apply(result, [next.x, next.y]);
2928
+ }
2929
+
2930
+ result = PLUGINS.polys.polygon(result, corner);
2931
+ break;
2932
+
2933
+ // Unknown shape or rectangle? Use bounding box
2934
+ default:
2935
+ result = elem.getBBox();
2936
+ result = {
2937
+ width: result.width,
2938
+ height: result.height,
2939
+ position: {
2940
+ left: result.x,
2941
+ top: result.y
2942
+ }
2943
+ };
2944
+ break;
2945
+ }
2946
+
2947
+ // Shortcut assignments
2948
+ position = result.position;
2949
+ root = root[0];
2950
+
2951
+ // Convert position into a pixel value
2952
+ if(root.createSVGPoint) {
2953
+ mtx = elem.getScreenCTM();
2954
+ points = root.createSVGPoint();
2955
+
2956
+ points.x = position.left;
2957
+ points.y = position.top;
2958
+ transformed = points.matrixTransform( mtx );
2959
+ position.left = transformed.x;
2960
+ position.top = transformed.y;
2961
+ }
2962
+
2963
+ // Check the element is not in a child document, and if so, adjust for frame elements offset
2964
+ if(ownerDocument !== document && api.position.target !== 'mouse') {
2965
+ frameOffset = $((ownerDocument.defaultView || ownerDocument.parentWindow).frameElement).offset();
2966
+ if(frameOffset) {
2967
+ position.left += frameOffset.left;
2968
+ position.top += frameOffset.top;
2969
+ }
2970
+ }
2971
+
2972
+ // Adjust by scroll offset of owner document
2973
+ ownerDocument = $(ownerDocument);
2974
+ position.left += ownerDocument.scrollLeft();
2975
+ position.top += ownerDocument.scrollTop();
2976
+
2977
+ return result;
2978
+ };
2979
+ ;var MODAL, OVERLAY,
2980
+ MODALCLASS = 'qtip-modal',
2981
+ MODALSELECTOR = '.'+MODALCLASS;
2982
+
2983
+ OVERLAY = function()
2984
+ {
2985
+ var self = this,
2986
+ focusableElems = {},
2987
+ current, onLast,
2988
+ prevState, elem;
2989
+
2990
+ // Modified code from jQuery UI 1.10.0 source
2991
+ // http://code.jquery.com/ui/1.10.0/jquery-ui.js
2992
+ function focusable(element) {
2993
+ // Use the defined focusable checker when possible
2994
+ if($.expr[':'].focusable) { return $.expr[':'].focusable; }
2995
+
2996
+ var isTabIndexNotNaN = !isNaN($.attr(element, 'tabindex')),
2997
+ nodeName = element.nodeName && element.nodeName.toLowerCase(),
2998
+ map, mapName, img;
2999
+
3000
+ if('area' === nodeName) {
3001
+ map = element.parentNode;
3002
+ mapName = map.name;
3003
+ if(!element.href || !mapName || map.nodeName.toLowerCase() !== 'map') {
3004
+ return false;
3005
+ }
3006
+ img = $('img[usemap=#' + mapName + ']')[0];
3007
+ return !!img && img.is(':visible');
3008
+ }
3009
+ return (/input|select|textarea|button|object/.test( nodeName ) ?
3010
+ !element.disabled :
3011
+ 'a' === nodeName ?
3012
+ element.href || isTabIndexNotNaN :
3013
+ isTabIndexNotNaN
3014
+ );
3015
+ }
3016
+
3017
+ // Focus inputs using cached focusable elements (see update())
3018
+ function focusInputs(blurElems) {
3019
+ // Blurring body element in IE causes window.open windows to unfocus!
3020
+ if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
3021
+
3022
+ // Focus the inputs
3023
+ else { focusableElems.first().focus(); }
3024
+ }
3025
+
3026
+ // Steal focus from elements outside tooltip
3027
+ function stealFocus(event) {
3028
+ if(!elem.is(':visible')) { return; }
3029
+
3030
+ var target = $(event.target),
3031
+ tooltip = current.tooltip,
3032
+ container = target.closest(SELECTOR),
3033
+ targetOnTop;
3034
+
3035
+ // Determine if input container target is above this
3036
+ targetOnTop = container.length < 1 ? FALSE :
3037
+ (parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10));
3038
+
3039
+ // If we're showing a modal, but focus has landed on an input below
3040
+ // this modal, divert focus to the first visible input in this modal
3041
+ // or if we can't find one... the tooltip itself
3042
+ if(!targetOnTop && target.closest(SELECTOR)[0] !== tooltip[0]) {
3043
+ focusInputs(target);
3044
+ }
3045
+
3046
+ // Detect when we leave the last focusable element...
3047
+ onLast = event.target === focusableElems[focusableElems.length - 1];
3048
+ }
3049
+
3050
+ $.extend(self, {
3051
+ init: function() {
3052
+ // Create document overlay
3053
+ elem = self.elem = $('<div />', {
3054
+ id: 'qtip-overlay',
3055
+ html: '<div></div>',
3056
+ mousedown: function() { return FALSE; }
3057
+ })
3058
+ .hide();
3059
+
3060
+ // Make sure we can't focus anything outside the tooltip
3061
+ $(document.body).bind('focusin'+MODALSELECTOR, stealFocus);
3062
+
3063
+ // Apply keyboard "Escape key" close handler
3064
+ $(document).bind('keydown'+MODALSELECTOR, function(event) {
3065
+ if(current && current.options.show.modal.escape && event.keyCode === 27) {
3066
+ current.hide(event);
3067
+ }
3068
+ });
3069
+
3070
+ // Apply click handler for blur option
3071
+ elem.bind('click'+MODALSELECTOR, function(event) {
3072
+ if(current && current.options.show.modal.blur) {
3073
+ current.hide(event);
3074
+ }
3075
+ });
3076
+
3077
+ return self;
3078
+ },
3079
+
3080
+ update: function(api) {
3081
+ // Update current API reference
3082
+ current = api;
3083
+
3084
+ // Update focusable elements if enabled
3085
+ if(api.options.show.modal.stealfocus !== FALSE) {
3086
+ focusableElems = api.tooltip.find('*').filter(function() {
3087
+ return focusable(this);
3088
+ });
3089
+ }
3090
+ else { focusableElems = []; }
3091
+ },
3092
+
3093
+ toggle: function(api, state, duration) {
3094
+ var docBody = $(document.body),
3095
+ tooltip = api.tooltip,
3096
+ options = api.options.show.modal,
3097
+ effect = options.effect,
3098
+ type = state ? 'show': 'hide',
3099
+ visible = elem.is(':visible'),
3100
+ visibleModals = $(MODALSELECTOR).filter(':visible:not(:animated)').not(tooltip),
3101
+ zindex;
3102
+
3103
+ // Set active tooltip API reference
3104
+ self.update(api);
3105
+
3106
+ // If the modal can steal the focus...
3107
+ // Blur the current item and focus anything in the modal we an
3108
+ if(state && options.stealfocus !== FALSE) {
3109
+ focusInputs( $(':focus') );
3110
+ }
3111
+
3112
+ // Toggle backdrop cursor style on show
3113
+ elem.toggleClass('blurs', options.blur);
3114
+
3115
+ // Append to body on show
3116
+ if(state) {
3117
+ elem.appendTo(document.body);
3118
+ }
3119
+
3120
+ // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
3121
+ if((elem.is(':animated') && visible === state && prevState !== FALSE) || (!state && visibleModals.length)) {
3122
+ return self;
3123
+ }
3124
+
3125
+ // Stop all animations
3126
+ elem.stop(TRUE, FALSE);
3127
+
3128
+ // Use custom function if provided
3129
+ if($.isFunction(effect)) {
3130
+ effect.call(elem, state);
3131
+ }
3132
+
3133
+ // If no effect type is supplied, use a simple toggle
3134
+ else if(effect === FALSE) {
3135
+ elem[ type ]();
3136
+ }
3137
+
3138
+ // Use basic fade function
3139
+ else {
3140
+ elem.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
3141
+ if(!state) { elem.hide(); }
3142
+ });
3143
+ }
3144
+
3145
+ // Reset position and detach from body on hide
3146
+ if(!state) {
3147
+ elem.queue(function(next) {
3148
+ elem.css({ left: '', top: '' });
3149
+ if(!$(MODALSELECTOR).length) { elem.detach(); }
3150
+ next();
3151
+ });
3152
+ }
3153
+
3154
+ // Cache the state
3155
+ prevState = state;
3156
+
3157
+ // If the tooltip is destroyed, set reference to null
3158
+ if(current.destroyed) { current = NULL; }
3159
+
3160
+ return self;
3161
+ }
3162
+ });
3163
+
3164
+ self.init();
3165
+ };
3166
+ OVERLAY = new OVERLAY();
3167
+
3168
+ function Modal(api, options) {
3169
+ this.options = options;
3170
+ this._ns = '-modal';
3171
+
3172
+ this.init( (this.qtip = api) );
3173
+ }
3174
+
3175
+ $.extend(Modal.prototype, {
3176
+ init: function(qtip) {
3177
+ var tooltip = qtip.tooltip;
3178
+
3179
+ // If modal is disabled... return
3180
+ if(!this.options.on) { return this; }
3181
+
3182
+ // Set overlay reference
3183
+ qtip.elements.overlay = OVERLAY.elem;
3184
+
3185
+ // Add unique attribute so we can grab modal tooltips easily via a SELECTOR, and set z-index
3186
+ tooltip.addClass(MODALCLASS).css('z-index', QTIP.modal_zindex + $(MODALSELECTOR).length);
3187
+
3188
+ // Apply our show/hide/focus modal events
3189
+ qtip._bind(tooltip, ['tooltipshow', 'tooltiphide'], function(event, api, duration) {
3190
+ var oEvent = event.originalEvent;
3191
+
3192
+ // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
3193
+ if(event.target === tooltip[0]) {
3194
+ if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(OVERLAY.elem[0]).length) {
3195
+ try { event.preventDefault(); } catch(e) {}
3196
+ }
3197
+ else if(!oEvent || (oEvent && oEvent.type !== 'tooltipsolo')) {
3198
+ this.toggle(event, event.type === 'tooltipshow', duration);
3199
+ }
3200
+ }
3201
+ }, this._ns, this);
3202
+
3203
+ // Adjust modal z-index on tooltip focus
3204
+ qtip._bind(tooltip, 'tooltipfocus', function(event, api) {
3205
+ // If focus was cancelled before it reached us, don't do anything
3206
+ if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
3207
+
3208
+ var qtips = $(MODALSELECTOR),
3209
+
3210
+ // Keep the modal's lower than other, regular qtips
3211
+ newIndex = QTIP.modal_zindex + qtips.length,
3212
+ curIndex = parseInt(tooltip[0].style.zIndex, 10);
3213
+
3214
+ // Set overlay z-index
3215
+ OVERLAY.elem[0].style.zIndex = newIndex - 1;
3216
+
3217
+ // Reduce modal z-index's and keep them properly ordered
3218
+ qtips.each(function() {
3219
+ if(this.style.zIndex > curIndex) {
3220
+ this.style.zIndex -= 1;
3221
+ }
3222
+ });
3223
+
3224
+ // Fire blur event for focused tooltip
3225
+ qtips.filter('.' + CLASS_FOCUS).qtip('blur', event.originalEvent);
3226
+
3227
+ // Set the new z-index
3228
+ tooltip.addClass(CLASS_FOCUS)[0].style.zIndex = newIndex;
3229
+
3230
+ // Set current
3231
+ OVERLAY.update(api);
3232
+
3233
+ // Prevent default handling
3234
+ try { event.preventDefault(); } catch(e) {}
3235
+ }, this._ns, this);
3236
+
3237
+ // Focus any other visible modals when this one hides
3238
+ qtip._bind(tooltip, 'tooltiphide', function(event) {
3239
+ if(event.target === tooltip[0]) {
3240
+ $(MODALSELECTOR).filter(':visible').not(tooltip).last().qtip('focus', event);
3241
+ }
3242
+ }, this._ns, this);
3243
+ },
3244
+
3245
+ toggle: function(event, state, duration) {
3246
+ // Make sure default event hasn't been prevented
3247
+ if(event && event.isDefaultPrevented()) { return this; }
3248
+
3249
+ // Toggle it
3250
+ OVERLAY.toggle(this.qtip, !!state, duration);
3251
+ },
3252
+
3253
+ destroy: function() {
3254
+ // Remove modal class
3255
+ this.qtip.tooltip.removeClass(MODALCLASS);
3256
+
3257
+ // Remove bound events
3258
+ this.qtip._unbind(this.qtip.tooltip, this._ns);
3259
+
3260
+ // Delete element reference
3261
+ OVERLAY.toggle(this.qtip, FALSE);
3262
+ delete this.qtip.elements.overlay;
3263
+ }
3264
+ });
3265
+
3266
+
3267
+ MODAL = PLUGINS.modal = function(api) {
3268
+ return new Modal(api, api.options.show.modal);
3269
+ };
3270
+
3271
+ // Setup sanitiztion rules
3272
+ MODAL.sanitize = function(opts) {
3273
+ if(opts.show) {
3274
+ if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
3275
+ else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
3276
+ }
3277
+ };
3278
+
3279
+ // Base z-index for all modal tooltips (use qTip core z-index as a base)
3280
+ QTIP.modal_zindex = QTIP.zindex - 200;
3281
+
3282
+ // Plugin needs to be initialized on render
3283
+ MODAL.initialize = 'render';
3284
+
3285
+ // Setup option set checks
3286
+ CHECKS.modal = {
3287
+ '^show.modal.(on|blur)$': function() {
3288
+ // Initialise
3289
+ this.destroy();
3290
+ this.init();
3291
+
3292
+ // Show the modal if not visible already and tooltip is visible
3293
+ this.qtip.elems.overlay.toggle(
3294
+ this.qtip.tooltip[0].offsetWidth > 0
3295
+ );
3296
+ }
3297
+ };
3298
+
3299
+ // Extend original api defaults
3300
+ $.extend(TRUE, QTIP.defaults, {
3301
+ show: {
3302
+ modal: {
3303
+ on: FALSE,
3304
+ effect: TRUE,
3305
+ blur: TRUE,
3306
+ stealfocus: TRUE,
3307
+ escape: TRUE
3308
+ }
3309
+ }
3310
+ });
3311
+ ;var IE6,
3312
+
3313
+ /*
3314
+ * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
3315
+ * Special thanks to Brandon Aaron
3316
+ */
3317
+ BGIFRAME = '<iframe class="qtip-bgiframe" frameborder="0" tabindex="-1" src="javascript:\'\';" ' +
3318
+ ' style="display:block; position:absolute; z-index:-1; filter:alpha(opacity=0); ' +
3319
+ '-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";"></iframe>';
3320
+
3321
+ function Ie6(api, qtip) {
3322
+ this._ns = 'ie6';
3323
+ this.init( (this.qtip = api) );
3324
+ }
3325
+
3326
+ $.extend(Ie6.prototype, {
3327
+ _scroll : function() {
3328
+ var overlay = this.qtip.elements.overlay;
3329
+ overlay && (overlay[0].style.top = $(window).scrollTop() + 'px');
3330
+ },
3331
+
3332
+ init: function(qtip) {
3333
+ var tooltip = qtip.tooltip,
3334
+ scroll;
3335
+
3336
+ // Create the BGIFrame element if needed
3337
+ if($('select, object').length < 1) {
3338
+ this.bgiframe = qtip.elements.bgiframe = $(BGIFRAME).appendTo(tooltip);
3339
+
3340
+ // Update BGIFrame on tooltip move
3341
+ qtip._bind(tooltip, 'tooltipmove', this.adjustBGIFrame, this._ns, this);
3342
+ }
3343
+
3344
+ // redraw() container for width/height calculations
3345
+ this.redrawContainer = $('<div/>', { id: NAMESPACE+'-rcontainer' })
3346
+ .appendTo(document.body);
3347
+
3348
+ // Fixup modal plugin if present too
3349
+ if( qtip.elements.overlay && qtip.elements.overlay.addClass('qtipmodal-ie6fix') ) {
3350
+ qtip._bind(window, ['scroll', 'resize'], this._scroll, this._ns, this);
3351
+ qtip._bind(tooltip, ['tooltipshow'], this._scroll, this._ns, this);
3352
+ }
3353
+
3354
+ // Set dimensions
3355
+ this.redraw();
3356
+ },
3357
+
3358
+ adjustBGIFrame: function() {
3359
+ var tooltip = this.qtip.tooltip,
3360
+ dimensions = {
3361
+ height: tooltip.outerHeight(FALSE),
3362
+ width: tooltip.outerWidth(FALSE)
3363
+ },
3364
+ plugin = this.qtip.plugins.tip,
3365
+ tip = this.qtip.elements.tip,
3366
+ tipAdjust, offset;
3367
+
3368
+ // Adjust border offset
3369
+ offset = parseInt(tooltip.css('borderLeftWidth'), 10) || 0;
3370
+ offset = { left: -offset, top: -offset };
3371
+
3372
+ // Adjust for tips plugin
3373
+ if(plugin && tip) {
3374
+ tipAdjust = (plugin.corner.precedance === 'x') ? [WIDTH, LEFT] : [HEIGHT, TOP];
3375
+ offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
3376
+ }
3377
+
3378
+ // Update bgiframe
3379
+ this.bgiframe.css(offset).css(dimensions);
3380
+ },
3381
+
3382
+ // Max/min width simulator function
3383
+ redraw: function() {
3384
+ if(this.qtip.rendered < 1 || this.drawing) { return this; }
3385
+
3386
+ var tooltip = this.qtip.tooltip,
3387
+ style = this.qtip.options.style,
3388
+ container = this.qtip.options.position.container,
3389
+ perc, width, max, min;
3390
+
3391
+ // Set drawing flag
3392
+ this.qtip.drawing = 1;
3393
+
3394
+ // If tooltip has a set height/width, just set it... like a boss!
3395
+ if(style.height) { tooltip.css(HEIGHT, style.height); }
3396
+ if(style.width) { tooltip.css(WIDTH, style.width); }
3397
+
3398
+ // Simulate max/min width if not set width present...
3399
+ else {
3400
+ // Reset width and add fluid class
3401
+ tooltip.css(WIDTH, '').appendTo(this.redrawContainer);
3402
+
3403
+ // Grab our tooltip width (add 1 if odd so we don't get wrapping problems.. huzzah!)
3404
+ width = tooltip.width();
3405
+ if(width % 2 < 1) { width += 1; }
3406
+
3407
+ // Grab our max/min properties
3408
+ max = tooltip.css('maxWidth') || '';
3409
+ min = tooltip.css('minWidth') || '';
3410
+
3411
+ // Parse into proper pixel values
3412
+ perc = (max + min).indexOf('%') > -1 ? container.width() / 100 : 0;
3413
+ max = ((max.indexOf('%') > -1 ? perc : 1) * parseInt(max, 10)) || width;
3414
+ min = ((min.indexOf('%') > -1 ? perc : 1) * parseInt(min, 10)) || 0;
3415
+
3416
+ // Determine new dimension size based on max/min/current values
3417
+ width = max + min ? Math.min(Math.max(width, min), max) : width;
3418
+
3419
+ // Set the newly calculated width and remvoe fluid class
3420
+ tooltip.css(WIDTH, Math.round(width)).appendTo(container);
3421
+ }
3422
+
3423
+ // Set drawing flag
3424
+ this.drawing = 0;
3425
+
3426
+ return this;
3427
+ },
3428
+
3429
+ destroy: function() {
3430
+ // Remove iframe
3431
+ this.bgiframe && this.bgiframe.remove();
3432
+
3433
+ // Remove bound events
3434
+ this.qtip._unbind([window, this.qtip.tooltip], this._ns);
3435
+ }
3436
+ });
3437
+
3438
+ IE6 = PLUGINS.ie6 = function(api) {
3439
+ // Proceed only if the browser is IE6
3440
+ return BROWSER.ie === 6 ? new Ie6(api) : FALSE;
3441
+ };
3442
+
3443
+ IE6.initialize = 'render';
3444
+
3445
+ CHECKS.ie6 = {
3446
+ '^content|style$': function() {
3447
+ this.redraw();
3448
+ }
3449
+ };
3450
+ ;}));
3451
+ }( window, document ));
lib/classes/KcSeoMetaData.php CHANGED
@@ -4,19 +4,40 @@ if(!class_exists('KcSeoMetaData')):
4
 
5
  class KcSeoMetaData
6
  {
7
- private $postType;
8
  function __construct()
9
  {
10
- $this->postType = get_post_types();
11
- add_action( 'add_meta_boxes', array($this, 'KcSeo_schema_meta_box'));
12
  add_action( 'save_post', array($this, 'save_KcSeo_schema_data'),10, 3);
13
- add_action('admin_print_scripts-post-new.php', array($this, 'kcSeo_wp_schema_admin_script'), 11);
14
- add_action('admin_print_scripts-post.php', array($this, 'kcSeo_wp_schema_admin_script'), 11);
15
  }
16
 
17
- function KcSeo_schema_meta_box(){
 
 
 
 
18
 
19
- foreach($this->postType as $postType){
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  add_meta_box(
21
  'kcseo-wordpres-seo-structured-data-schema-meta-box',
22
  __('WP SEO Structured Data Schema by <a href="http://kcseopro.com/">KCSEOPro.com</a>', KCSEO_WP_SCHEMA_SLUG),
@@ -47,7 +68,7 @@ if(!class_exists('KcSeoMetaData')):
47
  foreach($schemas->schemaTypes() as $schemaID => $schema){
48
  $tabId = $KcSeoWPSchema->KcSeoPrefix.$schemaID;
49
  $htmlMenu .= '<li><a href="#'.$tabId.'">'.$schema['title'].'</a></li>';
50
- $htmlCont .="<div id='{$tabId}'>";
51
  foreach($schema['fields'] as $fieldId => $data){
52
  $data['schemaId'] = $schemaID;
53
  $htmlCont .= $schemas->get_field($fieldId,$data, $post->ID);
@@ -61,16 +82,16 @@ if(!class_exists('KcSeoMetaData')):
61
  }
62
 
63
  function save_KcSeo_schema_data($post_id,$post, $update){
64
- if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
65
  global $KcSeoWPSchema;
66
- if (!wp_verify_nonce(@$_REQUEST['_kcseo_nonce'], $KcSeoWPSchema->nonceText())) return;
67
 
68
  // Check permissions
69
  if (@$_GET['post_type']) {
70
- if (!current_user_can('edit_' . @$_GET['post_type'], $post_id)) return;
71
  }
72
 
73
- if (!in_array(@$post->post_type,$this->postType) ) return;
74
 
75
  $meta = array();
76
  $schemaModel = new KcSeoSchemaModel;
@@ -93,16 +114,6 @@ if(!class_exists('KcSeoMetaData')):
93
  }
94
  }
95
 
96
- function kcSeo_wp_schema_admin_script(){
97
- global $KcSeoWPSchema;
98
- wp_enqueue_style( 'jquery-ui-datepicker' );
99
- wp_enqueue_style( 'kcseo-jquery-ui-css', 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css');
100
- wp_enqueue_style( 'kcseo-select2-css', $KcSeoWPSchema->assetsUrl .'css/select2.min.css');
101
- wp_enqueue_style( 'kcseo-admin-css', $KcSeoWPSchema->assetsUrl .'css/admin.css');
102
- wp_enqueue_script( 'kcseo-wordpres-seo-structured-data-schema-select2-js', $KcSeoWPSchema->assetsUrl . 'js/select2.min.js', array('jquery'), '', true);
103
- wp_enqueue_script('kcseo-wordpres-seo-structured-data-schema-admin-js', $KcSeoWPSchema->assetsUrl .'js/admin.js', array('jquery', 'jquery-ui-core', 'jquery-ui-tabs','jquery-ui-datepicker') ,'' , true);
104
- }
105
-
106
  }
107
 
108
  endif;
4
 
5
  class KcSeoMetaData
6
  {
 
7
  function __construct()
8
  {
9
+ add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
 
10
  add_action( 'save_post', array($this, 'save_KcSeo_schema_data'),10, 3);
 
 
11
  }
12
 
13
+ function admin_enqueue_scripts(){
14
+ global $pagenow, $typenow, $KcSeoWPSchema;
15
+ // validate page
16
+ if( !in_array( $pagenow, array('post.php', 'post-new.php') ) ) return;
17
+ if(!in_array($typenow, $KcSeoWPSchema->kcSep_postType)) return;
18
 
19
+ // scripts
20
+ wp_enqueue_script(array(
21
+ 'jquery',
22
+ 'jquery-ui-core',
23
+ 'jquery-ui-tabs',
24
+ 'kcseo-select2-js',
25
+ 'kcseo-admin-js',
26
+ ));
27
+
28
+ // styles
29
+ wp_enqueue_style(array(
30
+ 'kcseo-ui-css',
31
+ 'kcseo-select2-css',
32
+ 'kcseo-admin-css',
33
+ ));
34
+
35
+ add_action('admin_head', array($this,'admin_head'));
36
+ }
37
+
38
+ function admin_head(){
39
+ global $KcSeoWPSchema;
40
+ foreach($KcSeoWPSchema->kcSep_postType as $postType){
41
  add_meta_box(
42
  'kcseo-wordpres-seo-structured-data-schema-meta-box',
43
  __('WP SEO Structured Data Schema by <a href="http://kcseopro.com/">KCSEOPro.com</a>', KCSEO_WP_SCHEMA_SLUG),
68
  foreach($schemas->schemaTypes() as $schemaID => $schema){
69
  $tabId = $KcSeoWPSchema->KcSeoPrefix.$schemaID;
70
  $htmlMenu .= '<li><a href="#'.$tabId.'">'.$schema['title'].'</a></li>';
71
+ $htmlCont .="<div id='{$tabId}' class='kcseo-tab-container'>";
72
  foreach($schema['fields'] as $fieldId => $data){
73
  $data['schemaId'] = $schemaID;
74
  $htmlCont .= $schemas->get_field($fieldId,$data, $post->ID);
82
  }
83
 
84
  function save_KcSeo_schema_data($post_id,$post, $update){
85
+ if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return $post_id;
86
  global $KcSeoWPSchema;
87
+ if (!wp_verify_nonce(@$_REQUEST['_kcseo_nonce'], $KcSeoWPSchema->nonceText())) return $post_id;
88
 
89
  // Check permissions
90
  if (@$_GET['post_type']) {
91
+ if (!current_user_can('edit_' . @$_GET['post_type'], $post_id)) return $post_id;
92
  }
93
 
94
+ if (!in_array($post->post_type,$KcSeoWPSchema->kcSep_postType) ) return $post_id;
95
 
96
  $meta = array();
97
  $schemaModel = new KcSeoSchemaModel;
114
  }
115
  }
116
 
 
 
 
 
 
 
 
 
 
 
117
  }
118
 
119
  endif;
lib/classes/KcSeoSettings.php CHANGED
@@ -10,6 +10,71 @@ if(!class_exists('KcSeoSettings')):
10
  add_action( 'admin_menu' , array($this, 'kcSeo_Wp_Schema_menu'));
11
  add_action( 'wp_ajax_kcSeoWpSchemaSettings', array($this, 'kcSeoWpSchemaSettings'));
12
  add_action( 'wp_ajax_newSocial', array($this, 'newSocial'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  }
14
 
15
  function newSocial(){
@@ -58,25 +123,10 @@ if(!class_exists('KcSeoSettings')):
58
 
59
  function kcSeo_Wp_Schema_menu(){
60
  global $KcSeoWPSchema;
61
- $page = add_menu_page( 'WP SEO Structured Data Schema', 'WP SEO Schema', 'manage_options', 'wp-seo-schema', array($this,'wp_schema_page'), $KcSeoWPSchema->assetsUrl . 'images/wp-seo-schema.png');
62
 
63
- add_action('admin_print_styles-' . $page, array( $this,'tlp_schema_style'));
64
- add_action('admin_print_scripts-'. $page, array( $this,'tlp_schema_script'));
65
  }
66
 
67
- function tlp_schema_style(){
68
- global $KcSeoWPSchema;
69
- wp_enqueue_style( 'kcseo-jquery-ui-css', 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css');
70
- wp_enqueue_style( 'kcseo-select2-css', $KcSeoWPSchema->assetsUrl .'css/select2.min.css');
71
- wp_enqueue_style( 'kcseo-tooltip-css','http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.css');
72
- wp_enqueue_style( 'kcseo-admin-css', $KcSeoWPSchema->assetsUrl .'css/admin.css');
73
- }
74
- function tlp_schema_script(){
75
- global $KcSeoWPSchema;
76
- wp_enqueue_script( 'kcseo-wordpres-seo-structured-data-schema-select2-js', $KcSeoWPSchema->assetsUrl . 'js/select2.min.js', array('jquery'), '', true);
77
- wp_enqueue_script( 'kcseo-wordpres-seo-structured-data-schema-tooltip-js', 'http://cdnjs.cloudflare.com/ajax/libs/qtip2/2.2.1/jquery.qtip.js', array('jquery'), '', true);
78
- wp_enqueue_script( 'kcseo-wordpres-seo-structured-data-schema-admin-js', $KcSeoWPSchema->assetsUrl . 'js/admin.js', array('jquery', 'jquery-ui-core', 'jquery-ui-tabs','jquery-ui-datepicker'), '', true);
79
- }
80
 
81
  function kcSeo_pluginInit(){
82
  load_plugin_textdomain( KCSEO_WP_SCHEMA_SLUG, FALSE, KCSEO_WP_SCHEMA_LANGUAGE_PATH );
10
  add_action( 'admin_menu' , array($this, 'kcSeo_Wp_Schema_menu'));
11
  add_action( 'wp_ajax_kcSeoWpSchemaSettings', array($this, 'kcSeoWpSchemaSettings'));
12
  add_action( 'wp_ajax_newSocial', array($this, 'newSocial'));
13
+ add_action( 'init', array($this, 'kcSeoScript'));
14
+ add_action('admin_enqueue_scripts', array($this, 'admin_enqueue_scripts'));
15
+ }
16
+ function admin_enqueue_scripts(){
17
+ global $pagenow;
18
+ // validate page
19
+ $page = isset($_REQUEST['page']) ? $_REQUEST['page'] : null;
20
+ if( $pagenow != 'admin.php' || $page != 'wp-seo-schema' ) return;
21
+ // scripts
22
+ wp_enqueue_script(array(
23
+ 'jquery',
24
+ 'jquery-ui-core',
25
+ 'jquery-ui-tabs',
26
+ 'kcseo-select2-js',
27
+ 'kcseo-tooltip-js',
28
+ 'kcseo-admin-js',
29
+ ));
30
+
31
+ // styles
32
+ wp_enqueue_style(array(
33
+ 'kcseo-select2-css',
34
+ 'kcseo-tooltip-css',
35
+ 'kcseo-admin-css',
36
+ ));
37
+ }
38
+
39
+ function kcSeoScript(){
40
+ global $KcSeoWPSchema;
41
+ // register team scripts and styles
42
+ $scripts = array();
43
+ $styles = array();
44
+ if(is_admin()) {
45
+
46
+
47
+ $scripts[] = array(
48
+ 'handle' => 'kcseo-select2-js',
49
+ 'src' => $KcSeoWPSchema->assetsUrl . 'js/select2.min.js',
50
+ 'deps' => array('jquery'),
51
+ 'footer' => true
52
+ );
53
+ $scripts[] = array(
54
+ 'handle' => 'kcseo-tooltip-js',
55
+ 'src' => $KcSeoWPSchema->assetsUrl . 'js/jquery.qtip.js',
56
+ 'deps' => array('jquery'),
57
+ 'footer' => true
58
+ );
59
+ $scripts[] = array(
60
+ 'handle' => 'kcseo-admin-js',
61
+ 'src' => $KcSeoWPSchema->assetsUrl . 'js/admin.js',
62
+ 'deps' => array('jquery', 'jquery-ui-core', 'jquery-ui-tabs','jquery-ui-datepicker'),
63
+ 'footer' => true
64
+ );
65
+ $styles['kcseo-ui-css'] = 'http://ajax.googleapis.com/ajax/libs/jqueryui/1.9.0/themes/base/jquery-ui.css';
66
+ $styles['kcseo-select2-css'] = $KcSeoWPSchema->assetsUrl .'css/select2.min.css';
67
+ $styles['kcseo-tooltip-css'] = $KcSeoWPSchema->assetsUrl .'css/jquery.qtip.css';
68
+ $styles['kcseo-admin-css'] = $KcSeoWPSchema->assetsUrl .'css/admin.css';
69
+ }
70
+ foreach( $scripts as $script )
71
+ {
72
+ wp_register_script( $script['handle'], $script['src'], $script['deps'], $KcSeoWPSchema->options['version'], $script['footer'] );
73
+ }
74
+ foreach( $styles as $k => $v )
75
+ {
76
+ wp_register_style( $k, $v, false, $KcSeoWPSchema->options['version'] );
77
+ }
78
  }
79
 
80
  function newSocial(){
123
 
124
  function kcSeo_Wp_Schema_menu(){
125
  global $KcSeoWPSchema;
126
+ add_menu_page( 'WP SEO Structured Data Schema', 'WP SEO Schema', 'manage_options', 'wp-seo-schema', array($this,'wp_schema_page'), $KcSeoWPSchema->assetsUrl . 'images/wp-seo-schema.png');
127
 
 
 
128
  }
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
 
131
  function kcSeo_pluginInit(){
132
  load_plugin_textdomain( KCSEO_WP_SCHEMA_SLUG, FALSE, KCSEO_WP_SCHEMA_LANGUAGE_PATH );
lib/init.php CHANGED
@@ -6,10 +6,13 @@ if(!class_exists('KcSeoWPSchema')){
6
  {
7
  public $options;
8
  public $KcSeoPrefix;
9
-
10
  function __construct() {
11
  $this->KcSeoPrefix = "_schema_";
12
- $this->options = array('settings' => 'kcseo_wp_schema');
 
 
 
13
 
14
  $this->incPath = dirname(__FILE__);
15
  $this->functionsPath = $this->incPath . '/functions/';
@@ -17,6 +20,7 @@ if(!class_exists('KcSeoWPSchema')){
17
  $this->viewsPath = $this->incPath . '/views/';
18
  $this->assetsUrl = KCSEO_WP_SCHEMA_URL . '/assets/';
19
  $this->modelPath = $this->incPath . '/models/';
 
20
 
21
  $this->KeSeoLoadFunctions($this->functionsPath);
22
  $this->KcSeoLoadModel($this->modelPath);
@@ -27,6 +31,20 @@ if(!class_exists('KcSeoWPSchema')){
27
 
28
  }
29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  function KcSeoLoadClass($dir) {
31
  if (!file_exists($dir)) return;
32
 
6
  {
7
  public $options;
8
  public $KcSeoPrefix;
9
+ public $kcSep_postType = array();
10
  function __construct() {
11
  $this->KcSeoPrefix = "_schema_";
12
+ $this->options = array(
13
+ 'settings' => 'kcseo_wp_schema',
14
+ 'version' => '1.1'
15
+ );
16
 
17
  $this->incPath = dirname(__FILE__);
18
  $this->functionsPath = $this->incPath . '/functions/';
20
  $this->viewsPath = $this->incPath . '/views/';
21
  $this->assetsUrl = KCSEO_WP_SCHEMA_URL . '/assets/';
22
  $this->modelPath = $this->incPath . '/models/';
23
+ $this->kcSep_postType = $this->kcSeoPostTypes();
24
 
25
  $this->KeSeoLoadFunctions($this->functionsPath);
26
  $this->KcSeoLoadModel($this->modelPath);
31
 
32
  }
33
 
34
+
35
+ function kcSeoPostTypes(){
36
+ $post_types = get_post_types(
37
+ array(
38
+ '_builtin' => true
39
+ )
40
+ );
41
+ $exclude = array( 'attachment', 'revision', 'nav_menu_item' );
42
+ foreach($exclude as $ex){
43
+ unset($post_types[$ex]);
44
+ }
45
+ return $post_types;
46
+ }
47
+
48
  function KcSeoLoadClass($dir) {
49
  if (!file_exists($dir)) return;
50
 
lib/views/settings.php CHANGED
@@ -208,13 +208,13 @@ $schemaModel = new KcSeoSchemaModel;
208
  </tr>
209
  </table>
210
  </div>
211
- <div id="tabs-container">
212
  <ul class="tabs-menu">
213
  <li class="current"><a href="#tab-1">Organization Logo</a></li>
214
  <li><a href="#tab-2">Social Profile</a></li>
215
  <li><a href="#tab-3">Corporate Contacts</a></li>
216
  </ul>
217
- <div id="tab-1" class="tab-content">
218
  <table width="100%" cellpadding="10" class="form-table">
219
  <tr class="field_logo">
220
  <th>Logo URL </th>
@@ -224,7 +224,7 @@ $schemaModel = new KcSeoSchemaModel;
224
  </tr>
225
  </table>
226
  </div>
227
- <div id="tab-2" class="tab-content">
228
  <table width="100%" cellpadding="10" class="form-table">
229
  <tr class="field_social">
230
  <th>Company Name </th>
@@ -266,7 +266,7 @@ $schemaModel = new KcSeoSchemaModel;
266
  </tr>
267
  </table>
268
  </div>
269
- <div id="tab-3" class="tab-content">
270
  <table width="100%" cellpadding="10" class="form-table">
271
  <tr class="field_contact">
272
  <th style="font-size: 18px; padding: 10px 0;">Contacts</th>
208
  </tr>
209
  </table>
210
  </div>
211
+ <div id="tabs-kcseo-container">
212
  <ul class="tabs-menu">
213
  <li class="current"><a href="#tab-1">Organization Logo</a></li>
214
  <li><a href="#tab-2">Social Profile</a></li>
215
  <li><a href="#tab-3">Corporate Contacts</a></li>
216
  </ul>
217
+ <div id="tab-1" class="tab-content kcseo-tab-container">
218
  <table width="100%" cellpadding="10" class="form-table">
219
  <tr class="field_logo">
220
  <th>Logo URL </th>
224
  </tr>
225
  </table>
226
  </div>
227
+ <div id="tab-2" class="tab-content kcseo-tab-container">
228
  <table width="100%" cellpadding="10" class="form-table">
229
  <tr class="field_social">
230
  <th>Company Name </th>
266
  </tr>
267
  </table>
268
  </div>
269
+ <div id="tab-3" class="tab-content kcseo-tab-container">
270
  <table width="100%" cellpadding="10" class="form-table">
271
  <tr class="field_contact">
272
  <th style="font-size: 18px; padding: 10px 0;">Contacts</th>
readme.txt CHANGED
@@ -4,7 +4,7 @@ Donate link:
4
  Tags: seo, schema, structured data, rich snippets, microdata, json-ld, search engine optimization, local seo, google, sitelinks, schema.org, microformat, serp, amp
5
  Requires at least: 4
6
  Tested up to: 4.4
7
- Stable tag: 1.0
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -93,5 +93,9 @@ You'll find the [FAQ on Kcseopro.com](http://kcseopro.com/).
93
 
94
  == Changelog ==
95
 
 
 
 
 
96
  = 1.0 =
97
  * Initial load of the plugin.
4
  Tags: seo, schema, structured data, rich snippets, microdata, json-ld, search engine optimization, local seo, google, sitelinks, schema.org, microformat, serp, amp
5
  Requires at least: 4
6
  Tested up to: 4.4
7
+ Stable tag: 1.1
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
93
 
94
  == Changelog ==
95
 
96
+ = 1.1 =
97
+ * Layout change
98
+ * Fix some coding bug
99
+
100
  = 1.0 =
101
  * Initial load of the plugin.
wp-seo-structured-data-schema.php CHANGED
@@ -1,13 +1,13 @@
1
  <?php
2
- /*
3
- Plugin Name: WP SEO Structured Data Schema
4
- Plugin URI: http://kcseopro.com/
5
- Description: Comprehensive JSON-LD based Structured Data solution for WordPress for adding schema for organizations, businesses, blog posts, ratings & more.
6
- Version: 1.0
7
- Author: kcseopro
8
- Author URI: http://kcseopro.com/
9
- License: A "Slug" license name e.g. GPL2
10
- */
11
  if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }
1
  <?php
2
+ /**
3
+ *Plugin Name: WP SEO Structured Data Schema
4
+ * Plugin URI: http://kcseopro.com/
5
+ * Description: Comprehensive JSON-LD based Structured Data solution for WordPress for adding schema for organizations, businesses, blog posts, ratings & more.
6
+ * Version: 1.1
7
+ * Author: kcseopro
8
+ * Author URI: http://kcseopro.com/
9
+ * License: A "Slug" license name e.g. GPL2
10
+ */
11
  if ( ! defined( 'ABSPATH' ) ) {
12
  exit;
13
  }