· 6 years ago · Aug 14, 2019, 08:02 PM
1(function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2///////////////////////////////////////////
3// gmail.js
4// Kartik Talwar
5// https://github.com/KartikTalwar/gmail.js
6//
7
8/*eslint-env es6*/
9
10var Gmail = function(localJQuery) {
11
12 /*
13 Use the provided "jQuery" if possible, in order to avoid conflicts with
14 other extensions that use $ for other purposes.
15 */
16 var $;
17 if (typeof localJQuery !== "undefined") {
18 $ = localJQuery;
19 } else if (typeof jQuery !== "undefined") {
20 $ = jQuery;
21 } else {
22 // try load jQuery through node.
23 try {
24 $ = require("jquery");
25 }
26 catch(err) {
27 // else leave $ undefined, which may be fine for some purposes.
28 }
29 }
30
31 var window_opener = typeof (window) !== "undefined" ? window.opener : null;
32 if (window_opener) {
33 try {
34 // access to window.opener domain will fail in case of cross-origin access
35 var opener_domain = window_opener.document.domain;
36 if (opener_domain !== window.document.domain) {
37 console.warn("GmailJS: window.opener domain differs from window domain.");
38 window_opener = null;
39 }
40 } catch (error) {
41 console.warn("GmailJS: Unable to access window.opener!", error);
42 window_opener = null;
43 }
44 }
45
46 var api = {
47 get : {},
48 observe : {},
49 check : { data: {}},
50 tools : {},
51 tracker : {},
52 dom : {},
53 chat : {},
54 compose : {},
55 helper : {get: {}}
56 };
57
58 api.version = "0.8.0";
59 api.tracker.globals = typeof GLOBALS !== "undefined"
60 ? GLOBALS
61 : (
62 window_opener && window_opener.GLOBALS || []
63 );
64 api.tracker.view_data = typeof VIEW_DATA !== "undefined"
65 ? VIEW_DATA
66 : (
67 window_opener && window_opener.VIEW_DATA || []
68 );
69 api.tracker.ik = api.tracker.globals[9] || "";
70 api.tracker.hangouts = undefined;
71
72 // cache-store for passively pre-fetched/intercepted email-data from load_email_data.
73 api.cache = {};
74 api.cache.debug_xhr_fetch = false;
75 api.cache.emailIdCache = {};
76 api.cache.emailLegacyIdCache = {};
77 api.cache.threadCache = {};
78
79 api.get.last_active = function() {
80 var data = api.tracker.globals[17][15];
81 return {
82 time : data[1],
83 ip : data[3],
84 mac_address : data[9],
85 time_relative : data[10]
86 };
87 };
88
89
90 api.get.loggedin_accounts = function() {
91 var i, j, data;
92 var users = [];
93
94 var globals17 = api.tracker.globals[17];
95 for (i in globals17) {
96 // at least for the delegated inboxes, the index of the mla is not stable
97 // it was observed to be somewhere between 22 and 24, but we should not depend on it
98 data = globals17[i];
99
100 if (data[0] === "mla") {
101 for(j in data[1]) {
102 users.push({
103 name : data[1][j][4],
104 email : data[1][j][0],
105 index: data[1][j][3]
106 });
107 }
108
109 return users;
110 }
111 }
112
113 return users;
114 };
115
116
117 api.get.user_email = function() {
118 return api.tracker.globals[10];
119 };
120
121
122 api.get.manager_email = function() {
123 if (api.helper.get.is_delegated_inbox()) {
124 return api.get.delegated_to_email();
125 }
126
127 return api.get.user_email();
128 };
129
130
131 api.get.delegated_to_email = function() {
132 if (!api.helper.get.is_delegated_inbox()) {
133 return null;
134 }
135
136 var i, account;
137 var userIndexPrefix = "/u/";
138 var pathname = window.location.pathname;
139 var delegatedToUserIndex = parseInt(pathname.substring(pathname.indexOf(userIndexPrefix) + userIndexPrefix.length), 10);
140
141 var loggedInAccounts = api.get.loggedin_accounts();
142 if (loggedInAccounts && loggedInAccounts.length > 0) {
143 for (i in loggedInAccounts) {
144 account = loggedInAccounts[i];
145 if (account.index === delegatedToUserIndex) {
146 return account.email;
147 }
148 }
149 }
150
151 // as a last resort, we query the DOM of the upper right account selection menu
152 return $(".gb_rb[href$='" + userIndexPrefix + delegatedToUserIndex + "'] .gb_yb").text().split(" ")[0];
153 };
154
155 api.helper.get.is_locale = function(locale) {
156 // A locale is a string that begins with 2 letters, either lowercase or uppercase
157 // The "lowercase" check distinguishes locales from other 2-letter strings like "US"
158 // (the user"s location?).
159 if (!locale || ((typeof locale) !== "string") || locale.length < 2) {
160 return false;
161 }
162
163 if (locale.match(/[0-9]/)) {
164 return false;
165 }
166
167 var localePrefix = locale.slice(0, 2);
168 return localePrefix.toLowerCase() === localePrefix ||
169 localePrefix.toUpperCase() === localePrefix;
170 };
171
172 api.helper.filter_locale = function(locale) {
173 if (!api.helper.get.is_locale(locale)) {
174 return null;
175 }
176
177 // strip region-denominator
178 return locale.substring(0,2).toLowerCase();
179 };
180
181 api.helper.array_starts_with = function(list, item) {
182 if (list && list.length > 0 && list[0] === item) {
183 return true;
184 } else {
185 return false;
186 }
187 };
188
189 api.helper.get.array_sublist = function(nestedArray, itemKey) {
190 if (nestedArray) {
191 for(var i=0; i<nestedArray.length; i++) {
192 var list = nestedArray[i];
193 if (api.helper.array_starts_with(list, itemKey)) {
194 return list;
195 }
196 }
197 }
198
199 return null;
200 };
201
202 api.helper.get.locale_from_url_params = function(value) {
203 // check if is URL
204 if (value && value.indexOf && (value.indexOf("https://") === 0 || value.indexOf("http://") === 0)) {
205 var urlParts = value.split("?");
206 if (urlParts.length > 1) {
207 var hash = urlParts[1];
208 var hashParts = hash.split("&");
209 for (var i=0; i < hashParts.length; i++)
210 {
211 var kvp = hashParts[i].split("=");
212 if (kvp.length === 2 && kvp[0] === "hl") {
213 return kvp[1];
214 }
215 }
216 }
217 }
218
219 return null;
220 };
221
222 api.helper.get.locale_from_globals_item = function(list) {
223 if (!list) {
224 return null;
225 }
226
227 for (var i=0; i<list.length; i++) {
228 var item = list[i];
229 var locale = api.helper.get.locale_from_url_params(item);
230 if (locale) {
231 return locale;
232 }
233 }
234
235 // fallback to user-locale
236 return list[8];
237 };
238
239 api.get.localization = function() {
240 var globals = api.tracker.globals;
241
242 // candidate is globals[17]-subarray which starts with "ui"
243 // has historically been observed as [7], [8] and [9]!
244 var localeList = api.helper.get.array_sublist(globals[17], "ui");
245 if (localeList !== null && localeList.length > 8) {
246 let locale = api.helper.get.locale_from_globals_item(localeList);
247 locale = api.helper.filter_locale(locale);
248 if (locale) {
249 return locale;
250 }
251 }
252
253 // in new gmail, globals[12] may contain a link to an help-article, with a hl= language-code
254 if (globals[12] !== null) {
255 let locale = api.helper.get.locale_from_url_params(globals[12]);
256 locale = api.helper.filter_locale(locale);
257 if (locale) {
258 return locale;
259 }
260 }
261
262 return null;
263 };
264
265 api.check.is_new_data_layer = function () {
266 return window["GM_SPT_ENABLED"] === "true";
267 };
268
269 api.check.is_new_gui = function () {
270 return window.GM_RFT_ENABLED === "true";
271 };
272
273 api.check.is_thread = function() {
274 var check_1 = $(".nH .if").children(":eq(1)").children().children(":eq(1)").children();
275 var check_2 = api.get.email_ids();
276
277 return check_1.length > 1 || check_2.length > 1;
278 };
279
280
281 api.dom.inbox_content = function() {
282 return $("div[role=main]:first");
283 };
284
285
286 api.check.is_preview_pane = function() {
287 var dom = api.dom.inbox_content();
288 var boxes = dom.find("[gh=tl]");
289
290 var previewPaneFound = false;
291 boxes.each(function() {
292 if($(this).hasClass("aia")) {
293 previewPaneFound = true;
294 }
295 });
296
297 return previewPaneFound;
298 };
299
300 api.check.is_multiple_inbox = function() {
301 var dom = api.dom.inboxes();
302 return dom.length > 1;
303 };
304
305
306 api.check.is_horizontal_split = function() {
307 var dom = api.dom.inbox_content();
308 var box = dom.find("[gh=tl]").find(".nn");
309
310 return box.length === 0;
311 };
312
313
314 api.check.is_vertical_split = function() {
315 return api.check.is_horizontal_split() === false;
316 };
317
318
319 api.check.is_tabbed_inbox = function() {
320 return $(".aKh").length === 1;
321 };
322
323
324 api.check.is_right_side_chat = function() {
325 var chat = $(".ApVoH");
326 if(chat.length === 0) {
327 return false;
328 }
329
330 return chat[0].getAttribute("aria-labelledby") === ":wf";
331 };
332
333 api.check.should_compose_fullscreen = function(){
334 console.warn("gmail.js: This function is known to be unreliable, and may be deprecated in a future release.");
335 var bx_scfs = [];
336 try {
337 bx_scfs = api.tracker.globals[17][4][1][32];
338 } catch(er) {
339 bx_scfs = ["bx_scfs","false"];
340 }
341 return (bx_scfs[1] === "true" ) ? true : false;
342 };
343
344
345 api.check.is_google_apps_user =function() {
346 var email = api.get.user_email();
347 return email.indexOf("gmail.com", email.length - "gmail.com".length) === -1;
348 };
349
350
351 api.get.storage_info = function() {
352 var div = $(".md.mj").find("div")[0];
353 var used = $(div).find("span")[0].text;
354 var total = $(div).find("span")[1].text;
355 var percent = parseFloat(used.replace(/[^0-9\.]/g, "")) * 100 / parseFloat(total.replace(/[^0-9\.]/g, ""));
356
357 return {used : used, total : total, percent : Math.floor(percent)};
358 };
359
360
361 api.dom.inboxes = function() {
362 var dom = api.dom.inbox_content();
363 return dom.find("[gh=tl]");
364 };
365
366 api.dom.email_subject = function () {
367 var e = $(".hP");
368
369 for(var i=0; i<e.length; i++) {
370 if($(e[i]).is(":visible")) {
371 return $(e[i]);
372 }
373 }
374
375 return $();
376 };
377
378
379 api.get.email_subject = function() {
380 var subject_dom = api.dom.email_subject();
381
382 return subject_dom.text();
383 };
384
385
386 api.dom.email_body = function() {
387 return $(".nH.hx");
388 };
389
390 api.dom.toolbar = function() {
391 var tb = $("[gh='mtb']");
392
393 while($(tb).children().length === 1){
394 tb = $(tb).children().first();
395 }
396
397 return tb;
398 };
399
400 api.dom.right_toolbar = function() {
401 var rtb = $("[gh='tm'] [gh='s']").parent();
402
403 while($(rtb).children().length === 1){
404 rtb = $(rtb).children().first();
405 }
406
407 return rtb;
408 };
409
410 api.check.is_inside_email = function() {
411 if(api.get.current_page() !== "email" && !api.check.is_preview_pane()) {
412 return false;
413 }
414
415 var items = $(".ii.gt .a3s.aXjCH");
416 var ids = [];
417
418 for(var i=0; i<items.length; i++) {
419 var mail_id = items[i].getAttribute("class").split(" ")[2];
420 if(mail_id !== "undefined" && mail_id !== undefined) {
421 ids.push(items[i]);
422 }
423 }
424
425 return ids.length > 0;
426 };
427
428 api.check.is_plain_text = function() {
429 var settings = api.tracker.globals[17][4][1];
430
431 for (var i = 0; i < settings.length; i++) {
432 var plain_text_setting = settings[i];
433 if (plain_text_setting[0] === "bx_cm") {
434 return plain_text_setting[1] === "0";
435 }
436 }
437
438 // default to rich text mode, which is more common nowadays
439 return false;
440 };
441
442 api.dom.email_contents = function() {
443 var items = $(".ii.gt div.a3s.aXjCH");
444 var ids = [];
445
446 for(var i=0; i<items.length; i++) {
447 var mail_id = items[i].getAttribute("class").split(" ")[2];
448 var is_editable = items[i].getAttribute("contenteditable");
449 if(mail_id !== "undefined" && mail_id !== undefined) {
450 if(is_editable !== "true") {
451 ids.push(items[i]);
452 }
453 }
454 }
455
456 return ids;
457 };
458
459
460 api.get.email_ids = function() {
461 if(api.check.is_inside_email()) {
462 var data = api.get.email_data();
463 return Object.keys(data.threads);
464 }
465 return [];
466 };
467
468
469 api.get.compose_ids = function() {
470 var ret = [];
471 var dom = $(".M9 [name=draft]");
472 for(var i = 0; i < dom.length; i++) {
473 if(dom[i].value !== "undefined"){
474 ret.push(dom[i].value);
475 }
476 }
477 return ret;
478 };
479
480 api.get.thread_id = function() {
481 // multiple elements contains this attribute, but only the visible header of the visible email is a H2!
482 const elem = document.querySelector("h2[data-legacy-thread-id]");
483 if (elem !== null) {
484 return elem.dataset.legacyThreadId;
485 }
486 else {
487 // URL-based analysis is unreliable!
488 return undefined;
489 }
490 };
491
492 api.get.email_id = function() {
493 console.warn("GmailJS: api.get.email_id() invoked. Please note this function actually returns thread-id, and that email-id and thread-id may not always be used interchangably! Use api.get.thread_id() instead to silence this warning.");
494 return api.get.thread_id();
495 };
496
497 api.check.is_priority_inbox = function() {
498 return $(".qh").length > 0;
499 };
500
501
502 api.check.is_rapportive_installed = function() {
503 return $("#rapportive-sidebar").length === 1;
504 };
505
506
507 api.check.is_streak_installed = function() {
508 return $("[id^='bentoBox'],[id*=' bentoBox'],[class*=' bentoBox'],[class*='bentoBox']").length > 0;
509 };
510
511
512 api.check.is_anydo_installed = function() {
513 return $("[id^='anydo'],[id*=' anydo'],[class*=' anydo'],[class*='anydo']").length > 0;
514 };
515
516
517 api.check.is_boomerang_installed = function() {
518 return $("[id^='b4g_'],[id*=' b4g_'],[class*=' b4g_'],[class*='b4g_']").length > 0;
519 };
520
521
522 api.check.is_xobni_installed = function() {
523 return $("#xobni_frame").length > 0;
524 };
525
526
527 api.check.is_signal_installed = function() {
528 return $("[id^='Signal'],[id*=' Signal'],[class*=' signal'],[class*='signal']").length > 0;
529 };
530
531
532 api.check.are_shortcuts_enabled = function() {
533 var flag_name = "bx_hs";
534 var flag_value = undefined;
535
536 var check = true; // Flag possibly missing in convo view.
537
538 var array_with_flag = api.tracker.globals[17][4][1];
539
540 for(var i=0; i<array_with_flag.length; i++) {
541 var current = array_with_flag[i];
542
543 if(current[0] === flag_name) {
544 flag_value = current[1];
545 break;
546 }
547 }
548
549 if(flag_value !== undefined) {
550 var values = {
551 "0": true,
552 "1": false
553 };
554
555 check = values[flag_value];
556 }
557
558 return check;
559 };
560
561
562 api.dom.get_left_sidebar_links = function() {
563 return $("div[role=navigation] [title]");
564 };
565
566 api.dom.header = function() {
567 return $("#gb");
568 };
569
570 api.dom.search_bar = function() {
571 return $("[gh=sb]");
572 };
573
574
575 api.get.search_query = function() {
576 var dom = api.dom.search_bar();
577 return dom.find("input")[0].value;
578 };
579
580
581 api.get.unread_inbox_emails = function() {
582 return api.helper.get.navigation_count("inbox");
583 };
584
585
586 api.get.unread_draft_emails = function() {
587 return api.helper.get.navigation_count("drafts");
588 };
589
590
591 api.get.unread_spam_emails = function() {
592 return api.helper.get.navigation_count("spam");
593 };
594
595
596 api.get.unread_forum_emails = function() {
597 return api.helper.get.navigation_count("forums");
598 };
599
600
601 api.get.unread_update_emails = function() {
602 return api.helper.get.navigation_count("updates");
603 };
604
605
606 api.get.unread_promotion_emails = function() {
607 return api.helper.get.navigation_count("promotions");
608 };
609
610
611 api.get.unread_social_emails = function() {
612 return api.helper.get.navigation_count("social_updates");
613 };
614
615 api.helper.get.navigation_count = function(i18nName) {
616 const title = api.tools.i18n(i18nName);
617 const dom = $("div[role=navigation]").find("[title*='" + title + "']");
618
619 if (dom || dom.length > 0) {
620 // this check should implicitly always be true, but better safe than sorry?
621 if(dom[0].title.indexOf(title) !== -1) {
622 const value = parseInt(dom[0].attributes['aria-label'].value.replace(/[^0-9]/g, ""));
623 if (!isNaN(value)) {
624 return value;
625 }
626 }
627 }
628
629 return 0;
630 };
631
632
633 api.get.beta = function() {
634 var features = {
635 "new_nav_bar" : $("#gbz").length === 0
636 };
637
638 return features;
639 };
640
641
642 api.get.unread_emails = function() {
643 return {
644 inbox : api.get.unread_inbox_emails(),
645 drafts : api.get.unread_draft_emails(),
646 spam : api.get.unread_spam_emails(),
647 forum : api.get.unread_forum_emails(),
648 update : api.get.unread_update_emails(),
649 promotions : api.get.unread_promotion_emails(),
650 social : api.get.unread_social_emails()
651 };
652 };
653
654
655 api.tools.error = function(str) {
656 if (console) {
657 console.error(str);
658 } else {
659 throw(str);
660 }
661 };
662
663 api.tools.parse_url = function(url) {
664 var regex = /[?&]([^=#]+)=([^&#]*)/g;
665 var params = {};
666 var match = regex.exec(url);
667
668 while (match) {
669 params[match[1]] = match[2];
670 match = regex.exec(url);
671 }
672
673 return params;
674 };
675
676 api.tools.sleep = function(milliseconds) {
677 var start = new Date().getTime();
678 while(true) {
679 if ((new Date().getTime() - start) > milliseconds){
680 break;
681 }
682 }
683 };
684
685
686 api.tools.multitry = function(delay, tries, func, check, counter, retval) {
687 if(counter !== undefined && counter >= tries) {
688 return retval;
689 }
690
691 counter = (counter === undefined) ? 0 : counter;
692
693 var value = func();
694
695 if(check(value)) {
696 return value;
697 } else {
698 api.tools.sleep(delay);
699 api.tools.multitry(delay, tries, func, check, counter+1, value);
700 }
701 };
702
703
704 api.tools.deparam = function (params, coerce) {
705
706 var each = function (arr, fnc) {
707 var data = [];
708 for (var i = 0; i < arr.length; i++) {
709 data.push(fnc(arr[i]));
710 }
711 return data;
712 };
713
714 var isArray = Array.isArray || function(obj) {
715 return Object.prototype.toString.call(obj) === "[object Array]";
716 };
717
718 var obj = {},
719 coerce_types = {
720 "true": !0,
721 "false": !1,
722 "null": null
723 };
724 each(params.replace(/\+/g, " ").split("&"), function (v, j) {
725 var param = v.split("="),
726 key = decodeURIComponent(param[0]),
727 val,
728 cur = obj,
729 i = 0,
730 keys = key.split("]["),
731 keys_last = keys.length - 1;
732 if (/\[/.test(keys[0]) && /\]$/.test(keys[keys_last])) {
733 keys[keys_last] = keys[keys_last].replace(/\]$/, "");
734 keys = keys.shift().split("[").concat(keys);
735 keys_last = keys.length - 1;
736 } else {
737 keys_last = 0;
738 }
739 if (param.length === 2) {
740 val = decodeURIComponent(param[1]);
741 if (coerce) {
742 val = val && !isNaN(val) ? +val : val === "undefined" ? undefined : coerce_types[val] !== undefined ? coerce_types[val] : val;
743 }
744 if (keys_last) {
745 for (; i <= keys_last; i++) {
746 key = keys[i] === "" ? cur.length : keys[i];
747 cur = cur[key] = i < keys_last ? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : []) : val;
748 }
749 } else {
750 if (isArray(obj[key])) {
751 obj[key].push(val);
752 } else if (obj[key] !== undefined) {
753 obj[key] = [obj[key], val];
754 } else {
755 obj[key] = val;
756 }
757 }
758 } else if (key) {
759 obj[key] = coerce ? undefined : "";
760 }
761 });
762 return obj;
763 };
764
765 api.tools.get_pathname_from_url = function(url) {
766 if (typeof(document) !== "undefined") {
767 const a = document.createElement("a");
768 a.href = url;
769 return a.pathname;
770 } else {
771 return url;
772 }
773 };
774
775 api.tools.parse_actions = function(params, xhr) {
776
777 // upload_attachment event - if found, don"t check other observers. See issue #22
778 if(params.url.act === "fup" || params.url.act === "fuv" || params.body_is_object) {
779 return params.body_is_object && api.observe.bound("upload_attachment") ? { upload_attachment: [ params.body_params ] } : false; // trigger attachment event
780 }
781
782 if(params.url.search !== undefined) {
783 // console.log(params.url, params.body, params.url_raw);
784 }
785
786 var triggered = {}; // store an object of event_name: [response_args] for events triggered by parsing the actions
787 var action_map = {
788 "tae" : "add_to_tasks",
789 "rc_^i" : "archive",
790 "tr" : "delete",
791 "dm" : "delete_message_in_thread",
792 "dl" : "delete_forever",
793 "dc_" : "delete_label",
794 "dr" : "discard_draft",
795 "el" : "expand_categories",
796 "cffm" : "filter_messages_like_these",
797 "arl" : "label",
798 "mai" : "mark_as_important",
799 "mani" : "mark_as_not_important",
800 "us" : "mark_as_not_spam",
801 "sp" : "mark_as_spam",
802 "mt" : "move_label",
803 "ib" : "move_to_inbox",
804 "ig" : "mute",
805 "rd" : "read",
806 "sd" : "save_draft",
807 "sm" : "send_message",
808 "mo" : "show_newly_arrived_message",
809 "st" : "star",
810 "cs" : "undo_send",
811 "ug" : "unmute",
812 "ur" : "unread",
813 "xst" : "unstar",
814 "new_mail" : "new_email",
815 "poll" : "poll",
816 "refresh" : "refresh",
817 "rtr" : "restore_message_in_thread",
818 "open_email" : "open_email",
819 "toggle_threads" : "toggle_threads"
820 };
821
822 if(typeof params.url.ik === "string") {
823 api.tracker.ik = params.url.ik;
824 }
825
826 if(typeof params.url.at === "string") {
827 api.tracker.at = params.url.at;
828 }
829
830 if(typeof params.url.rid === "string") {
831 if(params.url.rid.indexOf("mail") !== -1) {
832 api.tracker.rid = params.url.rid;
833 }
834 }
835
836 var action = decodeURIComponent(params.url.act);
837 var sent_params = params.body_params;
838 var email_ids = (typeof sent_params.t === "string") ? [sent_params.t] : sent_params.t;
839 var response = null;
840
841 switch(action) {
842 case "cs":
843 case "ur":
844 case "rd":
845 case "tr":
846 case "sp":
847 case "us":
848 case "ib":
849 case "dl":
850 case "st":
851 case "xst":
852 case "mai":
853 case "mani":
854 case "ig":
855 case "ug":
856 case "dr":
857 case "mt":
858 case "cffm":
859 case "rc_^i":
860 response = [email_ids, params.url, params.body];
861 break;
862
863 case "arl":
864 case "dc_":
865 response = [email_ids, params.url, params.body, params.url.acn];
866 break;
867
868 case "sd":
869 response = [email_ids, params.url, sent_params];
870 break;
871
872 case "tae":
873 case "sm":
874 response = [params.url, params.body, sent_params];
875 break;
876
877 case "el":
878 response = [params.url, params.body, sent_params.ex === "1"];
879 break;
880
881 case "dm":
882 case "rtr":
883 case "mo":
884 response = [sent_params.m, params.url, params.body];
885 break;
886
887 }
888
889 if(typeof params.url._reqid === "string" && params.url.view === "tl" && params.url.auto !== undefined) {
890 response = [params.url.th, params.url, params.body];
891 if(api.observe.bound("new_email")) {
892 triggered.new_email = response;
893 }
894 }
895
896 if((params.url.view === "cv" || params.url.view === "ad") && typeof params.url.th === "string" && typeof params.url.search === "string" && params.url.rid === undefined) {
897 response = [params.url.th, params.url, params.body];
898 if(api.observe.bound("open_email")) {
899 triggered.open_email = response;
900 }
901 }
902
903 if((params.url.view === "cv" || params.url.view === "ad") && typeof params.url.th === "object" && typeof params.url.search === "string" && params.url.rid !== undefined) {
904 response = [params.url.th, params.url, params.body];
905 if(api.observe.bound("toggle_threads")) {
906 triggered.toggle_threads = response;
907 }
908 }
909
910 if((params.url.view === "cv" || params.url.view === "ad") && typeof params.url.th === "string" && typeof params.url.search === "string" && params.url.rid !== undefined) {
911 if(params.url.msgs !== undefined) {
912 response = [params.url.th, params.url, params.body];
913 if(api.observe.bound("toggle_threads")) {
914 triggered.toggle_threads = response;
915 }
916 }
917 }
918
919 if(typeof params.url.SID === "string" && typeof params.url.zx === "string" && params.body.indexOf("req0_") !== -1) {
920 api.tracker.SID = params.url.SID;
921 response = [params.url, params.body, sent_params];
922 if(api.observe.bound("poll")) {
923 triggered.poll = response;
924 }
925 }
926
927 if(typeof params.url.ik === "string" && typeof params.url.search === "string" && params.body.length === 0 && typeof params.url._reqid === "string") {
928 response = [params.url, params.body, sent_params];
929 if(api.observe.bound("refresh")) {
930 triggered.refresh = response;
931 }
932 }
933
934 if(response && action_map[action] && api.observe.bound(action_map[action])) {
935 triggered[action_map[action]] = response;
936 }
937
938 if(params.method === "POST" && (typeof params.url.SID === "string"
939 || typeof params.url.ik === "string"
940 || typeof params.url.act === "string")) {
941 triggered.http_event = [params]; // send every event and all data
942 }
943
944 // handle new data-format introduced with new gmail 2018.
945 if (api.check.is_new_data_layer()) {
946 api.tools.parse_request_payload(params, triggered);
947 }
948
949 return triggered;
950 };
951
952 api.check.data.is_thread_id = function(id) {
953 return id
954 && typeof id === "string"
955 && /^thread-[a|f]:/.test(id);
956 };
957
958 api.check.data.is_thread = function(obj) {
959 return obj
960 && typeof obj === "object"
961 && obj["1"]
962 && api.check.data.is_thread_id(obj["1"]);
963 };
964
965 api.check.data.is_email_id = function(id) {
966 return id
967 && typeof id === "string"
968 && id.indexOf('bump-') === -1
969 && /^msg-[a|f]:/.test(id);
970 };
971
972 api.check.data.is_email = function(obj) {
973 return obj
974 && typeof obj === "object"
975 && obj["1"]
976 && api.check.data.is_email_id(obj["1"]);
977 };
978
979 api.check.data.is_legacy_email_id = function(id) {
980 return id
981 && typeof id === "string"
982 && /^[0-9a-f]{16,}$/.test(id);
983 };
984
985 api.check.data.is_action = function(obj) {
986 return api.check.data.is_first_type_action(obj)
987 || api.check.data.is_second_type_action(obj);
988 };
989
990 api.check.data.is_first_type_action = function(obj) {
991 return obj
992 && obj["1"]
993 && Array.isArray(obj["1"])
994 && obj["1"].length === 1
995 && typeof obj["1"]["0"] === 'string';
996 };
997
998 api.check.data.is_second_type_action = function(obj) {
999 return obj
1000 && obj["2"]
1001 && Array.isArray(obj["2"])
1002 && obj["2"].length
1003 && typeof obj["2"]["0"] === 'string';
1004 };
1005
1006 api.check.data.is_smartlabels_array = function(obj) {
1007 const isNotArray = !obj || !Array.isArray(obj) ||obj.length === 0;
1008 if (isNotArray) {
1009 return false;
1010 }
1011
1012 for (let item of obj) {
1013 if (typeof item !== "string") {
1014 return false;
1015 }
1016
1017 if (!/^\^[a-z]+/.test(item)) {
1018 return false;
1019 }
1020 }
1021
1022 return true;
1023 };
1024
1025 /**
1026 A lightweight check to see if a object (most likely) is a JSON-string.
1027 */
1028 api.check.data.is_json_string = function(obj) {
1029 if (!obj || typeof obj !== "string") {
1030 return false;
1031 }
1032
1033 let str = obj.trim();
1034 return ((str.startsWith("{") && str.endsWith("}"))
1035 || (str.startsWith("[") && str.endsWith("]")));
1036 };
1037
1038 api.tools.get_thread_id = function(obj) {
1039 return api.check.data.is_thread(obj)
1040 && obj["1"];
1041 };
1042
1043 api.tools.get_thread_data = function(obj) {
1044 return obj
1045 && obj["2"]
1046 && typeof obj["2"] === "object"
1047 && obj["2"]["7"]
1048 && typeof obj["2"]["7"] === "object"
1049 && obj["2"]["7"];
1050 };
1051
1052 api.tools.get_action = function(obj) {
1053 return api.tools.get_first_type_action(obj)
1054 || api.tools.get_second_type_action(obj);
1055 };
1056
1057 api.tools.get_first_type_action = function(obj) {
1058 return obj
1059 && obj[1]
1060 && obj[1].join('');
1061 };
1062
1063 api.tools.get_second_type_action = function(obj) {
1064 return obj
1065 && obj[2]
1066 && obj[2].join('');
1067 };
1068
1069 api.tools.get_message_ids = function(obj) {
1070 return obj
1071 && obj["3"]
1072 && Array.isArray(obj["3"])
1073 && obj["3"];
1074 };
1075
1076 api.tools.extract_from_graph = function(obj, predicate) {
1077 const result = [];
1078
1079 const safePredicate = function(item) {
1080 try {
1081 return predicate(item);
1082 }
1083 catch (err) {
1084 return false;
1085 }
1086 };
1087
1088 const forEachGraph = function(obj) {
1089 // check root-node too!
1090 if (safePredicate(obj)) {
1091 result.push(obj);
1092 return;
1093 }
1094
1095 for (let key in obj) {
1096 let item = obj[key];
1097
1098 if (safePredicate(item)) {
1099 result.push(item);
1100 continue;
1101 }
1102
1103 // special-case digging for arrays!
1104 if (Array.isArray(item)) {
1105 for (let listItem of item) {
1106 forEachGraph(listItem, obj);
1107 }
1108 } else if (typeof item === "object") {
1109 // keep on digging.
1110 forEachGraph(item);
1111 }
1112 }
1113 };
1114
1115 forEachGraph(obj);
1116 return result;
1117 };
1118
1119 api.tools.check_event_type = function(threadObj) {
1120 const action_map = {
1121 // "" : "add_to_tasks",
1122 "^a": "archive",
1123 "^k": "delete",
1124 // "" : "delete_message_in_thread",
1125 // "" : "delete_forever",
1126 // "" : "delete_label",
1127 // "" : "discard_draft",
1128 // "" : "expand_categories",
1129 // "" : "filter_messages_like_these",
1130 // "" : "label",
1131 // "^io_im^imi": "mark_as_important",
1132 // "^imn": "mark_as_not_important",
1133 // "" : "mark_as_not_spam",
1134 // "" : "mark_as_spam",
1135 // "" : "move_label",
1136 // "" : "move_to_inbox",
1137 // "" : "mute",
1138 "^u^us": "read",
1139 // "" : "save_draft",
1140 // "" : "send_message",
1141 // "" : "show_newly_arrived_message",
1142 // "^t^ss_sy": "star",
1143 // "" : "undo_send",
1144 // "" : "unmute",
1145 "^u" : "unread",
1146 // "^t^ss_sy^ss_so^ss_sr^ss_sp^ss_sb^ss_sg^ss_cr^ss_co^ss_cy^ss_cg^ss_cb^ss_cp": "unstar",
1147 "^us" : "new_email",
1148 // "" : "poll",
1149 // "" : "refresh",
1150 // "" : "restore_message_in_thread",
1151 "^o": "open_email",
1152 // "" : "toggle_threads"
1153 };
1154 const threadData = api.tools.get_thread_data(threadObj);
1155
1156 if (threadData && api.check.data.is_action(threadData)) {
1157 const action = api.tools.get_action(threadData);
1158
1159 return action_map[action];
1160 } else {
1161 return null;
1162 }
1163 };
1164
1165 api.tools.parse_fd_email = function(json) {
1166 if (!json || !Array.isArray(json)) {
1167 return [];
1168 }
1169
1170 const res = [];
1171
1172 for (let item of json) {
1173 res.push(api.tools.parse_fd_email2(item));
1174 }
1175
1176 return res;
1177 };
1178
1179 api.tools.parse_fd_email2 = function(item) {
1180 try
1181 {
1182 return {
1183 name: item["3"],
1184 address: item["2"]
1185 };
1186 }
1187 catch (e) {
1188 return null;
1189 }
1190 };
1191
1192 api.tools.parse_fd_attachments = function(json) {
1193 let res = [];
1194
1195 if (Array.isArray(json)) {
1196 for (let item of json) {
1197 let data = item["1"]["4"] || "";
1198
1199 res.push({
1200 attachment_id: item["1"]["2"],
1201 name: data["3"],
1202 type: data["4"],
1203 url: api.tools.check_fd_attachment_url(data["2"]),
1204 size: Number.parseInt(data["5"])
1205 });
1206 }
1207 }
1208
1209 return res;
1210 };
1211
1212 api.tools.check_fd_attachment_url = function(url) {
1213 var userAccountUrlPart = api.tracker.globals[7];
1214 if (url && userAccountUrlPart && url.indexOf(userAccountUrlPart) < 0) {
1215 url = url.replace('/mail/?', userAccountUrlPart + '?');
1216 }
1217
1218 return url;
1219 };
1220
1221 api.tools.parse_fd_request_html_payload = function(fd_email) {
1222 let fd_email_content_html = null;
1223 try {
1224 const fd_html_containers = fd_email["2"]["6"]["2"];
1225
1226 for (let fd_html_container of fd_html_containers) {
1227 fd_email_content_html = (fd_email_content_html || "") + fd_html_container["3"]["2"];
1228 }
1229 }
1230 catch(e) {
1231 // don't crash gmail when we cant parse email-contents
1232 }
1233
1234 return fd_email_content_html;
1235 };
1236
1237 api.tools.parse_fd_request_payload_get_email2 = function(fd_thread_container, fd_email_id) {
1238 try {
1239 const fd_emails2 = fd_thread_container["2"]["2"];
1240 const fd_email2 = fd_emails2.filter(i => i["1"] === fd_email_id);
1241 return fd_email2[0];
1242 }
1243 catch (e) {
1244 return {};
1245 }
1246 };
1247
1248 api.tools.parse_fd_request_payload = function(json) {
1249 // ensure JSON-format is known and understood?
1250 let thread_root = json["2"];
1251 if (!thread_root || !Array.isArray(thread_root)) {
1252 return null;
1253 }
1254
1255 try
1256 {
1257 const res = [];
1258
1259 const fd_threads = thread_root; // array
1260 for (let fd_thread_container of fd_threads) {
1261 const fd_thread_id = fd_thread_container["1"];
1262
1263 let fd_emails = fd_thread_container["3"]; // array
1264 for (let fd_email of fd_emails) {
1265 //console.log(fd_email)
1266 const fd_email_id = fd_email["1"];
1267
1268 // detailed to/from-fields must be obtained through the -other- email message node.
1269 const fd_email2 = api.tools.parse_fd_request_payload_get_email2(fd_thread_container, fd_email_id);
1270
1271 const fd_legacy_email_id = fd_email["2"]["35"];
1272 const fd_email_smtp_id = fd_email["2"]["8"];
1273
1274 const fd_email_subject = fd_email["2"]["5"];
1275 const fd_email_timestamp = Number.parseInt(fd_email["2"]["17"]);
1276 const fd_email_date = new Date(fd_email_timestamp);
1277
1278 const fd_email_content_html = api.tools.parse_fd_request_html_payload(fd_email);
1279
1280 const fd_attachments = api.tools.parse_fd_attachments(fd_email["2"]["14"]);
1281
1282 const fd_email_sender_address = fd_email["2"]["11"]["17"];
1283
1284 let fd_from = api.tools.parse_fd_email2(fd_email2["2"]);
1285 if (!fd_from) {
1286 fd_from = { address: fd_email_sender_address, name: "" };
1287 }
1288
1289 const fd_to = api.tools.parse_fd_email(fd_email["2"]["1"]);
1290 const fd_cc = api.tools.parse_fd_email(fd_email["2"]["2"]);
1291 const fd_bcc = api.tools.parse_fd_email(fd_email["2"]["3"]);
1292
1293 const email = {
1294 id: fd_email_id,
1295 legacy_email_id: fd_legacy_email_id,
1296 thread_id: fd_thread_id,
1297 smtp_id: fd_email_smtp_id,
1298 subject: fd_email_subject,
1299 timestamp: fd_email_timestamp,
1300 content_html: fd_email_content_html,
1301 date: fd_email_date,
1302 from: fd_from,
1303 to: fd_to,
1304 cc: fd_cc,
1305 bcc: fd_bcc,
1306 attachments: fd_attachments
1307 };
1308 if (api.cache.debug_xhr_fetch) {
1309 email["$email_node"] = fd_email;
1310 email["$thread_node"] = fd_thread_container;
1311 }
1312 //console.log(email);
1313 res.push(email);
1314 }
1315 }
1316
1317 return res;
1318 }
1319 catch (error) {
1320 console.warn("Gmail.js encountered an error trying to parse email-data!", error);
1321 return null;
1322 }
1323 };
1324
1325 api.tools.parse_sent_message_html_payload = function(sent_email) {
1326 let sent_email_content_html = null;
1327 try {
1328 const sent_html_containers = sent_email["9"]["2"];
1329
1330 for (let sent_html_container of sent_html_containers) {
1331 sent_email_content_html = (sent_email_content_html || "") + sent_html_container["2"];
1332 }
1333 }
1334 catch(e) {
1335 // don't crash gmail when we cant parse email-contents
1336 }
1337
1338 return sent_email_content_html;
1339 };
1340
1341 api.tools.parse_sent_message_attachments = function(json) {
1342 let res = [];
1343
1344 if (Array.isArray(json)) {
1345 for (let item of json) {
1346
1347 res.push({
1348 id: item["5"],
1349 name: item["2"],
1350 type: item["1"],
1351 url: item["6"],
1352 size: Number.parseInt(item["3"])
1353 });
1354 }
1355 }
1356
1357 return res;
1358 };
1359
1360 api.tools.parse_sent_message_payload = function(json) {
1361 try
1362 {
1363 let sent_email = json;
1364 //console.log(sent_email);
1365
1366 const sent_email_id = sent_email["1"];
1367
1368 const sent_email_subject = sent_email["8"];
1369 const sent_email_timestamp = Number.parseInt(sent_email["7"]);
1370 const sent_email_date = new Date(sent_email_timestamp);
1371
1372 const sent_email_content_html = api.tools.parse_sent_message_html_payload(sent_email);
1373 const sent_email_ishtml = sent_email["9"]["7"];
1374
1375 const sent_attachments = api.tools.parse_sent_message_attachments(sent_email["12"]);
1376
1377 const sent_from = api.tools.parse_fd_email2(sent_email["2"]);
1378 const sent_to = api.tools.parse_fd_email(sent_email["3"]);
1379 const sent_cc = api.tools.parse_fd_email(sent_email["4"]);
1380 const sent_bcc = api.tools.parse_fd_email(sent_email["5"]);
1381
1382 const email = {
1383 1: sent_email_id,
1384 id: sent_email_id,
1385 subject: sent_email_subject,
1386 timestamp: sent_email_timestamp,
1387 content_html: sent_email_content_html,
1388 ishtml: sent_email_ishtml,
1389 date: sent_email_date,
1390 from: sent_from,
1391 to: sent_to,
1392 cc: sent_cc,
1393 bcc: sent_bcc,
1394 attachments: sent_attachments,
1395 email_node: json
1396 };
1397
1398 return email;
1399 }
1400 catch (error) {
1401 console.warn("Gmail.js encountered an error trying to parse sent message!", error);
1402 return null;
1403 }
1404 };
1405
1406 api.tools.parse_request_payload = function(params, events, force) {
1407 const pathname = api.tools.get_pathname_from_url(params.url_raw);
1408 if (!force && !pathname) {
1409 return;
1410 }
1411
1412 const isSynch = (pathname || "").endsWith("/i/s");
1413 const isFetch = (pathname || "").endsWith("/i/fd");
1414 if (!force && !isFetch && !isSynch) {
1415 return;
1416 }
1417
1418 if (isFetch) {
1419 // register event, so that after triggers (where we parse response-data) gets triggered.
1420 events.load_email_data = [null];
1421 }
1422
1423 const threads = api.tools.extract_from_graph(params, api.check.data.is_thread);
1424 // console.log("Threads:");
1425 // console.log(threads);
1426 const emails = api.tools.extract_from_graph(params, api.check.data.is_email);
1427 // console.log("Emails:");
1428 // console.log(emails);
1429
1430 for (let email of emails) {
1431 // console.log("Email:");
1432 // console.log(email);
1433 for (let key in email) {
1434 let prop = email[key];
1435 if (api.check.data.is_smartlabels_array(prop)) {
1436 let sent_email = api.tools.parse_sent_message_payload(email);
1437 if (prop.indexOf("^pfg") !== -1) {
1438 events.send_message = [params.url, params.body, sent_email];
1439 } else if (prop.indexOf("^scheduled") > -1) {
1440 events.send_scheduled_message = [params.url, params.body, sent_email];
1441 }
1442 }
1443 }
1444 }
1445
1446 try {
1447 if (Array.isArray(threads) && api.check.data.is_thread(threads[0])) {
1448 const actionType = api.tools.check_event_type(threads[0]);
1449
1450 if (actionType) {
1451 // console.log(threads[0]);
1452 const threadsData = threads.map(thread => api.tools.get_thread_data(thread));
1453
1454 const new_thread_ids = threads.map(thread => api.tools.get_thread_id(thread));
1455 const new_email_ids = threadsData.map(threadData => api.tools.get_message_ids(threadData)).reduce((a, b) => a.concat(b), []);
1456 events[actionType] = [null, params.url, params.body, new_email_ids, new_thread_ids];
1457 }
1458 }
1459 } catch (e) {
1460 console.error('Error: ', e);
1461 }
1462 };
1463
1464 api.tools.parse_response = function(response) {
1465 // first try parse as pure json!
1466 if (api.check.data.is_json_string(response)) {
1467 try {
1468 let json = JSON.parse(response);
1469 return json;
1470 } catch(err) {
1471 // ignore, and fallback to old implementation!
1472 }
1473 }
1474
1475 // early XHR interception also means we intercept HTML, CSS, JS payloads. etc
1476 // dont crash on those.
1477 if (response.startsWith("<!DOCTYPE html")
1478 || response.indexOf("display:inline-block") !== -1
1479 ) {
1480 return [];
1481 }
1482
1483 let parsedResponse = [];
1484 let originalResponse = response;
1485 try {
1486 // gmail post response structure
1487 // )}]"\n<datalength><rawData>\n<dataLength><rawData>...
1488
1489 // prepare response, remove eval protectors
1490 response = response.replace(/\n/g, " ");
1491 response = response.substring(response.indexOf("'") + 1, response.length);
1492
1493 while(response.replace(/\s/g, "").length > 1) {
1494
1495 // how long is the data to get
1496 let dataLength = response.substring(0, response.indexOf("[")).replace(/\s/g, "");
1497 if (!dataLength) {dataLength = response.length;}
1498
1499 let endIndex = (parseInt(dataLength, 10) - 2) + response.indexOf("[");
1500 let data = response.substring(response.indexOf("["), endIndex);
1501
1502 let json = JSON.parse(data);
1503 parsedResponse.push(json);
1504
1505 // prepare response for next loop
1506 response = response.substring(response.indexOf("["), response.length);
1507 response = response.substring(data.length, response.length);
1508 }
1509 } catch (e) {
1510 // console.log("GmailJS post response-parsing failed.", e, originalResponse);
1511 }
1512
1513 return parsedResponse;
1514 };
1515
1516 /**
1517 parses a download_url attribute from the attachments main span-element.
1518 */
1519 api.tools.parse_attachment_url = function(url) {
1520 var parts = url.split(":");
1521 return {
1522 type: parts[0],
1523 url: parts[2] + ":" + parts[3]
1524 };
1525 };
1526
1527 /**
1528 Node-friendly function to extend objects without depending on jQuery
1529 (which requires a browser-context)
1530 */
1531 var extend = function(target, extension) {
1532 for (var key in extension) {
1533 target[key] = extension[key];
1534 }
1535 };
1536
1537 /**
1538 Node-friendly function to merge arrays without depending on jQuery
1539 (which requires a browser-context).
1540
1541 All subsequent arrays are merged into the first one, to match
1542 $.merge's behaviour.
1543 */
1544 var merge = function(target, mergee) {
1545
1546 for (var i=0; i < mergee.length; i++) {
1547 var value = mergee[i];
1548 target.push(value);
1549 }
1550
1551 return target;
1552 };
1553
1554 api.tools.parse_requests = function(params, xhr) {
1555 params.url_raw = params.url;
1556 params.url = api.tools.parse_url(params.url);
1557 if(typeof params.body === "object") {
1558 params.body_params = params.body;
1559 params.body_is_object = true;
1560 } else if (api.check.data.is_json_string(params.body)) {
1561 params.body_params = JSON.parse(params.body);
1562 } else if (params.body !== undefined) {
1563 params.body_params = api.tools.deparam(params.body);
1564 } else {
1565 params.body_params = {};
1566 }
1567
1568 if(typeof api.tracker.events !== "object" && typeof api.tracker.actions !== "object") {
1569 api.tracker.events = [];
1570 api.tracker.actions = [];
1571 }
1572
1573 api.tracker.events.unshift(params);
1574 var events = api.tools.parse_actions(params, xhr);
1575
1576 if(params.method === "POST" && typeof params.url.act === "string") {
1577 api.tracker.actions.unshift(params);
1578 }
1579
1580 if(api.tracker.events.length > 50) {
1581 api.tracker.events.pop();
1582 }
1583
1584 if(api.tracker.actions.length > 10) {
1585 api.tracker.actions.pop();
1586 }
1587 return events;
1588 };
1589
1590 api.tools.patch = function(patchee, patch) {
1591 patch(patchee);
1592 };
1593
1594
1595 api.tools.cache_email_data = function(email_data) {
1596 if (email_data === null) {
1597 return;
1598 }
1599
1600 const c = api.cache;
1601
1602 for (let email of email_data) {
1603 // cache email directly on IDs
1604 c.emailIdCache[email.id] = email;
1605 c.emailLegacyIdCache[email.legacy_email_id] = email;
1606
1607 // ensure we have a thread-object before appending emails to it!
1608 let thread = c.threadCache[email.thread_id];
1609 if (!thread) {
1610 thread = {
1611 thread_id: email.thread_id,
1612 emails: []
1613 };
1614 c.threadCache[email.thread_id] = thread;
1615 }
1616
1617 // only append email to cache if not already there.
1618 if (thread.emails.filter(i => i.id === email.id).length === 0) {
1619 thread.emails.push(email);
1620 }
1621 }
1622 };
1623
1624 api.tools.xhr_watcher = function () {
1625 if (api.tracker.xhr_init) {
1626 return;
1627 }
1628
1629 api.tracker.xhr_init = true;
1630
1631 const win = api.helper.get_xhr_window();
1632
1633 api.tools.patch(win.XMLHttpRequest.prototype.open, (orig) => {
1634 win.XMLHttpRequest.prototype.open = function (method, url, async, user, password) {
1635 var out = orig.apply(this, arguments);
1636 this.xhrParams = {
1637 method: method.toString(),
1638 url: url.toString()
1639 };
1640 return out;
1641 };
1642 });
1643
1644 api.tools.patch(win.XMLHttpRequest.prototype.send, (orig) => {
1645 win.XMLHttpRequest.prototype.send = function (body) {
1646 // parse the xhr request to determine if any events should be triggered
1647 var events = false;
1648 if (this.xhrParams) {
1649 this.xhrParams.body = body;
1650 events = api.tools.parse_requests(this.xhrParams, this);
1651 }
1652
1653 // fire before events
1654 if(api.observe.trigger("before", events, this)) {
1655
1656 // if before events were fired, rebuild arguments[0]/body strings
1657 // TODO: recreate the url if we want to support manipulating url args (is there a use case where this would be needed?)
1658 if (api.check.is_new_data_layer()) {
1659 body = arguments[0] = this.xhrParams.body_is_object ? this.xhrParams.body_params : JSON.stringify(this.xhrParams.body_params);
1660 } else {
1661 body = arguments[0] = this.xhrParams.body_is_object ? this.xhrParams.body_params : $.param(this.xhrParams.body_params,true).replace(/\+/g, "%20");
1662 }
1663 }
1664
1665 // if any matching after events, bind onreadystatechange callback
1666 // also: on new gmail we want to intercept email-data from /i/fd-request responses.
1667 if(api.observe.bound(events, "after") || api.check.is_new_data_layer()) {
1668 var curr_onreadystatechange = this.onreadystatechange;
1669 var xhr = this;
1670 this.onreadystatechange = function(progress) {
1671 if (this.readyState === this.DONE) {
1672 if (progress.target.responseType === "" || progress.target.responseType === "text") {
1673 xhr.xhrResponse = api.tools.parse_response(progress.target.responseText);
1674 } else {
1675 xhr.xhrResponse = progress.target.response;
1676 }
1677
1678 // intercept email-data passively, instead of actively trying to fetch it later!
1679 // (which we won't be able to do once 2019 hits anyway...)
1680 if (api.check.is_new_data_layer()) {
1681 if (api.tools.get_pathname_from_url(xhr.xhrParams.url_raw).endsWith("/i/fd")) {
1682 let parsed_emails = api.tools.parse_fd_request_payload(xhr.xhrResponse);
1683 api.tools.cache_email_data(parsed_emails);
1684 events.load_email_data = [parsed_emails];
1685 }
1686 }
1687 api.observe.trigger("after", events, xhr);
1688 }
1689 if (curr_onreadystatechange) {
1690 curr_onreadystatechange.apply(this, arguments);
1691 }
1692 };
1693 }
1694
1695 // send the original request
1696 var out = orig.apply(this, arguments);
1697
1698 // fire on events
1699 api.observe.trigger("on", events, this);
1700 return out;
1701 };
1702 });
1703 };
1704
1705 api.helper.get_xhr_window = function() {
1706 var js_frame = null;
1707
1708 if (top.document.getElementById("js_frame")){
1709 js_frame = top.document.getElementById("js_frame");
1710 } else if (window_opener) {
1711 js_frame = window_opener.top.document.getElementById("js_frame");
1712 }
1713 if (!js_frame){
1714 if (window_opener) {
1715 js_frame = window_opener.top;
1716 } else {
1717 js_frame = top;
1718 }
1719 }
1720 var win;
1721 if (js_frame.contentDocument) {
1722 win = js_frame.contentDocument.defaultView;
1723 } else {
1724 win = js_frame;
1725 }
1726 return win;
1727 };
1728
1729
1730 api.observe.http_requests = function() {
1731 return api.tracker.events;
1732 };
1733
1734
1735 api.observe.actions = function() {
1736 return api.tracker.actions;
1737 };
1738
1739 /**
1740 Bind a specified callback to an array of callbacks against a specified type & action
1741 */
1742 api.observe.bind = function(type, action, callback) {
1743
1744 // set up watchdog data structure
1745 if(typeof api.tracker.watchdog !== "object") {
1746 api.tracker.watchdog = {
1747 before: {},
1748 on: {},
1749 after: {},
1750 dom: {}
1751 };
1752 api.tracker.bound = {};
1753 }
1754 if(typeof api.tracker.watchdog[type] !== "object") {
1755 api.tools.error("api.observe.bind called with invalid type: " + type);
1756 }
1757
1758 // ensure we are watching xhr requests
1759 if(type !== "dom") {
1760 api.tools.xhr_watcher();
1761 }
1762
1763 // add callback to an array in the watchdog
1764 if(typeof api.tracker.watchdog[type][action] !== "object") {
1765 api.tracker.watchdog[type][action] = [];
1766 }
1767 api.tracker.watchdog[type][action].push(callback);
1768
1769 // allow checking for bound events to specific action/type as efficiently as possible (without in looping) - bit dirtier code,
1770 // but lookups (api.observer.bound) are executed by the hundreds & I think the extra efficiency is worth the tradeoff
1771 api.tracker.bound[action] = typeof api.tracker.bound[action] === "undefined" ? 1 : api.tracker.bound[action]+1;
1772 api.tracker.bound[type] = typeof api.tracker.bound[type] === "undefined" ? 1 : api.tracker.bound[type]+1;
1773 //api.tracker.watchdog[action] = callback;
1774 };
1775
1776 /**
1777 an on event is observed just after gmail sends an xhr request
1778 */
1779 api.observe.on = function(action, callback, response_callback) {
1780
1781 // check for DOM observer actions, and if none found, the assume an XHR observer
1782 if(api.observe.on_dom(action, callback)) return true;
1783
1784 // bind xhr observers
1785 api.observe.bind("on", action, callback);
1786 if (response_callback) {
1787 api.observe.after(action, callback);
1788 }
1789 };
1790
1791 /**
1792 an before event is observed just prior to the gmail xhr request being sent
1793 before events have the ability to modify the xhr request before it is sent
1794 */
1795 api.observe.before = function(action, callback) {
1796 api.observe.bind("before", action, callback);
1797 };
1798
1799 /**
1800 an after event is observed when the gmail xhr request returns from the server
1801 with the server response
1802 */
1803 api.observe.after = function(action, callback) {
1804 api.observe.bind("after", action, callback);
1805 };
1806
1807 /**
1808 Checks if a specified action & type has anything bound to it
1809 If type is null, will check for this action bound on any type
1810 If action is null, will check for any actions bound to a type
1811 */
1812 api.observe.bound = function(action, type) {
1813 if (typeof api.tracker.watchdog !== "object") return false;
1814 if (action) {
1815
1816 // if an object of actions (triggered events of format { event: [response] }) is passed, check if any of these are bound
1817 if(typeof action === "object") {
1818 var match = false;
1819 $.each(action,function(key,response){
1820 if(typeof api.tracker.watchdog[type][key] === "object") match = true;
1821 });
1822 return match;
1823 }
1824 if(type) return typeof api.tracker.watchdog[type][action] === "object";
1825 return api.tracker.bound[action] > 0;
1826 } else {
1827 if(type) return api.tracker.bound[type] > 0;
1828 api.tools.error("api.observe.bound called with invalid args");
1829 }
1830 };
1831
1832 /**
1833 Clear all callbacks for a specific type (before, on, after, dom) and action
1834 If action is null, all actions will be cleared
1835 If type is null, all types will be cleared
1836 */
1837 api.observe.off = function(action, type) {
1838
1839 // if watchdog is not set, bind has not yet been called so nothing to turn off
1840 if(typeof api.tracker.watchdog !== "object") return true;
1841
1842 // loop through applicable types
1843 var types = type ? [ type ] : [ "before", "on", "after", "dom" ];
1844 $.each(types, function(idx, type) {
1845 if(typeof api.tracker.watchdog[type] !== "object") return true; // no callbacks for this type
1846
1847 // if action specified, remove any callbacks for this action, otherwise remove all callbacks for all actions
1848 if(action) {
1849 if(typeof api.tracker.watchdog[type][action] === "object") {
1850 api.tracker.bound[action] -= api.tracker.watchdog[type][action].length;
1851 api.tracker.bound[type] -= api.tracker.watchdog[type][action].length;
1852 delete api.tracker.watchdog[type][action];
1853 }
1854 } else {
1855 $.each(api.tracker.watchdog[type], function(act,callbacks) {
1856 if(typeof api.tracker.watchdog[type][act] === "object") {
1857 api.tracker.bound[act] -= api.tracker.watchdog[type][act].length;
1858 api.tracker.bound[type] -= api.tracker.watchdog[type][act].length;
1859 delete api.tracker.watchdog[type][act];
1860 }
1861 });
1862 }
1863 });
1864 };
1865
1866 /**
1867 Trigger any specified events bound to the passed type
1868 Returns true or false depending if any events were fired
1869 */
1870 api.observe.trigger = function(type, events, xhr) {
1871 if(!events) return false;
1872 var fired = false;
1873 $.each(events, function(action,response) {
1874
1875 // we have to do this here each time to keep backwards compatibility with old response_callback implementation
1876 response = $.extend([], response); // break the reference so it doesn"t keep growing each trigger
1877 if(type === "after") response.push(xhr.xhrResponse); // backwards compat for after events requires we push onreadystatechange parsed response first
1878 response.push(xhr);
1879 if(api.observe.bound(action, type)) {
1880 fired = true;
1881 $.each(api.tracker.watchdog[type][action], function(idx, callback) {
1882 callback.apply(undefined, response);
1883 });
1884 }
1885 });
1886 return fired;
1887 };
1888
1889 /**
1890 Trigger any specified DOM events passing a specified element & optional handler
1891 */
1892 api.observe.trigger_dom = function(observer, element, handler) {
1893
1894 // if no defined handler, just call the callback
1895 if (!handler) {
1896 handler = function(match, callback) {
1897 callback(match);
1898 };
1899 }
1900 if (!api.tracker.watchdog.dom[observer]) {
1901 return;
1902 }
1903 $.each(api.tracker.watchdog.dom[observer], function(idx, callback) {
1904 handler(element, callback);
1905 });
1906 };
1907
1908 // pre-configured DOM observers
1909 // map observers to DOM class names
1910 // as elements are inserted into the DOM, these classes will be checked for and mapped events triggered,
1911 // receiving "e" event object, and a jquery bound inserted DOM element
1912 // NOTE: supported observers must be registered in the supported_observers array as well as the dom_observers config
1913 // Config example: event_name: {
1914 // class: "className", // required - check for this className in the inserted DOM element
1915 // selector: "div.className#myId", // if you need to match more than just the className of a specific element to indicate a match, you can use this selector for further checking (uses element.is(selector) on matched element). E.g. if there are multiple elements with a class indicating an observer should fire, but you only want it to fire on a specific id, then you would use this
1916 // sub_selector: "div.className", // if specified, we do a jquery element.find for the passed selector on the inserted element and ensure we can find a match
1917 // handler: function( matchElement, callback ) {} // if specified this handler is called if a match is found. Otherwise default calls the callback & passes the jQuery matchElement
1918 // },
1919 // TODO: current limitation allows only 1 action per watched className (i.e. each watched class must be
1920 // unique). If this functionality is needed this can be worked around by pushing actions to an array
1921 // in api.tracker.dom_observer_map below
1922 // console.log( "Observer set for", action, callback);
1923 api.observe.initialize_dom_observers = function() {
1924 api.tracker.dom_observer_init = true;
1925 api.tracker.supported_observers = ["view_thread", "view_email", "load_email_menu", "recipient_change", "compose"];
1926 api.tracker.dom_observers = {
1927
1928 // when a thread is clicked on in a mailbox for viewing - note: this should fire at a similar time (directly after) as the open_email XHR observer
1929 // which is triggered by the XHR request rather than nodes being inserted into the DOM (and thus returns different information)
1930 "view_thread": {
1931 class: ["Bu", "nH"], // class depends if is_preview_pane - Bu for preview pane, nH for standard view
1932 sub_selector: "div.if",
1933 handler: function(match, callback) {
1934 match = new api.dom.thread(match);
1935 callback(match);
1936 }
1937 },
1938
1939 // when an individual email is loaded within a thread (also fires when thread loads displaying the latest email)
1940 "view_email": {
1941 // class depends if is_preview_pane - Bu for preview pane, nH for standard view,
1942 // the empty class ("") is for emails opened after thread is rendered.
1943 class: ["Bu", "nH", ""],
1944 sub_selector: "div.adn",
1945 handler: function(match, callback) {
1946 match = new api.dom.email(match);
1947 callback(match);
1948 }
1949 },
1950
1951 // when the dropdown menu next to the reply button is inserted into the DOM when viewing an email
1952 "load_email_menu": {
1953 class: "J-N",
1954 selector: "div[role=menu] div[role=menuitem]:first-child", // use the first menu item in the popoup as the indicator to trigger this observer
1955 handler: function(match, callback) {
1956 match = match.closest("div[role=menu]");
1957 callback(match);
1958 }
1959 },
1960
1961 // a new email address is added to any of the to,cc,bcc fields when composing a new email or replying/forwarding
1962 "recipient_change": {
1963 class: "vR",
1964 handler: function(match, callback) {
1965 // console.log("compose:recipient handler called",match,callback);
1966
1967 // we need to small delay on the execution of the handler as when the recipients field initialises on a reply (or reinstated compose/draft)
1968 // then multiple DOM elements will be inserted for each recipient causing this handler to execute multiple times
1969 // in reality we only want a single callback, so give other nodes time to be inserted & then only execute the callback once
1970 if(typeof api.tracker.recipient_matches !== "object") {
1971 api.tracker.recipient_matches = [];
1972 }
1973 api.tracker.recipient_matches.push(match);
1974 setTimeout(function(){
1975 // console.log("recipient timeout handler", api.tracker.recipient_matches.length);
1976 if(!api.tracker.recipient_matches.length) return;
1977
1978 // determine an array of all emails specified for To, CC and BCC and extract addresses into an object for the callback
1979 var compose = new api.dom.compose(api.tracker.recipient_matches[0].closest("div.M9"));
1980 var recipients = compose.recipients();
1981 callback(compose, recipients, api.tracker.recipient_matches);
1982
1983 // reset matches so no future delayed instances of this function execute
1984 api.tracker.recipient_matches = [];
1985 },100);
1986 }
1987 },
1988
1989 // this will fire if a new compose, reply or forward is created. it won"t fire if a reply changes to a forward & vice versa
1990 // passes a type of compose, reply, or forward to the callback
1991 "compose": {
1992 class: "An", // M9 would be better but this isn"t set at the point of insertion
1993 handler: function(match, callback) {
1994 // console.log("reply_forward handler called", match, callback);
1995
1996 var originalMatch = match;
1997 // look back up the DOM tree for M9 (the main reply/forward node)
1998 match = match.closest("div.M9");
1999 if (!match.length) return;
2000 match = new api.dom.compose(match);
2001 var type;
2002 if (match.is_inline()) {
2003 type = match.find("input[name=subject]").val().indexOf("Fw") === 0 ? "forward" : "reply";
2004 } else {
2005 type = "compose";
2006
2007 //Find the close button and set an event listener so we can forward the compose_cancelled event.
2008 var composeWindow = originalMatch.closest("div.AD");
2009 composeWindow.find(".Ha").mouseup(function() {
2010 if(api.tracker.composeCancelledCallback) {
2011 api.tracker.composeCancelledCallback(match);
2012 }
2013 return true;
2014 });
2015
2016 }
2017 callback(match,type);
2018 }
2019 }
2020 };
2021
2022 // support extending with custom observers
2023 if (api.tracker.custom_supported_observers) {
2024 $.merge(api.tracker.supported_observers, api.tracker.custom_supported_observers);
2025 $.extend(true, api.tracker.dom_observers, api.tracker.custom_dom_observers); // deep copy to copy in sub_observers where relevant
2026 }
2027
2028 // map observed classNames to actions
2029 api.tracker.dom_observer_map = {};
2030 $.each(api.tracker.dom_observers, function(act,config){
2031 if(!$.isArray(config.class)) config.class = [config.class];
2032 $.each(config.class, function(idx, className) {
2033 if (!api.tracker.dom_observer_map[className]) {
2034 api.tracker.dom_observer_map[className] = [];
2035 }
2036 api.tracker.dom_observer_map[className].push(act);
2037 });
2038 });
2039 //console.log( "observer_config", api.tracker.dom_observers, "dom_observer_map", api.tracker.dom_observer_map);
2040 };
2041
2042 /**
2043 Allow an application to register a custom DOM observer specific to their app.
2044 Adds it to the configured DOM observers and is supported by the dom insertion observer
2045 This method can be called two different ways:
2046 Args:
2047 action - the name of the new DOM observer
2048 className / args - for a simple observer, this arg can simply be the class on an inserted DOM element that identifies this event should be
2049 triggered. For a more complicated observer, this can be an object containing properties for each of the supported DOM observer config arguments
2050 */
2051 api.observe.register = function(action, args) {
2052
2053 // check observers configured
2054 if (api.tracker.dom_observer_init) {
2055 api.tools.error("Error: Please register all custom DOM observers before binding handlers using gmail.observe.on etc");
2056 }
2057 if (!api.tracker.custom_supported_observers) {
2058 api.tracker.custom_supported_observers = [];
2059 api.tracker.custom_dom_observers = {};
2060 }
2061
2062 // was an object of arguments passed, or just a className
2063 var config = {};
2064 if (typeof args === "object" && !$.isArray(args)) {
2065
2066 // copy over supported config
2067 $.each(["class","selector","sub_selector","handler"], function(idx, arg) {
2068 if(args[arg]) {
2069 config[arg] = args[arg];
2070 }
2071 });
2072 } else {
2073 config["class"] = args;
2074 }
2075 api.tracker.custom_supported_observers.push(action);
2076 api.tracker.custom_dom_observers[action] = config;
2077 };
2078
2079 /**
2080 Observe DOM nodes being inserted. When a node with a class defined in api.tracker.dom_observers is inserted,
2081 trigger the related event and fire off any relevant bound callbacks
2082 This function should return true if a dom observer is found for the specified action
2083 */
2084 api.observe.on_dom = function(action, callback) {
2085
2086 // check observers configured
2087 if(!api.tracker.dom_observer_init) {
2088 api.observe.initialize_dom_observers();
2089 }
2090
2091 // support for DOM observers
2092 if($.inArray(action, api.tracker.supported_observers) > -1) {
2093
2094 //console.log("observer found",api.tracker.dom_observers[action]);
2095
2096 // if we haven"t yet bound the DOM insertion observer, do it now
2097 if(!api.tracker.observing_dom) {
2098 api.tracker.observing_dom = true;
2099 //api.tracker.dom_watchdog = {}; // store passed observer callbacks for different DOM events
2100
2101 // this listener will check every element inserted into the DOM
2102 // for specified classes (as defined in api.tracker.dom_observers above) which indicate
2103 // related actions which need triggering
2104 $(window.document).on("DOMNodeInserted", function(e) {
2105 api.tools.insertion_observer(e.target, api.tracker.dom_observers, api.tracker.dom_observer_map);
2106 });
2107
2108 // recipient_change also needs to listen to removals
2109 var mutationObserver = new MutationObserver(function(mutations) {
2110 for (var i = 0; i < mutations.length; i++) {
2111 var mutation = mutations[i];
2112 var removedNodes = mutation.removedNodes;
2113 for (var j = 0; j < removedNodes.length; j++) {
2114 var removedNode = removedNodes[j];
2115 if (removedNode.className === "vR") {
2116 var observer = api.tracker.dom_observer_map["vR"];
2117 var handler = api.tracker.dom_observers.recipient_change.handler;
2118 api.observe.trigger_dom(observer, $(mutation.target), handler);
2119 }
2120 }
2121 }
2122 });
2123 mutationObserver.observe(document.body, {subtree: true, childList: true});
2124
2125 }
2126 api.observe.bind("dom",action,callback);
2127 // console.log(api.tracker.observing_dom,"dom_watchdog is now:",api.tracker.dom_watchdog);
2128 return true;
2129
2130 // support for gmail interface load event
2131 }
2132 else if(action === "compose_cancelled") {
2133 console.log("set compose cancelled callback");
2134 api.tracker.composeCancelledCallback = callback;
2135 }
2136 else if(action === "load") {
2137
2138 // wait until the gmail interface has finished loading and then
2139 // execute the passed handler. If interface is already loaded,
2140 // then will just execute callback
2141 if(api.dom.inbox_content().length) return callback();
2142 var load_count = 0;
2143 var delay = 200; // 200ms per check
2144 var attempts = 50; // try 50 times before giving up & assuming an error
2145 var timer = setInterval(function() {
2146 var test = api.dom.inbox_content().length;
2147 if(test > 0) {
2148 clearInterval(timer);
2149 return callback();
2150 } else if(++load_count > attempts) {
2151 clearInterval(timer);
2152 console.log("Failed to detect interface load in " + (delay*attempts/1000) + " seconds. Will automatically fire event in 5 further seconds.");
2153 setTimeout(callback, 5000);
2154 }
2155 }, delay);
2156 return true;
2157 }
2158 };
2159
2160 // observes every element inserted into the DOM by Gmail and looks at the classes on those elements,
2161 // checking for any configured observers related to those classes
2162 api.tools.insertion_observer = function(target, dom_observers, dom_observer_map, sub) {
2163 //console.log("insertion", target, target.className);
2164 if(!api.tracker.dom_observer_map) return;
2165
2166 // loop through each of the inserted elements classes & check for a defined observer on that class
2167 var cn = target.className || "";
2168 var classes = cn.trim ? cn.trim().split(/\s+/) : [];
2169 if(!classes.length) classes.push(""); // if no class, then check for anything observing nodes with no class
2170 $.each(classes, function(idx, className) {
2171 var observers = dom_observer_map[className];
2172 if (!observers) {
2173 return;
2174 }
2175
2176 for (var observer of observers) {
2177
2178 // check if this is a defined observer, and callbacks are bound to that observer
2179 if(observer && api.tracker.watchdog.dom[observer]) {
2180 var element = $(target);
2181 var config = dom_observers[observer];
2182
2183 // if a config id specified for this observer, ensure it matches for this element
2184 if(config.selector && !element.is(config.selector)) {
2185 return;
2186 }
2187
2188 // check for any defined sub_selector match - if not found, then this is not a match for this observer
2189 // if found, then set the matching element to be the one that matches the sub_selector
2190 if(config.sub_selector) {
2191 element = element.find(config.sub_selector);
2192 // console.log("checking for subselector", config.sub_selector, element);
2193 }
2194
2195 // if an element has been found, execute the observer handler (or if none defined, execute the callback)
2196 if(element.length) {
2197
2198 var handler = config.handler ? config.handler : function(match, callback) { callback(match); };
2199 // console.log( "inserted DOM: class match in watchdog",observer,api.tracker.watchdog.dom[observer] );
2200 api.observe.trigger_dom(observer, element, handler);
2201 }
2202 }
2203 }
2204 });
2205 };
2206
2207
2208 api.tools.make_request = function (_link, method, disable_cache) {
2209 var link = decodeURIComponent(_link.replace(/%23/g, "#-#-#"));
2210 method = method || "GET";
2211
2212 link = encodeURI(link).replace(/#-#-#/gi, "%23");
2213 var config = {type: method, url: link, async: false, dataType:"text"};
2214 if (disable_cache) {
2215 config.cache = false;
2216 }
2217 var request = $.ajax(config);
2218 return request.responseText;
2219 };
2220
2221
2222 api.tools.make_request_async = function (_link, method, callback, error_callback, disable_cache) {
2223 var link = decodeURIComponent(_link.replace(/%23/g, "#-#-#"));
2224 method = method || "GET";
2225
2226 link = encodeURI(link).replace(/#-#-#/gi, "%23");
2227 var config = {type: method, url: link, async: true, dataType: "text"};
2228 if (disable_cache){
2229 config.cache = false;
2230 }
2231 $.ajax(config)
2232 .done(function(data, textStatus, jqxhr) {
2233 callback(jqxhr.responseText);
2234 })
2235 .fail(function(jqxhr, textStatus, errorThrown) {
2236 console.error("Request Failed", errorThrown);
2237 if (typeof error_callback === 'function'){
2238 error_callback(jqxhr, textStatus, errorThrown);
2239 }
2240 });
2241 };
2242
2243 /**
2244 Creates a request to download user-content from Gmail.
2245 This can be used to download email_source or attachments.
2246
2247 Set `preferBinary` to receive data as an Uint8Array which is unaffected
2248 by string-parsing or resolving of text-encoding.
2249
2250 This is required in order to correctly download attachments!
2251 */
2252 api.tools.make_request_download_promise = function (url, preferBinary) {
2253 // if we try to download the same email/url several times,
2254 // something weird happens with our cookies, causing the 302
2255 // redirect to mail-attachment.googleusercontent.com (MAGUC)
2256 // to redirect back to mail.google.com.
2257 //
2258 // mail.google.com does NOT have CORS-headers for MAGUC, so
2259 // this redirect (and thus our request) fails.
2260 //
2261 // Adding a random variable with a constantly changing value defeats
2262 // any cache, and seems to solve our problem.
2263 const timeStamp = Date.now();
2264 url += "&cacheCounter=" + timeStamp;
2265
2266 let responseType = "text";
2267 if (preferBinary) {
2268 responseType = "arraybuffer";
2269 }
2270
2271 // now go download!
2272 return new Promise((resolve, reject) => {
2273 const request = new XMLHttpRequest();
2274 request.open("GET", url, true);
2275 request.responseType = responseType;
2276
2277 request.onreadystatechange = () => {
2278 if (request.readyState !== XMLHttpRequest.DONE) {
2279 return;
2280 }
2281
2282 if (request.status >= 200 && request.status <= 302) {
2283 const result = request.response;
2284 if (result) {
2285 if (preferBinary) {
2286 const byteArray = new Uint8Array(result);
2287 resolve(byteArray);
2288 } else {
2289 // result is regular text!
2290 resolve(result);
2291 }
2292 }
2293 }
2294 };
2295 request.onerror = (ev) => {
2296 reject(ev);
2297 };
2298
2299 request.send();
2300 });
2301 };
2302
2303
2304 api.tools.parse_view_data = function(view_data) {
2305 var parsed = [];
2306 var data = [];
2307
2308 for(var j=0; j < view_data.length; j++) {
2309 if(view_data[j][0] === "tb") {
2310 for(var k=0; k < view_data[j][2].length; k++) {
2311 data.push(view_data[j][2][k]);
2312 }
2313 }
2314 }
2315
2316 for(var i=0; i < data.length; i++) {
2317 var x = data[i];
2318
2319 parsed.push({
2320 id: x[0],
2321 title : x[9],
2322 excerpt : x[10],
2323 time : x[15],
2324 sender : x[28],
2325 attachment : x[13],
2326 labels: x[5]
2327 });
2328 }
2329
2330 return parsed;
2331 };
2332
2333
2334 api.helper.get.is_delegated_inbox = function() {
2335 return $(".identityUserDelegatedAccount").length === 1;
2336 };
2337
2338
2339 api.helper.get.visible_emails_pre = function(customInboxQuery) {
2340 var page = api.get.current_page();
2341 var url = window.location.origin + window.location.pathname + "?ui=2&ik=" + api.tracker.ik+"&rid=" + api.tracker.rid + "&view=tl&num=120&rt=1";
2342 var start = $(".aqK:visible .Dj").find("span:first").text().replace(",", "").replace(".", "").split('–')[0];
2343 if (start) {
2344 start = parseInt(start - 1);
2345 url += "&start=" + start +
2346 "&sstart=" + start;
2347 } else {
2348 url += "&start=0";
2349 }
2350
2351 var cat_label = "";
2352
2353 if(page.indexOf("label/") === 0) {
2354 url += "&cat=" + page.split("/")[1] +"&search=cat";
2355 } else if(page.indexOf("category/") === 0) {
2356 if(page.indexOf("forums") !== -1) {
2357 cat_label = "group";
2358 } else if(page.indexOf("updates") !== -1) {
2359 cat_label = "notification";
2360 } else if(page.indexOf("promotion") !== -1) {
2361 cat_label = "promo";
2362 } else if(page.indexOf("social") !== -1) {
2363 cat_label = "social";
2364 }
2365 url += "&cat=^smartlabel_" + cat_label +"&search=category";
2366 } else if(page.indexOf("search/") === 0) {
2367 var at = $("input[name=at]").val();
2368 url += "&qs=true&q=" + page.split("/")[1] +"&at=" + at + "&search=query";
2369 } else if(page === "inbox"){
2370 if ($("div[aria-label='Social']").attr("aria-selected") === "true") {
2371 cat_label = "social";
2372 url += "&cat=^smartlabel_" + cat_label + "&search=category";
2373 } else if ($("div[aria-label='Promotions']").attr("aria-selected") === "true") {
2374 cat_label = "promo";
2375 url += "&cat=^smartlabel_" + cat_label + "&search=category";
2376 } else if ($("div[aria-label='Updates']").attr("aria-selected") === "true") {
2377 cat_label = "notification";
2378 url += "&cat=^smartlabel_" + cat_label + "&search=category";
2379 } else if ($("div[aria-label='Forums']").attr("aria-selected") === "true") {
2380 cat_label = "group";
2381 url += "&cat=^smartlabel_" + cat_label + "&search=category";
2382 } else {
2383 // control the behaviour with a given parameter
2384 if (customInboxQuery) {
2385 url += "&search=" + customInboxQuery;
2386 }
2387 // tentative fix for https://github.com/KartikTalwar/gmail.js/issues/417
2388 else if (api.check.is_google_apps_user()) {
2389 url += "&search=" + "inbox";
2390 } else {
2391 url += "&search=" + "mbox";
2392 }
2393 }
2394 }else {
2395 url += "&search=" + page;
2396 }
2397 return url;
2398 };
2399
2400
2401 api.helper.get.visible_emails_post = function(get_data) {
2402 var emails = [];
2403
2404 if (!get_data) {
2405 return emails;
2406 }
2407
2408 var data = get_data.substring(get_data.indexOf("["), get_data.length);
2409 var json = JSON.parse(data);
2410 api.tracker.view_data = json;
2411
2412 for(var i in api.tracker.view_data) {
2413 if (typeof(api.tracker.view_data[i]) === "function") {
2414 continue;
2415 }
2416
2417 var cdata = api.tools.parse_view_data(api.tracker.view_data[i]);
2418 if(cdata.length > 0) {
2419 merge(emails, cdata);
2420 }
2421 }
2422 return emails;
2423 };
2424
2425 // dispatch mousedown and mouseup event on passed element
2426 api.helper.trigger_mouse_click = function(element) {
2427 if(element) {
2428 //Trigger mouse down event
2429 var mouseDown = document.createEvent("MouseEvents");
2430 mouseDown.initEvent( "mousedown", true, false );
2431 element.dispatchEvent(mouseDown);
2432
2433 //Trigger mouse up event
2434 var mouseUp = document.createEvent("MouseEvents");
2435 mouseUp.initEvent( "mouseup", true, false );
2436 element.dispatchEvent(mouseUp);
2437
2438 return true;
2439 }
2440 return false;
2441 };
2442
2443 api.get.visible_emails = function(customInboxQuery) {
2444 var url = api.helper.get.visible_emails_pre(customInboxQuery);
2445 var get_data = api.tools.make_request(url);
2446 var emails = api.helper.get.visible_emails_post(get_data);
2447
2448 return emails;
2449 };
2450
2451
2452 api.get.visible_emails_async = function(callback, customInboxQuery) {
2453 var url = api.helper.get.visible_emails_pre(customInboxQuery);
2454 api.tools.make_request_async(url, "GET", function(get_data) {
2455 var emails = api.helper.get.visible_emails_post(get_data);
2456 callback(emails);
2457 });
2458 };
2459
2460
2461 api.get.selected_emails_data = function(customInboxQuery) {
2462 var selected_emails = [];
2463 if(!api.check.is_inside_email()){
2464 if($("[gh='tl'] div[role='checkbox'][aria-checked='true']").length){
2465 var email = null;
2466 var emails = api.get.visible_emails(customInboxQuery);
2467 $("[gh='tl'] div[role='checkbox']").each(function(index){
2468 if($(this).attr("aria-checked") === "true"){
2469 email = api.get.email_data(emails[index].id);
2470 selected_emails.push(email);
2471 }
2472 });
2473 }
2474 }else {
2475 selected_emails.push(api.get.email_data());
2476 }
2477 return selected_emails;
2478 };
2479
2480
2481 api.get.current_page = function(hash) {
2482 hash = hash || window.location.hash;
2483
2484 var hashPart = hash.split("#").pop().split("?").shift() || "inbox";
2485
2486 if(hashPart.match(/\/[0-9a-zA-Z]{16,}$/gi)) {
2487 return "email";
2488 }
2489
2490 var isTwopart = (hashPart.indexOf("search/") === 0
2491 || hashPart.indexOf("category/") === 0
2492 || hashPart.indexOf("label/") === 0);
2493
2494 var result = null;
2495 if (!isTwopart) {
2496 result = hashPart.split("/").shift();
2497 return result;
2498 } else {
2499 var parts = hashPart.split("/");
2500 result = parts[0] + "/" + parts[1];
2501 return result;
2502 }
2503 };
2504
2505
2506 api.tools.infobox = function(message, time, html){
2507 var top = $(".b8.UC");
2508
2509 // initial Gmail style I noticed on 26 / 05 / 2014 for $(".b8.UC") :
2510 // style="position: relative; top: -10000px;"
2511 // Seems that when Gmail shows infobox, the style is simply removed
2512 // - from what I can see in DevTools Elements Panel
2513
2514 if(top.length > 0){
2515 top.stop(false, true); // cancel any existing fade so we can start again
2516 var info = top.find(".vh");
2517 if (!html) {
2518 info.text(message);
2519 } else {
2520 info.html(message);
2521 }
2522 if(typeof time !== "undefined"){
2523 var initialInfoboxStyle = top.attr("style"); // backup initial style
2524 top.removeAttr("style").fadeTo(time, 0, function(){ // simply remove then restore
2525 $(this).attr("style", initialInfoboxStyle); // style attribute insteed of playing
2526 }); // on visibility property
2527 }
2528 else{
2529 top.removeAttr("style"); // dito
2530 }
2531 }
2532 };
2533
2534 /**
2535 * Re-renders the UI using the available data.
2536 *
2537 * This method does _not_ cause Gmail to fetch new data. This method is useful
2538 * in circumstances where Gmail has data available but does not immediately
2539 * render it. `observe.after` may be used to detect when Gmail has fetched the
2540 * relevant data. For instance, to refresh a conversation after Gmail fetches
2541 * its data:
2542 *
2543 * gmail.observe.after("refresh", function(url, body, data, xhr) {
2544 * if (url.view === "cv") {
2545 * gmail.tools.rerender();
2546 * }
2547 * });
2548 *
2549 * If a callback is passed, it will be invoked after re-rendering is complete.
2550 */
2551 api.tools.rerender = function(callback) {
2552 var url = window.location.href;
2553 var hash = window.location.hash;
2554
2555 // Get Gmail to re-render by navigating away and then back to the current URL. We keep the
2556 // UI from changing as we navigate away by visiting an equivalent URL: the current URL with the
2557 // first parameter of the hash stripped ("#inbox/14a16fab4adc1456" -> "#/14a16fab4adc1456" or
2558 // "#inbox" -> "#").
2559 var tempUrl;
2560 if (hash.indexOf("/") !== -1) {
2561 tempUrl = url.replace(/#.*?\//, "#/");
2562 } else {
2563 tempUrl = url.replace(/#.*/, "#");
2564 }
2565 window.location.replace(tempUrl);
2566
2567 // Return to the original URL after a 0-timeout to force Gmail to navigate to the temp URL.
2568 setTimeout(function() {
2569 window.location.replace(url);
2570
2571 // For some reason, the two replace operations above create a history entry (tested in
2572 // Chrome 39.0.2171.71). Pop it to hide our URL manipulation.
2573 window.history.back();
2574
2575 if (callback) callback();
2576 }, 0);
2577 };
2578
2579 api.tools.get_reply_to = function(ms13) {
2580 // reply to is an array if exists
2581 var reply_to = ms13 ? ms13[4] : [];
2582
2583 // if reply to set get email from it and return it
2584 if (reply_to.length !== 0) {
2585 return api.tools.extract_email_address(reply_to[0]);
2586 }
2587
2588 // otherwise return null
2589 return null;
2590 };
2591
2592 api.tools.parse_attachment_data = function(x) {
2593 if (!x[7] || ! x[7][0])
2594 {
2595 return null;
2596 }
2597
2598 var baseUrl = "";
2599 if (typeof(window) !== "undefined") {
2600 baseUrl = window.location.origin + window.location.pathname;
2601 }
2602
2603 var ad = x[7][0];
2604 api.tracker.attachment_data = ad;
2605
2606 var attachments = [];
2607 for (var i = 0; i < ad.length; i++)
2608 {
2609 var a = ad[i];
2610 attachments.push({
2611 attachment_id: a[0],
2612 name: a[1],
2613 type: a[2],
2614 size: a[3],
2615 url: baseUrl + a[9]
2616 });
2617 }
2618 return attachments;
2619 };
2620
2621 api.tools.parse_email_data = function(email_data) {
2622 var data = {};
2623
2624 for(var i in email_data) {
2625 var x = email_data[i];
2626 if(x[0] === "cs") {
2627 data.thread_id = x[1];
2628 data.first_email= x[8][0];
2629 data.last_email = x[2];
2630 data.total_emails = x[3];
2631 data.total_threads = x[8];
2632 data.people_involved = x[15];
2633 data.subject = x[23];
2634 }
2635
2636 if(x[0] === "ms") {
2637 if(data.threads === undefined) {
2638 data.threads = {};
2639 }
2640
2641 data.threads[x[1]] = {};
2642 data.threads[x[1]].is_deleted = (x[9] && x[9].indexOf("^k") > -1);
2643 data.threads[x[1]].reply_to_id = x[2];
2644 data.threads[x[1]].from = x[5];
2645 data.threads[x[1]].from_email = x[6];
2646 data.threads[x[1]].timestamp = x[7];
2647 data.threads[x[1]].datetime = x[24];
2648 data.threads[x[1]].attachments = x[21].split(",");
2649 data.threads[x[1]].attachments_details = x[13] ? api.tools.parse_attachment_data(x[13]) : null;
2650 data.threads[x[1]].subject = x[12];
2651 data.threads[x[1]].content_html = x[13] ? x[13][6] : x[8];
2652 data.threads[x[1]].to = x[13] ? x[13][1] : ((x[37] !== undefined) ? x[37][1]:[]);
2653 data.threads[x[1]].cc = x[13] ? x[13][2] : [];
2654 data.threads[x[1]].bcc = x[13] ? x[13][3] : [];
2655 data.threads[x[1]].reply_to = api.tools.get_reply_to(x[13]);
2656 data.threads[x[1]].labels = x[9];
2657
2658 try { // jQuery will sometime fail to parse x[13][6], if so, putting the raw HTML
2659 data.threads[x[1]].content_plain = x[13] ? $(x[13][6]).text() : x[8];
2660 }
2661 catch(e) {
2662 data.threads[x[1]].content_plain = x[13] ? x[13][6] : x[8];
2663 }
2664 }
2665 }
2666
2667 return data;
2668 };
2669
2670
2671 api.helper.get.email_data_pre = function(thread_id) {
2672 console.warn("Gmail.js: Usage of legacy email-data APIs have been deprecated by Google and will most likely fail. Migrate code to use gmail.new.get.email_data() to fix this problem.");
2673 if(api.check.is_inside_email() && thread_id === undefined) {
2674 thread_id = api.get.thread_id();
2675 }
2676
2677 var url = null;
2678 if(thread_id !== undefined) {
2679 url = window.location.origin + window.location.pathname + "?ui=2&ik=" + api.tracker.ik + "&rid=" + api.tracker.rid + "&view=cv&th=" + thread_id + "&msgs=&mb=0&rt=1&search=inbox";
2680 }
2681 return url;
2682 };
2683
2684
2685 api.helper.get.email_data_post = function(get_data) {
2686 if (!get_data) {
2687 return {};
2688 }
2689 var data = get_data.substring(get_data.indexOf("["), get_data.length);
2690 var json = JSON.parse(data);
2691
2692 api.tracker.email_data = json[0];
2693 return api.tools.parse_email_data(api.tracker.email_data);
2694 };
2695
2696
2697 api.get.email_data = function(thread_id) {
2698 var url = api.helper.get.email_data_pre(thread_id);
2699
2700 if (url !== null) {
2701 var get_data = api.tools.make_request(url);
2702 var email_data = api.helper.get.email_data_post(get_data);
2703 return email_data;
2704 }
2705
2706 return {};
2707 };
2708
2709
2710 api.get.email_data_async = function(email_id, callback) {
2711 var url = api.helper.get.email_data_pre(email_id);
2712 if (url !== null) {
2713 api.tools.make_request_async(url, "GET", function (get_data) {
2714 var email_data = api.helper.get.email_data_post(get_data);
2715 callback(email_data);
2716 });
2717 } else {
2718 callback({});
2719 }
2720 };
2721
2722
2723 api.helper.get.legacy_email_id = function(identifier) {
2724 if (!identifier) {
2725 return null;
2726 } else if (api.check.data.is_legacy_email_id(identifier)) {
2727 return identifier;
2728 } else if (identifier.legacy_email_id) {
2729 return identifier.legacy_email_id;
2730 } else if (api.check.data.is_email_id(identifier)) {
2731 console.warn("GmailJS: Warning! Using new-style ID in method expecting legacy-style IDs! Attempting to resolve via cache, but there's no guarantee this will work!");
2732 const emailData = api.cache.emailIdCache[identifier];
2733 return emailData && emailData.legacy_email_id;
2734 }
2735
2736 // DOMEmail
2737 if (identifier.$el && identifier.$el[0]) {
2738 identifier = identifier.$el[0]; // fallback to element-lookup.
2739 }
2740
2741 // HTML Element
2742 if (identifier.dataset && identifier.dataset.legacyMessageId) {
2743 return identifier.dataset.legacyMessageId;
2744 }
2745
2746 return null;
2747 };
2748
2749 api.helper.get.new_email_id = function(identifier) {
2750 if (!identifier) {
2751 return null;
2752 } else if (api.check.data.is_email_id(identifier)) {
2753 return identifier;
2754 } else if (identifier.id && !identifier.$el) { // ensure to only email_data, not DomEmail!
2755 return identifier.id;
2756 } else if (api.check.data.is_legacy_email_id(identifier)) {
2757 console.warn("GmailJS: Warning! Using legacy-style ID in method expecting new-style IDs! Attempting to resolve via cache, but there's no guarantee this will work!");
2758 const emailData = api.cache.emailLegacyIdCache[identifier];
2759 return emailData && emailData.id;
2760 }
2761
2762 // DOMEmail
2763 if (identifier.$el && identifier.$el[0]) {
2764 identifier = identifier.$el[0]; // fallback to element-lookup.
2765 }
2766
2767 // HTML Element
2768 if (identifier.dataset && identifier.dataset.messageId) {
2769 let id = identifier.dataset.messageId;
2770 if (id.indexOf("#") === 0) {
2771 id = id.substring(1);
2772 }
2773
2774 return id;
2775 }
2776
2777 return null;
2778 };
2779
2780 api.helper.get.thread_id = function(identifier) {
2781 if (!identifier) {
2782 return null;
2783 } else if (api.check.data.is_thread_id(identifier)) {
2784 return identifier;
2785 } else if (identifier.thread_id) { // NewEmailData
2786 return identifier.thread_id;
2787 } else if (api.check.data.is_email_id(identifier)) {
2788 console.warn("GmailJS: Warning! Using email-ID in method expecting thread-ID! Attempting to resolve via cache, but there's no guarantee this will work!");
2789 const emailData = api.cache.emailIdCache[identifier];
2790 return emailData && emailData.thread_id;
2791 } else if (api.check.data.is_legacy_email_id(identifier)) {
2792 console.warn("GmailJS: Warning! Using legacy-style ID in method expecting thread-ID! Attempting to resolve via cache, but there's no guarantee this will work!");
2793 const emailData = api.cache.emailLegacyIdCache[identifier];
2794 return emailData && emailData.thread_id;
2795 }
2796
2797 // DOMEmail or DOMThread
2798 if (identifier.$el && identifier.$el[0]) {
2799 identifier = identifier.$el[0]; // fallback to element-lookup.
2800 }
2801
2802 // HTML Element - Thread
2803 if (identifier.dataset && identifier.dataset.threadPermId) {
2804 let id = identifier.dataset.threadPermId;
2805 if (id.indexOf("#") === 0) {
2806 id = id.substring(1);
2807 }
2808
2809 return id;
2810 }
2811
2812 // HTML Element - Email
2813 if (identifier.dataset && identifier.dataset.messageId) {
2814 let id = identifier.dataset.messageId;
2815 if (id.indexOf("#") === 0) {
2816 id = id.substring(1);
2817 }
2818
2819 console.warn("GmailJS: Warning! Using DomEmail instance to lookup thread-ID. Attempting to resolve via cache, but there's no guarantee this will work!");
2820 const emailData = api.cache.emailIdCache[id];
2821 return emailData && emailData.thread_id;
2822 }
2823
2824 return null;
2825 };
2826
2827 api.helper.clean_thread_id = function(thread_id) {
2828 // handle new gmail style email-ids
2829 if (thread_id.startsWith("#")) {
2830 thread_id = thread_id.substring(1);
2831 }
2832
2833 return thread_id;
2834 };
2835
2836 api.helper.get.email_source_pre = function(identifier) {
2837 if(!identifier && api.check.is_inside_email()) {
2838 identifier = api.get.email_id();
2839 }
2840
2841 const email_id = api.helper.get.legacy_email_id(identifier);
2842 if(!email_id) {
2843 return null;
2844 } else {
2845 return window.location.origin + window.location.pathname + "?view=att&th=" + email_id + "&attid=0&disp=comp&safe=1&zw";
2846 }
2847 };
2848
2849
2850 api.get.email_source = function(identifier) {
2851 console.warn("Gmail.js: This function has been deprecated and will be removed in an upcoming release! Please migrate to email_source_async!");
2852 var url = api.helper.get.email_source_pre(identifier);
2853 if (url !== null) {
2854 return api.tools.make_request(url, "GET", true);
2855 }
2856 return "";
2857 };
2858
2859
2860 api.get.email_source_async = function(identifier, callback, error_callback, preferBinary) {
2861 api.get.email_source_promise(identifier, preferBinary)
2862 .then(callback)
2863 .catch(error_callback);
2864 };
2865
2866 api.get.email_source_promise = function(identifier, preferBinary) {
2867 const url = api.helper.get.email_source_pre(identifier);
2868 if (url !== null) {
2869 return api.tools.make_request_download_promise(url, preferBinary);
2870 } else {
2871 return new Promise((resolve, reject) => {
2872 reject("Unable to resolve URL for email source!");
2873 });
2874 }
2875 };
2876
2877 api.get.displayed_email_data = function() {
2878 var email_data = api.get.email_data();
2879
2880 if (api.check.is_conversation_view()) {
2881 return get_displayed_email_data_for_thread(email_data);
2882 }
2883 else { // Supposing only one displayed email.
2884 return get_displayed_email_data_for_single_email(email_data);
2885 }
2886 };
2887
2888 api.get.displayed_email_data_async = function (callback) {
2889 api.get.email_data_async(undefined, function (email_data) {
2890 if (api.check.is_conversation_view()) {
2891 callback(get_displayed_email_data_for_thread(email_data));
2892 }
2893 else { // Supposing only one displayed email.
2894 callback(get_displayed_email_data_for_single_email(email_data));
2895 }
2896 });
2897 };
2898
2899 var get_displayed_email_data_for_thread = function(email_data) {
2900 var displayed_email_data = email_data;
2901
2902 var threads = displayed_email_data.threads;
2903 var total_threads = displayed_email_data.total_threads;
2904
2905 var hash = window.location.hash.split("#")[1] || "";
2906 var is_in_trash = (hash.indexOf("trash") === 0);
2907
2908 for (var id in threads) {
2909 var email = threads[id];
2910 var keep_email = (is_in_trash) ? email.is_deleted : !email.is_deleted;
2911
2912 if (!keep_email) {
2913 delete threads[id];
2914 total_threads.splice(total_threads.indexOf(id), 1);
2915 displayed_email_data.total_emails--;
2916 // TODO: remove people involved only in this email.
2917 }
2918 }
2919 return displayed_email_data;
2920 };
2921
2922 var get_displayed_email_data_for_single_email = function(email_data) {
2923 var displayed_email_data = {};
2924 for (var id in email_data.threads) {
2925 var displayed_email_element = document.querySelector("div[data-legacy-message-id='" + id + "']");
2926
2927 if (displayed_email_element) {
2928 var email = email_data.threads[id];
2929
2930 displayed_email_data.first_email = id;
2931 displayed_email_data.last_email = id;
2932 displayed_email_data.subject = email_data.subject;
2933
2934 displayed_email_data.threads = {};
2935 displayed_email_data.threads[id] = email;
2936 displayed_email_data.total_emails = 1;
2937 displayed_email_data.total_threads = [id];
2938
2939 displayed_email_data.people_involved = [];
2940
2941 displayed_email_data.people_involved.push(
2942 [email.from, email.from_email]
2943 );
2944
2945 email.to.forEach(function(recipient) {
2946 var address = api.tools.extract_email_address(recipient);
2947 var name = api.tools.extract_name(recipient.replace(address, "")) || "";
2948
2949 displayed_email_data.people_involved.push(
2950 [name, address]
2951 );
2952 });
2953
2954 break;
2955 }
2956 }
2957 return displayed_email_data;
2958 };
2959
2960
2961 api.check.is_conversation_view = function() {
2962 if( api.check.is_new_data_layer() ) {
2963 var conversation_flag = undefined;
2964 conversation_flag = api.tracker.globals[24].indexOf(7164);
2965 return conversation_flag !== -1;
2966 } else { //To handle classic gmail UI
2967 var flag_name = "bx_vmb";
2968 var flag_value = undefined;
2969 var array_with_flag = api.tracker.globals[17][4][1];
2970 for (var i = 0; i < array_with_flag.length; i++) {
2971 var current = array_with_flag[i];
2972 if (current[0] === flag_name) {
2973 flag_value = current[1];
2974 break;
2975 }
2976 }
2977 return flag_value === "0" || flag_value === undefined;
2978 }
2979 };
2980
2981 api.tools.extract_email_address = function(str) {
2982 var regex = /[\+a-z0-9._-]+@[a-z0-9._-]+\.[a-z0-9._-]+/gi;
2983 var matches = (str) ? str.match(regex) : undefined;
2984
2985 return (matches) ? matches[0] : undefined;
2986 };
2987
2988
2989 api.tools.extract_name = function(str) {
2990 var regex = /[a-z\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF"._\s-]+/gi;
2991 var matches = (str) ? str.match(regex) : undefined;
2992
2993 return (matches && matches[0]) ? matches[0].trim() : undefined;
2994 };
2995
2996
2997 api.tools.i18n = function(label) {
2998 var locale = api.get.localization();
2999 var dictionary;
3000
3001 switch (locale) {
3002 case "fr":
3003 dictionary = {
3004 "inbox": "Boîte de réception",
3005 "drafts": "Brouillons",
3006 "spam": "Spam",
3007 "forums": "Forums",
3008 "updates": "Mises à jour",
3009 "promotions": "Promotions",
3010 "social_updates": "Réseaux sociaux"
3011 };
3012 break;
3013
3014 case "no":
3015 dictionary = {
3016 "inbox": "Innboks",
3017 "drafts": "Utkast",
3018 "spam": "Søppelpost",
3019 "forums": "Forumer",
3020 "updates": "Oppdateringer",
3021 "promotions": "Reklame",
3022 "social_updates": "Sosialt"
3023 };
3024 break;
3025
3026 case "nl":
3027 dictionary = {
3028 "inbox": "Postvak IN",
3029 "drafts": "Concepten",
3030 "spam": "Spam",
3031 "forums": "Forums",
3032 "updates": "Updates",
3033 "promotions": "Reclame",
3034 "social_updates": "Sociaal"
3035 };
3036 break;
3037
3038 case "it":
3039 dictionary = {
3040 "inbox": "Posta in arrivo",
3041 "drafts": "Bozza",
3042 "spam": "Spam",
3043 "forums": "Forum",
3044 "updates": "Aggiornamenti",
3045 "promotions": "Promozioni",
3046 "social_updates": "Social"
3047 };
3048 break;
3049
3050 case "en":
3051 default:
3052 dictionary = {
3053 "inbox": "Inbox",
3054 "drafts": "Drafts",
3055 "spam": "Spam",
3056 "forums": "Forums",
3057 "updates": "Updates",
3058 "promotions": "Promotions",
3059 "social_updates": "Social Updates"
3060 };
3061 break;
3062 }
3063
3064 return dictionary[label];
3065 };
3066
3067 var create_generic_toolbar_button = function(content_html, onClickFunction, basicStyle, defaultStyle, styleClass, selector) {
3068 var container = $(document.createElement("div"));
3069 container.attr("class","G-Ni J-J5-Ji");
3070
3071 var button = $(document.createElement("div"));
3072 var buttonClasses = "T-I J-J5-Ji ";
3073 if(styleClass !== undefined &&
3074 styleClass !== null &&
3075 styleClass !== ""){
3076 buttonClasses += basicStyle+styleClass;
3077 }else{
3078 buttonClasses += basicStyle+defaultStyle;
3079 }
3080 button.attr("class", buttonClasses);
3081
3082 button.html(content_html);
3083 button.click(onClickFunction);
3084
3085 var content = $(document.createElement("div"));
3086 content.attr("class","asa");
3087
3088 container.html(button);
3089
3090 selector.append(container);
3091
3092 return container;
3093 };
3094
3095 api.tools.add_toolbar_button = function(content_html, onClickFunction, styleClass) {
3096 var basicLeftStyle = "lS ";
3097 var defaultLeftStyle = "T-I-ax7 ar7";
3098
3099 return create_generic_toolbar_button(content_html, onClickFunction, basicLeftStyle, defaultLeftStyle, styleClass, api.dom.toolbar());
3100 };
3101
3102 api.tools.add_right_toolbar_button = function(content_html, onClickFunction, styleClass) {
3103 var basicRightStyle = "ash ";
3104 var defaultRightStyle = "T-I-ax7 L3";
3105
3106 return create_generic_toolbar_button(content_html, onClickFunction, basicRightStyle, defaultRightStyle, styleClass, api.dom.right_toolbar());
3107 };
3108
3109 api.tools.add_compose_button = function(composeWindow, content_html, onClickFunction, styleClass) {
3110 var button = $(document.createElement("div"));
3111 var buttonClasses = "T-I J-J5-Ji aoO T-I-atl L3 gmailjscomposebutton ";
3112 if(styleClass !== undefined){
3113 buttonClasses += styleClass;
3114 }
3115 button.attr("class", buttonClasses);
3116 button.attr("style", "margin-left: 8px; max-width: 500px;");
3117 button.html(content_html);
3118 button.click(onClickFunction);
3119
3120 composeWindow.find(".gU.Up > .J-J5-Ji").append(button);
3121
3122 return button;
3123 };
3124
3125 /**
3126 adds a button to an email attachment.
3127
3128 'attachment'-parameter must be the object returned from api.dom.email().attachments().
3129 'contentHtml' should represent a 21x21 image of some kind. optional.
3130 'customCssClass' styling used on the buttons central area. optional.
3131 'tooltip' will be shown on hover.
3132
3133 return-value is jQuery-instance representing the created button.
3134 */
3135 api.tools.add_attachment_button = function(attachment, contentHtml, customCssClass, tooltip, onClickFunction) {
3136 var button = $(document.createElement("div"));
3137 button.attr("class", "T-I J-J5-Ji aQv T-I-ax7 L3");
3138 button.attr("style", "user-select: none;");
3139 button.attr("aria-label", tooltip);
3140 button.attr("data-tooltip", tooltip);
3141
3142 // make hover-state match existing buttons
3143 var hoverClass = "T-I-JW";
3144 button.mouseover(function() { this.classList.add(hoverClass); });
3145 button.mouseout(function() { this.classList.remove(hoverClass); });
3146
3147 var div = $(document.createElement("div"));
3148 var divClass = "wtScjd J-J5-Ji aYr";
3149 if (customCssClass) {
3150 divClass += " " + customCssClass;
3151 }
3152 div.attr("class", divClass);
3153 if (contentHtml) {
3154 div.html(contentHtml);
3155 }
3156
3157 button.append(div);
3158 button.click(onClickFunction);
3159 attachment.$el.find("div.aQw").append(button);
3160
3161 return button;
3162 };
3163
3164 api.tools.remove_modal_window = function() {
3165 $("#gmailJsModalBackground").remove();
3166 $("#gmailJsModalWindow").remove();
3167 };
3168
3169 api.tools.add_modal_window = function(title, content_html, onClickOk, onClickCancel, onClickClose, okText, cancelText) {
3170 // By default, clicking on cancel or close should clean up the modal window
3171 onClickClose = onClickClose || api.tools.remove_modal_window;
3172 onClickCancel = onClickCancel || api.tools.remove_modal_window;
3173
3174 okText = okText || "OK";
3175 cancelText = cancelText || "Cancel";
3176
3177 var background = $(document.createElement("div"));
3178 background.attr("id","gmailJsModalBackground");
3179 background.attr("class","Kj-JD-Jh");
3180 background.attr("aria-hidden","true");
3181 background.attr("style","opacity:0.75;width:100%;height:100%;");
3182
3183 // Modal window wrapper
3184 var container = $(document.createElement("div"));
3185 container.attr("id","gmailJsModalWindow");
3186 container.attr("class", "Kj-JD");
3187 container.attr("tabindex", "0");
3188 container.attr("role", "alertdialog");
3189 container.attr("aria-labelledby", "gmailJsModalWindowTitle");
3190 container.attr("style", "left:50%;top:50%;opacity:1;");
3191
3192 // Modal window header contents
3193 var header = $(document.createElement("div"));
3194 header.attr("class", "Kj-JD-K7 Kj-JD-K7-GIHV4");
3195
3196 var heading = $(document.createElement("span"));
3197 heading.attr("id", "gmailJsModalWindowTitle");
3198 heading.attr("class", "Kj-JD-K7-K0");
3199 heading.attr("role", "heading");
3200 heading.html(title);
3201
3202 var closeButton = $(document.createElement("span"));
3203 closeButton.attr("id", "gmailJsModalWindowClose");
3204 closeButton.attr("class", "Kj-JD-K7-Jq");
3205 closeButton.attr("role", "button");
3206 closeButton.attr("tabindex", "0");
3207 closeButton.attr("aria-label", "Close");
3208 closeButton.click(onClickClose);
3209
3210 header.append(heading);
3211 header.append(closeButton);
3212
3213 // Modal window contents
3214 var contents = $(document.createElement("div"));
3215 contents.attr("id", "gmailJsModalWindowContent");
3216 contents.attr("class", "Kj-JD-Jz");
3217 contents.html(content_html);
3218
3219 // Modal window controls
3220 var controls = $(document.createElement("div"));
3221 controls.attr("class", "Kj-JD-Jl");
3222
3223 var okButton = $(document.createElement("button"));
3224 okButton.attr("id", "gmailJsModalWindowOk");
3225 okButton.attr("class", "J-at1-auR J-at1-atl");
3226 okButton.attr("name", "ok");
3227 okButton.text(okText);
3228 okButton.click(onClickOk);
3229
3230 var cancelButton = $(document.createElement("button"));
3231 cancelButton.attr("id", "gmailJsModalWindowCancel");
3232 cancelButton.attr("name", "cancel");
3233 cancelButton.text(cancelText);
3234 cancelButton.click(onClickCancel);
3235
3236 controls.append(okButton);
3237 controls.append(cancelButton);
3238
3239 container.append(header);
3240 container.append(contents);
3241 container.append(controls);
3242
3243 $(document.body).append(background);
3244 $(document.body).append(container);
3245
3246 var center = function() {
3247 container.css({
3248 top: ($(window).height() - container.outerHeight()) / 2,
3249 left: ($(window).width() - container.outerWidth()) / 2
3250 });
3251 };
3252
3253 center();
3254
3255 container.on("DOMSubtreeModified", center);
3256 $(window).resize(center);
3257 };
3258
3259 api.tools.toggle_minimize = function (){
3260 //The minimize button
3261 var minimizeButton = $("[alt='Minimize']")[0];
3262
3263 if(minimizeButton) {
3264 api.helper.trigger_mouse_click(minimizeButton);
3265
3266 return true;
3267 }
3268 return false;
3269 };
3270
3271 api.chat.is_hangouts = function() {
3272 if(api.tracker.hangouts !== undefined) {
3273 return api.tracker.hangouts;
3274 }
3275
3276 // Returns true if the user is using hangouts instead of the classic chat
3277 var dwClasses = $(".dw");
3278 if(dwClasses.length > 1) {
3279 throw "Figuring out is hangouts - more than one dw classes found";
3280 }
3281 if(dwClasses.length === 0) {
3282 throw "Figuring out is hangouts - no dw classes found";
3283 }
3284
3285 var dw = dwClasses[0];
3286
3287 var chatWindows = $(".nH.aJl.nn", dw);
3288 if(chatWindows.length > 0) {
3289 // hangouts
3290 api.tracker.hangouts = true;
3291 return true;
3292 }
3293
3294 chatWindows = $(".nH.nn", dw);
3295
3296 if(chatWindows.length > 2) {
3297 // classic
3298 api.tracker.hangouts = false;
3299 return false;
3300 }
3301 return undefined;
3302 };
3303
3304 /**
3305 * Returns data about the currently visible messages available in the DOM:
3306 * {
3307 * from: {
3308 * name: string,
3309 * email: string,
3310 * },
3311 * summary: string, // subject and email summary
3312 * thread_id: string,
3313 * legacy_email_id: string,
3314 * $el: HTMLElement,
3315 * }
3316 */
3317 api.dom.visible_messages = function() {
3318 const ret = [];
3319 $('tbody>tr.zA[draggable="true"]:visible', api.dom.inbox_content())
3320 .each((index, msgEle) => {
3321 const nameAndEmail = $('*[email][name]', msgEle);
3322 const linkAndSubject = $('*[role=link]', msgEle);
3323 // example value: #thread-f:1638756560099919527|msg-f:1638756560099919527"
3324 const idNode = msgEle.querySelector("span[data-thread-id]");
3325 ret.push({
3326 from: {
3327 name: nameAndEmail.attr('name'),
3328 email: nameAndEmail.attr('email'),
3329 },
3330 summary: linkAndSubject[0].innerText,
3331 thread_id: api.helper.clean_thread_id(idNode.dataset.threadId || ""),
3332 legacy_email_id: idNode.dataset.legacyMessageId,
3333 $el: $(msgEle),
3334 });
3335 });
3336 return ret;
3337 };
3338
3339 // retrieve queue of compose window dom objects
3340 // latest compose at the start of the queue (index 0)
3341 api.dom.composes = function() {
3342 var objs = [];
3343 $("div.M9").each(function(idx, el) {
3344 objs.push( new api.dom.compose(el));
3345 });
3346 return objs;
3347 };
3348
3349 /**
3350 A compose object. Represents a compose window in the DOM and provides a bunch of methods and properties to access & interact with the window
3351 Expects a jQuery DOM element for the compose div
3352 */
3353 api.dom.compose = function(element) {
3354 if (this.constructor !== api.dom.compose) {
3355 // if not invoked through new(), nothing works as expected!
3356 return new api.dom.compose(element);
3357 }
3358
3359 element = $(element);
3360 if(!element || (!element.hasClass("M9") && !element.hasClass("AD"))) api.tools.error("api.dom.compose called with invalid element");
3361 this.$el = element;
3362 return this;
3363 };
3364
3365 extend(api.dom.compose.prototype, {
3366 /**
3367 Retrieve the compose id
3368 */
3369 id: function() {
3370 return this.dom("id").val();
3371 },
3372
3373 /**
3374 Retrieve the draft email id
3375 */
3376 email_id: function() {
3377 let email_id = this.dom("draft").val();
3378 // handle new gmail style email-ids
3379 if (email_id && email_id.startsWith("#")) {
3380 return email_id.substring(1);
3381 } else {
3382 return email_id;
3383 }
3384 },
3385
3386 /**
3387 Retrieve the draft thread id
3388 */
3389 thread_id: function() {
3390 let thread_id = this.dom("thread").val() || "";
3391
3392 return api.helper.clean_thread_id(thread_id);
3393 },
3394
3395 /**
3396 Is this compose instance inline (as with reply & forwards) or a popup (as with a new compose)
3397 */
3398 is_inline: function() {
3399 return this.$el.closest("td.Bu").length > 0;
3400 },
3401
3402 /**
3403 Retrieves to, cc, bcc and returns them in a hash of arrays
3404 Parameters:
3405 options.type string to, cc, or bcc to check a specific one
3406 options.flat boolean if true will just return an array of all recipients instead of splitting out into to, cc, and bcc
3407 */
3408 recipients: function(options) {
3409 if( typeof options !== "object" ) options = {};
3410 var name_selector = options.type ? "[name=" + options.type + "]" : "";
3411
3412 // determine an array of all emails specified for To, CC and BCC and extract addresses into an object for the callback
3413 var recipients = options.flat ? [] : { to: [], cc: [], bcc: [] };
3414 this.$el.find(".GS input[type=hidden]"+name_selector).each(function(idx, recipient ){
3415 if(options.flat) {
3416 recipients.push(recipient.value);
3417 } else {
3418 if(!recipients[recipient.name]) recipients[recipient.name] = [];
3419 recipients[recipient.name].push(recipient.value);
3420 }
3421 });
3422 return recipients;
3423 },
3424
3425 /**
3426 Retrieve the current "to" recipients
3427 */
3428 to: function(to) {
3429 return this.dom("to").val(to);
3430 },
3431
3432 /**
3433 Retrieve the current "cc" recipients
3434 */
3435 cc: function(cc) {
3436 return this.dom("cc").val(cc);
3437 },
3438
3439 /**
3440 Retrieve the current "bcc" recipients
3441 */
3442 bcc: function(bcc) {
3443 return this.dom("bcc").val(bcc);
3444 },
3445
3446 /**
3447 Get/Set the current subject
3448 Parameters:
3449 subject string set as new subject
3450 */
3451 subject: function(subject) {
3452 if(subject) this.dom("all_subjects").val(subject);
3453 subject = this.dom("subjectbox").val();
3454 return subject ? subject : this.dom("subject").val();
3455 },
3456
3457 /**
3458 Get the from email
3459 if user only has one email account they can send from, returns that email address
3460 */
3461 from: function() {
3462 var el = this.dom("from");
3463 if (el.length) {
3464 var fromNameAndEmail = el.val();
3465 if (fromNameAndEmail) {
3466 return api.tools.extract_email_address(fromNameAndEmail);
3467 }
3468 }
3469 return api.get.user_email();
3470 },
3471
3472 /**
3473 Get/Set the email body
3474 */
3475 body: function(body) {
3476 var el = this.dom("body");
3477 if(body) el.html(body);
3478 return el.html();
3479 },
3480
3481 /**
3482 Triggers the same action as clicking the "send" button would do.
3483 */
3484 send: function() {
3485 return this.dom("send_button").click();
3486 },
3487
3488 /**
3489 Map find through to jquery element
3490 */
3491 find: function(selector) {
3492 return this.$el.find(selector);
3493 },
3494
3495 /**
3496 Close compose window
3497 */
3498 close: function() {
3499 const e = document.createEvent('Events');
3500 e.initEvent('keydown', true, true);
3501 e.which = 27;
3502 e.keyCode = 27;
3503
3504 var $body = this.dom('body');
3505 $body.focus();
3506 $body[0].dispatchEvent(e);
3507 },
3508
3509 /**
3510 Retrieve preconfigured dom elements for this compose window
3511 */
3512 dom: function(lookup) {
3513 if (!lookup) return this.$el;
3514 var config = {
3515 to:"textarea[name=to]",
3516 cc:"textarea[name=cc]",
3517 bcc:"textarea[name=bcc]",
3518 id: "input[name=composeid]",
3519 draft: "input[name=draft]",
3520 thread: "input[name=rt]",
3521 subject: "input[name=subject]",
3522 subjectbox: "input[name=subjectbox]",
3523 all_subjects: "input[name=subjectbox], input[name=subject]",
3524 body: "div[contenteditable=true]",
3525 reply: "M9",
3526 forward: "M9",
3527 from: "input[name=from]",
3528 send_button: "div.T-I.T-I-atl:not(.gmailjscomposebutton)"
3529 };
3530 if(!config[lookup]) api.tools.error("Dom lookup failed. Unable to find config for \"" + lookup + "\"",config,lookup,config[lookup]);
3531 return this.$el.find(config[lookup]);
3532 }
3533
3534 });
3535
3536 /**
3537 An object for interacting with an email currently present in the DOM. Represents an individual email message within a thread
3538 Provides a number of methods and properties to access & interact with it
3539 Expects a jQuery DOM element for the email div (div.adn as returned by the "view_email" observer), or an email_id
3540 */
3541 api.dom.email = function(element) {
3542 if (this.constructor !== api.dom.email) {
3543 // if not invoked through new(), nothing works as expected!
3544 return new api.dom.email(element);
3545 }
3546
3547 if (typeof element === "string") {
3548 this.id = element;
3549 element = $("div.adn[data-legacy-message-id='" + this.id + "']");
3550 } else {
3551 element = $(element);
3552 }
3553
3554 if (!element || (!element.hasClass("adn"))) api.tools.error("api.dom.email called with invalid element/id");
3555
3556 this.$el = element;
3557 if (!this.id) {
3558 this.id = this.$el.data("legacyMessageId");
3559 }
3560
3561 return this;
3562 };
3563
3564 extend(api.dom.email.prototype, {
3565
3566 /**
3567 Get/Set the full email body as it sits in the DOM
3568 If you want the actual DOM element use .dom("body");
3569 Note: This gets & sets the body html after it has been parsed & marked up by GMAIL. To retrieve it as it exists in the email message source, use a call to .data();
3570 */
3571 body: function(body) {
3572 var el = this.dom("body");
3573 if (body) {
3574 el.html(body);
3575 }
3576 return el.html();
3577 },
3578
3579 /**
3580 Get/Set the sender
3581 Optionally receives email and name properties. If received updates the values in the DOM
3582 Returns an object containing email & name of the sender and dom element
3583 */
3584 from: function(email, name) {
3585 var el = this.dom("from");
3586 if (email) {
3587 el.attr("email",email);
3588 }
3589 if (name) {
3590 el.attr("name",name);
3591 el.html(name);
3592 }
3593 return {
3594 email: el.attr("email"),
3595 name: el.attr("name"),
3596 el: el
3597 };
3598 },
3599
3600 /**
3601 Get/Set who the email is showing as To
3602 Optionally receives an object containing email and/or name properties. If received updates the values in the DOM.
3603 Optionally receives an array of these objects if multiple recipients
3604 Returns an array of objects containing email & name of who is showing in the DOM as the email is to
3605 */
3606 to: function(to_array) {
3607
3608 // if update data has been passeed, loop through & create a new to_wrapper contents
3609 if (to_array) {
3610 if (!$.isArray(to_array)) {
3611 to_array = [to_array];
3612 }
3613 var html = [];
3614 $.each(to_array, function(index, obj) {
3615 html.push( $("<span />").attr({
3616 dir: "ltr",
3617 email: obj.email,
3618 name: obj.name
3619 }).addClass("g2").html(obj.name).wrap("<p/>").parent().html());
3620 });
3621 this.dom("to_wrapper").html("to " + html.join(", "));
3622 }
3623
3624
3625 // loop through any matching to elements & prepare for output
3626 var out = [];
3627
3628 this.dom("to").each(function() {
3629 var el = $(this);
3630 out.push({
3631 email: el.attr("email"),
3632 name: el.attr("name"),
3633 el: el
3634 });
3635 });
3636 return out;
3637 },
3638
3639 /**
3640 Retries the DOM elements which represents the emails attachments.
3641 Returns undefined if UI-elements are not yet ready for parsing.
3642 */
3643 attachments: function() {
3644 var out = [];
3645 var failed = false;
3646
3647 this.dom("attachments").each(function() {
3648 var el = $(this);
3649
3650 var result = {};
3651 result.$el = el;
3652 result.name = el.find(".aV3").html();
3653 result.size = el.find(".SaH2Ve").html();
3654
3655 // Gmail only emits the following attribute for Chrome!
3656 // use email_data.threads[].attachments_details in other browsers!
3657 var url = el.attr("download_url");
3658 if (url) {
3659 var url_type = api.tools.parse_attachment_url(url);
3660 result.url = url_type.url;
3661 result.type = url_type.type;
3662 }
3663
3664 out.push(result);
3665 });
3666
3667 if (failed) {
3668 return undefined;
3669 } else {
3670 return out;
3671 }
3672 },
3673
3674 /**
3675 Retrieve relevant email from the Gmail servers for this email
3676 Makes use of the gmail.get.email_data() method
3677 Returns an object
3678 */
3679 data: function() {
3680 if (typeof api.dom.email_cache !== "object") {
3681 api.dom.email_cache = {};
3682 }
3683 if (!api.dom.email_cache[this.id]) {
3684
3685 // retrieve & cache the data for this whole thread of emails
3686 var data = api.get.email_data(this.id);
3687 $.each(data.threads, function(email_id, email_data) {
3688 api.dom.email_cache[email_id] = email_data;
3689 });
3690 }
3691 return api.dom.email_cache[this.id];
3692 },
3693
3694 /**
3695 Retrieve email source for this email from the Gmail servers
3696 Makes use of the gmail.get.email_source() method
3697 Returns string of email raw source
3698 */
3699 source: function() {
3700 return api.get.email_source(this.id);
3701 },
3702
3703 /**
3704 Retrieve preconfigured dom elements for this email
3705 */
3706 dom: function(lookup) {
3707 if (!lookup) return this.$el;
3708 var config = {
3709 body: "div.a3s",
3710 from: "span[email].gD",
3711 to: "span[email].g2",
3712 to_wrapper: "span.hb",
3713 timestamp: "span.g3",
3714 star: "div.zd",
3715 attachments: "div.hq.gt div.aQH span.aZo",
3716
3717 // buttons
3718 reply_button: "div[role=button].aaq",
3719 menu_button: "div[role=button].aap",
3720 details_button: "div[role=button].ajz"
3721 };
3722 if(!config[lookup]) api.tools.error("Dom lookup failed. Unable to find config for \"" + lookup + "\"");
3723 return this.$el.find(config[lookup]);
3724 }
3725
3726 });
3727
3728 /**
3729 An object for interacting with an email currently present in the DOM. Represents a conversation thread
3730 Provides a number of methods and properties to access & interact with it
3731 Expects a jQuery DOM element for the thread wrapper div (div.if as returned by the "view_thread" observer)
3732 */
3733 api.dom.thread = function(element) {
3734 if (this.constructor !== api.dom.thread) {
3735 // if not invoked through new(), nothing works as expected!
3736 return new api.dom.thread(element);
3737 }
3738
3739 if (!element || (!element.hasClass("if"))) api.tools.error("api.dom.thread called with invalid element/id");
3740 this.$el = element;
3741 return this;
3742 };
3743
3744 extend(api.dom.thread.prototype, {
3745
3746 /**
3747 Retrieve preconfigured dom elements for this email
3748 */
3749 dom: function(lookup) {
3750 if (!lookup) return this.$el;
3751 var config = {
3752 opened_email: "div.adn",
3753 subject: "h2.hP",
3754 labels: "div.hN"
3755 };
3756 if(!config[lookup]) api.tools.error("Dom lookup failed. Unable to find config for \"" + lookup + "\"");
3757 return this.$el.find(config[lookup]);
3758 }
3759
3760 });
3761
3762 /**
3763 * Show a compose window
3764 * @returns {boolean}
3765 */
3766 api.compose.start_compose = function() {
3767
3768 //The compose button
3769 var composeEl = $(".T-I.J-J5-Ji.T-I-KE.L3")[0];
3770
3771 if(composeEl) {
3772 api.helper.trigger_mouse_click(composeEl);
3773
3774 return true;
3775 }
3776 return false;
3777 };
3778
3779 /**
3780 * Shadow API commands specifically made to interact with old gmail.
3781 * (And in the future we can either remove "regular" api.get or replace it with something else)
3782 */
3783
3784 api.old = {};
3785 api.old.get = api.get;
3786
3787
3788 /**
3789 * API commands specifically made to interact with new gmail.
3790 */
3791 api.new = {};
3792 api.new.get = {};
3793
3794 /**
3795 * Returns the new-style email_id of the latest email visible in the DOM,
3796 * or for the provided email-node if provided.
3797 *
3798 * @param emailElem: Node to extract email-id from. Optional.
3799 */
3800 api.new.get.email_id = function(emailElem) {
3801 // ensure we have an email-element to work with
3802 if (!emailElem) {
3803 const emailElems = document.querySelectorAll(".adn[data-message-id]");
3804 if (!emailElems || emailElems.length === 0) {
3805 return null;
3806 }
3807 emailElem = emailElems[emailElems.length - 1];
3808 }
3809
3810 return api.helper.get.new_email_id(emailElem);
3811 };
3812
3813 /**
3814 * Returns the new-style thread_id of the current thread visible in the DOM.
3815 */
3816 api.new.get.thread_id = function() {
3817 const threadElem = document.querySelector("[data-thread-perm-id]");
3818 if (!threadElem) {
3819 return null;
3820 }
3821
3822 return threadElem.dataset["threadPermId"];
3823 };
3824
3825 /**
3826 * Returns available information about a specific email.
3827 *
3828 * @param email_id: new style email id. Legacy IDs not supported. If empty, default to latest in view.
3829 */
3830 api.new.get.email_data = function(identifier) {
3831 identifier = identifier || api.new.get.email_id();
3832 const email_id = api.helper.get.new_email_id(identifier);
3833
3834 if (!email_id) {
3835 return null;
3836 } else {
3837 return api.cache.emailIdCache[email_id];
3838 }
3839 };
3840
3841 /**
3842 * Returns available information about a specific thread.
3843 *
3844 * @param thread_id: new style thread id. Legacy IDs not supported. If empty, default to current.
3845 */
3846 api.new.get.thread_data = function(identifier) {
3847 identifier = identifier || api.new.get.thread_id();
3848 const thread_id = api.helper.get.thread_id(identifier);
3849
3850 if (!thread_id) {
3851 return null;
3852 } else {
3853 return api.cache.threadCache[thread_id];
3854 }
3855 };
3856
3857 // setup XHR interception as early as possible, to ensure we get all relevant email-data!
3858 if (typeof(document) !== "undefined") {
3859 api.tools.xhr_watcher();
3860 }
3861 return api;
3862};
3863
3864// make class accessible to require()-users.
3865if (typeof(exports) !== "undefined") {
3866 exports.Gmail = Gmail;
3867}
3868
3869},{"jquery":2}],2:[function(require,module,exports){
3870/*!
3871 * jQuery JavaScript Library v3.4.1
3872 * https://jquery.com/
3873 *
3874 * Includes Sizzle.js
3875 * https://sizzlejs.com/
3876 *
3877 * Copyright JS Foundation and other contributors
3878 * Released under the MIT license
3879 * https://jquery.org/license
3880 *
3881 * Date: 2019-05-01T21:04Z
3882 */
3883( function( global, factory ) {
3884
3885 "use strict";
3886
3887 if ( typeof module === "object" && typeof module.exports === "object" ) {
3888
3889 // For CommonJS and CommonJS-like environments where a proper `window`
3890 // is present, execute the factory and get jQuery.
3891 // For environments that do not have a `window` with a `document`
3892 // (such as Node.js), expose a factory as module.exports.
3893 // This accentuates the need for the creation of a real `window`.
3894 // e.g. var jQuery = require("jquery")(window);
3895 // See ticket #14549 for more info.
3896 module.exports = global.document ?
3897 factory( global, true ) :
3898 function( w ) {
3899 if ( !w.document ) {
3900 throw new Error( "jQuery requires a window with a document" );
3901 }
3902 return factory( w );
3903 };
3904 } else {
3905 factory( global );
3906 }
3907
3908// Pass this if window is not defined yet
3909} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
3910
3911// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
3912// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
3913// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
3914// enough that all such attempts are guarded in a try block.
3915"use strict";
3916
3917var arr = [];
3918
3919var document = window.document;
3920
3921var getProto = Object.getPrototypeOf;
3922
3923var slice = arr.slice;
3924
3925var concat = arr.concat;
3926
3927var push = arr.push;
3928
3929var indexOf = arr.indexOf;
3930
3931var class2type = {};
3932
3933var toString = class2type.toString;
3934
3935var hasOwn = class2type.hasOwnProperty;
3936
3937var fnToString = hasOwn.toString;
3938
3939var ObjectFunctionString = fnToString.call( Object );
3940
3941var support = {};
3942
3943var isFunction = function isFunction( obj ) {
3944
3945 // Support: Chrome <=57, Firefox <=52
3946 // In some browsers, typeof returns "function" for HTML <object> elements
3947 // (i.e., `typeof document.createElement( "object" ) === "function"`).
3948 // We don't want to classify *any* DOM node as a function.
3949 return typeof obj === "function" && typeof obj.nodeType !== "number";
3950 };
3951
3952
3953var isWindow = function isWindow( obj ) {
3954 return obj != null && obj === obj.window;
3955 };
3956
3957
3958
3959
3960 var preservedScriptAttributes = {
3961 type: true,
3962 src: true,
3963 nonce: true,
3964 noModule: true
3965 };
3966
3967 function DOMEval( code, node, doc ) {
3968 doc = doc || document;
3969
3970 var i, val,
3971 script = doc.createElement( "script" );
3972
3973 script.text = code;
3974 if ( node ) {
3975 for ( i in preservedScriptAttributes ) {
3976
3977 // Support: Firefox 64+, Edge 18+
3978 // Some browsers don't support the "nonce" property on scripts.
3979 // On the other hand, just using `getAttribute` is not enough as
3980 // the `nonce` attribute is reset to an empty string whenever it
3981 // becomes browsing-context connected.
3982 // See https://github.com/whatwg/html/issues/2369
3983 // See https://html.spec.whatwg.org/#nonce-attributes
3984 // The `node.getAttribute` check was added for the sake of
3985 // `jQuery.globalEval` so that it can fake a nonce-containing node
3986 // via an object.
3987 val = node[ i ] || node.getAttribute && node.getAttribute( i );
3988 if ( val ) {
3989 script.setAttribute( i, val );
3990 }
3991 }
3992 }
3993 doc.head.appendChild( script ).parentNode.removeChild( script );
3994 }
3995
3996
3997function toType( obj ) {
3998 if ( obj == null ) {
3999 return obj + "";
4000 }
4001
4002 // Support: Android <=2.3 only (functionish RegExp)
4003 return typeof obj === "object" || typeof obj === "function" ?
4004 class2type[ toString.call( obj ) ] || "object" :
4005 typeof obj;
4006}
4007/* global Symbol */
4008// Defining this global in .eslintrc.json would create a danger of using the global
4009// unguarded in another place, it seems safer to define global only for this module
4010
4011
4012
4013var
4014 version = "3.4.1",
4015
4016 // Define a local copy of jQuery
4017 jQuery = function( selector, context ) {
4018
4019 // The jQuery object is actually just the init constructor 'enhanced'
4020 // Need init if jQuery is called (just allow error to be thrown if not included)
4021 return new jQuery.fn.init( selector, context );
4022 },
4023
4024 // Support: Android <=4.0 only
4025 // Make sure we trim BOM and NBSP
4026 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
4027
4028jQuery.fn = jQuery.prototype = {
4029
4030 // The current version of jQuery being used
4031 jquery: version,
4032
4033 constructor: jQuery,
4034
4035 // The default length of a jQuery object is 0
4036 length: 0,
4037
4038 toArray: function() {
4039 return slice.call( this );
4040 },
4041
4042 // Get the Nth element in the matched element set OR
4043 // Get the whole matched element set as a clean array
4044 get: function( num ) {
4045
4046 // Return all the elements in a clean array
4047 if ( num == null ) {
4048 return slice.call( this );
4049 }
4050
4051 // Return just the one element from the set
4052 return num < 0 ? this[ num + this.length ] : this[ num ];
4053 },
4054
4055 // Take an array of elements and push it onto the stack
4056 // (returning the new matched element set)
4057 pushStack: function( elems ) {
4058
4059 // Build a new jQuery matched element set
4060 var ret = jQuery.merge( this.constructor(), elems );
4061
4062 // Add the old object onto the stack (as a reference)
4063 ret.prevObject = this;
4064
4065 // Return the newly-formed element set
4066 return ret;
4067 },
4068
4069 // Execute a callback for every element in the matched set.
4070 each: function( callback ) {
4071 return jQuery.each( this, callback );
4072 },
4073
4074 map: function( callback ) {
4075 return this.pushStack( jQuery.map( this, function( elem, i ) {
4076 return callback.call( elem, i, elem );
4077 } ) );
4078 },
4079
4080 slice: function() {
4081 return this.pushStack( slice.apply( this, arguments ) );
4082 },
4083
4084 first: function() {
4085 return this.eq( 0 );
4086 },
4087
4088 last: function() {
4089 return this.eq( -1 );
4090 },
4091
4092 eq: function( i ) {
4093 var len = this.length,
4094 j = +i + ( i < 0 ? len : 0 );
4095 return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
4096 },
4097
4098 end: function() {
4099 return this.prevObject || this.constructor();
4100 },
4101
4102 // For internal use only.
4103 // Behaves like an Array's method, not like a jQuery method.
4104 push: push,
4105 sort: arr.sort,
4106 splice: arr.splice
4107};
4108
4109jQuery.extend = jQuery.fn.extend = function() {
4110 var options, name, src, copy, copyIsArray, clone,
4111 target = arguments[ 0 ] || {},
4112 i = 1,
4113 length = arguments.length,
4114 deep = false;
4115
4116 // Handle a deep copy situation
4117 if ( typeof target === "boolean" ) {
4118 deep = target;
4119
4120 // Skip the boolean and the target
4121 target = arguments[ i ] || {};
4122 i++;
4123 }
4124
4125 // Handle case when target is a string or something (possible in deep copy)
4126 if ( typeof target !== "object" && !isFunction( target ) ) {
4127 target = {};
4128 }
4129
4130 // Extend jQuery itself if only one argument is passed
4131 if ( i === length ) {
4132 target = this;
4133 i--;
4134 }
4135
4136 for ( ; i < length; i++ ) {
4137
4138 // Only deal with non-null/undefined values
4139 if ( ( options = arguments[ i ] ) != null ) {
4140
4141 // Extend the base object
4142 for ( name in options ) {
4143 copy = options[ name ];
4144
4145 // Prevent Object.prototype pollution
4146 // Prevent never-ending loop
4147 if ( name === "__proto__" || target === copy ) {
4148 continue;
4149 }
4150
4151 // Recurse if we're merging plain objects or arrays
4152 if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
4153 ( copyIsArray = Array.isArray( copy ) ) ) ) {
4154 src = target[ name ];
4155
4156 // Ensure proper type for the source value
4157 if ( copyIsArray && !Array.isArray( src ) ) {
4158 clone = [];
4159 } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
4160 clone = {};
4161 } else {
4162 clone = src;
4163 }
4164 copyIsArray = false;
4165
4166 // Never move original objects, clone them
4167 target[ name ] = jQuery.extend( deep, clone, copy );
4168
4169 // Don't bring in undefined values
4170 } else if ( copy !== undefined ) {
4171 target[ name ] = copy;
4172 }
4173 }
4174 }
4175 }
4176
4177 // Return the modified object
4178 return target;
4179};
4180
4181jQuery.extend( {
4182
4183 // Unique for each copy of jQuery on the page
4184 expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
4185
4186 // Assume jQuery is ready without the ready module
4187 isReady: true,
4188
4189 error: function( msg ) {
4190 throw new Error( msg );
4191 },
4192
4193 noop: function() {},
4194
4195 isPlainObject: function( obj ) {
4196 var proto, Ctor;
4197
4198 // Detect obvious negatives
4199 // Use toString instead of jQuery.type to catch host objects
4200 if ( !obj || toString.call( obj ) !== "[object Object]" ) {
4201 return false;
4202 }
4203
4204 proto = getProto( obj );
4205
4206 // Objects with no prototype (e.g., `Object.create( null )`) are plain
4207 if ( !proto ) {
4208 return true;
4209 }
4210
4211 // Objects with prototype are plain iff they were constructed by a global Object function
4212 Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
4213 return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
4214 },
4215
4216 isEmptyObject: function( obj ) {
4217 var name;
4218
4219 for ( name in obj ) {
4220 return false;
4221 }
4222 return true;
4223 },
4224
4225 // Evaluates a script in a global context
4226 globalEval: function( code, options ) {
4227 DOMEval( code, { nonce: options && options.nonce } );
4228 },
4229
4230 each: function( obj, callback ) {
4231 var length, i = 0;
4232
4233 if ( isArrayLike( obj ) ) {
4234 length = obj.length;
4235 for ( ; i < length; i++ ) {
4236 if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
4237 break;
4238 }
4239 }
4240 } else {
4241 for ( i in obj ) {
4242 if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
4243 break;
4244 }
4245 }
4246 }
4247
4248 return obj;
4249 },
4250
4251 // Support: Android <=4.0 only
4252 trim: function( text ) {
4253 return text == null ?
4254 "" :
4255 ( text + "" ).replace( rtrim, "" );
4256 },
4257
4258 // results is for internal usage only
4259 makeArray: function( arr, results ) {
4260 var ret = results || [];
4261
4262 if ( arr != null ) {
4263 if ( isArrayLike( Object( arr ) ) ) {
4264 jQuery.merge( ret,
4265 typeof arr === "string" ?
4266 [ arr ] : arr
4267 );
4268 } else {
4269 push.call( ret, arr );
4270 }
4271 }
4272
4273 return ret;
4274 },
4275
4276 inArray: function( elem, arr, i ) {
4277 return arr == null ? -1 : indexOf.call( arr, elem, i );
4278 },
4279
4280 // Support: Android <=4.0 only, PhantomJS 1 only
4281 // push.apply(_, arraylike) throws on ancient WebKit
4282 merge: function( first, second ) {
4283 var len = +second.length,
4284 j = 0,
4285 i = first.length;
4286
4287 for ( ; j < len; j++ ) {
4288 first[ i++ ] = second[ j ];
4289 }
4290
4291 first.length = i;
4292
4293 return first;
4294 },
4295
4296 grep: function( elems, callback, invert ) {
4297 var callbackInverse,
4298 matches = [],
4299 i = 0,
4300 length = elems.length,
4301 callbackExpect = !invert;
4302
4303 // Go through the array, only saving the items
4304 // that pass the validator function
4305 for ( ; i < length; i++ ) {
4306 callbackInverse = !callback( elems[ i ], i );
4307 if ( callbackInverse !== callbackExpect ) {
4308 matches.push( elems[ i ] );
4309 }
4310 }
4311
4312 return matches;
4313 },
4314
4315 // arg is for internal usage only
4316 map: function( elems, callback, arg ) {
4317 var length, value,
4318 i = 0,
4319 ret = [];
4320
4321 // Go through the array, translating each of the items to their new values
4322 if ( isArrayLike( elems ) ) {
4323 length = elems.length;
4324 for ( ; i < length; i++ ) {
4325 value = callback( elems[ i ], i, arg );
4326
4327 if ( value != null ) {
4328 ret.push( value );
4329 }
4330 }
4331
4332 // Go through every key on the object,
4333 } else {
4334 for ( i in elems ) {
4335 value = callback( elems[ i ], i, arg );
4336
4337 if ( value != null ) {
4338 ret.push( value );
4339 }
4340 }
4341 }
4342
4343 // Flatten any nested arrays
4344 return concat.apply( [], ret );
4345 },
4346
4347 // A global GUID counter for objects
4348 guid: 1,
4349
4350 // jQuery.support is not used in Core but other projects attach their
4351 // properties to it so it needs to exist.
4352 support: support
4353} );
4354
4355if ( typeof Symbol === "function" ) {
4356 jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
4357}
4358
4359// Populate the class2type map
4360jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
4361function( i, name ) {
4362 class2type[ "[object " + name + "]" ] = name.toLowerCase();
4363} );
4364
4365function isArrayLike( obj ) {
4366
4367 // Support: real iOS 8.2 only (not reproducible in simulator)
4368 // `in` check used to prevent JIT error (gh-2145)
4369 // hasOwn isn't used here due to false negatives
4370 // regarding Nodelist length in IE
4371 var length = !!obj && "length" in obj && obj.length,
4372 type = toType( obj );
4373
4374 if ( isFunction( obj ) || isWindow( obj ) ) {
4375 return false;
4376 }
4377
4378 return type === "array" || length === 0 ||
4379 typeof length === "number" && length > 0 && ( length - 1 ) in obj;
4380}
4381var Sizzle =
4382/*!
4383 * Sizzle CSS Selector Engine v2.3.4
4384 * https://sizzlejs.com/
4385 *
4386 * Copyright JS Foundation and other contributors
4387 * Released under the MIT license
4388 * https://js.foundation/
4389 *
4390 * Date: 2019-04-08
4391 */
4392(function( window ) {
4393
4394var i,
4395 support,
4396 Expr,
4397 getText,
4398 isXML,
4399 tokenize,
4400 compile,
4401 select,
4402 outermostContext,
4403 sortInput,
4404 hasDuplicate,
4405
4406 // Local document vars
4407 setDocument,
4408 document,
4409 docElem,
4410 documentIsHTML,
4411 rbuggyQSA,
4412 rbuggyMatches,
4413 matches,
4414 contains,
4415
4416 // Instance-specific data
4417 expando = "sizzle" + 1 * new Date(),
4418 preferredDoc = window.document,
4419 dirruns = 0,
4420 done = 0,
4421 classCache = createCache(),
4422 tokenCache = createCache(),
4423 compilerCache = createCache(),
4424 nonnativeSelectorCache = createCache(),
4425 sortOrder = function( a, b ) {
4426 if ( a === b ) {
4427 hasDuplicate = true;
4428 }
4429 return 0;
4430 },
4431
4432 // Instance methods
4433 hasOwn = ({}).hasOwnProperty,
4434 arr = [],
4435 pop = arr.pop,
4436 push_native = arr.push,
4437 push = arr.push,
4438 slice = arr.slice,
4439 // Use a stripped-down indexOf as it's faster than native
4440 // https://jsperf.com/thor-indexof-vs-for/5
4441 indexOf = function( list, elem ) {
4442 var i = 0,
4443 len = list.length;
4444 for ( ; i < len; i++ ) {
4445 if ( list[i] === elem ) {
4446 return i;
4447 }
4448 }
4449 return -1;
4450 },
4451
4452 booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
4453
4454 // Regular expressions
4455
4456 // http://www.w3.org/TR/css3-selectors/#whitespace
4457 whitespace = "[\\x20\\t\\r\\n\\f]",
4458
4459 // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
4460 identifier = "(?:\\\\.|[\\w-]|[^\0-\\xa0])+",
4461
4462 // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors
4463 attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
4464 // Operator (capture 2)
4465 "*([*^$|!~]?=)" + whitespace +
4466 // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]"
4467 "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace +
4468 "*\\]",
4469
4470 pseudos = ":(" + identifier + ")(?:\\((" +
4471 // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments:
4472 // 1. quoted (capture 3; capture 4 or capture 5)
4473 "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
4474 // 2. simple (capture 6)
4475 "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
4476 // 3. anything else (capture 2)
4477 ".*" +
4478 ")\\)|)",
4479
4480 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
4481 rwhitespace = new RegExp( whitespace + "+", "g" ),
4482 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
4483
4484 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
4485 rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ),
4486 rdescend = new RegExp( whitespace + "|>" ),
4487
4488 rpseudo = new RegExp( pseudos ),
4489 ridentifier = new RegExp( "^" + identifier + "$" ),
4490
4491 matchExpr = {
4492 "ID": new RegExp( "^#(" + identifier + ")" ),
4493 "CLASS": new RegExp( "^\\.(" + identifier + ")" ),
4494 "TAG": new RegExp( "^(" + identifier + "|[*])" ),
4495 "ATTR": new RegExp( "^" + attributes ),
4496 "PSEUDO": new RegExp( "^" + pseudos ),
4497 "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
4498 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
4499 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
4500 "bool": new RegExp( "^(?:" + booleans + ")$", "i" ),
4501 // For use in libraries implementing .is()
4502 // We use this for POS matching in `select`
4503 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
4504 whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
4505 },
4506
4507 rhtml = /HTML$/i,
4508 rinputs = /^(?:input|select|textarea|button)$/i,
4509 rheader = /^h\d$/i,
4510
4511 rnative = /^[^{]+\{\s*\[native \w/,
4512
4513 // Easily-parseable/retrievable ID or TAG or CLASS selectors
4514 rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
4515
4516 rsibling = /[+~]/,
4517
4518 // CSS escapes
4519 // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
4520 runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ),
4521 funescape = function( _, escaped, escapedWhitespace ) {
4522 var high = "0x" + escaped - 0x10000;
4523 // NaN means non-codepoint
4524 // Support: Firefox<24
4525 // Workaround erroneous numeric interpretation of +"0x"
4526 return high !== high || escapedWhitespace ?
4527 escaped :
4528 high < 0 ?
4529 // BMP codepoint
4530 String.fromCharCode( high + 0x10000 ) :
4531 // Supplemental Plane codepoint (surrogate pair)
4532 String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
4533 },
4534
4535 // CSS string/identifier serialization
4536 // https://drafts.csswg.org/cssom/#common-serializing-idioms
4537 rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,
4538 fcssescape = function( ch, asCodePoint ) {
4539 if ( asCodePoint ) {
4540
4541 // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
4542 if ( ch === "\0" ) {
4543 return "\uFFFD";
4544 }
4545
4546 // Control characters and (dependent upon position) numbers get escaped as code points
4547 return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
4548 }
4549
4550 // Other potentially-special ASCII characters get backslash-escaped
4551 return "\\" + ch;
4552 },
4553
4554 // Used for iframes
4555 // See setDocument()
4556 // Removing the function wrapper causes a "Permission Denied"
4557 // error in IE
4558 unloadHandler = function() {
4559 setDocument();
4560 },
4561
4562 inDisabledFieldset = addCombinator(
4563 function( elem ) {
4564 return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset";
4565 },
4566 { dir: "parentNode", next: "legend" }
4567 );
4568
4569// Optimize for push.apply( _, NodeList )
4570try {
4571 push.apply(
4572 (arr = slice.call( preferredDoc.childNodes )),
4573 preferredDoc.childNodes
4574 );
4575 // Support: Android<4.0
4576 // Detect silently failing push.apply
4577 arr[ preferredDoc.childNodes.length ].nodeType;
4578} catch ( e ) {
4579 push = { apply: arr.length ?
4580
4581 // Leverage slice if possible
4582 function( target, els ) {
4583 push_native.apply( target, slice.call(els) );
4584 } :
4585
4586 // Support: IE<9
4587 // Otherwise append directly
4588 function( target, els ) {
4589 var j = target.length,
4590 i = 0;
4591 // Can't trust NodeList.length
4592 while ( (target[j++] = els[i++]) ) {}
4593 target.length = j - 1;
4594 }
4595 };
4596}
4597
4598function Sizzle( selector, context, results, seed ) {
4599 var m, i, elem, nid, match, groups, newSelector,
4600 newContext = context && context.ownerDocument,
4601
4602 // nodeType defaults to 9, since context defaults to document
4603 nodeType = context ? context.nodeType : 9;
4604
4605 results = results || [];
4606
4607 // Return early from calls with invalid selector or context
4608 if ( typeof selector !== "string" || !selector ||
4609 nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
4610
4611 return results;
4612 }
4613
4614 // Try to shortcut find operations (as opposed to filters) in HTML documents
4615 if ( !seed ) {
4616
4617 if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
4618 setDocument( context );
4619 }
4620 context = context || document;
4621
4622 if ( documentIsHTML ) {
4623
4624 // If the selector is sufficiently simple, try using a "get*By*" DOM method
4625 // (excepting DocumentFragment context, where the methods don't exist)
4626 if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) {
4627
4628 // ID selector
4629 if ( (m = match[1]) ) {
4630
4631 // Document context
4632 if ( nodeType === 9 ) {
4633 if ( (elem = context.getElementById( m )) ) {
4634
4635 // Support: IE, Opera, Webkit
4636 // TODO: identify versions
4637 // getElementById can match elements by name instead of ID
4638 if ( elem.id === m ) {
4639 results.push( elem );
4640 return results;
4641 }
4642 } else {
4643 return results;
4644 }
4645
4646 // Element context
4647 } else {
4648
4649 // Support: IE, Opera, Webkit
4650 // TODO: identify versions
4651 // getElementById can match elements by name instead of ID
4652 if ( newContext && (elem = newContext.getElementById( m )) &&
4653 contains( context, elem ) &&
4654 elem.id === m ) {
4655
4656 results.push( elem );
4657 return results;
4658 }
4659 }
4660
4661 // Type selector
4662 } else if ( match[2] ) {
4663 push.apply( results, context.getElementsByTagName( selector ) );
4664 return results;
4665
4666 // Class selector
4667 } else if ( (m = match[3]) && support.getElementsByClassName &&
4668 context.getElementsByClassName ) {
4669
4670 push.apply( results, context.getElementsByClassName( m ) );
4671 return results;
4672 }
4673 }
4674
4675 // Take advantage of querySelectorAll
4676 if ( support.qsa &&
4677 !nonnativeSelectorCache[ selector + " " ] &&
4678 (!rbuggyQSA || !rbuggyQSA.test( selector )) &&
4679
4680 // Support: IE 8 only
4681 // Exclude object elements
4682 (nodeType !== 1 || context.nodeName.toLowerCase() !== "object") ) {
4683
4684 newSelector = selector;
4685 newContext = context;
4686
4687 // qSA considers elements outside a scoping root when evaluating child or
4688 // descendant combinators, which is not what we want.
4689 // In such cases, we work around the behavior by prefixing every selector in the
4690 // list with an ID selector referencing the scope context.
4691 // Thanks to Andrew Dupont for this technique.
4692 if ( nodeType === 1 && rdescend.test( selector ) ) {
4693
4694 // Capture the context ID, setting it first if necessary
4695 if ( (nid = context.getAttribute( "id" )) ) {
4696 nid = nid.replace( rcssescape, fcssescape );
4697 } else {
4698 context.setAttribute( "id", (nid = expando) );
4699 }
4700
4701 // Prefix every selector in the list
4702 groups = tokenize( selector );
4703 i = groups.length;
4704 while ( i-- ) {
4705 groups[i] = "#" + nid + " " + toSelector( groups[i] );
4706 }
4707 newSelector = groups.join( "," );
4708
4709 // Expand context for sibling selectors
4710 newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
4711 context;
4712 }
4713
4714 try {
4715 push.apply( results,
4716 newContext.querySelectorAll( newSelector )
4717 );
4718 return results;
4719 } catch ( qsaError ) {
4720 nonnativeSelectorCache( selector, true );
4721 } finally {
4722 if ( nid === expando ) {
4723 context.removeAttribute( "id" );
4724 }
4725 }
4726 }
4727 }
4728 }
4729
4730 // All others
4731 return select( selector.replace( rtrim, "$1" ), context, results, seed );
4732}
4733
4734/**
4735 * Create key-value caches of limited size
4736 * @returns {function(string, object)} Returns the Object data after storing it on itself with
4737 * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
4738 * deleting the oldest entry
4739 */
4740function createCache() {
4741 var keys = [];
4742
4743 function cache( key, value ) {
4744 // Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
4745 if ( keys.push( key + " " ) > Expr.cacheLength ) {
4746 // Only keep the most recent entries
4747 delete cache[ keys.shift() ];
4748 }
4749 return (cache[ key + " " ] = value);
4750 }
4751 return cache;
4752}
4753
4754/**
4755 * Mark a function for special use by Sizzle
4756 * @param {Function} fn The function to mark
4757 */
4758function markFunction( fn ) {
4759 fn[ expando ] = true;
4760 return fn;
4761}
4762
4763/**
4764 * Support testing using an element
4765 * @param {Function} fn Passed the created element and returns a boolean result
4766 */
4767function assert( fn ) {
4768 var el = document.createElement("fieldset");
4769
4770 try {
4771 return !!fn( el );
4772 } catch (e) {
4773 return false;
4774 } finally {
4775 // Remove from its parent by default
4776 if ( el.parentNode ) {
4777 el.parentNode.removeChild( el );
4778 }
4779 // release memory in IE
4780 el = null;
4781 }
4782}
4783
4784/**
4785 * Adds the same handler for all of the specified attrs
4786 * @param {String} attrs Pipe-separated list of attributes
4787 * @param {Function} handler The method that will be applied
4788 */
4789function addHandle( attrs, handler ) {
4790 var arr = attrs.split("|"),
4791 i = arr.length;
4792
4793 while ( i-- ) {
4794 Expr.attrHandle[ arr[i] ] = handler;
4795 }
4796}
4797
4798/**
4799 * Checks document order of two siblings
4800 * @param {Element} a
4801 * @param {Element} b
4802 * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
4803 */
4804function siblingCheck( a, b ) {
4805 var cur = b && a,
4806 diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
4807 a.sourceIndex - b.sourceIndex;
4808
4809 // Use IE sourceIndex if available on both nodes
4810 if ( diff ) {
4811 return diff;
4812 }
4813
4814 // Check if b follows a
4815 if ( cur ) {
4816 while ( (cur = cur.nextSibling) ) {
4817 if ( cur === b ) {
4818 return -1;
4819 }
4820 }
4821 }
4822
4823 return a ? 1 : -1;
4824}
4825
4826/**
4827 * Returns a function to use in pseudos for input types
4828 * @param {String} type
4829 */
4830function createInputPseudo( type ) {
4831 return function( elem ) {
4832 var name = elem.nodeName.toLowerCase();
4833 return name === "input" && elem.type === type;
4834 };
4835}
4836
4837/**
4838 * Returns a function to use in pseudos for buttons
4839 * @param {String} type
4840 */
4841function createButtonPseudo( type ) {
4842 return function( elem ) {
4843 var name = elem.nodeName.toLowerCase();
4844 return (name === "input" || name === "button") && elem.type === type;
4845 };
4846}
4847
4848/**
4849 * Returns a function to use in pseudos for :enabled/:disabled
4850 * @param {Boolean} disabled true for :disabled; false for :enabled
4851 */
4852function createDisabledPseudo( disabled ) {
4853
4854 // Known :disabled false positives: fieldset[disabled] > legend:nth-of-type(n+2) :can-disable
4855 return function( elem ) {
4856
4857 // Only certain elements can match :enabled or :disabled
4858 // https://html.spec.whatwg.org/multipage/scripting.html#selector-enabled
4859 // https://html.spec.whatwg.org/multipage/scripting.html#selector-disabled
4860 if ( "form" in elem ) {
4861
4862 // Check for inherited disabledness on relevant non-disabled elements:
4863 // * listed form-associated elements in a disabled fieldset
4864 // https://html.spec.whatwg.org/multipage/forms.html#category-listed
4865 // https://html.spec.whatwg.org/multipage/forms.html#concept-fe-disabled
4866 // * option elements in a disabled optgroup
4867 // https://html.spec.whatwg.org/multipage/forms.html#concept-option-disabled
4868 // All such elements have a "form" property.
4869 if ( elem.parentNode && elem.disabled === false ) {
4870
4871 // Option elements defer to a parent optgroup if present
4872 if ( "label" in elem ) {
4873 if ( "label" in elem.parentNode ) {
4874 return elem.parentNode.disabled === disabled;
4875 } else {
4876 return elem.disabled === disabled;
4877 }
4878 }
4879
4880 // Support: IE 6 - 11
4881 // Use the isDisabled shortcut property to check for disabled fieldset ancestors
4882 return elem.isDisabled === disabled ||
4883
4884 // Where there is no isDisabled, check manually
4885 /* jshint -W018 */
4886 elem.isDisabled !== !disabled &&
4887 inDisabledFieldset( elem ) === disabled;
4888 }
4889
4890 return elem.disabled === disabled;
4891
4892 // Try to winnow out elements that can't be disabled before trusting the disabled property.
4893 // Some victims get caught in our net (label, legend, menu, track), but it shouldn't
4894 // even exist on them, let alone have a boolean value.
4895 } else if ( "label" in elem ) {
4896 return elem.disabled === disabled;
4897 }
4898
4899 // Remaining elements are neither :enabled nor :disabled
4900 return false;
4901 };
4902}
4903
4904/**
4905 * Returns a function to use in pseudos for positionals
4906 * @param {Function} fn
4907 */
4908function createPositionalPseudo( fn ) {
4909 return markFunction(function( argument ) {
4910 argument = +argument;
4911 return markFunction(function( seed, matches ) {
4912 var j,
4913 matchIndexes = fn( [], seed.length, argument ),
4914 i = matchIndexes.length;
4915
4916 // Match elements found at the specified indexes
4917 while ( i-- ) {
4918 if ( seed[ (j = matchIndexes[i]) ] ) {
4919 seed[j] = !(matches[j] = seed[j]);
4920 }
4921 }
4922 });
4923 });
4924}
4925
4926/**
4927 * Checks a node for validity as a Sizzle context
4928 * @param {Element|Object=} context
4929 * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value
4930 */
4931function testContext( context ) {
4932 return context && typeof context.getElementsByTagName !== "undefined" && context;
4933}
4934
4935// Expose support vars for convenience
4936support = Sizzle.support = {};
4937
4938/**
4939 * Detects XML nodes
4940 * @param {Element|Object} elem An element or a document
4941 * @returns {Boolean} True iff elem is a non-HTML XML node
4942 */
4943isXML = Sizzle.isXML = function( elem ) {
4944 var namespace = elem.namespaceURI,
4945 docElem = (elem.ownerDocument || elem).documentElement;
4946
4947 // Support: IE <=8
4948 // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes
4949 // https://bugs.jquery.com/ticket/4833
4950 return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" );
4951};
4952
4953/**
4954 * Sets document-related variables once based on the current document
4955 * @param {Element|Object} [doc] An element or document object to use to set the document
4956 * @returns {Object} Returns the current document
4957 */
4958setDocument = Sizzle.setDocument = function( node ) {
4959 var hasCompare, subWindow,
4960 doc = node ? node.ownerDocument || node : preferredDoc;
4961
4962 // Return early if doc is invalid or already selected
4963 if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
4964 return document;
4965 }
4966
4967 // Update global variables
4968 document = doc;
4969 docElem = document.documentElement;
4970 documentIsHTML = !isXML( document );
4971
4972 // Support: IE 9-11, Edge
4973 // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936)
4974 if ( preferredDoc !== document &&
4975 (subWindow = document.defaultView) && subWindow.top !== subWindow ) {
4976
4977 // Support: IE 11, Edge
4978 if ( subWindow.addEventListener ) {
4979 subWindow.addEventListener( "unload", unloadHandler, false );
4980
4981 // Support: IE 9 - 10 only
4982 } else if ( subWindow.attachEvent ) {
4983 subWindow.attachEvent( "onunload", unloadHandler );
4984 }
4985 }
4986
4987 /* Attributes
4988 ---------------------------------------------------------------------- */
4989
4990 // Support: IE<8
4991 // Verify that getAttribute really returns attributes and not properties
4992 // (excepting IE8 booleans)
4993 support.attributes = assert(function( el ) {
4994 el.className = "i";
4995 return !el.getAttribute("className");
4996 });
4997
4998 /* getElement(s)By*
4999 ---------------------------------------------------------------------- */
5000
5001 // Check if getElementsByTagName("*") returns only elements
5002 support.getElementsByTagName = assert(function( el ) {
5003 el.appendChild( document.createComment("") );
5004 return !el.getElementsByTagName("*").length;
5005 });
5006
5007 // Support: IE<9
5008 support.getElementsByClassName = rnative.test( document.getElementsByClassName );
5009
5010 // Support: IE<10
5011 // Check if getElementById returns elements by name
5012 // The broken getElementById methods don't pick up programmatically-set names,
5013 // so use a roundabout getElementsByName test
5014 support.getById = assert(function( el ) {
5015 docElem.appendChild( el ).id = expando;
5016 return !document.getElementsByName || !document.getElementsByName( expando ).length;
5017 });
5018
5019 // ID filter and find
5020 if ( support.getById ) {
5021 Expr.filter["ID"] = function( id ) {
5022 var attrId = id.replace( runescape, funescape );
5023 return function( elem ) {
5024 return elem.getAttribute("id") === attrId;
5025 };
5026 };
5027 Expr.find["ID"] = function( id, context ) {
5028 if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
5029 var elem = context.getElementById( id );
5030 return elem ? [ elem ] : [];
5031 }
5032 };
5033 } else {
5034 Expr.filter["ID"] = function( id ) {
5035 var attrId = id.replace( runescape, funescape );
5036 return function( elem ) {
5037 var node = typeof elem.getAttributeNode !== "undefined" &&
5038 elem.getAttributeNode("id");
5039 return node && node.value === attrId;
5040 };
5041 };
5042
5043 // Support: IE 6 - 7 only
5044 // getElementById is not reliable as a find shortcut
5045 Expr.find["ID"] = function( id, context ) {
5046 if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
5047 var node, i, elems,
5048 elem = context.getElementById( id );
5049
5050 if ( elem ) {
5051
5052 // Verify the id attribute
5053 node = elem.getAttributeNode("id");
5054 if ( node && node.value === id ) {
5055 return [ elem ];
5056 }
5057
5058 // Fall back on getElementsByName
5059 elems = context.getElementsByName( id );
5060 i = 0;
5061 while ( (elem = elems[i++]) ) {
5062 node = elem.getAttributeNode("id");
5063 if ( node && node.value === id ) {
5064 return [ elem ];
5065 }
5066 }
5067 }
5068
5069 return [];
5070 }
5071 };
5072 }
5073
5074 // Tag
5075 Expr.find["TAG"] = support.getElementsByTagName ?
5076 function( tag, context ) {
5077 if ( typeof context.getElementsByTagName !== "undefined" ) {
5078 return context.getElementsByTagName( tag );
5079
5080 // DocumentFragment nodes don't have gEBTN
5081 } else if ( support.qsa ) {
5082 return context.querySelectorAll( tag );
5083 }
5084 } :
5085
5086 function( tag, context ) {
5087 var elem,
5088 tmp = [],
5089 i = 0,
5090 // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too
5091 results = context.getElementsByTagName( tag );
5092
5093 // Filter out possible comments
5094 if ( tag === "*" ) {
5095 while ( (elem = results[i++]) ) {
5096 if ( elem.nodeType === 1 ) {
5097 tmp.push( elem );
5098 }
5099 }
5100
5101 return tmp;
5102 }
5103 return results;
5104 };
5105
5106 // Class
5107 Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) {
5108 if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
5109 return context.getElementsByClassName( className );
5110 }
5111 };
5112
5113 /* QSA/matchesSelector
5114 ---------------------------------------------------------------------- */
5115
5116 // QSA and matchesSelector support
5117
5118 // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
5119 rbuggyMatches = [];
5120
5121 // qSa(:focus) reports false when true (Chrome 21)
5122 // We allow this because of a bug in IE8/9 that throws an error
5123 // whenever `document.activeElement` is accessed on an iframe
5124 // So, we allow :focus to pass through QSA all the time to avoid the IE error
5125 // See https://bugs.jquery.com/ticket/13378
5126 rbuggyQSA = [];
5127
5128 if ( (support.qsa = rnative.test( document.querySelectorAll )) ) {
5129 // Build QSA regex
5130 // Regex strategy adopted from Diego Perini
5131 assert(function( el ) {
5132 // Select is set to empty string on purpose
5133 // This is to test IE's treatment of not explicitly
5134 // setting a boolean content attribute,
5135 // since its presence should be enough
5136 // https://bugs.jquery.com/ticket/12359
5137 docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" +
5138 "<select id='" + expando + "-\r\\' msallowcapture=''>" +
5139 "<option selected=''></option></select>";
5140
5141 // Support: IE8, Opera 11-12.16
5142 // Nothing should be selected when empty strings follow ^= or $= or *=
5143 // The test attribute must be unknown in Opera but "safe" for WinRT
5144 // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section
5145 if ( el.querySelectorAll("[msallowcapture^='']").length ) {
5146 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" );
5147 }
5148
5149 // Support: IE8
5150 // Boolean attributes and "value" are not treated correctly
5151 if ( !el.querySelectorAll("[selected]").length ) {
5152 rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
5153 }
5154
5155 // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+
5156 if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
5157 rbuggyQSA.push("~=");
5158 }
5159
5160 // Webkit/Opera - :checked should return selected option elements
5161 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
5162 // IE8 throws error here and will not see later tests
5163 if ( !el.querySelectorAll(":checked").length ) {
5164 rbuggyQSA.push(":checked");
5165 }
5166
5167 // Support: Safari 8+, iOS 8+
5168 // https://bugs.webkit.org/show_bug.cgi?id=136851
5169 // In-page `selector#id sibling-combinator selector` fails
5170 if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
5171 rbuggyQSA.push(".#.+[+~]");
5172 }
5173 });
5174
5175 assert(function( el ) {
5176 el.innerHTML = "<a href='' disabled='disabled'></a>" +
5177 "<select disabled='disabled'><option/></select>";
5178
5179 // Support: Windows 8 Native Apps
5180 // The type and name attributes are restricted during .innerHTML assignment
5181 var input = document.createElement("input");
5182 input.setAttribute( "type", "hidden" );
5183 el.appendChild( input ).setAttribute( "name", "D" );
5184
5185 // Support: IE8
5186 // Enforce case-sensitivity of name attribute
5187 if ( el.querySelectorAll("[name=d]").length ) {
5188 rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" );
5189 }
5190
5191 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
5192 // IE8 throws error here and will not see later tests
5193 if ( el.querySelectorAll(":enabled").length !== 2 ) {
5194 rbuggyQSA.push( ":enabled", ":disabled" );
5195 }
5196
5197 // Support: IE9-11+
5198 // IE's :disabled selector does not pick up the children of disabled fieldsets
5199 docElem.appendChild( el ).disabled = true;
5200 if ( el.querySelectorAll(":disabled").length !== 2 ) {
5201 rbuggyQSA.push( ":enabled", ":disabled" );
5202 }
5203
5204 // Opera 10-11 does not throw on post-comma invalid pseudos
5205 el.querySelectorAll("*,:x");
5206 rbuggyQSA.push(",.*:");
5207 });
5208 }
5209
5210 if ( (support.matchesSelector = rnative.test( (matches = docElem.matches ||
5211 docElem.webkitMatchesSelector ||
5212 docElem.mozMatchesSelector ||
5213 docElem.oMatchesSelector ||
5214 docElem.msMatchesSelector) )) ) {
5215
5216 assert(function( el ) {
5217 // Check to see if it's possible to do matchesSelector
5218 // on a disconnected node (IE 9)
5219 support.disconnectedMatch = matches.call( el, "*" );
5220
5221 // This should fail with an exception
5222 // Gecko does not error, returns false instead
5223 matches.call( el, "[s!='']:x" );
5224 rbuggyMatches.push( "!=", pseudos );
5225 });
5226 }
5227
5228 rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") );
5229 rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") );
5230
5231 /* Contains
5232 ---------------------------------------------------------------------- */
5233 hasCompare = rnative.test( docElem.compareDocumentPosition );
5234
5235 // Element contains another
5236 // Purposefully self-exclusive
5237 // As in, an element does not contain itself
5238 contains = hasCompare || rnative.test( docElem.contains ) ?
5239 function( a, b ) {
5240 var adown = a.nodeType === 9 ? a.documentElement : a,
5241 bup = b && b.parentNode;
5242 return a === bup || !!( bup && bup.nodeType === 1 && (
5243 adown.contains ?
5244 adown.contains( bup ) :
5245 a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
5246 ));
5247 } :
5248 function( a, b ) {
5249 if ( b ) {
5250 while ( (b = b.parentNode) ) {
5251 if ( b === a ) {
5252 return true;
5253 }
5254 }
5255 }
5256 return false;
5257 };
5258
5259 /* Sorting
5260 ---------------------------------------------------------------------- */
5261
5262 // Document order sorting
5263 sortOrder = hasCompare ?
5264 function( a, b ) {
5265
5266 // Flag for duplicate removal
5267 if ( a === b ) {
5268 hasDuplicate = true;
5269 return 0;
5270 }
5271
5272 // Sort on method existence if only one input has compareDocumentPosition
5273 var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
5274 if ( compare ) {
5275 return compare;
5276 }
5277
5278 // Calculate position if both inputs belong to the same document
5279 compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ?
5280 a.compareDocumentPosition( b ) :
5281
5282 // Otherwise we know they are disconnected
5283 1;
5284
5285 // Disconnected nodes
5286 if ( compare & 1 ||
5287 (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) {
5288
5289 // Choose the first element that is related to our preferred document
5290 if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
5291 return -1;
5292 }
5293 if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
5294 return 1;
5295 }
5296
5297 // Maintain original order
5298 return sortInput ?
5299 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
5300 0;
5301 }
5302
5303 return compare & 4 ? -1 : 1;
5304 } :
5305 function( a, b ) {
5306 // Exit early if the nodes are identical
5307 if ( a === b ) {
5308 hasDuplicate = true;
5309 return 0;
5310 }
5311
5312 var cur,
5313 i = 0,
5314 aup = a.parentNode,
5315 bup = b.parentNode,
5316 ap = [ a ],
5317 bp = [ b ];
5318
5319 // Parentless nodes are either documents or disconnected
5320 if ( !aup || !bup ) {
5321 return a === document ? -1 :
5322 b === document ? 1 :
5323 aup ? -1 :
5324 bup ? 1 :
5325 sortInput ?
5326 ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) :
5327 0;
5328
5329 // If the nodes are siblings, we can do a quick check
5330 } else if ( aup === bup ) {
5331 return siblingCheck( a, b );
5332 }
5333
5334 // Otherwise we need full lists of their ancestors for comparison
5335 cur = a;
5336 while ( (cur = cur.parentNode) ) {
5337 ap.unshift( cur );
5338 }
5339 cur = b;
5340 while ( (cur = cur.parentNode) ) {
5341 bp.unshift( cur );
5342 }
5343
5344 // Walk down the tree looking for a discrepancy
5345 while ( ap[i] === bp[i] ) {
5346 i++;
5347 }
5348
5349 return i ?
5350 // Do a sibling check if the nodes have a common ancestor
5351 siblingCheck( ap[i], bp[i] ) :
5352
5353 // Otherwise nodes in our document sort first
5354 ap[i] === preferredDoc ? -1 :
5355 bp[i] === preferredDoc ? 1 :
5356 0;
5357 };
5358
5359 return document;
5360};
5361
5362Sizzle.matches = function( expr, elements ) {
5363 return Sizzle( expr, null, null, elements );
5364};
5365
5366Sizzle.matchesSelector = function( elem, expr ) {
5367 // Set document vars if needed
5368 if ( ( elem.ownerDocument || elem ) !== document ) {
5369 setDocument( elem );
5370 }
5371
5372 if ( support.matchesSelector && documentIsHTML &&
5373 !nonnativeSelectorCache[ expr + " " ] &&
5374 ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) &&
5375 ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
5376
5377 try {
5378 var ret = matches.call( elem, expr );
5379
5380 // IE 9's matchesSelector returns false on disconnected nodes
5381 if ( ret || support.disconnectedMatch ||
5382 // As well, disconnected nodes are said to be in a document
5383 // fragment in IE 9
5384 elem.document && elem.document.nodeType !== 11 ) {
5385 return ret;
5386 }
5387 } catch (e) {
5388 nonnativeSelectorCache( expr, true );
5389 }
5390 }
5391
5392 return Sizzle( expr, document, null, [ elem ] ).length > 0;
5393};
5394
5395Sizzle.contains = function( context, elem ) {
5396 // Set document vars if needed
5397 if ( ( context.ownerDocument || context ) !== document ) {
5398 setDocument( context );
5399 }
5400 return contains( context, elem );
5401};
5402
5403Sizzle.attr = function( elem, name ) {
5404 // Set document vars if needed
5405 if ( ( elem.ownerDocument || elem ) !== document ) {
5406 setDocument( elem );
5407 }
5408
5409 var fn = Expr.attrHandle[ name.toLowerCase() ],
5410 // Don't get fooled by Object.prototype properties (jQuery #13807)
5411 val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
5412 fn( elem, name, !documentIsHTML ) :
5413 undefined;
5414
5415 return val !== undefined ?
5416 val :
5417 support.attributes || !documentIsHTML ?
5418 elem.getAttribute( name ) :
5419 (val = elem.getAttributeNode(name)) && val.specified ?
5420 val.value :
5421 null;
5422};
5423
5424Sizzle.escape = function( sel ) {
5425 return (sel + "").replace( rcssescape, fcssescape );
5426};
5427
5428Sizzle.error = function( msg ) {
5429 throw new Error( "Syntax error, unrecognized expression: " + msg );
5430};
5431
5432/**
5433 * Document sorting and removing duplicates
5434 * @param {ArrayLike} results
5435 */
5436Sizzle.uniqueSort = function( results ) {
5437 var elem,
5438 duplicates = [],
5439 j = 0,
5440 i = 0;
5441
5442 // Unless we *know* we can detect duplicates, assume their presence
5443 hasDuplicate = !support.detectDuplicates;
5444 sortInput = !support.sortStable && results.slice( 0 );
5445 results.sort( sortOrder );
5446
5447 if ( hasDuplicate ) {
5448 while ( (elem = results[i++]) ) {
5449 if ( elem === results[ i ] ) {
5450 j = duplicates.push( i );
5451 }
5452 }
5453 while ( j-- ) {
5454 results.splice( duplicates[ j ], 1 );
5455 }
5456 }
5457
5458 // Clear input after sorting to release objects
5459 // See https://github.com/jquery/sizzle/pull/225
5460 sortInput = null;
5461
5462 return results;
5463};
5464
5465/**
5466 * Utility function for retrieving the text value of an array of DOM nodes
5467 * @param {Array|Element} elem
5468 */
5469getText = Sizzle.getText = function( elem ) {
5470 var node,
5471 ret = "",
5472 i = 0,
5473 nodeType = elem.nodeType;
5474
5475 if ( !nodeType ) {
5476 // If no nodeType, this is expected to be an array
5477 while ( (node = elem[i++]) ) {
5478 // Do not traverse comment nodes
5479 ret += getText( node );
5480 }
5481 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
5482 // Use textContent for elements
5483 // innerText usage removed for consistency of new lines (jQuery #11153)
5484 if ( typeof elem.textContent === "string" ) {
5485 return elem.textContent;
5486 } else {
5487 // Traverse its children
5488 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5489 ret += getText( elem );
5490 }
5491 }
5492 } else if ( nodeType === 3 || nodeType === 4 ) {
5493 return elem.nodeValue;
5494 }
5495 // Do not include comment or processing instruction nodes
5496
5497 return ret;
5498};
5499
5500Expr = Sizzle.selectors = {
5501
5502 // Can be adjusted by the user
5503 cacheLength: 50,
5504
5505 createPseudo: markFunction,
5506
5507 match: matchExpr,
5508
5509 attrHandle: {},
5510
5511 find: {},
5512
5513 relative: {
5514 ">": { dir: "parentNode", first: true },
5515 " ": { dir: "parentNode" },
5516 "+": { dir: "previousSibling", first: true },
5517 "~": { dir: "previousSibling" }
5518 },
5519
5520 preFilter: {
5521 "ATTR": function( match ) {
5522 match[1] = match[1].replace( runescape, funescape );
5523
5524 // Move the given value to match[3] whether quoted or unquoted
5525 match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape );
5526
5527 if ( match[2] === "~=" ) {
5528 match[3] = " " + match[3] + " ";
5529 }
5530
5531 return match.slice( 0, 4 );
5532 },
5533
5534 "CHILD": function( match ) {
5535 /* matches from matchExpr["CHILD"]
5536 1 type (only|nth|...)
5537 2 what (child|of-type)
5538 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
5539 4 xn-component of xn+y argument ([+-]?\d*n|)
5540 5 sign of xn-component
5541 6 x of xn-component
5542 7 sign of y-component
5543 8 y of y-component
5544 */
5545 match[1] = match[1].toLowerCase();
5546
5547 if ( match[1].slice( 0, 3 ) === "nth" ) {
5548 // nth-* requires argument
5549 if ( !match[3] ) {
5550 Sizzle.error( match[0] );
5551 }
5552
5553 // numeric x and y parameters for Expr.filter.CHILD
5554 // remember that false/true cast respectively to 0/1
5555 match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) );
5556 match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" );
5557
5558 // other types prohibit arguments
5559 } else if ( match[3] ) {
5560 Sizzle.error( match[0] );
5561 }
5562
5563 return match;
5564 },
5565
5566 "PSEUDO": function( match ) {
5567 var excess,
5568 unquoted = !match[6] && match[2];
5569
5570 if ( matchExpr["CHILD"].test( match[0] ) ) {
5571 return null;
5572 }
5573
5574 // Accept quoted arguments as-is
5575 if ( match[3] ) {
5576 match[2] = match[4] || match[5] || "";
5577
5578 // Strip excess characters from unquoted arguments
5579 } else if ( unquoted && rpseudo.test( unquoted ) &&
5580 // Get excess from tokenize (recursively)
5581 (excess = tokenize( unquoted, true )) &&
5582 // advance to the next closing parenthesis
5583 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
5584
5585 // excess is a negative index
5586 match[0] = match[0].slice( 0, excess );
5587 match[2] = unquoted.slice( 0, excess );
5588 }
5589
5590 // Return only captures needed by the pseudo filter method (type and argument)
5591 return match.slice( 0, 3 );
5592 }
5593 },
5594
5595 filter: {
5596
5597 "TAG": function( nodeNameSelector ) {
5598 var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
5599 return nodeNameSelector === "*" ?
5600 function() { return true; } :
5601 function( elem ) {
5602 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
5603 };
5604 },
5605
5606 "CLASS": function( className ) {
5607 var pattern = classCache[ className + " " ];
5608
5609 return pattern ||
5610 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
5611 classCache( className, function( elem ) {
5612 return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" );
5613 });
5614 },
5615
5616 "ATTR": function( name, operator, check ) {
5617 return function( elem ) {
5618 var result = Sizzle.attr( elem, name );
5619
5620 if ( result == null ) {
5621 return operator === "!=";
5622 }
5623 if ( !operator ) {
5624 return true;
5625 }
5626
5627 result += "";
5628
5629 return operator === "=" ? result === check :
5630 operator === "!=" ? result !== check :
5631 operator === "^=" ? check && result.indexOf( check ) === 0 :
5632 operator === "*=" ? check && result.indexOf( check ) > -1 :
5633 operator === "$=" ? check && result.slice( -check.length ) === check :
5634 operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 :
5635 operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" :
5636 false;
5637 };
5638 },
5639
5640 "CHILD": function( type, what, argument, first, last ) {
5641 var simple = type.slice( 0, 3 ) !== "nth",
5642 forward = type.slice( -4 ) !== "last",
5643 ofType = what === "of-type";
5644
5645 return first === 1 && last === 0 ?
5646
5647 // Shortcut for :nth-*(n)
5648 function( elem ) {
5649 return !!elem.parentNode;
5650 } :
5651
5652 function( elem, context, xml ) {
5653 var cache, uniqueCache, outerCache, node, nodeIndex, start,
5654 dir = simple !== forward ? "nextSibling" : "previousSibling",
5655 parent = elem.parentNode,
5656 name = ofType && elem.nodeName.toLowerCase(),
5657 useCache = !xml && !ofType,
5658 diff = false;
5659
5660 if ( parent ) {
5661
5662 // :(first|last|only)-(child|of-type)
5663 if ( simple ) {
5664 while ( dir ) {
5665 node = elem;
5666 while ( (node = node[ dir ]) ) {
5667 if ( ofType ?
5668 node.nodeName.toLowerCase() === name :
5669 node.nodeType === 1 ) {
5670
5671 return false;
5672 }
5673 }
5674 // Reverse direction for :only-* (if we haven't yet done so)
5675 start = dir = type === "only" && !start && "nextSibling";
5676 }
5677 return true;
5678 }
5679
5680 start = [ forward ? parent.firstChild : parent.lastChild ];
5681
5682 // non-xml :nth-child(...) stores cache data on `parent`
5683 if ( forward && useCache ) {
5684
5685 // Seek `elem` from a previously-cached index
5686
5687 // ...in a gzip-friendly way
5688 node = parent;
5689 outerCache = node[ expando ] || (node[ expando ] = {});
5690
5691 // Support: IE <9 only
5692 // Defend against cloned attroperties (jQuery gh-1709)
5693 uniqueCache = outerCache[ node.uniqueID ] ||
5694 (outerCache[ node.uniqueID ] = {});
5695
5696 cache = uniqueCache[ type ] || [];
5697 nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
5698 diff = nodeIndex && cache[ 2 ];
5699 node = nodeIndex && parent.childNodes[ nodeIndex ];
5700
5701 while ( (node = ++nodeIndex && node && node[ dir ] ||
5702
5703 // Fallback to seeking `elem` from the start
5704 (diff = nodeIndex = 0) || start.pop()) ) {
5705
5706 // When found, cache indexes on `parent` and break
5707 if ( node.nodeType === 1 && ++diff && node === elem ) {
5708 uniqueCache[ type ] = [ dirruns, nodeIndex, diff ];
5709 break;
5710 }
5711 }
5712
5713 } else {
5714 // Use previously-cached element index if available
5715 if ( useCache ) {
5716 // ...in a gzip-friendly way
5717 node = elem;
5718 outerCache = node[ expando ] || (node[ expando ] = {});
5719
5720 // Support: IE <9 only
5721 // Defend against cloned attroperties (jQuery gh-1709)
5722 uniqueCache = outerCache[ node.uniqueID ] ||
5723 (outerCache[ node.uniqueID ] = {});
5724
5725 cache = uniqueCache[ type ] || [];
5726 nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
5727 diff = nodeIndex;
5728 }
5729
5730 // xml :nth-child(...)
5731 // or :nth-last-child(...) or :nth(-last)?-of-type(...)
5732 if ( diff === false ) {
5733 // Use the same loop as above to seek `elem` from the start
5734 while ( (node = ++nodeIndex && node && node[ dir ] ||
5735 (diff = nodeIndex = 0) || start.pop()) ) {
5736
5737 if ( ( ofType ?
5738 node.nodeName.toLowerCase() === name :
5739 node.nodeType === 1 ) &&
5740 ++diff ) {
5741
5742 // Cache the index of each encountered element
5743 if ( useCache ) {
5744 outerCache = node[ expando ] || (node[ expando ] = {});
5745
5746 // Support: IE <9 only
5747 // Defend against cloned attroperties (jQuery gh-1709)
5748 uniqueCache = outerCache[ node.uniqueID ] ||
5749 (outerCache[ node.uniqueID ] = {});
5750
5751 uniqueCache[ type ] = [ dirruns, diff ];
5752 }
5753
5754 if ( node === elem ) {
5755 break;
5756 }
5757 }
5758 }
5759 }
5760 }
5761
5762 // Incorporate the offset, then check against cycle size
5763 diff -= last;
5764 return diff === first || ( diff % first === 0 && diff / first >= 0 );
5765 }
5766 };
5767 },
5768
5769 "PSEUDO": function( pseudo, argument ) {
5770 // pseudo-class names are case-insensitive
5771 // http://www.w3.org/TR/selectors/#pseudo-classes
5772 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
5773 // Remember that setFilters inherits from pseudos
5774 var args,
5775 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
5776 Sizzle.error( "unsupported pseudo: " + pseudo );
5777
5778 // The user may use createPseudo to indicate that
5779 // arguments are needed to create the filter function
5780 // just as Sizzle does
5781 if ( fn[ expando ] ) {
5782 return fn( argument );
5783 }
5784
5785 // But maintain support for old signatures
5786 if ( fn.length > 1 ) {
5787 args = [ pseudo, pseudo, "", argument ];
5788 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
5789 markFunction(function( seed, matches ) {
5790 var idx,
5791 matched = fn( seed, argument ),
5792 i = matched.length;
5793 while ( i-- ) {
5794 idx = indexOf( seed, matched[i] );
5795 seed[ idx ] = !( matches[ idx ] = matched[i] );
5796 }
5797 }) :
5798 function( elem ) {
5799 return fn( elem, 0, args );
5800 };
5801 }
5802
5803 return fn;
5804 }
5805 },
5806
5807 pseudos: {
5808 // Potentially complex pseudos
5809 "not": markFunction(function( selector ) {
5810 // Trim the selector passed to compile
5811 // to avoid treating leading and trailing
5812 // spaces as combinators
5813 var input = [],
5814 results = [],
5815 matcher = compile( selector.replace( rtrim, "$1" ) );
5816
5817 return matcher[ expando ] ?
5818 markFunction(function( seed, matches, context, xml ) {
5819 var elem,
5820 unmatched = matcher( seed, null, xml, [] ),
5821 i = seed.length;
5822
5823 // Match elements unmatched by `matcher`
5824 while ( i-- ) {
5825 if ( (elem = unmatched[i]) ) {
5826 seed[i] = !(matches[i] = elem);
5827 }
5828 }
5829 }) :
5830 function( elem, context, xml ) {
5831 input[0] = elem;
5832 matcher( input, null, xml, results );
5833 // Don't keep the element (issue #299)
5834 input[0] = null;
5835 return !results.pop();
5836 };
5837 }),
5838
5839 "has": markFunction(function( selector ) {
5840 return function( elem ) {
5841 return Sizzle( selector, elem ).length > 0;
5842 };
5843 }),
5844
5845 "contains": markFunction(function( text ) {
5846 text = text.replace( runescape, funescape );
5847 return function( elem ) {
5848 return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1;
5849 };
5850 }),
5851
5852 // "Whether an element is represented by a :lang() selector
5853 // is based solely on the element's language value
5854 // being equal to the identifier C,
5855 // or beginning with the identifier C immediately followed by "-".
5856 // The matching of C against the element's language value is performed case-insensitively.
5857 // The identifier C does not have to be a valid language name."
5858 // http://www.w3.org/TR/selectors/#lang-pseudo
5859 "lang": markFunction( function( lang ) {
5860 // lang value must be a valid identifier
5861 if ( !ridentifier.test(lang || "") ) {
5862 Sizzle.error( "unsupported lang: " + lang );
5863 }
5864 lang = lang.replace( runescape, funescape ).toLowerCase();
5865 return function( elem ) {
5866 var elemLang;
5867 do {
5868 if ( (elemLang = documentIsHTML ?
5869 elem.lang :
5870 elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) {
5871
5872 elemLang = elemLang.toLowerCase();
5873 return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
5874 }
5875 } while ( (elem = elem.parentNode) && elem.nodeType === 1 );
5876 return false;
5877 };
5878 }),
5879
5880 // Miscellaneous
5881 "target": function( elem ) {
5882 var hash = window.location && window.location.hash;
5883 return hash && hash.slice( 1 ) === elem.id;
5884 },
5885
5886 "root": function( elem ) {
5887 return elem === docElem;
5888 },
5889
5890 "focus": function( elem ) {
5891 return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
5892 },
5893
5894 // Boolean properties
5895 "enabled": createDisabledPseudo( false ),
5896 "disabled": createDisabledPseudo( true ),
5897
5898 "checked": function( elem ) {
5899 // In CSS3, :checked should return both checked and selected elements
5900 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
5901 var nodeName = elem.nodeName.toLowerCase();
5902 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
5903 },
5904
5905 "selected": function( elem ) {
5906 // Accessing this property makes selected-by-default
5907 // options in Safari work properly
5908 if ( elem.parentNode ) {
5909 elem.parentNode.selectedIndex;
5910 }
5911
5912 return elem.selected === true;
5913 },
5914
5915 // Contents
5916 "empty": function( elem ) {
5917 // http://www.w3.org/TR/selectors/#empty-pseudo
5918 // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5),
5919 // but not by others (comment: 8; processing instruction: 7; etc.)
5920 // nodeType < 6 works because attributes (2) do not appear as children
5921 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
5922 if ( elem.nodeType < 6 ) {
5923 return false;
5924 }
5925 }
5926 return true;
5927 },
5928
5929 "parent": function( elem ) {
5930 return !Expr.pseudos["empty"]( elem );
5931 },
5932
5933 // Element/input types
5934 "header": function( elem ) {
5935 return rheader.test( elem.nodeName );
5936 },
5937
5938 "input": function( elem ) {
5939 return rinputs.test( elem.nodeName );
5940 },
5941
5942 "button": function( elem ) {
5943 var name = elem.nodeName.toLowerCase();
5944 return name === "input" && elem.type === "button" || name === "button";
5945 },
5946
5947 "text": function( elem ) {
5948 var attr;
5949 return elem.nodeName.toLowerCase() === "input" &&
5950 elem.type === "text" &&
5951
5952 // Support: IE<8
5953 // New HTML5 attribute values (e.g., "search") appear with elem.type === "text"
5954 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" );
5955 },
5956
5957 // Position-in-collection
5958 "first": createPositionalPseudo(function() {
5959 return [ 0 ];
5960 }),
5961
5962 "last": createPositionalPseudo(function( matchIndexes, length ) {
5963 return [ length - 1 ];
5964 }),
5965
5966 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
5967 return [ argument < 0 ? argument + length : argument ];
5968 }),
5969
5970 "even": createPositionalPseudo(function( matchIndexes, length ) {
5971 var i = 0;
5972 for ( ; i < length; i += 2 ) {
5973 matchIndexes.push( i );
5974 }
5975 return matchIndexes;
5976 }),
5977
5978 "odd": createPositionalPseudo(function( matchIndexes, length ) {
5979 var i = 1;
5980 for ( ; i < length; i += 2 ) {
5981 matchIndexes.push( i );
5982 }
5983 return matchIndexes;
5984 }),
5985
5986 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5987 var i = argument < 0 ?
5988 argument + length :
5989 argument > length ?
5990 length :
5991 argument;
5992 for ( ; --i >= 0; ) {
5993 matchIndexes.push( i );
5994 }
5995 return matchIndexes;
5996 }),
5997
5998 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
5999 var i = argument < 0 ? argument + length : argument;
6000 for ( ; ++i < length; ) {
6001 matchIndexes.push( i );
6002 }
6003 return matchIndexes;
6004 })
6005 }
6006};
6007
6008Expr.pseudos["nth"] = Expr.pseudos["eq"];
6009
6010// Add button/input type pseudos
6011for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
6012 Expr.pseudos[ i ] = createInputPseudo( i );
6013}
6014for ( i in { submit: true, reset: true } ) {
6015 Expr.pseudos[ i ] = createButtonPseudo( i );
6016}
6017
6018// Easy API for creating new setFilters
6019function setFilters() {}
6020setFilters.prototype = Expr.filters = Expr.pseudos;
6021Expr.setFilters = new setFilters();
6022
6023tokenize = Sizzle.tokenize = function( selector, parseOnly ) {
6024 var matched, match, tokens, type,
6025 soFar, groups, preFilters,
6026 cached = tokenCache[ selector + " " ];
6027
6028 if ( cached ) {
6029 return parseOnly ? 0 : cached.slice( 0 );
6030 }
6031
6032 soFar = selector;
6033 groups = [];
6034 preFilters = Expr.preFilter;
6035
6036 while ( soFar ) {
6037
6038 // Comma and first run
6039 if ( !matched || (match = rcomma.exec( soFar )) ) {
6040 if ( match ) {
6041 // Don't consume trailing commas as valid
6042 soFar = soFar.slice( match[0].length ) || soFar;
6043 }
6044 groups.push( (tokens = []) );
6045 }
6046
6047 matched = false;
6048
6049 // Combinators
6050 if ( (match = rcombinators.exec( soFar )) ) {
6051 matched = match.shift();
6052 tokens.push({
6053 value: matched,
6054 // Cast descendant combinators to space
6055 type: match[0].replace( rtrim, " " )
6056 });
6057 soFar = soFar.slice( matched.length );
6058 }
6059
6060 // Filters
6061 for ( type in Expr.filter ) {
6062 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
6063 (match = preFilters[ type ]( match ))) ) {
6064 matched = match.shift();
6065 tokens.push({
6066 value: matched,
6067 type: type,
6068 matches: match
6069 });
6070 soFar = soFar.slice( matched.length );
6071 }
6072 }
6073
6074 if ( !matched ) {
6075 break;
6076 }
6077 }
6078
6079 // Return the length of the invalid excess
6080 // if we're just parsing
6081 // Otherwise, throw an error or return tokens
6082 return parseOnly ?
6083 soFar.length :
6084 soFar ?
6085 Sizzle.error( selector ) :
6086 // Cache the tokens
6087 tokenCache( selector, groups ).slice( 0 );
6088};
6089
6090function toSelector( tokens ) {
6091 var i = 0,
6092 len = tokens.length,
6093 selector = "";
6094 for ( ; i < len; i++ ) {
6095 selector += tokens[i].value;
6096 }
6097 return selector;
6098}
6099
6100function addCombinator( matcher, combinator, base ) {
6101 var dir = combinator.dir,
6102 skip = combinator.next,
6103 key = skip || dir,
6104 checkNonElements = base && key === "parentNode",
6105 doneName = done++;
6106
6107 return combinator.first ?
6108 // Check against closest ancestor/preceding element
6109 function( elem, context, xml ) {
6110 while ( (elem = elem[ dir ]) ) {
6111 if ( elem.nodeType === 1 || checkNonElements ) {
6112 return matcher( elem, context, xml );
6113 }
6114 }
6115 return false;
6116 } :
6117
6118 // Check against all ancestor/preceding elements
6119 function( elem, context, xml ) {
6120 var oldCache, uniqueCache, outerCache,
6121 newCache = [ dirruns, doneName ];
6122
6123 // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching
6124 if ( xml ) {
6125 while ( (elem = elem[ dir ]) ) {
6126 if ( elem.nodeType === 1 || checkNonElements ) {
6127 if ( matcher( elem, context, xml ) ) {
6128 return true;
6129 }
6130 }
6131 }
6132 } else {
6133 while ( (elem = elem[ dir ]) ) {
6134 if ( elem.nodeType === 1 || checkNonElements ) {
6135 outerCache = elem[ expando ] || (elem[ expando ] = {});
6136
6137 // Support: IE <9 only
6138 // Defend against cloned attroperties (jQuery gh-1709)
6139 uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {});
6140
6141 if ( skip && skip === elem.nodeName.toLowerCase() ) {
6142 elem = elem[ dir ] || elem;
6143 } else if ( (oldCache = uniqueCache[ key ]) &&
6144 oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
6145
6146 // Assign to newCache so results back-propagate to previous elements
6147 return (newCache[ 2 ] = oldCache[ 2 ]);
6148 } else {
6149 // Reuse newcache so results back-propagate to previous elements
6150 uniqueCache[ key ] = newCache;
6151
6152 // A match means we're done; a fail means we have to keep checking
6153 if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) {
6154 return true;
6155 }
6156 }
6157 }
6158 }
6159 }
6160 return false;
6161 };
6162}
6163
6164function elementMatcher( matchers ) {
6165 return matchers.length > 1 ?
6166 function( elem, context, xml ) {
6167 var i = matchers.length;
6168 while ( i-- ) {
6169 if ( !matchers[i]( elem, context, xml ) ) {
6170 return false;
6171 }
6172 }
6173 return true;
6174 } :
6175 matchers[0];
6176}
6177
6178function multipleContexts( selector, contexts, results ) {
6179 var i = 0,
6180 len = contexts.length;
6181 for ( ; i < len; i++ ) {
6182 Sizzle( selector, contexts[i], results );
6183 }
6184 return results;
6185}
6186
6187function condense( unmatched, map, filter, context, xml ) {
6188 var elem,
6189 newUnmatched = [],
6190 i = 0,
6191 len = unmatched.length,
6192 mapped = map != null;
6193
6194 for ( ; i < len; i++ ) {
6195 if ( (elem = unmatched[i]) ) {
6196 if ( !filter || filter( elem, context, xml ) ) {
6197 newUnmatched.push( elem );
6198 if ( mapped ) {
6199 map.push( i );
6200 }
6201 }
6202 }
6203 }
6204
6205 return newUnmatched;
6206}
6207
6208function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
6209 if ( postFilter && !postFilter[ expando ] ) {
6210 postFilter = setMatcher( postFilter );
6211 }
6212 if ( postFinder && !postFinder[ expando ] ) {
6213 postFinder = setMatcher( postFinder, postSelector );
6214 }
6215 return markFunction(function( seed, results, context, xml ) {
6216 var temp, i, elem,
6217 preMap = [],
6218 postMap = [],
6219 preexisting = results.length,
6220
6221 // Get initial elements from seed or context
6222 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
6223
6224 // Prefilter to get matcher input, preserving a map for seed-results synchronization
6225 matcherIn = preFilter && ( seed || !selector ) ?
6226 condense( elems, preMap, preFilter, context, xml ) :
6227 elems,
6228
6229 matcherOut = matcher ?
6230 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
6231 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
6232
6233 // ...intermediate processing is necessary
6234 [] :
6235
6236 // ...otherwise use results directly
6237 results :
6238 matcherIn;
6239
6240 // Find primary matches
6241 if ( matcher ) {
6242 matcher( matcherIn, matcherOut, context, xml );
6243 }
6244
6245 // Apply postFilter
6246 if ( postFilter ) {
6247 temp = condense( matcherOut, postMap );
6248 postFilter( temp, [], context, xml );
6249
6250 // Un-match failing elements by moving them back to matcherIn
6251 i = temp.length;
6252 while ( i-- ) {
6253 if ( (elem = temp[i]) ) {
6254 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
6255 }
6256 }
6257 }
6258
6259 if ( seed ) {
6260 if ( postFinder || preFilter ) {
6261 if ( postFinder ) {
6262 // Get the final matcherOut by condensing this intermediate into postFinder contexts
6263 temp = [];
6264 i = matcherOut.length;
6265 while ( i-- ) {
6266 if ( (elem = matcherOut[i]) ) {
6267 // Restore matcherIn since elem is not yet a final match
6268 temp.push( (matcherIn[i] = elem) );
6269 }
6270 }
6271 postFinder( null, (matcherOut = []), temp, xml );
6272 }
6273
6274 // Move matched elements from seed to results to keep them synchronized
6275 i = matcherOut.length;
6276 while ( i-- ) {
6277 if ( (elem = matcherOut[i]) &&
6278 (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) {
6279
6280 seed[temp] = !(results[temp] = elem);
6281 }
6282 }
6283 }
6284
6285 // Add elements to results, through postFinder if defined
6286 } else {
6287 matcherOut = condense(
6288 matcherOut === results ?
6289 matcherOut.splice( preexisting, matcherOut.length ) :
6290 matcherOut
6291 );
6292 if ( postFinder ) {
6293 postFinder( null, results, matcherOut, xml );
6294 } else {
6295 push.apply( results, matcherOut );
6296 }
6297 }
6298 });
6299}
6300
6301function matcherFromTokens( tokens ) {
6302 var checkContext, matcher, j,
6303 len = tokens.length,
6304 leadingRelative = Expr.relative[ tokens[0].type ],
6305 implicitRelative = leadingRelative || Expr.relative[" "],
6306 i = leadingRelative ? 1 : 0,
6307
6308 // The foundational matcher ensures that elements are reachable from top-level context(s)
6309 matchContext = addCombinator( function( elem ) {
6310 return elem === checkContext;
6311 }, implicitRelative, true ),
6312 matchAnyContext = addCombinator( function( elem ) {
6313 return indexOf( checkContext, elem ) > -1;
6314 }, implicitRelative, true ),
6315 matchers = [ function( elem, context, xml ) {
6316 var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
6317 (checkContext = context).nodeType ?
6318 matchContext( elem, context, xml ) :
6319 matchAnyContext( elem, context, xml ) );
6320 // Avoid hanging onto element (issue #299)
6321 checkContext = null;
6322 return ret;
6323 } ];
6324
6325 for ( ; i < len; i++ ) {
6326 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
6327 matchers = [ addCombinator(elementMatcher( matchers ), matcher) ];
6328 } else {
6329 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
6330
6331 // Return special upon seeing a positional matcher
6332 if ( matcher[ expando ] ) {
6333 // Find the next relative operator (if any) for proper handling
6334 j = ++i;
6335 for ( ; j < len; j++ ) {
6336 if ( Expr.relative[ tokens[j].type ] ) {
6337 break;
6338 }
6339 }
6340 return setMatcher(
6341 i > 1 && elementMatcher( matchers ),
6342 i > 1 && toSelector(
6343 // If the preceding token was a descendant combinator, insert an implicit any-element `*`
6344 tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" })
6345 ).replace( rtrim, "$1" ),
6346 matcher,
6347 i < j && matcherFromTokens( tokens.slice( i, j ) ),
6348 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
6349 j < len && toSelector( tokens )
6350 );
6351 }
6352 matchers.push( matcher );
6353 }
6354 }
6355
6356 return elementMatcher( matchers );
6357}
6358
6359function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
6360 var bySet = setMatchers.length > 0,
6361 byElement = elementMatchers.length > 0,
6362 superMatcher = function( seed, context, xml, results, outermost ) {
6363 var elem, j, matcher,
6364 matchedCount = 0,
6365 i = "0",
6366 unmatched = seed && [],
6367 setMatched = [],
6368 contextBackup = outermostContext,
6369 // We must always have either seed elements or outermost context
6370 elems = seed || byElement && Expr.find["TAG"]( "*", outermost ),
6371 // Use integer dirruns iff this is the outermost matcher
6372 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
6373 len = elems.length;
6374
6375 if ( outermost ) {
6376 outermostContext = context === document || context || outermost;
6377 }
6378
6379 // Add elements passing elementMatchers directly to results
6380 // Support: IE<9, Safari
6381 // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
6382 for ( ; i !== len && (elem = elems[i]) != null; i++ ) {
6383 if ( byElement && elem ) {
6384 j = 0;
6385 if ( !context && elem.ownerDocument !== document ) {
6386 setDocument( elem );
6387 xml = !documentIsHTML;
6388 }
6389 while ( (matcher = elementMatchers[j++]) ) {
6390 if ( matcher( elem, context || document, xml) ) {
6391 results.push( elem );
6392 break;
6393 }
6394 }
6395 if ( outermost ) {
6396 dirruns = dirrunsUnique;
6397 }
6398 }
6399
6400 // Track unmatched elements for set filters
6401 if ( bySet ) {
6402 // They will have gone through all possible matchers
6403 if ( (elem = !matcher && elem) ) {
6404 matchedCount--;
6405 }
6406
6407 // Lengthen the array for every element, matched or not
6408 if ( seed ) {
6409 unmatched.push( elem );
6410 }
6411 }
6412 }
6413
6414 // `i` is now the count of elements visited above, and adding it to `matchedCount`
6415 // makes the latter nonnegative.
6416 matchedCount += i;
6417
6418 // Apply set filters to unmatched elements
6419 // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount`
6420 // equals `i`), unless we didn't visit _any_ elements in the above loop because we have
6421 // no element matchers and no seed.
6422 // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that
6423 // case, which will result in a "00" `matchedCount` that differs from `i` but is also
6424 // numerically zero.
6425 if ( bySet && i !== matchedCount ) {
6426 j = 0;
6427 while ( (matcher = setMatchers[j++]) ) {
6428 matcher( unmatched, setMatched, context, xml );
6429 }
6430
6431 if ( seed ) {
6432 // Reintegrate element matches to eliminate the need for sorting
6433 if ( matchedCount > 0 ) {
6434 while ( i-- ) {
6435 if ( !(unmatched[i] || setMatched[i]) ) {
6436 setMatched[i] = pop.call( results );
6437 }
6438 }
6439 }
6440
6441 // Discard index placeholder values to get only actual matches
6442 setMatched = condense( setMatched );
6443 }
6444
6445 // Add matches to results
6446 push.apply( results, setMatched );
6447
6448 // Seedless set matches succeeding multiple successful matchers stipulate sorting
6449 if ( outermost && !seed && setMatched.length > 0 &&
6450 ( matchedCount + setMatchers.length ) > 1 ) {
6451
6452 Sizzle.uniqueSort( results );
6453 }
6454 }
6455
6456 // Override manipulation of globals by nested matchers
6457 if ( outermost ) {
6458 dirruns = dirrunsUnique;
6459 outermostContext = contextBackup;
6460 }
6461
6462 return unmatched;
6463 };
6464
6465 return bySet ?
6466 markFunction( superMatcher ) :
6467 superMatcher;
6468}
6469
6470compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) {
6471 var i,
6472 setMatchers = [],
6473 elementMatchers = [],
6474 cached = compilerCache[ selector + " " ];
6475
6476 if ( !cached ) {
6477 // Generate a function of recursive functions that can be used to check each element
6478 if ( !match ) {
6479 match = tokenize( selector );
6480 }
6481 i = match.length;
6482 while ( i-- ) {
6483 cached = matcherFromTokens( match[i] );
6484 if ( cached[ expando ] ) {
6485 setMatchers.push( cached );
6486 } else {
6487 elementMatchers.push( cached );
6488 }
6489 }
6490
6491 // Cache the compiled function
6492 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
6493
6494 // Save selector and tokenization
6495 cached.selector = selector;
6496 }
6497 return cached;
6498};
6499
6500/**
6501 * A low-level selection function that works with Sizzle's compiled
6502 * selector functions
6503 * @param {String|Function} selector A selector or a pre-compiled
6504 * selector function built with Sizzle.compile
6505 * @param {Element} context
6506 * @param {Array} [results]
6507 * @param {Array} [seed] A set of elements to match against
6508 */
6509select = Sizzle.select = function( selector, context, results, seed ) {
6510 var i, tokens, token, type, find,
6511 compiled = typeof selector === "function" && selector,
6512 match = !seed && tokenize( (selector = compiled.selector || selector) );
6513
6514 results = results || [];
6515
6516 // Try to minimize operations if there is only one selector in the list and no seed
6517 // (the latter of which guarantees us context)
6518 if ( match.length === 1 ) {
6519
6520 // Reduce context if the leading compound selector is an ID
6521 tokens = match[0] = match[0].slice( 0 );
6522 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
6523 context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[1].type ] ) {
6524
6525 context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0];
6526 if ( !context ) {
6527 return results;
6528
6529 // Precompiled matchers will still verify ancestry, so step up a level
6530 } else if ( compiled ) {
6531 context = context.parentNode;
6532 }
6533
6534 selector = selector.slice( tokens.shift().value.length );
6535 }
6536
6537 // Fetch a seed set for right-to-left matching
6538 i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length;
6539 while ( i-- ) {
6540 token = tokens[i];
6541
6542 // Abort if we hit a combinator
6543 if ( Expr.relative[ (type = token.type) ] ) {
6544 break;
6545 }
6546 if ( (find = Expr.find[ type ]) ) {
6547 // Search, expanding context for leading sibling combinators
6548 if ( (seed = find(
6549 token.matches[0].replace( runescape, funescape ),
6550 rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context
6551 )) ) {
6552
6553 // If seed is empty or no tokens remain, we can return early
6554 tokens.splice( i, 1 );
6555 selector = seed.length && toSelector( tokens );
6556 if ( !selector ) {
6557 push.apply( results, seed );
6558 return results;
6559 }
6560
6561 break;
6562 }
6563 }
6564 }
6565 }
6566
6567 // Compile and execute a filtering function if one is not provided
6568 // Provide `match` to avoid retokenization if we modified the selector above
6569 ( compiled || compile( selector, match ) )(
6570 seed,
6571 context,
6572 !documentIsHTML,
6573 results,
6574 !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
6575 );
6576 return results;
6577};
6578
6579// One-time assignments
6580
6581// Sort stability
6582support.sortStable = expando.split("").sort( sortOrder ).join("") === expando;
6583
6584// Support: Chrome 14-35+
6585// Always assume duplicates if they aren't passed to the comparison function
6586support.detectDuplicates = !!hasDuplicate;
6587
6588// Initialize against the default document
6589setDocument();
6590
6591// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
6592// Detached nodes confoundingly follow *each other*
6593support.sortDetached = assert(function( el ) {
6594 // Should return 1, but returns 4 (following)
6595 return el.compareDocumentPosition( document.createElement("fieldset") ) & 1;
6596});
6597
6598// Support: IE<8
6599// Prevent attribute/property "interpolation"
6600// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
6601if ( !assert(function( el ) {
6602 el.innerHTML = "<a href='#'></a>";
6603 return el.firstChild.getAttribute("href") === "#" ;
6604}) ) {
6605 addHandle( "type|href|height|width", function( elem, name, isXML ) {
6606 if ( !isXML ) {
6607 return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 );
6608 }
6609 });
6610}
6611
6612// Support: IE<9
6613// Use defaultValue in place of getAttribute("value")
6614if ( !support.attributes || !assert(function( el ) {
6615 el.innerHTML = "<input/>";
6616 el.firstChild.setAttribute( "value", "" );
6617 return el.firstChild.getAttribute( "value" ) === "";
6618}) ) {
6619 addHandle( "value", function( elem, name, isXML ) {
6620 if ( !isXML && elem.nodeName.toLowerCase() === "input" ) {
6621 return elem.defaultValue;
6622 }
6623 });
6624}
6625
6626// Support: IE<9
6627// Use getAttributeNode to fetch booleans when getAttribute lies
6628if ( !assert(function( el ) {
6629 return el.getAttribute("disabled") == null;
6630}) ) {
6631 addHandle( booleans, function( elem, name, isXML ) {
6632 var val;
6633 if ( !isXML ) {
6634 return elem[ name ] === true ? name.toLowerCase() :
6635 (val = elem.getAttributeNode( name )) && val.specified ?
6636 val.value :
6637 null;
6638 }
6639 });
6640}
6641
6642return Sizzle;
6643
6644})( window );
6645
6646
6647
6648jQuery.find = Sizzle;
6649jQuery.expr = Sizzle.selectors;
6650
6651// Deprecated
6652jQuery.expr[ ":" ] = jQuery.expr.pseudos;
6653jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort;
6654jQuery.text = Sizzle.getText;
6655jQuery.isXMLDoc = Sizzle.isXML;
6656jQuery.contains = Sizzle.contains;
6657jQuery.escapeSelector = Sizzle.escape;
6658
6659
6660
6661
6662var dir = function( elem, dir, until ) {
6663 var matched = [],
6664 truncate = until !== undefined;
6665
6666 while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
6667 if ( elem.nodeType === 1 ) {
6668 if ( truncate && jQuery( elem ).is( until ) ) {
6669 break;
6670 }
6671 matched.push( elem );
6672 }
6673 }
6674 return matched;
6675};
6676
6677
6678var siblings = function( n, elem ) {
6679 var matched = [];
6680
6681 for ( ; n; n = n.nextSibling ) {
6682 if ( n.nodeType === 1 && n !== elem ) {
6683 matched.push( n );
6684 }
6685 }
6686
6687 return matched;
6688};
6689
6690
6691var rneedsContext = jQuery.expr.match.needsContext;
6692
6693
6694
6695function nodeName( elem, name ) {
6696
6697 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
6698
6699};
6700var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
6701
6702
6703
6704// Implement the identical functionality for filter and not
6705function winnow( elements, qualifier, not ) {
6706 if ( isFunction( qualifier ) ) {
6707 return jQuery.grep( elements, function( elem, i ) {
6708 return !!qualifier.call( elem, i, elem ) !== not;
6709 } );
6710 }
6711
6712 // Single element
6713 if ( qualifier.nodeType ) {
6714 return jQuery.grep( elements, function( elem ) {
6715 return ( elem === qualifier ) !== not;
6716 } );
6717 }
6718
6719 // Arraylike of elements (jQuery, arguments, Array)
6720 if ( typeof qualifier !== "string" ) {
6721 return jQuery.grep( elements, function( elem ) {
6722 return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
6723 } );
6724 }
6725
6726 // Filtered directly for both simple and complex selectors
6727 return jQuery.filter( qualifier, elements, not );
6728}
6729
6730jQuery.filter = function( expr, elems, not ) {
6731 var elem = elems[ 0 ];
6732
6733 if ( not ) {
6734 expr = ":not(" + expr + ")";
6735 }
6736
6737 if ( elems.length === 1 && elem.nodeType === 1 ) {
6738 return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
6739 }
6740
6741 return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
6742 return elem.nodeType === 1;
6743 } ) );
6744};
6745
6746jQuery.fn.extend( {
6747 find: function( selector ) {
6748 var i, ret,
6749 len = this.length,
6750 self = this;
6751
6752 if ( typeof selector !== "string" ) {
6753 return this.pushStack( jQuery( selector ).filter( function() {
6754 for ( i = 0; i < len; i++ ) {
6755 if ( jQuery.contains( self[ i ], this ) ) {
6756 return true;
6757 }
6758 }
6759 } ) );
6760 }
6761
6762 ret = this.pushStack( [] );
6763
6764 for ( i = 0; i < len; i++ ) {
6765 jQuery.find( selector, self[ i ], ret );
6766 }
6767
6768 return len > 1 ? jQuery.uniqueSort( ret ) : ret;
6769 },
6770 filter: function( selector ) {
6771 return this.pushStack( winnow( this, selector || [], false ) );
6772 },
6773 not: function( selector ) {
6774 return this.pushStack( winnow( this, selector || [], true ) );
6775 },
6776 is: function( selector ) {
6777 return !!winnow(
6778 this,
6779
6780 // If this is a positional/relative selector, check membership in the returned set
6781 // so $("p:first").is("p:last") won't return true for a doc with two "p".
6782 typeof selector === "string" && rneedsContext.test( selector ) ?
6783 jQuery( selector ) :
6784 selector || [],
6785 false
6786 ).length;
6787 }
6788} );
6789
6790
6791// Initialize a jQuery object
6792
6793
6794// A central reference to the root jQuery(document)
6795var rootjQuery,
6796
6797 // A simple way to check for HTML strings
6798 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
6799 // Strict HTML recognition (#11290: must start with <)
6800 // Shortcut simple #id case for speed
6801 rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
6802
6803 init = jQuery.fn.init = function( selector, context, root ) {
6804 var match, elem;
6805
6806 // HANDLE: $(""), $(null), $(undefined), $(false)
6807 if ( !selector ) {
6808 return this;
6809 }
6810
6811 // Method init() accepts an alternate rootjQuery
6812 // so migrate can support jQuery.sub (gh-2101)
6813 root = root || rootjQuery;
6814
6815 // Handle HTML strings
6816 if ( typeof selector === "string" ) {
6817 if ( selector[ 0 ] === "<" &&
6818 selector[ selector.length - 1 ] === ">" &&
6819 selector.length >= 3 ) {
6820
6821 // Assume that strings that start and end with <> are HTML and skip the regex check
6822 match = [ null, selector, null ];
6823
6824 } else {
6825 match = rquickExpr.exec( selector );
6826 }
6827
6828 // Match html or make sure no context is specified for #id
6829 if ( match && ( match[ 1 ] || !context ) ) {
6830
6831 // HANDLE: $(html) -> $(array)
6832 if ( match[ 1 ] ) {
6833 context = context instanceof jQuery ? context[ 0 ] : context;
6834
6835 // Option to run scripts is true for back-compat
6836 // Intentionally let the error be thrown if parseHTML is not present
6837 jQuery.merge( this, jQuery.parseHTML(
6838 match[ 1 ],
6839 context && context.nodeType ? context.ownerDocument || context : document,
6840 true
6841 ) );
6842
6843 // HANDLE: $(html, props)
6844 if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
6845 for ( match in context ) {
6846
6847 // Properties of context are called as methods if possible
6848 if ( isFunction( this[ match ] ) ) {
6849 this[ match ]( context[ match ] );
6850
6851 // ...and otherwise set as attributes
6852 } else {
6853 this.attr( match, context[ match ] );
6854 }
6855 }
6856 }
6857
6858 return this;
6859
6860 // HANDLE: $(#id)
6861 } else {
6862 elem = document.getElementById( match[ 2 ] );
6863
6864 if ( elem ) {
6865
6866 // Inject the element directly into the jQuery object
6867 this[ 0 ] = elem;
6868 this.length = 1;
6869 }
6870 return this;
6871 }
6872
6873 // HANDLE: $(expr, $(...))
6874 } else if ( !context || context.jquery ) {
6875 return ( context || root ).find( selector );
6876
6877 // HANDLE: $(expr, context)
6878 // (which is just equivalent to: $(context).find(expr)
6879 } else {
6880 return this.constructor( context ).find( selector );
6881 }
6882
6883 // HANDLE: $(DOMElement)
6884 } else if ( selector.nodeType ) {
6885 this[ 0 ] = selector;
6886 this.length = 1;
6887 return this;
6888
6889 // HANDLE: $(function)
6890 // Shortcut for document ready
6891 } else if ( isFunction( selector ) ) {
6892 return root.ready !== undefined ?
6893 root.ready( selector ) :
6894
6895 // Execute immediately if ready is not present
6896 selector( jQuery );
6897 }
6898
6899 return jQuery.makeArray( selector, this );
6900 };
6901
6902// Give the init function the jQuery prototype for later instantiation
6903init.prototype = jQuery.fn;
6904
6905// Initialize central reference
6906rootjQuery = jQuery( document );
6907
6908
6909var rparentsprev = /^(?:parents|prev(?:Until|All))/,
6910
6911 // Methods guaranteed to produce a unique set when starting from a unique set
6912 guaranteedUnique = {
6913 children: true,
6914 contents: true,
6915 next: true,
6916 prev: true
6917 };
6918
6919jQuery.fn.extend( {
6920 has: function( target ) {
6921 var targets = jQuery( target, this ),
6922 l = targets.length;
6923
6924 return this.filter( function() {
6925 var i = 0;
6926 for ( ; i < l; i++ ) {
6927 if ( jQuery.contains( this, targets[ i ] ) ) {
6928 return true;
6929 }
6930 }
6931 } );
6932 },
6933
6934 closest: function( selectors, context ) {
6935 var cur,
6936 i = 0,
6937 l = this.length,
6938 matched = [],
6939 targets = typeof selectors !== "string" && jQuery( selectors );
6940
6941 // Positional selectors never match, since there's no _selection_ context
6942 if ( !rneedsContext.test( selectors ) ) {
6943 for ( ; i < l; i++ ) {
6944 for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
6945
6946 // Always skip document fragments
6947 if ( cur.nodeType < 11 && ( targets ?
6948 targets.index( cur ) > -1 :
6949
6950 // Don't pass non-elements to Sizzle
6951 cur.nodeType === 1 &&
6952 jQuery.find.matchesSelector( cur, selectors ) ) ) {
6953
6954 matched.push( cur );
6955 break;
6956 }
6957 }
6958 }
6959 }
6960
6961 return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
6962 },
6963
6964 // Determine the position of an element within the set
6965 index: function( elem ) {
6966
6967 // No argument, return index in parent
6968 if ( !elem ) {
6969 return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
6970 }
6971
6972 // Index in selector
6973 if ( typeof elem === "string" ) {
6974 return indexOf.call( jQuery( elem ), this[ 0 ] );
6975 }
6976
6977 // Locate the position of the desired element
6978 return indexOf.call( this,
6979
6980 // If it receives a jQuery object, the first element is used
6981 elem.jquery ? elem[ 0 ] : elem
6982 );
6983 },
6984
6985 add: function( selector, context ) {
6986 return this.pushStack(
6987 jQuery.uniqueSort(
6988 jQuery.merge( this.get(), jQuery( selector, context ) )
6989 )
6990 );
6991 },
6992
6993 addBack: function( selector ) {
6994 return this.add( selector == null ?
6995 this.prevObject : this.prevObject.filter( selector )
6996 );
6997 }
6998} );
6999
7000function sibling( cur, dir ) {
7001 while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
7002 return cur;
7003}
7004
7005jQuery.each( {
7006 parent: function( elem ) {
7007 var parent = elem.parentNode;
7008 return parent && parent.nodeType !== 11 ? parent : null;
7009 },
7010 parents: function( elem ) {
7011 return dir( elem, "parentNode" );
7012 },
7013 parentsUntil: function( elem, i, until ) {
7014 return dir( elem, "parentNode", until );
7015 },
7016 next: function( elem ) {
7017 return sibling( elem, "nextSibling" );
7018 },
7019 prev: function( elem ) {
7020 return sibling( elem, "previousSibling" );
7021 },
7022 nextAll: function( elem ) {
7023 return dir( elem, "nextSibling" );
7024 },
7025 prevAll: function( elem ) {
7026 return dir( elem, "previousSibling" );
7027 },
7028 nextUntil: function( elem, i, until ) {
7029 return dir( elem, "nextSibling", until );
7030 },
7031 prevUntil: function( elem, i, until ) {
7032 return dir( elem, "previousSibling", until );
7033 },
7034 siblings: function( elem ) {
7035 return siblings( ( elem.parentNode || {} ).firstChild, elem );
7036 },
7037 children: function( elem ) {
7038 return siblings( elem.firstChild );
7039 },
7040 contents: function( elem ) {
7041 if ( typeof elem.contentDocument !== "undefined" ) {
7042 return elem.contentDocument;
7043 }
7044
7045 // Support: IE 9 - 11 only, iOS 7 only, Android Browser <=4.3 only
7046 // Treat the template element as a regular one in browsers that
7047 // don't support it.
7048 if ( nodeName( elem, "template" ) ) {
7049 elem = elem.content || elem;
7050 }
7051
7052 return jQuery.merge( [], elem.childNodes );
7053 }
7054}, function( name, fn ) {
7055 jQuery.fn[ name ] = function( until, selector ) {
7056 var matched = jQuery.map( this, fn, until );
7057
7058 if ( name.slice( -5 ) !== "Until" ) {
7059 selector = until;
7060 }
7061
7062 if ( selector && typeof selector === "string" ) {
7063 matched = jQuery.filter( selector, matched );
7064 }
7065
7066 if ( this.length > 1 ) {
7067
7068 // Remove duplicates
7069 if ( !guaranteedUnique[ name ] ) {
7070 jQuery.uniqueSort( matched );
7071 }
7072
7073 // Reverse order for parents* and prev-derivatives
7074 if ( rparentsprev.test( name ) ) {
7075 matched.reverse();
7076 }
7077 }
7078
7079 return this.pushStack( matched );
7080 };
7081} );
7082var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
7083
7084
7085
7086// Convert String-formatted options into Object-formatted ones
7087function createOptions( options ) {
7088 var object = {};
7089 jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
7090 object[ flag ] = true;
7091 } );
7092 return object;
7093}
7094
7095/*
7096 * Create a callback list using the following parameters:
7097 *
7098 * options: an optional list of space-separated options that will change how
7099 * the callback list behaves or a more traditional option object
7100 *
7101 * By default a callback list will act like an event callback list and can be
7102 * "fired" multiple times.
7103 *
7104 * Possible options:
7105 *
7106 * once: will ensure the callback list can only be fired once (like a Deferred)
7107 *
7108 * memory: will keep track of previous values and will call any callback added
7109 * after the list has been fired right away with the latest "memorized"
7110 * values (like a Deferred)
7111 *
7112 * unique: will ensure a callback can only be added once (no duplicate in the list)
7113 *
7114 * stopOnFalse: interrupt callings when a callback returns false
7115 *
7116 */
7117jQuery.Callbacks = function( options ) {
7118
7119 // Convert options from String-formatted to Object-formatted if needed
7120 // (we check in cache first)
7121 options = typeof options === "string" ?
7122 createOptions( options ) :
7123 jQuery.extend( {}, options );
7124
7125 var // Flag to know if list is currently firing
7126 firing,
7127
7128 // Last fire value for non-forgettable lists
7129 memory,
7130
7131 // Flag to know if list was already fired
7132 fired,
7133
7134 // Flag to prevent firing
7135 locked,
7136
7137 // Actual callback list
7138 list = [],
7139
7140 // Queue of execution data for repeatable lists
7141 queue = [],
7142
7143 // Index of currently firing callback (modified by add/remove as needed)
7144 firingIndex = -1,
7145
7146 // Fire callbacks
7147 fire = function() {
7148
7149 // Enforce single-firing
7150 locked = locked || options.once;
7151
7152 // Execute callbacks for all pending executions,
7153 // respecting firingIndex overrides and runtime changes
7154 fired = firing = true;
7155 for ( ; queue.length; firingIndex = -1 ) {
7156 memory = queue.shift();
7157 while ( ++firingIndex < list.length ) {
7158
7159 // Run callback and check for early termination
7160 if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
7161 options.stopOnFalse ) {
7162
7163 // Jump to end and forget the data so .add doesn't re-fire
7164 firingIndex = list.length;
7165 memory = false;
7166 }
7167 }
7168 }
7169
7170 // Forget the data if we're done with it
7171 if ( !options.memory ) {
7172 memory = false;
7173 }
7174
7175 firing = false;
7176
7177 // Clean up if we're done firing for good
7178 if ( locked ) {
7179
7180 // Keep an empty list if we have data for future add calls
7181 if ( memory ) {
7182 list = [];
7183
7184 // Otherwise, this object is spent
7185 } else {
7186 list = "";
7187 }
7188 }
7189 },
7190
7191 // Actual Callbacks object
7192 self = {
7193
7194 // Add a callback or a collection of callbacks to the list
7195 add: function() {
7196 if ( list ) {
7197
7198 // If we have memory from a past run, we should fire after adding
7199 if ( memory && !firing ) {
7200 firingIndex = list.length - 1;
7201 queue.push( memory );
7202 }
7203
7204 ( function add( args ) {
7205 jQuery.each( args, function( _, arg ) {
7206 if ( isFunction( arg ) ) {
7207 if ( !options.unique || !self.has( arg ) ) {
7208 list.push( arg );
7209 }
7210 } else if ( arg && arg.length && toType( arg ) !== "string" ) {
7211
7212 // Inspect recursively
7213 add( arg );
7214 }
7215 } );
7216 } )( arguments );
7217
7218 if ( memory && !firing ) {
7219 fire();
7220 }
7221 }
7222 return this;
7223 },
7224
7225 // Remove a callback from the list
7226 remove: function() {
7227 jQuery.each( arguments, function( _, arg ) {
7228 var index;
7229 while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
7230 list.splice( index, 1 );
7231
7232 // Handle firing indexes
7233 if ( index <= firingIndex ) {
7234 firingIndex--;
7235 }
7236 }
7237 } );
7238 return this;
7239 },
7240
7241 // Check if a given callback is in the list.
7242 // If no argument is given, return whether or not list has callbacks attached.
7243 has: function( fn ) {
7244 return fn ?
7245 jQuery.inArray( fn, list ) > -1 :
7246 list.length > 0;
7247 },
7248
7249 // Remove all callbacks from the list
7250 empty: function() {
7251 if ( list ) {
7252 list = [];
7253 }
7254 return this;
7255 },
7256
7257 // Disable .fire and .add
7258 // Abort any current/pending executions
7259 // Clear all callbacks and values
7260 disable: function() {
7261 locked = queue = [];
7262 list = memory = "";
7263 return this;
7264 },
7265 disabled: function() {
7266 return !list;
7267 },
7268
7269 // Disable .fire
7270 // Also disable .add unless we have memory (since it would have no effect)
7271 // Abort any pending executions
7272 lock: function() {
7273 locked = queue = [];
7274 if ( !memory && !firing ) {
7275 list = memory = "";
7276 }
7277 return this;
7278 },
7279 locked: function() {
7280 return !!locked;
7281 },
7282
7283 // Call all callbacks with the given context and arguments
7284 fireWith: function( context, args ) {
7285 if ( !locked ) {
7286 args = args || [];
7287 args = [ context, args.slice ? args.slice() : args ];
7288 queue.push( args );
7289 if ( !firing ) {
7290 fire();
7291 }
7292 }
7293 return this;
7294 },
7295
7296 // Call all the callbacks with the given arguments
7297 fire: function() {
7298 self.fireWith( this, arguments );
7299 return this;
7300 },
7301
7302 // To know if the callbacks have already been called at least once
7303 fired: function() {
7304 return !!fired;
7305 }
7306 };
7307
7308 return self;
7309};
7310
7311
7312function Identity( v ) {
7313 return v;
7314}
7315function Thrower( ex ) {
7316 throw ex;
7317}
7318
7319function adoptValue( value, resolve, reject, noValue ) {
7320 var method;
7321
7322 try {
7323
7324 // Check for promise aspect first to privilege synchronous behavior
7325 if ( value && isFunction( ( method = value.promise ) ) ) {
7326 method.call( value ).done( resolve ).fail( reject );
7327
7328 // Other thenables
7329 } else if ( value && isFunction( ( method = value.then ) ) ) {
7330 method.call( value, resolve, reject );
7331
7332 // Other non-thenables
7333 } else {
7334
7335 // Control `resolve` arguments by letting Array#slice cast boolean `noValue` to integer:
7336 // * false: [ value ].slice( 0 ) => resolve( value )
7337 // * true: [ value ].slice( 1 ) => resolve()
7338 resolve.apply( undefined, [ value ].slice( noValue ) );
7339 }
7340
7341 // For Promises/A+, convert exceptions into rejections
7342 // Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
7343 // Deferred#then to conditionally suppress rejection.
7344 } catch ( value ) {
7345
7346 // Support: Android 4.0 only
7347 // Strict mode functions invoked without .call/.apply get global-object context
7348 reject.apply( undefined, [ value ] );
7349 }
7350}
7351
7352jQuery.extend( {
7353
7354 Deferred: function( func ) {
7355 var tuples = [
7356
7357 // action, add listener, callbacks,
7358 // ... .then handlers, argument index, [final state]
7359 [ "notify", "progress", jQuery.Callbacks( "memory" ),
7360 jQuery.Callbacks( "memory" ), 2 ],
7361 [ "resolve", "done", jQuery.Callbacks( "once memory" ),
7362 jQuery.Callbacks( "once memory" ), 0, "resolved" ],
7363 [ "reject", "fail", jQuery.Callbacks( "once memory" ),
7364 jQuery.Callbacks( "once memory" ), 1, "rejected" ]
7365 ],
7366 state = "pending",
7367 promise = {
7368 state: function() {
7369 return state;
7370 },
7371 always: function() {
7372 deferred.done( arguments ).fail( arguments );
7373 return this;
7374 },
7375 "catch": function( fn ) {
7376 return promise.then( null, fn );
7377 },
7378
7379 // Keep pipe for back-compat
7380 pipe: function( /* fnDone, fnFail, fnProgress */ ) {
7381 var fns = arguments;
7382
7383 return jQuery.Deferred( function( newDefer ) {
7384 jQuery.each( tuples, function( i, tuple ) {
7385
7386 // Map tuples (progress, done, fail) to arguments (done, fail, progress)
7387 var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
7388
7389 // deferred.progress(function() { bind to newDefer or newDefer.notify })
7390 // deferred.done(function() { bind to newDefer or newDefer.resolve })
7391 // deferred.fail(function() { bind to newDefer or newDefer.reject })
7392 deferred[ tuple[ 1 ] ]( function() {
7393 var returned = fn && fn.apply( this, arguments );
7394 if ( returned && isFunction( returned.promise ) ) {
7395 returned.promise()
7396 .progress( newDefer.notify )
7397 .done( newDefer.resolve )
7398 .fail( newDefer.reject );
7399 } else {
7400 newDefer[ tuple[ 0 ] + "With" ](
7401 this,
7402 fn ? [ returned ] : arguments
7403 );
7404 }
7405 } );
7406 } );
7407 fns = null;
7408 } ).promise();
7409 },
7410 then: function( onFulfilled, onRejected, onProgress ) {
7411 var maxDepth = 0;
7412 function resolve( depth, deferred, handler, special ) {
7413 return function() {
7414 var that = this,
7415 args = arguments,
7416 mightThrow = function() {
7417 var returned, then;
7418
7419 // Support: Promises/A+ section 2.3.3.3.3
7420 // https://promisesaplus.com/#point-59
7421 // Ignore double-resolution attempts
7422 if ( depth < maxDepth ) {
7423 return;
7424 }
7425
7426 returned = handler.apply( that, args );
7427
7428 // Support: Promises/A+ section 2.3.1
7429 // https://promisesaplus.com/#point-48
7430 if ( returned === deferred.promise() ) {
7431 throw new TypeError( "Thenable self-resolution" );
7432 }
7433
7434 // Support: Promises/A+ sections 2.3.3.1, 3.5
7435 // https://promisesaplus.com/#point-54
7436 // https://promisesaplus.com/#point-75
7437 // Retrieve `then` only once
7438 then = returned &&
7439
7440 // Support: Promises/A+ section 2.3.4
7441 // https://promisesaplus.com/#point-64
7442 // Only check objects and functions for thenability
7443 ( typeof returned === "object" ||
7444 typeof returned === "function" ) &&
7445 returned.then;
7446
7447 // Handle a returned thenable
7448 if ( isFunction( then ) ) {
7449
7450 // Special processors (notify) just wait for resolution
7451 if ( special ) {
7452 then.call(
7453 returned,
7454 resolve( maxDepth, deferred, Identity, special ),
7455 resolve( maxDepth, deferred, Thrower, special )
7456 );
7457
7458 // Normal processors (resolve) also hook into progress
7459 } else {
7460
7461 // ...and disregard older resolution values
7462 maxDepth++;
7463
7464 then.call(
7465 returned,
7466 resolve( maxDepth, deferred, Identity, special ),
7467 resolve( maxDepth, deferred, Thrower, special ),
7468 resolve( maxDepth, deferred, Identity,
7469 deferred.notifyWith )
7470 );
7471 }
7472
7473 // Handle all other returned values
7474 } else {
7475
7476 // Only substitute handlers pass on context
7477 // and multiple values (non-spec behavior)
7478 if ( handler !== Identity ) {
7479 that = undefined;
7480 args = [ returned ];
7481 }
7482
7483 // Process the value(s)
7484 // Default process is resolve
7485 ( special || deferred.resolveWith )( that, args );
7486 }
7487 },
7488
7489 // Only normal processors (resolve) catch and reject exceptions
7490 process = special ?
7491 mightThrow :
7492 function() {
7493 try {
7494 mightThrow();
7495 } catch ( e ) {
7496
7497 if ( jQuery.Deferred.exceptionHook ) {
7498 jQuery.Deferred.exceptionHook( e,
7499 process.stackTrace );
7500 }
7501
7502 // Support: Promises/A+ section 2.3.3.3.4.1
7503 // https://promisesaplus.com/#point-61
7504 // Ignore post-resolution exceptions
7505 if ( depth + 1 >= maxDepth ) {
7506
7507 // Only substitute handlers pass on context
7508 // and multiple values (non-spec behavior)
7509 if ( handler !== Thrower ) {
7510 that = undefined;
7511 args = [ e ];
7512 }
7513
7514 deferred.rejectWith( that, args );
7515 }
7516 }
7517 };
7518
7519 // Support: Promises/A+ section 2.3.3.3.1
7520 // https://promisesaplus.com/#point-57
7521 // Re-resolve promises immediately to dodge false rejection from
7522 // subsequent errors
7523 if ( depth ) {
7524 process();
7525 } else {
7526
7527 // Call an optional hook to record the stack, in case of exception
7528 // since it's otherwise lost when execution goes async
7529 if ( jQuery.Deferred.getStackHook ) {
7530 process.stackTrace = jQuery.Deferred.getStackHook();
7531 }
7532 window.setTimeout( process );
7533 }
7534 };
7535 }
7536
7537 return jQuery.Deferred( function( newDefer ) {
7538
7539 // progress_handlers.add( ... )
7540 tuples[ 0 ][ 3 ].add(
7541 resolve(
7542 0,
7543 newDefer,
7544 isFunction( onProgress ) ?
7545 onProgress :
7546 Identity,
7547 newDefer.notifyWith
7548 )
7549 );
7550
7551 // fulfilled_handlers.add( ... )
7552 tuples[ 1 ][ 3 ].add(
7553 resolve(
7554 0,
7555 newDefer,
7556 isFunction( onFulfilled ) ?
7557 onFulfilled :
7558 Identity
7559 )
7560 );
7561
7562 // rejected_handlers.add( ... )
7563 tuples[ 2 ][ 3 ].add(
7564 resolve(
7565 0,
7566 newDefer,
7567 isFunction( onRejected ) ?
7568 onRejected :
7569 Thrower
7570 )
7571 );
7572 } ).promise();
7573 },
7574
7575 // Get a promise for this deferred
7576 // If obj is provided, the promise aspect is added to the object
7577 promise: function( obj ) {
7578 return obj != null ? jQuery.extend( obj, promise ) : promise;
7579 }
7580 },
7581 deferred = {};
7582
7583 // Add list-specific methods
7584 jQuery.each( tuples, function( i, tuple ) {
7585 var list = tuple[ 2 ],
7586 stateString = tuple[ 5 ];
7587
7588 // promise.progress = list.add
7589 // promise.done = list.add
7590 // promise.fail = list.add
7591 promise[ tuple[ 1 ] ] = list.add;
7592
7593 // Handle state
7594 if ( stateString ) {
7595 list.add(
7596 function() {
7597
7598 // state = "resolved" (i.e., fulfilled)
7599 // state = "rejected"
7600 state = stateString;
7601 },
7602
7603 // rejected_callbacks.disable
7604 // fulfilled_callbacks.disable
7605 tuples[ 3 - i ][ 2 ].disable,
7606
7607 // rejected_handlers.disable
7608 // fulfilled_handlers.disable
7609 tuples[ 3 - i ][ 3 ].disable,
7610
7611 // progress_callbacks.lock
7612 tuples[ 0 ][ 2 ].lock,
7613
7614 // progress_handlers.lock
7615 tuples[ 0 ][ 3 ].lock
7616 );
7617 }
7618
7619 // progress_handlers.fire
7620 // fulfilled_handlers.fire
7621 // rejected_handlers.fire
7622 list.add( tuple[ 3 ].fire );
7623
7624 // deferred.notify = function() { deferred.notifyWith(...) }
7625 // deferred.resolve = function() { deferred.resolveWith(...) }
7626 // deferred.reject = function() { deferred.rejectWith(...) }
7627 deferred[ tuple[ 0 ] ] = function() {
7628 deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
7629 return this;
7630 };
7631
7632 // deferred.notifyWith = list.fireWith
7633 // deferred.resolveWith = list.fireWith
7634 // deferred.rejectWith = list.fireWith
7635 deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
7636 } );
7637
7638 // Make the deferred a promise
7639 promise.promise( deferred );
7640
7641 // Call given func if any
7642 if ( func ) {
7643 func.call( deferred, deferred );
7644 }
7645
7646 // All done!
7647 return deferred;
7648 },
7649
7650 // Deferred helper
7651 when: function( singleValue ) {
7652 var
7653
7654 // count of uncompleted subordinates
7655 remaining = arguments.length,
7656
7657 // count of unprocessed arguments
7658 i = remaining,
7659
7660 // subordinate fulfillment data
7661 resolveContexts = Array( i ),
7662 resolveValues = slice.call( arguments ),
7663
7664 // the master Deferred
7665 master = jQuery.Deferred(),
7666
7667 // subordinate callback factory
7668 updateFunc = function( i ) {
7669 return function( value ) {
7670 resolveContexts[ i ] = this;
7671 resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
7672 if ( !( --remaining ) ) {
7673 master.resolveWith( resolveContexts, resolveValues );
7674 }
7675 };
7676 };
7677
7678 // Single- and empty arguments are adopted like Promise.resolve
7679 if ( remaining <= 1 ) {
7680 adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject,
7681 !remaining );
7682
7683 // Use .then() to unwrap secondary thenables (cf. gh-3000)
7684 if ( master.state() === "pending" ||
7685 isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
7686
7687 return master.then();
7688 }
7689 }
7690
7691 // Multiple arguments are aggregated like Promise.all array elements
7692 while ( i-- ) {
7693 adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
7694 }
7695
7696 return master.promise();
7697 }
7698} );
7699
7700
7701// These usually indicate a programmer mistake during development,
7702// warn about them ASAP rather than swallowing them by default.
7703var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
7704
7705jQuery.Deferred.exceptionHook = function( error, stack ) {
7706
7707 // Support: IE 8 - 9 only
7708 // Console exists when dev tools are open, which can happen at any time
7709 if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
7710 window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack );
7711 }
7712};
7713
7714
7715
7716
7717jQuery.readyException = function( error ) {
7718 window.setTimeout( function() {
7719 throw error;
7720 } );
7721};
7722
7723
7724
7725
7726// The deferred used on DOM ready
7727var readyList = jQuery.Deferred();
7728
7729jQuery.fn.ready = function( fn ) {
7730
7731 readyList
7732 .then( fn )
7733
7734 // Wrap jQuery.readyException in a function so that the lookup
7735 // happens at the time of error handling instead of callback
7736 // registration.
7737 .catch( function( error ) {
7738 jQuery.readyException( error );
7739 } );
7740
7741 return this;
7742};
7743
7744jQuery.extend( {
7745
7746 // Is the DOM ready to be used? Set to true once it occurs.
7747 isReady: false,
7748
7749 // A counter to track how many items to wait for before
7750 // the ready event fires. See #6781
7751 readyWait: 1,
7752
7753 // Handle when the DOM is ready
7754 ready: function( wait ) {
7755
7756 // Abort if there are pending holds or we're already ready
7757 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
7758 return;
7759 }
7760
7761 // Remember that the DOM is ready
7762 jQuery.isReady = true;
7763
7764 // If a normal DOM Ready event fired, decrement, and wait if need be
7765 if ( wait !== true && --jQuery.readyWait > 0 ) {
7766 return;
7767 }
7768
7769 // If there are functions bound, to execute
7770 readyList.resolveWith( document, [ jQuery ] );
7771 }
7772} );
7773
7774jQuery.ready.then = readyList.then;
7775
7776// The ready event handler and self cleanup method
7777function completed() {
7778 document.removeEventListener( "DOMContentLoaded", completed );
7779 window.removeEventListener( "load", completed );
7780 jQuery.ready();
7781}
7782
7783// Catch cases where $(document).ready() is called
7784// after the browser event has already occurred.
7785// Support: IE <=9 - 10 only
7786// Older IE sometimes signals "interactive" too soon
7787if ( document.readyState === "complete" ||
7788 ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
7789
7790 // Handle it asynchronously to allow scripts the opportunity to delay ready
7791 window.setTimeout( jQuery.ready );
7792
7793} else {
7794
7795 // Use the handy event callback
7796 document.addEventListener( "DOMContentLoaded", completed );
7797
7798 // A fallback to window.onload, that will always work
7799 window.addEventListener( "load", completed );
7800}
7801
7802
7803
7804
7805// Multifunctional method to get and set values of a collection
7806// The value/s can optionally be executed if it's a function
7807var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
7808 var i = 0,
7809 len = elems.length,
7810 bulk = key == null;
7811
7812 // Sets many values
7813 if ( toType( key ) === "object" ) {
7814 chainable = true;
7815 for ( i in key ) {
7816 access( elems, fn, i, key[ i ], true, emptyGet, raw );
7817 }
7818
7819 // Sets one value
7820 } else if ( value !== undefined ) {
7821 chainable = true;
7822
7823 if ( !isFunction( value ) ) {
7824 raw = true;
7825 }
7826
7827 if ( bulk ) {
7828
7829 // Bulk operations run against the entire set
7830 if ( raw ) {
7831 fn.call( elems, value );
7832 fn = null;
7833
7834 // ...except when executing function values
7835 } else {
7836 bulk = fn;
7837 fn = function( elem, key, value ) {
7838 return bulk.call( jQuery( elem ), value );
7839 };
7840 }
7841 }
7842
7843 if ( fn ) {
7844 for ( ; i < len; i++ ) {
7845 fn(
7846 elems[ i ], key, raw ?
7847 value :
7848 value.call( elems[ i ], i, fn( elems[ i ], key ) )
7849 );
7850 }
7851 }
7852 }
7853
7854 if ( chainable ) {
7855 return elems;
7856 }
7857
7858 // Gets
7859 if ( bulk ) {
7860 return fn.call( elems );
7861 }
7862
7863 return len ? fn( elems[ 0 ], key ) : emptyGet;
7864};
7865
7866
7867// Matches dashed string for camelizing
7868var rmsPrefix = /^-ms-/,
7869 rdashAlpha = /-([a-z])/g;
7870
7871// Used by camelCase as callback to replace()
7872function fcamelCase( all, letter ) {
7873 return letter.toUpperCase();
7874}
7875
7876// Convert dashed to camelCase; used by the css and data modules
7877// Support: IE <=9 - 11, Edge 12 - 15
7878// Microsoft forgot to hump their vendor prefix (#9572)
7879function camelCase( string ) {
7880 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
7881}
7882var acceptData = function( owner ) {
7883
7884 // Accepts only:
7885 // - Node
7886 // - Node.ELEMENT_NODE
7887 // - Node.DOCUMENT_NODE
7888 // - Object
7889 // - Any
7890 return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
7891};
7892
7893
7894
7895
7896function Data() {
7897 this.expando = jQuery.expando + Data.uid++;
7898}
7899
7900Data.uid = 1;
7901
7902Data.prototype = {
7903
7904 cache: function( owner ) {
7905
7906 // Check if the owner object already has a cache
7907 var value = owner[ this.expando ];
7908
7909 // If not, create one
7910 if ( !value ) {
7911 value = {};
7912
7913 // We can accept data for non-element nodes in modern browsers,
7914 // but we should not, see #8335.
7915 // Always return an empty object.
7916 if ( acceptData( owner ) ) {
7917
7918 // If it is a node unlikely to be stringify-ed or looped over
7919 // use plain assignment
7920 if ( owner.nodeType ) {
7921 owner[ this.expando ] = value;
7922
7923 // Otherwise secure it in a non-enumerable property
7924 // configurable must be true to allow the property to be
7925 // deleted when data is removed
7926 } else {
7927 Object.defineProperty( owner, this.expando, {
7928 value: value,
7929 configurable: true
7930 } );
7931 }
7932 }
7933 }
7934
7935 return value;
7936 },
7937 set: function( owner, data, value ) {
7938 var prop,
7939 cache = this.cache( owner );
7940
7941 // Handle: [ owner, key, value ] args
7942 // Always use camelCase key (gh-2257)
7943 if ( typeof data === "string" ) {
7944 cache[ camelCase( data ) ] = value;
7945
7946 // Handle: [ owner, { properties } ] args
7947 } else {
7948
7949 // Copy the properties one-by-one to the cache object
7950 for ( prop in data ) {
7951 cache[ camelCase( prop ) ] = data[ prop ];
7952 }
7953 }
7954 return cache;
7955 },
7956 get: function( owner, key ) {
7957 return key === undefined ?
7958 this.cache( owner ) :
7959
7960 // Always use camelCase key (gh-2257)
7961 owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
7962 },
7963 access: function( owner, key, value ) {
7964
7965 // In cases where either:
7966 //
7967 // 1. No key was specified
7968 // 2. A string key was specified, but no value provided
7969 //
7970 // Take the "read" path and allow the get method to determine
7971 // which value to return, respectively either:
7972 //
7973 // 1. The entire cache object
7974 // 2. The data stored at the key
7975 //
7976 if ( key === undefined ||
7977 ( ( key && typeof key === "string" ) && value === undefined ) ) {
7978
7979 return this.get( owner, key );
7980 }
7981
7982 // When the key is not a string, or both a key and value
7983 // are specified, set or extend (existing objects) with either:
7984 //
7985 // 1. An object of properties
7986 // 2. A key and value
7987 //
7988 this.set( owner, key, value );
7989
7990 // Since the "set" path can have two possible entry points
7991 // return the expected data based on which path was taken[*]
7992 return value !== undefined ? value : key;
7993 },
7994 remove: function( owner, key ) {
7995 var i,
7996 cache = owner[ this.expando ];
7997
7998 if ( cache === undefined ) {
7999 return;
8000 }
8001
8002 if ( key !== undefined ) {
8003
8004 // Support array or space separated string of keys
8005 if ( Array.isArray( key ) ) {
8006
8007 // If key is an array of keys...
8008 // We always set camelCase keys, so remove that.
8009 key = key.map( camelCase );
8010 } else {
8011 key = camelCase( key );
8012
8013 // If a key with the spaces exists, use it.
8014 // Otherwise, create an array by matching non-whitespace
8015 key = key in cache ?
8016 [ key ] :
8017 ( key.match( rnothtmlwhite ) || [] );
8018 }
8019
8020 i = key.length;
8021
8022 while ( i-- ) {
8023 delete cache[ key[ i ] ];
8024 }
8025 }
8026
8027 // Remove the expando if there's no more data
8028 if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
8029
8030 // Support: Chrome <=35 - 45
8031 // Webkit & Blink performance suffers when deleting properties
8032 // from DOM nodes, so set to undefined instead
8033 // https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
8034 if ( owner.nodeType ) {
8035 owner[ this.expando ] = undefined;
8036 } else {
8037 delete owner[ this.expando ];
8038 }
8039 }
8040 },
8041 hasData: function( owner ) {
8042 var cache = owner[ this.expando ];
8043 return cache !== undefined && !jQuery.isEmptyObject( cache );
8044 }
8045};
8046var dataPriv = new Data();
8047
8048var dataUser = new Data();
8049
8050
8051
8052// Implementation Summary
8053//
8054// 1. Enforce API surface and semantic compatibility with 1.9.x branch
8055// 2. Improve the module's maintainability by reducing the storage
8056// paths to a single mechanism.
8057// 3. Use the same single mechanism to support "private" and "user" data.
8058// 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData)
8059// 5. Avoid exposing implementation details on user objects (eg. expando properties)
8060// 6. Provide a clear path for implementation upgrade to WeakMap in 2014
8061
8062var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
8063 rmultiDash = /[A-Z]/g;
8064
8065function getData( data ) {
8066 if ( data === "true" ) {
8067 return true;
8068 }
8069
8070 if ( data === "false" ) {
8071 return false;
8072 }
8073
8074 if ( data === "null" ) {
8075 return null;
8076 }
8077
8078 // Only convert to a number if it doesn't change the string
8079 if ( data === +data + "" ) {
8080 return +data;
8081 }
8082
8083 if ( rbrace.test( data ) ) {
8084 return JSON.parse( data );
8085 }
8086
8087 return data;
8088}
8089
8090function dataAttr( elem, key, data ) {
8091 var name;
8092
8093 // If nothing was found internally, try to fetch any
8094 // data from the HTML5 data-* attribute
8095 if ( data === undefined && elem.nodeType === 1 ) {
8096 name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
8097 data = elem.getAttribute( name );
8098
8099 if ( typeof data === "string" ) {
8100 try {
8101 data = getData( data );
8102 } catch ( e ) {}
8103
8104 // Make sure we set the data so it isn't changed later
8105 dataUser.set( elem, key, data );
8106 } else {
8107 data = undefined;
8108 }
8109 }
8110 return data;
8111}
8112
8113jQuery.extend( {
8114 hasData: function( elem ) {
8115 return dataUser.hasData( elem ) || dataPriv.hasData( elem );
8116 },
8117
8118 data: function( elem, name, data ) {
8119 return dataUser.access( elem, name, data );
8120 },
8121
8122 removeData: function( elem, name ) {
8123 dataUser.remove( elem, name );
8124 },
8125
8126 // TODO: Now that all calls to _data and _removeData have been replaced
8127 // with direct calls to dataPriv methods, these can be deprecated.
8128 _data: function( elem, name, data ) {
8129 return dataPriv.access( elem, name, data );
8130 },
8131
8132 _removeData: function( elem, name ) {
8133 dataPriv.remove( elem, name );
8134 }
8135} );
8136
8137jQuery.fn.extend( {
8138 data: function( key, value ) {
8139 var i, name, data,
8140 elem = this[ 0 ],
8141 attrs = elem && elem.attributes;
8142
8143 // Gets all values
8144 if ( key === undefined ) {
8145 if ( this.length ) {
8146 data = dataUser.get( elem );
8147
8148 if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
8149 i = attrs.length;
8150 while ( i-- ) {
8151
8152 // Support: IE 11 only
8153 // The attrs elements can be null (#14894)
8154 if ( attrs[ i ] ) {
8155 name = attrs[ i ].name;
8156 if ( name.indexOf( "data-" ) === 0 ) {
8157 name = camelCase( name.slice( 5 ) );
8158 dataAttr( elem, name, data[ name ] );
8159 }
8160 }
8161 }
8162 dataPriv.set( elem, "hasDataAttrs", true );
8163 }
8164 }
8165
8166 return data;
8167 }
8168
8169 // Sets multiple values
8170 if ( typeof key === "object" ) {
8171 return this.each( function() {
8172 dataUser.set( this, key );
8173 } );
8174 }
8175
8176 return access( this, function( value ) {
8177 var data;
8178
8179 // The calling jQuery object (element matches) is not empty
8180 // (and therefore has an element appears at this[ 0 ]) and the
8181 // `value` parameter was not undefined. An empty jQuery object
8182 // will result in `undefined` for elem = this[ 0 ] which will
8183 // throw an exception if an attempt to read a data cache is made.
8184 if ( elem && value === undefined ) {
8185
8186 // Attempt to get data from the cache
8187 // The key will always be camelCased in Data
8188 data = dataUser.get( elem, key );
8189 if ( data !== undefined ) {
8190 return data;
8191 }
8192
8193 // Attempt to "discover" the data in
8194 // HTML5 custom data-* attrs
8195 data = dataAttr( elem, key );
8196 if ( data !== undefined ) {
8197 return data;
8198 }
8199
8200 // We tried really hard, but the data doesn't exist.
8201 return;
8202 }
8203
8204 // Set the data...
8205 this.each( function() {
8206
8207 // We always store the camelCased key
8208 dataUser.set( this, key, value );
8209 } );
8210 }, null, value, arguments.length > 1, null, true );
8211 },
8212
8213 removeData: function( key ) {
8214 return this.each( function() {
8215 dataUser.remove( this, key );
8216 } );
8217 }
8218} );
8219
8220
8221jQuery.extend( {
8222 queue: function( elem, type, data ) {
8223 var queue;
8224
8225 if ( elem ) {
8226 type = ( type || "fx" ) + "queue";
8227 queue = dataPriv.get( elem, type );
8228
8229 // Speed up dequeue by getting out quickly if this is just a lookup
8230 if ( data ) {
8231 if ( !queue || Array.isArray( data ) ) {
8232 queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
8233 } else {
8234 queue.push( data );
8235 }
8236 }
8237 return queue || [];
8238 }
8239 },
8240
8241 dequeue: function( elem, type ) {
8242 type = type || "fx";
8243
8244 var queue = jQuery.queue( elem, type ),
8245 startLength = queue.length,
8246 fn = queue.shift(),
8247 hooks = jQuery._queueHooks( elem, type ),
8248 next = function() {
8249 jQuery.dequeue( elem, type );
8250 };
8251
8252 // If the fx queue is dequeued, always remove the progress sentinel
8253 if ( fn === "inprogress" ) {
8254 fn = queue.shift();
8255 startLength--;
8256 }
8257
8258 if ( fn ) {
8259
8260 // Add a progress sentinel to prevent the fx queue from being
8261 // automatically dequeued
8262 if ( type === "fx" ) {
8263 queue.unshift( "inprogress" );
8264 }
8265
8266 // Clear up the last queue stop function
8267 delete hooks.stop;
8268 fn.call( elem, next, hooks );
8269 }
8270
8271 if ( !startLength && hooks ) {
8272 hooks.empty.fire();
8273 }
8274 },
8275
8276 // Not public - generate a queueHooks object, or return the current one
8277 _queueHooks: function( elem, type ) {
8278 var key = type + "queueHooks";
8279 return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
8280 empty: jQuery.Callbacks( "once memory" ).add( function() {
8281 dataPriv.remove( elem, [ type + "queue", key ] );
8282 } )
8283 } );
8284 }
8285} );
8286
8287jQuery.fn.extend( {
8288 queue: function( type, data ) {
8289 var setter = 2;
8290
8291 if ( typeof type !== "string" ) {
8292 data = type;
8293 type = "fx";
8294 setter--;
8295 }
8296
8297 if ( arguments.length < setter ) {
8298 return jQuery.queue( this[ 0 ], type );
8299 }
8300
8301 return data === undefined ?
8302 this :
8303 this.each( function() {
8304 var queue = jQuery.queue( this, type, data );
8305
8306 // Ensure a hooks for this queue
8307 jQuery._queueHooks( this, type );
8308
8309 if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
8310 jQuery.dequeue( this, type );
8311 }
8312 } );
8313 },
8314 dequeue: function( type ) {
8315 return this.each( function() {
8316 jQuery.dequeue( this, type );
8317 } );
8318 },
8319 clearQueue: function( type ) {
8320 return this.queue( type || "fx", [] );
8321 },
8322
8323 // Get a promise resolved when queues of a certain type
8324 // are emptied (fx is the type by default)
8325 promise: function( type, obj ) {
8326 var tmp,
8327 count = 1,
8328 defer = jQuery.Deferred(),
8329 elements = this,
8330 i = this.length,
8331 resolve = function() {
8332 if ( !( --count ) ) {
8333 defer.resolveWith( elements, [ elements ] );
8334 }
8335 };
8336
8337 if ( typeof type !== "string" ) {
8338 obj = type;
8339 type = undefined;
8340 }
8341 type = type || "fx";
8342
8343 while ( i-- ) {
8344 tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
8345 if ( tmp && tmp.empty ) {
8346 count++;
8347 tmp.empty.add( resolve );
8348 }
8349 }
8350 resolve();
8351 return defer.promise( obj );
8352 }
8353} );
8354var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
8355
8356var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
8357
8358
8359var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
8360
8361var documentElement = document.documentElement;
8362
8363
8364
8365 var isAttached = function( elem ) {
8366 return jQuery.contains( elem.ownerDocument, elem );
8367 },
8368 composed = { composed: true };
8369
8370 // Support: IE 9 - 11+, Edge 12 - 18+, iOS 10.0 - 10.2 only
8371 // Check attachment across shadow DOM boundaries when possible (gh-3504)
8372 // Support: iOS 10.0-10.2 only
8373 // Early iOS 10 versions support `attachShadow` but not `getRootNode`,
8374 // leading to errors. We need to check for `getRootNode`.
8375 if ( documentElement.getRootNode ) {
8376 isAttached = function( elem ) {
8377 return jQuery.contains( elem.ownerDocument, elem ) ||
8378 elem.getRootNode( composed ) === elem.ownerDocument;
8379 };
8380 }
8381var isHiddenWithinTree = function( elem, el ) {
8382
8383 // isHiddenWithinTree might be called from jQuery#filter function;
8384 // in that case, element will be second argument
8385 elem = el || elem;
8386
8387 // Inline style trumps all
8388 return elem.style.display === "none" ||
8389 elem.style.display === "" &&
8390
8391 // Otherwise, check computed style
8392 // Support: Firefox <=43 - 45
8393 // Disconnected elements can have computed display: none, so first confirm that elem is
8394 // in the document.
8395 isAttached( elem ) &&
8396
8397 jQuery.css( elem, "display" ) === "none";
8398 };
8399
8400var swap = function( elem, options, callback, args ) {
8401 var ret, name,
8402 old = {};
8403
8404 // Remember the old values, and insert the new ones
8405 for ( name in options ) {
8406 old[ name ] = elem.style[ name ];
8407 elem.style[ name ] = options[ name ];
8408 }
8409
8410 ret = callback.apply( elem, args || [] );
8411
8412 // Revert the old values
8413 for ( name in options ) {
8414 elem.style[ name ] = old[ name ];
8415 }
8416
8417 return ret;
8418};
8419
8420
8421
8422
8423function adjustCSS( elem, prop, valueParts, tween ) {
8424 var adjusted, scale,
8425 maxIterations = 20,
8426 currentValue = tween ?
8427 function() {
8428 return tween.cur();
8429 } :
8430 function() {
8431 return jQuery.css( elem, prop, "" );
8432 },
8433 initial = currentValue(),
8434 unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
8435
8436 // Starting value computation is required for potential unit mismatches
8437 initialInUnit = elem.nodeType &&
8438 ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
8439 rcssNum.exec( jQuery.css( elem, prop ) );
8440
8441 if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
8442
8443 // Support: Firefox <=54
8444 // Halve the iteration target value to prevent interference from CSS upper bounds (gh-2144)
8445 initial = initial / 2;
8446
8447 // Trust units reported by jQuery.css
8448 unit = unit || initialInUnit[ 3 ];
8449
8450 // Iteratively approximate from a nonzero starting point
8451 initialInUnit = +initial || 1;
8452
8453 while ( maxIterations-- ) {
8454
8455 // Evaluate and update our best guess (doubling guesses that zero out).
8456 // Finish if the scale equals or crosses 1 (making the old*new product non-positive).
8457 jQuery.style( elem, prop, initialInUnit + unit );
8458 if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
8459 maxIterations = 0;
8460 }
8461 initialInUnit = initialInUnit / scale;
8462
8463 }
8464
8465 initialInUnit = initialInUnit * 2;
8466 jQuery.style( elem, prop, initialInUnit + unit );
8467
8468 // Make sure we update the tween properties later on
8469 valueParts = valueParts || [];
8470 }
8471
8472 if ( valueParts ) {
8473 initialInUnit = +initialInUnit || +initial || 0;
8474
8475 // Apply relative offset (+=/-=) if specified
8476 adjusted = valueParts[ 1 ] ?
8477 initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
8478 +valueParts[ 2 ];
8479 if ( tween ) {
8480 tween.unit = unit;
8481 tween.start = initialInUnit;
8482 tween.end = adjusted;
8483 }
8484 }
8485 return adjusted;
8486}
8487
8488
8489var defaultDisplayMap = {};
8490
8491function getDefaultDisplay( elem ) {
8492 var temp,
8493 doc = elem.ownerDocument,
8494 nodeName = elem.nodeName,
8495 display = defaultDisplayMap[ nodeName ];
8496
8497 if ( display ) {
8498 return display;
8499 }
8500
8501 temp = doc.body.appendChild( doc.createElement( nodeName ) );
8502 display = jQuery.css( temp, "display" );
8503
8504 temp.parentNode.removeChild( temp );
8505
8506 if ( display === "none" ) {
8507 display = "block";
8508 }
8509 defaultDisplayMap[ nodeName ] = display;
8510
8511 return display;
8512}
8513
8514function showHide( elements, show ) {
8515 var display, elem,
8516 values = [],
8517 index = 0,
8518 length = elements.length;
8519
8520 // Determine new display value for elements that need to change
8521 for ( ; index < length; index++ ) {
8522 elem = elements[ index ];
8523 if ( !elem.style ) {
8524 continue;
8525 }
8526
8527 display = elem.style.display;
8528 if ( show ) {
8529
8530 // Since we force visibility upon cascade-hidden elements, an immediate (and slow)
8531 // check is required in this first loop unless we have a nonempty display value (either
8532 // inline or about-to-be-restored)
8533 if ( display === "none" ) {
8534 values[ index ] = dataPriv.get( elem, "display" ) || null;
8535 if ( !values[ index ] ) {
8536 elem.style.display = "";
8537 }
8538 }
8539 if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
8540 values[ index ] = getDefaultDisplay( elem );
8541 }
8542 } else {
8543 if ( display !== "none" ) {
8544 values[ index ] = "none";
8545
8546 // Remember what we're overwriting
8547 dataPriv.set( elem, "display", display );
8548 }
8549 }
8550 }
8551
8552 // Set the display of the elements in a second loop to avoid constant reflow
8553 for ( index = 0; index < length; index++ ) {
8554 if ( values[ index ] != null ) {
8555 elements[ index ].style.display = values[ index ];
8556 }
8557 }
8558
8559 return elements;
8560}
8561
8562jQuery.fn.extend( {
8563 show: function() {
8564 return showHide( this, true );
8565 },
8566 hide: function() {
8567 return showHide( this );
8568 },
8569 toggle: function( state ) {
8570 if ( typeof state === "boolean" ) {
8571 return state ? this.show() : this.hide();
8572 }
8573
8574 return this.each( function() {
8575 if ( isHiddenWithinTree( this ) ) {
8576 jQuery( this ).show();
8577 } else {
8578 jQuery( this ).hide();
8579 }
8580 } );
8581 }
8582} );
8583var rcheckableType = ( /^(?:checkbox|radio)$/i );
8584
8585var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
8586
8587var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
8588
8589
8590
8591// We have to close these tags to support XHTML (#13200)
8592var wrapMap = {
8593
8594 // Support: IE <=9 only
8595 option: [ 1, "<select multiple='multiple'>", "</select>" ],
8596
8597 // XHTML parsers do not magically insert elements in the
8598 // same way that tag soup parsers do. So we cannot shorten
8599 // this by omitting <tbody> or other required elements.
8600 thead: [ 1, "<table>", "</table>" ],
8601 col: [ 2, "<table><colgroup>", "</colgroup></table>" ],
8602 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
8603 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
8604
8605 _default: [ 0, "", "" ]
8606};
8607
8608// Support: IE <=9 only
8609wrapMap.optgroup = wrapMap.option;
8610
8611wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
8612wrapMap.th = wrapMap.td;
8613
8614
8615function getAll( context, tag ) {
8616
8617 // Support: IE <=9 - 11 only
8618 // Use typeof to avoid zero-argument method invocation on host objects (#15151)
8619 var ret;
8620
8621 if ( typeof context.getElementsByTagName !== "undefined" ) {
8622 ret = context.getElementsByTagName( tag || "*" );
8623
8624 } else if ( typeof context.querySelectorAll !== "undefined" ) {
8625 ret = context.querySelectorAll( tag || "*" );
8626
8627 } else {
8628 ret = [];
8629 }
8630
8631 if ( tag === undefined || tag && nodeName( context, tag ) ) {
8632 return jQuery.merge( [ context ], ret );
8633 }
8634
8635 return ret;
8636}
8637
8638
8639// Mark scripts as having already been evaluated
8640function setGlobalEval( elems, refElements ) {
8641 var i = 0,
8642 l = elems.length;
8643
8644 for ( ; i < l; i++ ) {
8645 dataPriv.set(
8646 elems[ i ],
8647 "globalEval",
8648 !refElements || dataPriv.get( refElements[ i ], "globalEval" )
8649 );
8650 }
8651}
8652
8653
8654var rhtml = /<|&#?\w+;/;
8655
8656function buildFragment( elems, context, scripts, selection, ignored ) {
8657 var elem, tmp, tag, wrap, attached, j,
8658 fragment = context.createDocumentFragment(),
8659 nodes = [],
8660 i = 0,
8661 l = elems.length;
8662
8663 for ( ; i < l; i++ ) {
8664 elem = elems[ i ];
8665
8666 if ( elem || elem === 0 ) {
8667
8668 // Add nodes directly
8669 if ( toType( elem ) === "object" ) {
8670
8671 // Support: Android <=4.0 only, PhantomJS 1 only
8672 // push.apply(_, arraylike) throws on ancient WebKit
8673 jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
8674
8675 // Convert non-html into a text node
8676 } else if ( !rhtml.test( elem ) ) {
8677 nodes.push( context.createTextNode( elem ) );
8678
8679 // Convert html into DOM nodes
8680 } else {
8681 tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
8682
8683 // Deserialize a standard representation
8684 tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
8685 wrap = wrapMap[ tag ] || wrapMap._default;
8686 tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
8687
8688 // Descend through wrappers to the right content
8689 j = wrap[ 0 ];
8690 while ( j-- ) {
8691 tmp = tmp.lastChild;
8692 }
8693
8694 // Support: Android <=4.0 only, PhantomJS 1 only
8695 // push.apply(_, arraylike) throws on ancient WebKit
8696 jQuery.merge( nodes, tmp.childNodes );
8697
8698 // Remember the top-level container
8699 tmp = fragment.firstChild;
8700
8701 // Ensure the created nodes are orphaned (#12392)
8702 tmp.textContent = "";
8703 }
8704 }
8705 }
8706
8707 // Remove wrapper from fragment
8708 fragment.textContent = "";
8709
8710 i = 0;
8711 while ( ( elem = nodes[ i++ ] ) ) {
8712
8713 // Skip elements already in the context collection (trac-4087)
8714 if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
8715 if ( ignored ) {
8716 ignored.push( elem );
8717 }
8718 continue;
8719 }
8720
8721 attached = isAttached( elem );
8722
8723 // Append to fragment
8724 tmp = getAll( fragment.appendChild( elem ), "script" );
8725
8726 // Preserve script evaluation history
8727 if ( attached ) {
8728 setGlobalEval( tmp );
8729 }
8730
8731 // Capture executables
8732 if ( scripts ) {
8733 j = 0;
8734 while ( ( elem = tmp[ j++ ] ) ) {
8735 if ( rscriptType.test( elem.type || "" ) ) {
8736 scripts.push( elem );
8737 }
8738 }
8739 }
8740 }
8741
8742 return fragment;
8743}
8744
8745
8746( function() {
8747 var fragment = document.createDocumentFragment(),
8748 div = fragment.appendChild( document.createElement( "div" ) ),
8749 input = document.createElement( "input" );
8750
8751 // Support: Android 4.0 - 4.3 only
8752 // Check state lost if the name is set (#11217)
8753 // Support: Windows Web Apps (WWA)
8754 // `name` and `type` must use .setAttribute for WWA (#14901)
8755 input.setAttribute( "type", "radio" );
8756 input.setAttribute( "checked", "checked" );
8757 input.setAttribute( "name", "t" );
8758
8759 div.appendChild( input );
8760
8761 // Support: Android <=4.1 only
8762 // Older WebKit doesn't clone checked state correctly in fragments
8763 support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
8764
8765 // Support: IE <=11 only
8766 // Make sure textarea (and checkbox) defaultValue is properly cloned
8767 div.innerHTML = "<textarea>x</textarea>";
8768 support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
8769} )();
8770
8771
8772var
8773 rkeyEvent = /^key/,
8774 rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/,
8775 rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
8776
8777function returnTrue() {
8778 return true;
8779}
8780
8781function returnFalse() {
8782 return false;
8783}
8784
8785// Support: IE <=9 - 11+
8786// focus() and blur() are asynchronous, except when they are no-op.
8787// So expect focus to be synchronous when the element is already active,
8788// and blur to be synchronous when the element is not already active.
8789// (focus and blur are always synchronous in other supported browsers,
8790// this just defines when we can count on it).
8791function expectSync( elem, type ) {
8792 return ( elem === safeActiveElement() ) === ( type === "focus" );
8793}
8794
8795// Support: IE <=9 only
8796// Accessing document.activeElement can throw unexpectedly
8797// https://bugs.jquery.com/ticket/13393
8798function safeActiveElement() {
8799 try {
8800 return document.activeElement;
8801 } catch ( err ) { }
8802}
8803
8804function on( elem, types, selector, data, fn, one ) {
8805 var origFn, type;
8806
8807 // Types can be a map of types/handlers
8808 if ( typeof types === "object" ) {
8809
8810 // ( types-Object, selector, data )
8811 if ( typeof selector !== "string" ) {
8812
8813 // ( types-Object, data )
8814 data = data || selector;
8815 selector = undefined;
8816 }
8817 for ( type in types ) {
8818 on( elem, type, selector, data, types[ type ], one );
8819 }
8820 return elem;
8821 }
8822
8823 if ( data == null && fn == null ) {
8824
8825 // ( types, fn )
8826 fn = selector;
8827 data = selector = undefined;
8828 } else if ( fn == null ) {
8829 if ( typeof selector === "string" ) {
8830
8831 // ( types, selector, fn )
8832 fn = data;
8833 data = undefined;
8834 } else {
8835
8836 // ( types, data, fn )
8837 fn = data;
8838 data = selector;
8839 selector = undefined;
8840 }
8841 }
8842 if ( fn === false ) {
8843 fn = returnFalse;
8844 } else if ( !fn ) {
8845 return elem;
8846 }
8847
8848 if ( one === 1 ) {
8849 origFn = fn;
8850 fn = function( event ) {
8851
8852 // Can use an empty set, since event contains the info
8853 jQuery().off( event );
8854 return origFn.apply( this, arguments );
8855 };
8856
8857 // Use same guid so caller can remove using origFn
8858 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
8859 }
8860 return elem.each( function() {
8861 jQuery.event.add( this, types, fn, data, selector );
8862 } );
8863}
8864
8865/*
8866 * Helper functions for managing events -- not part of the public interface.
8867 * Props to Dean Edwards' addEvent library for many of the ideas.
8868 */
8869jQuery.event = {
8870
8871 global: {},
8872
8873 add: function( elem, types, handler, data, selector ) {
8874
8875 var handleObjIn, eventHandle, tmp,
8876 events, t, handleObj,
8877 special, handlers, type, namespaces, origType,
8878 elemData = dataPriv.get( elem );
8879
8880 // Don't attach events to noData or text/comment nodes (but allow plain objects)
8881 if ( !elemData ) {
8882 return;
8883 }
8884
8885 // Caller can pass in an object of custom data in lieu of the handler
8886 if ( handler.handler ) {
8887 handleObjIn = handler;
8888 handler = handleObjIn.handler;
8889 selector = handleObjIn.selector;
8890 }
8891
8892 // Ensure that invalid selectors throw exceptions at attach time
8893 // Evaluate against documentElement in case elem is a non-element node (e.g., document)
8894 if ( selector ) {
8895 jQuery.find.matchesSelector( documentElement, selector );
8896 }
8897
8898 // Make sure that the handler has a unique ID, used to find/remove it later
8899 if ( !handler.guid ) {
8900 handler.guid = jQuery.guid++;
8901 }
8902
8903 // Init the element's event structure and main handler, if this is the first
8904 if ( !( events = elemData.events ) ) {
8905 events = elemData.events = {};
8906 }
8907 if ( !( eventHandle = elemData.handle ) ) {
8908 eventHandle = elemData.handle = function( e ) {
8909
8910 // Discard the second event of a jQuery.event.trigger() and
8911 // when an event is called after a page has unloaded
8912 return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
8913 jQuery.event.dispatch.apply( elem, arguments ) : undefined;
8914 };
8915 }
8916
8917 // Handle multiple events separated by a space
8918 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
8919 t = types.length;
8920 while ( t-- ) {
8921 tmp = rtypenamespace.exec( types[ t ] ) || [];
8922 type = origType = tmp[ 1 ];
8923 namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
8924
8925 // There *must* be a type, no attaching namespace-only handlers
8926 if ( !type ) {
8927 continue;
8928 }
8929
8930 // If event changes its type, use the special event handlers for the changed type
8931 special = jQuery.event.special[ type ] || {};
8932
8933 // If selector defined, determine special event api type, otherwise given type
8934 type = ( selector ? special.delegateType : special.bindType ) || type;
8935
8936 // Update special based on newly reset type
8937 special = jQuery.event.special[ type ] || {};
8938
8939 // handleObj is passed to all event handlers
8940 handleObj = jQuery.extend( {
8941 type: type,
8942 origType: origType,
8943 data: data,
8944 handler: handler,
8945 guid: handler.guid,
8946 selector: selector,
8947 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
8948 namespace: namespaces.join( "." )
8949 }, handleObjIn );
8950
8951 // Init the event handler queue if we're the first
8952 if ( !( handlers = events[ type ] ) ) {
8953 handlers = events[ type ] = [];
8954 handlers.delegateCount = 0;
8955
8956 // Only use addEventListener if the special events handler returns false
8957 if ( !special.setup ||
8958 special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
8959
8960 if ( elem.addEventListener ) {
8961 elem.addEventListener( type, eventHandle );
8962 }
8963 }
8964 }
8965
8966 if ( special.add ) {
8967 special.add.call( elem, handleObj );
8968
8969 if ( !handleObj.handler.guid ) {
8970 handleObj.handler.guid = handler.guid;
8971 }
8972 }
8973
8974 // Add to the element's handler list, delegates in front
8975 if ( selector ) {
8976 handlers.splice( handlers.delegateCount++, 0, handleObj );
8977 } else {
8978 handlers.push( handleObj );
8979 }
8980
8981 // Keep track of which events have ever been used, for event optimization
8982 jQuery.event.global[ type ] = true;
8983 }
8984
8985 },
8986
8987 // Detach an event or set of events from an element
8988 remove: function( elem, types, handler, selector, mappedTypes ) {
8989
8990 var j, origCount, tmp,
8991 events, t, handleObj,
8992 special, handlers, type, namespaces, origType,
8993 elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
8994
8995 if ( !elemData || !( events = elemData.events ) ) {
8996 return;
8997 }
8998
8999 // Once for each type.namespace in types; type may be omitted
9000 types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
9001 t = types.length;
9002 while ( t-- ) {
9003 tmp = rtypenamespace.exec( types[ t ] ) || [];
9004 type = origType = tmp[ 1 ];
9005 namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
9006
9007 // Unbind all events (on this namespace, if provided) for the element
9008 if ( !type ) {
9009 for ( type in events ) {
9010 jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
9011 }
9012 continue;
9013 }
9014
9015 special = jQuery.event.special[ type ] || {};
9016 type = ( selector ? special.delegateType : special.bindType ) || type;
9017 handlers = events[ type ] || [];
9018 tmp = tmp[ 2 ] &&
9019 new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
9020
9021 // Remove matching events
9022 origCount = j = handlers.length;
9023 while ( j-- ) {
9024 handleObj = handlers[ j ];
9025
9026 if ( ( mappedTypes || origType === handleObj.origType ) &&
9027 ( !handler || handler.guid === handleObj.guid ) &&
9028 ( !tmp || tmp.test( handleObj.namespace ) ) &&
9029 ( !selector || selector === handleObj.selector ||
9030 selector === "**" && handleObj.selector ) ) {
9031 handlers.splice( j, 1 );
9032
9033 if ( handleObj.selector ) {
9034 handlers.delegateCount--;
9035 }
9036 if ( special.remove ) {
9037 special.remove.call( elem, handleObj );
9038 }
9039 }
9040 }
9041
9042 // Remove generic event handler if we removed something and no more handlers exist
9043 // (avoids potential for endless recursion during removal of special event handlers)
9044 if ( origCount && !handlers.length ) {
9045 if ( !special.teardown ||
9046 special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
9047
9048 jQuery.removeEvent( elem, type, elemData.handle );
9049 }
9050
9051 delete events[ type ];
9052 }
9053 }
9054
9055 // Remove data and the expando if it's no longer used
9056 if ( jQuery.isEmptyObject( events ) ) {
9057 dataPriv.remove( elem, "handle events" );
9058 }
9059 },
9060
9061 dispatch: function( nativeEvent ) {
9062
9063 // Make a writable jQuery.Event from the native event object
9064 var event = jQuery.event.fix( nativeEvent );
9065
9066 var i, j, ret, matched, handleObj, handlerQueue,
9067 args = new Array( arguments.length ),
9068 handlers = ( dataPriv.get( this, "events" ) || {} )[ event.type ] || [],
9069 special = jQuery.event.special[ event.type ] || {};
9070
9071 // Use the fix-ed jQuery.Event rather than the (read-only) native event
9072 args[ 0 ] = event;
9073
9074 for ( i = 1; i < arguments.length; i++ ) {
9075 args[ i ] = arguments[ i ];
9076 }
9077
9078 event.delegateTarget = this;
9079
9080 // Call the preDispatch hook for the mapped type, and let it bail if desired
9081 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
9082 return;
9083 }
9084
9085 // Determine handlers
9086 handlerQueue = jQuery.event.handlers.call( this, event, handlers );
9087
9088 // Run delegates first; they may want to stop propagation beneath us
9089 i = 0;
9090 while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
9091 event.currentTarget = matched.elem;
9092
9093 j = 0;
9094 while ( ( handleObj = matched.handlers[ j++ ] ) &&
9095 !event.isImmediatePropagationStopped() ) {
9096
9097 // If the event is namespaced, then each handler is only invoked if it is
9098 // specially universal or its namespaces are a superset of the event's.
9099 if ( !event.rnamespace || handleObj.namespace === false ||
9100 event.rnamespace.test( handleObj.namespace ) ) {
9101
9102 event.handleObj = handleObj;
9103 event.data = handleObj.data;
9104
9105 ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
9106 handleObj.handler ).apply( matched.elem, args );
9107
9108 if ( ret !== undefined ) {
9109 if ( ( event.result = ret ) === false ) {
9110 event.preventDefault();
9111 event.stopPropagation();
9112 }
9113 }
9114 }
9115 }
9116 }
9117
9118 // Call the postDispatch hook for the mapped type
9119 if ( special.postDispatch ) {
9120 special.postDispatch.call( this, event );
9121 }
9122
9123 return event.result;
9124 },
9125
9126 handlers: function( event, handlers ) {
9127 var i, handleObj, sel, matchedHandlers, matchedSelectors,
9128 handlerQueue = [],
9129 delegateCount = handlers.delegateCount,
9130 cur = event.target;
9131
9132 // Find delegate handlers
9133 if ( delegateCount &&
9134
9135 // Support: IE <=9
9136 // Black-hole SVG <use> instance trees (trac-13180)
9137 cur.nodeType &&
9138
9139 // Support: Firefox <=42
9140 // Suppress spec-violating clicks indicating a non-primary pointer button (trac-3861)
9141 // https://www.w3.org/TR/DOM-Level-3-Events/#event-type-click
9142 // Support: IE 11 only
9143 // ...but not arrow key "clicks" of radio inputs, which can have `button` -1 (gh-2343)
9144 !( event.type === "click" && event.button >= 1 ) ) {
9145
9146 for ( ; cur !== this; cur = cur.parentNode || this ) {
9147
9148 // Don't check non-elements (#13208)
9149 // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764)
9150 if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
9151 matchedHandlers = [];
9152 matchedSelectors = {};
9153 for ( i = 0; i < delegateCount; i++ ) {
9154 handleObj = handlers[ i ];
9155
9156 // Don't conflict with Object.prototype properties (#13203)
9157 sel = handleObj.selector + " ";
9158
9159 if ( matchedSelectors[ sel ] === undefined ) {
9160 matchedSelectors[ sel ] = handleObj.needsContext ?
9161 jQuery( sel, this ).index( cur ) > -1 :
9162 jQuery.find( sel, this, null, [ cur ] ).length;
9163 }
9164 if ( matchedSelectors[ sel ] ) {
9165 matchedHandlers.push( handleObj );
9166 }
9167 }
9168 if ( matchedHandlers.length ) {
9169 handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
9170 }
9171 }
9172 }
9173 }
9174
9175 // Add the remaining (directly-bound) handlers
9176 cur = this;
9177 if ( delegateCount < handlers.length ) {
9178 handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
9179 }
9180
9181 return handlerQueue;
9182 },
9183
9184 addProp: function( name, hook ) {
9185 Object.defineProperty( jQuery.Event.prototype, name, {
9186 enumerable: true,
9187 configurable: true,
9188
9189 get: isFunction( hook ) ?
9190 function() {
9191 if ( this.originalEvent ) {
9192 return hook( this.originalEvent );
9193 }
9194 } :
9195 function() {
9196 if ( this.originalEvent ) {
9197 return this.originalEvent[ name ];
9198 }
9199 },
9200
9201 set: function( value ) {
9202 Object.defineProperty( this, name, {
9203 enumerable: true,
9204 configurable: true,
9205 writable: true,
9206 value: value
9207 } );
9208 }
9209 } );
9210 },
9211
9212 fix: function( originalEvent ) {
9213 return originalEvent[ jQuery.expando ] ?
9214 originalEvent :
9215 new jQuery.Event( originalEvent );
9216 },
9217
9218 special: {
9219 load: {
9220
9221 // Prevent triggered image.load events from bubbling to window.load
9222 noBubble: true
9223 },
9224 click: {
9225
9226 // Utilize native event to ensure correct state for checkable inputs
9227 setup: function( data ) {
9228
9229 // For mutual compressibility with _default, replace `this` access with a local var.
9230 // `|| data` is dead code meant only to preserve the variable through minification.
9231 var el = this || data;
9232
9233 // Claim the first handler
9234 if ( rcheckableType.test( el.type ) &&
9235 el.click && nodeName( el, "input" ) ) {
9236
9237 // dataPriv.set( el, "click", ... )
9238 leverageNative( el, "click", returnTrue );
9239 }
9240
9241 // Return false to allow normal processing in the caller
9242 return false;
9243 },
9244 trigger: function( data ) {
9245
9246 // For mutual compressibility with _default, replace `this` access with a local var.
9247 // `|| data` is dead code meant only to preserve the variable through minification.
9248 var el = this || data;
9249
9250 // Force setup before triggering a click
9251 if ( rcheckableType.test( el.type ) &&
9252 el.click && nodeName( el, "input" ) ) {
9253
9254 leverageNative( el, "click" );
9255 }
9256
9257 // Return non-false to allow normal event-path propagation
9258 return true;
9259 },
9260
9261 // For cross-browser consistency, suppress native .click() on links
9262 // Also prevent it if we're currently inside a leveraged native-event stack
9263 _default: function( event ) {
9264 var target = event.target;
9265 return rcheckableType.test( target.type ) &&
9266 target.click && nodeName( target, "input" ) &&
9267 dataPriv.get( target, "click" ) ||
9268 nodeName( target, "a" );
9269 }
9270 },
9271
9272 beforeunload: {
9273 postDispatch: function( event ) {
9274
9275 // Support: Firefox 20+
9276 // Firefox doesn't alert if the returnValue field is not set.
9277 if ( event.result !== undefined && event.originalEvent ) {
9278 event.originalEvent.returnValue = event.result;
9279 }
9280 }
9281 }
9282 }
9283};
9284
9285// Ensure the presence of an event listener that handles manually-triggered
9286// synthetic events by interrupting progress until reinvoked in response to
9287// *native* events that it fires directly, ensuring that state changes have
9288// already occurred before other listeners are invoked.
9289function leverageNative( el, type, expectSync ) {
9290
9291 // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add
9292 if ( !expectSync ) {
9293 if ( dataPriv.get( el, type ) === undefined ) {
9294 jQuery.event.add( el, type, returnTrue );
9295 }
9296 return;
9297 }
9298
9299 // Register the controller as a special universal handler for all event namespaces
9300 dataPriv.set( el, type, false );
9301 jQuery.event.add( el, type, {
9302 namespace: false,
9303 handler: function( event ) {
9304 var notAsync, result,
9305 saved = dataPriv.get( this, type );
9306
9307 if ( ( event.isTrigger & 1 ) && this[ type ] ) {
9308
9309 // Interrupt processing of the outer synthetic .trigger()ed event
9310 // Saved data should be false in such cases, but might be a leftover capture object
9311 // from an async native handler (gh-4350)
9312 if ( !saved.length ) {
9313
9314 // Store arguments for use when handling the inner native event
9315 // There will always be at least one argument (an event object), so this array
9316 // will not be confused with a leftover capture object.
9317 saved = slice.call( arguments );
9318 dataPriv.set( this, type, saved );
9319
9320 // Trigger the native event and capture its result
9321 // Support: IE <=9 - 11+
9322 // focus() and blur() are asynchronous
9323 notAsync = expectSync( this, type );
9324 this[ type ]();
9325 result = dataPriv.get( this, type );
9326 if ( saved !== result || notAsync ) {
9327 dataPriv.set( this, type, false );
9328 } else {
9329 result = {};
9330 }
9331 if ( saved !== result ) {
9332
9333 // Cancel the outer synthetic event
9334 event.stopImmediatePropagation();
9335 event.preventDefault();
9336 return result.value;
9337 }
9338
9339 // If this is an inner synthetic event for an event with a bubbling surrogate
9340 // (focus or blur), assume that the surrogate already propagated from triggering the
9341 // native event and prevent that from happening again here.
9342 // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the
9343 // bubbling surrogate propagates *after* the non-bubbling base), but that seems
9344 // less bad than duplication.
9345 } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
9346 event.stopPropagation();
9347 }
9348
9349 // If this is a native event triggered above, everything is now in order
9350 // Fire an inner synthetic event with the original arguments
9351 } else if ( saved.length ) {
9352
9353 // ...and capture the result
9354 dataPriv.set( this, type, {
9355 value: jQuery.event.trigger(
9356
9357 // Support: IE <=9 - 11+
9358 // Extend with the prototype to reset the above stopImmediatePropagation()
9359 jQuery.extend( saved[ 0 ], jQuery.Event.prototype ),
9360 saved.slice( 1 ),
9361 this
9362 )
9363 } );
9364
9365 // Abort handling of the native event
9366 event.stopImmediatePropagation();
9367 }
9368 }
9369 } );
9370}
9371
9372jQuery.removeEvent = function( elem, type, handle ) {
9373
9374 // This "if" is needed for plain objects
9375 if ( elem.removeEventListener ) {
9376 elem.removeEventListener( type, handle );
9377 }
9378};
9379
9380jQuery.Event = function( src, props ) {
9381
9382 // Allow instantiation without the 'new' keyword
9383 if ( !( this instanceof jQuery.Event ) ) {
9384 return new jQuery.Event( src, props );
9385 }
9386
9387 // Event object
9388 if ( src && src.type ) {
9389 this.originalEvent = src;
9390 this.type = src.type;
9391
9392 // Events bubbling up the document may have been marked as prevented
9393 // by a handler lower down the tree; reflect the correct value.
9394 this.isDefaultPrevented = src.defaultPrevented ||
9395 src.defaultPrevented === undefined &&
9396
9397 // Support: Android <=2.3 only
9398 src.returnValue === false ?
9399 returnTrue :
9400 returnFalse;
9401
9402 // Create target properties
9403 // Support: Safari <=6 - 7 only
9404 // Target should not be a text node (#504, #13143)
9405 this.target = ( src.target && src.target.nodeType === 3 ) ?
9406 src.target.parentNode :
9407 src.target;
9408
9409 this.currentTarget = src.currentTarget;
9410 this.relatedTarget = src.relatedTarget;
9411
9412 // Event type
9413 } else {
9414 this.type = src;
9415 }
9416
9417 // Put explicitly provided properties onto the event object
9418 if ( props ) {
9419 jQuery.extend( this, props );
9420 }
9421
9422 // Create a timestamp if incoming event doesn't have one
9423 this.timeStamp = src && src.timeStamp || Date.now();
9424
9425 // Mark it as fixed
9426 this[ jQuery.expando ] = true;
9427};
9428
9429// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
9430// https://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
9431jQuery.Event.prototype = {
9432 constructor: jQuery.Event,
9433 isDefaultPrevented: returnFalse,
9434 isPropagationStopped: returnFalse,
9435 isImmediatePropagationStopped: returnFalse,
9436 isSimulated: false,
9437
9438 preventDefault: function() {
9439 var e = this.originalEvent;
9440
9441 this.isDefaultPrevented = returnTrue;
9442
9443 if ( e && !this.isSimulated ) {
9444 e.preventDefault();
9445 }
9446 },
9447 stopPropagation: function() {
9448 var e = this.originalEvent;
9449
9450 this.isPropagationStopped = returnTrue;
9451
9452 if ( e && !this.isSimulated ) {
9453 e.stopPropagation();
9454 }
9455 },
9456 stopImmediatePropagation: function() {
9457 var e = this.originalEvent;
9458
9459 this.isImmediatePropagationStopped = returnTrue;
9460
9461 if ( e && !this.isSimulated ) {
9462 e.stopImmediatePropagation();
9463 }
9464
9465 this.stopPropagation();
9466 }
9467};
9468
9469// Includes all common event props including KeyEvent and MouseEvent specific props
9470jQuery.each( {
9471 altKey: true,
9472 bubbles: true,
9473 cancelable: true,
9474 changedTouches: true,
9475 ctrlKey: true,
9476 detail: true,
9477 eventPhase: true,
9478 metaKey: true,
9479 pageX: true,
9480 pageY: true,
9481 shiftKey: true,
9482 view: true,
9483 "char": true,
9484 code: true,
9485 charCode: true,
9486 key: true,
9487 keyCode: true,
9488 button: true,
9489 buttons: true,
9490 clientX: true,
9491 clientY: true,
9492 offsetX: true,
9493 offsetY: true,
9494 pointerId: true,
9495 pointerType: true,
9496 screenX: true,
9497 screenY: true,
9498 targetTouches: true,
9499 toElement: true,
9500 touches: true,
9501
9502 which: function( event ) {
9503 var button = event.button;
9504
9505 // Add which for key events
9506 if ( event.which == null && rkeyEvent.test( event.type ) ) {
9507 return event.charCode != null ? event.charCode : event.keyCode;
9508 }
9509
9510 // Add which for click: 1 === left; 2 === middle; 3 === right
9511 if ( !event.which && button !== undefined && rmouseEvent.test( event.type ) ) {
9512 if ( button & 1 ) {
9513 return 1;
9514 }
9515
9516 if ( button & 2 ) {
9517 return 3;
9518 }
9519
9520 if ( button & 4 ) {
9521 return 2;
9522 }
9523
9524 return 0;
9525 }
9526
9527 return event.which;
9528 }
9529}, jQuery.event.addProp );
9530
9531jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
9532 jQuery.event.special[ type ] = {
9533
9534 // Utilize native event if possible so blur/focus sequence is correct
9535 setup: function() {
9536
9537 // Claim the first handler
9538 // dataPriv.set( this, "focus", ... )
9539 // dataPriv.set( this, "blur", ... )
9540 leverageNative( this, type, expectSync );
9541
9542 // Return false to allow normal processing in the caller
9543 return false;
9544 },
9545 trigger: function() {
9546
9547 // Force setup before trigger
9548 leverageNative( this, type );
9549
9550 // Return non-false to allow normal event-path propagation
9551 return true;
9552 },
9553
9554 delegateType: delegateType
9555 };
9556} );
9557
9558// Create mouseenter/leave events using mouseover/out and event-time checks
9559// so that event delegation works in jQuery.
9560// Do the same for pointerenter/pointerleave and pointerover/pointerout
9561//
9562// Support: Safari 7 only
9563// Safari sends mouseenter too often; see:
9564// https://bugs.chromium.org/p/chromium/issues/detail?id=470258
9565// for the description of the bug (it existed in older Chrome versions as well).
9566jQuery.each( {
9567 mouseenter: "mouseover",
9568 mouseleave: "mouseout",
9569 pointerenter: "pointerover",
9570 pointerleave: "pointerout"
9571}, function( orig, fix ) {
9572 jQuery.event.special[ orig ] = {
9573 delegateType: fix,
9574 bindType: fix,
9575
9576 handle: function( event ) {
9577 var ret,
9578 target = this,
9579 related = event.relatedTarget,
9580 handleObj = event.handleObj;
9581
9582 // For mouseenter/leave call the handler if related is outside the target.
9583 // NB: No relatedTarget if the mouse left/entered the browser window
9584 if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
9585 event.type = handleObj.origType;
9586 ret = handleObj.handler.apply( this, arguments );
9587 event.type = fix;
9588 }
9589 return ret;
9590 }
9591 };
9592} );
9593
9594jQuery.fn.extend( {
9595
9596 on: function( types, selector, data, fn ) {
9597 return on( this, types, selector, data, fn );
9598 },
9599 one: function( types, selector, data, fn ) {
9600 return on( this, types, selector, data, fn, 1 );
9601 },
9602 off: function( types, selector, fn ) {
9603 var handleObj, type;
9604 if ( types && types.preventDefault && types.handleObj ) {
9605
9606 // ( event ) dispatched jQuery.Event
9607 handleObj = types.handleObj;
9608 jQuery( types.delegateTarget ).off(
9609 handleObj.namespace ?
9610 handleObj.origType + "." + handleObj.namespace :
9611 handleObj.origType,
9612 handleObj.selector,
9613 handleObj.handler
9614 );
9615 return this;
9616 }
9617 if ( typeof types === "object" ) {
9618
9619 // ( types-object [, selector] )
9620 for ( type in types ) {
9621 this.off( type, selector, types[ type ] );
9622 }
9623 return this;
9624 }
9625 if ( selector === false || typeof selector === "function" ) {
9626
9627 // ( types [, fn] )
9628 fn = selector;
9629 selector = undefined;
9630 }
9631 if ( fn === false ) {
9632 fn = returnFalse;
9633 }
9634 return this.each( function() {
9635 jQuery.event.remove( this, types, fn, selector );
9636 } );
9637 }
9638} );
9639
9640
9641var
9642
9643 /* eslint-disable max-len */
9644
9645 // See https://github.com/eslint/eslint/issues/3229
9646 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([a-z][^\/\0>\x20\t\r\n\f]*)[^>]*)\/>/gi,
9647
9648 /* eslint-enable */
9649
9650 // Support: IE <=10 - 11, Edge 12 - 13 only
9651 // In IE/Edge using regex groups here causes severe slowdowns.
9652 // See https://connect.microsoft.com/IE/feedback/details/1736512/
9653 rnoInnerhtml = /<script|<style|<link/i,
9654
9655 // checked="checked" or checked
9656 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
9657 rcleanScript = /^\s*<!(?:\[CDATA\[|--)|(?:\]\]|--)>\s*$/g;
9658
9659// Prefer a tbody over its parent table for containing new rows
9660function manipulationTarget( elem, content ) {
9661 if ( nodeName( elem, "table" ) &&
9662 nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ) {
9663
9664 return jQuery( elem ).children( "tbody" )[ 0 ] || elem;
9665 }
9666
9667 return elem;
9668}
9669
9670// Replace/restore the type attribute of script elements for safe DOM manipulation
9671function disableScript( elem ) {
9672 elem.type = ( elem.getAttribute( "type" ) !== null ) + "/" + elem.type;
9673 return elem;
9674}
9675function restoreScript( elem ) {
9676 if ( ( elem.type || "" ).slice( 0, 5 ) === "true/" ) {
9677 elem.type = elem.type.slice( 5 );
9678 } else {
9679 elem.removeAttribute( "type" );
9680 }
9681
9682 return elem;
9683}
9684
9685function cloneCopyEvent( src, dest ) {
9686 var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events;
9687
9688 if ( dest.nodeType !== 1 ) {
9689 return;
9690 }
9691
9692 // 1. Copy private data: events, handlers, etc.
9693 if ( dataPriv.hasData( src ) ) {
9694 pdataOld = dataPriv.access( src );
9695 pdataCur = dataPriv.set( dest, pdataOld );
9696 events = pdataOld.events;
9697
9698 if ( events ) {
9699 delete pdataCur.handle;
9700 pdataCur.events = {};
9701
9702 for ( type in events ) {
9703 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
9704 jQuery.event.add( dest, type, events[ type ][ i ] );
9705 }
9706 }
9707 }
9708 }
9709
9710 // 2. Copy user data
9711 if ( dataUser.hasData( src ) ) {
9712 udataOld = dataUser.access( src );
9713 udataCur = jQuery.extend( {}, udataOld );
9714
9715 dataUser.set( dest, udataCur );
9716 }
9717}
9718
9719// Fix IE bugs, see support tests
9720function fixInput( src, dest ) {
9721 var nodeName = dest.nodeName.toLowerCase();
9722
9723 // Fails to persist the checked state of a cloned checkbox or radio button.
9724 if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
9725 dest.checked = src.checked;
9726
9727 // Fails to return the selected option to the default selected state when cloning options
9728 } else if ( nodeName === "input" || nodeName === "textarea" ) {
9729 dest.defaultValue = src.defaultValue;
9730 }
9731}
9732
9733function domManip( collection, args, callback, ignored ) {
9734
9735 // Flatten any nested arrays
9736 args = concat.apply( [], args );
9737
9738 var fragment, first, scripts, hasScripts, node, doc,
9739 i = 0,
9740 l = collection.length,
9741 iNoClone = l - 1,
9742 value = args[ 0 ],
9743 valueIsFunction = isFunction( value );
9744
9745 // We can't cloneNode fragments that contain checked, in WebKit
9746 if ( valueIsFunction ||
9747 ( l > 1 && typeof value === "string" &&
9748 !support.checkClone && rchecked.test( value ) ) ) {
9749 return collection.each( function( index ) {
9750 var self = collection.eq( index );
9751 if ( valueIsFunction ) {
9752 args[ 0 ] = value.call( this, index, self.html() );
9753 }
9754 domManip( self, args, callback, ignored );
9755 } );
9756 }
9757
9758 if ( l ) {
9759 fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored );
9760 first = fragment.firstChild;
9761
9762 if ( fragment.childNodes.length === 1 ) {
9763 fragment = first;
9764 }
9765
9766 // Require either new content or an interest in ignored elements to invoke the callback
9767 if ( first || ignored ) {
9768 scripts = jQuery.map( getAll( fragment, "script" ), disableScript );
9769 hasScripts = scripts.length;
9770
9771 // Use the original fragment for the last item
9772 // instead of the first because it can end up
9773 // being emptied incorrectly in certain situations (#8070).
9774 for ( ; i < l; i++ ) {
9775 node = fragment;
9776
9777 if ( i !== iNoClone ) {
9778 node = jQuery.clone( node, true, true );
9779
9780 // Keep references to cloned scripts for later restoration
9781 if ( hasScripts ) {
9782
9783 // Support: Android <=4.0 only, PhantomJS 1 only
9784 // push.apply(_, arraylike) throws on ancient WebKit
9785 jQuery.merge( scripts, getAll( node, "script" ) );
9786 }
9787 }
9788
9789 callback.call( collection[ i ], node, i );
9790 }
9791
9792 if ( hasScripts ) {
9793 doc = scripts[ scripts.length - 1 ].ownerDocument;
9794
9795 // Reenable scripts
9796 jQuery.map( scripts, restoreScript );
9797
9798 // Evaluate executable scripts on first document insertion
9799 for ( i = 0; i < hasScripts; i++ ) {
9800 node = scripts[ i ];
9801 if ( rscriptType.test( node.type || "" ) &&
9802 !dataPriv.access( node, "globalEval" ) &&
9803 jQuery.contains( doc, node ) ) {
9804
9805 if ( node.src && ( node.type || "" ).toLowerCase() !== "module" ) {
9806
9807 // Optional AJAX dependency, but won't run scripts if not present
9808 if ( jQuery._evalUrl && !node.noModule ) {
9809 jQuery._evalUrl( node.src, {
9810 nonce: node.nonce || node.getAttribute( "nonce" )
9811 } );
9812 }
9813 } else {
9814 DOMEval( node.textContent.replace( rcleanScript, "" ), node, doc );
9815 }
9816 }
9817 }
9818 }
9819 }
9820 }
9821
9822 return collection;
9823}
9824
9825function remove( elem, selector, keepData ) {
9826 var node,
9827 nodes = selector ? jQuery.filter( selector, elem ) : elem,
9828 i = 0;
9829
9830 for ( ; ( node = nodes[ i ] ) != null; i++ ) {
9831 if ( !keepData && node.nodeType === 1 ) {
9832 jQuery.cleanData( getAll( node ) );
9833 }
9834
9835 if ( node.parentNode ) {
9836 if ( keepData && isAttached( node ) ) {
9837 setGlobalEval( getAll( node, "script" ) );
9838 }
9839 node.parentNode.removeChild( node );
9840 }
9841 }
9842
9843 return elem;
9844}
9845
9846jQuery.extend( {
9847 htmlPrefilter: function( html ) {
9848 return html.replace( rxhtmlTag, "<$1></$2>" );
9849 },
9850
9851 clone: function( elem, dataAndEvents, deepDataAndEvents ) {
9852 var i, l, srcElements, destElements,
9853 clone = elem.cloneNode( true ),
9854 inPage = isAttached( elem );
9855
9856 // Fix IE cloning issues
9857 if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) &&
9858 !jQuery.isXMLDoc( elem ) ) {
9859
9860 // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2
9861 destElements = getAll( clone );
9862 srcElements = getAll( elem );
9863
9864 for ( i = 0, l = srcElements.length; i < l; i++ ) {
9865 fixInput( srcElements[ i ], destElements[ i ] );
9866 }
9867 }
9868
9869 // Copy the events from the original to the clone
9870 if ( dataAndEvents ) {
9871 if ( deepDataAndEvents ) {
9872 srcElements = srcElements || getAll( elem );
9873 destElements = destElements || getAll( clone );
9874
9875 for ( i = 0, l = srcElements.length; i < l; i++ ) {
9876 cloneCopyEvent( srcElements[ i ], destElements[ i ] );
9877 }
9878 } else {
9879 cloneCopyEvent( elem, clone );
9880 }
9881 }
9882
9883 // Preserve script evaluation history
9884 destElements = getAll( clone, "script" );
9885 if ( destElements.length > 0 ) {
9886 setGlobalEval( destElements, !inPage && getAll( elem, "script" ) );
9887 }
9888
9889 // Return the cloned set
9890 return clone;
9891 },
9892
9893 cleanData: function( elems ) {
9894 var data, elem, type,
9895 special = jQuery.event.special,
9896 i = 0;
9897
9898 for ( ; ( elem = elems[ i ] ) !== undefined; i++ ) {
9899 if ( acceptData( elem ) ) {
9900 if ( ( data = elem[ dataPriv.expando ] ) ) {
9901 if ( data.events ) {
9902 for ( type in data.events ) {
9903 if ( special[ type ] ) {
9904 jQuery.event.remove( elem, type );
9905
9906 // This is a shortcut to avoid jQuery.event.remove's overhead
9907 } else {
9908 jQuery.removeEvent( elem, type, data.handle );
9909 }
9910 }
9911 }
9912
9913 // Support: Chrome <=35 - 45+
9914 // Assign undefined instead of using delete, see Data#remove
9915 elem[ dataPriv.expando ] = undefined;
9916 }
9917 if ( elem[ dataUser.expando ] ) {
9918
9919 // Support: Chrome <=35 - 45+
9920 // Assign undefined instead of using delete, see Data#remove
9921 elem[ dataUser.expando ] = undefined;
9922 }
9923 }
9924 }
9925 }
9926} );
9927
9928jQuery.fn.extend( {
9929 detach: function( selector ) {
9930 return remove( this, selector, true );
9931 },
9932
9933 remove: function( selector ) {
9934 return remove( this, selector );
9935 },
9936
9937 text: function( value ) {
9938 return access( this, function( value ) {
9939 return value === undefined ?
9940 jQuery.text( this ) :
9941 this.empty().each( function() {
9942 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
9943 this.textContent = value;
9944 }
9945 } );
9946 }, null, value, arguments.length );
9947 },
9948
9949 append: function() {
9950 return domManip( this, arguments, function( elem ) {
9951 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
9952 var target = manipulationTarget( this, elem );
9953 target.appendChild( elem );
9954 }
9955 } );
9956 },
9957
9958 prepend: function() {
9959 return domManip( this, arguments, function( elem ) {
9960 if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) {
9961 var target = manipulationTarget( this, elem );
9962 target.insertBefore( elem, target.firstChild );
9963 }
9964 } );
9965 },
9966
9967 before: function() {
9968 return domManip( this, arguments, function( elem ) {
9969 if ( this.parentNode ) {
9970 this.parentNode.insertBefore( elem, this );
9971 }
9972 } );
9973 },
9974
9975 after: function() {
9976 return domManip( this, arguments, function( elem ) {
9977 if ( this.parentNode ) {
9978 this.parentNode.insertBefore( elem, this.nextSibling );
9979 }
9980 } );
9981 },
9982
9983 empty: function() {
9984 var elem,
9985 i = 0;
9986
9987 for ( ; ( elem = this[ i ] ) != null; i++ ) {
9988 if ( elem.nodeType === 1 ) {
9989
9990 // Prevent memory leaks
9991 jQuery.cleanData( getAll( elem, false ) );
9992
9993 // Remove any remaining nodes
9994 elem.textContent = "";
9995 }
9996 }
9997
9998 return this;
9999 },
10000
10001 clone: function( dataAndEvents, deepDataAndEvents ) {
10002 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
10003 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
10004
10005 return this.map( function() {
10006 return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
10007 } );
10008 },
10009
10010 html: function( value ) {
10011 return access( this, function( value ) {
10012 var elem = this[ 0 ] || {},
10013 i = 0,
10014 l = this.length;
10015
10016 if ( value === undefined && elem.nodeType === 1 ) {
10017 return elem.innerHTML;
10018 }
10019
10020 // See if we can take a shortcut and just use innerHTML
10021 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
10022 !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) {
10023
10024 value = jQuery.htmlPrefilter( value );
10025
10026 try {
10027 for ( ; i < l; i++ ) {
10028 elem = this[ i ] || {};
10029
10030 // Remove element nodes and prevent memory leaks
10031 if ( elem.nodeType === 1 ) {
10032 jQuery.cleanData( getAll( elem, false ) );
10033 elem.innerHTML = value;
10034 }
10035 }
10036
10037 elem = 0;
10038
10039 // If using innerHTML throws an exception, use the fallback method
10040 } catch ( e ) {}
10041 }
10042
10043 if ( elem ) {
10044 this.empty().append( value );
10045 }
10046 }, null, value, arguments.length );
10047 },
10048
10049 replaceWith: function() {
10050 var ignored = [];
10051
10052 // Make the changes, replacing each non-ignored context element with the new content
10053 return domManip( this, arguments, function( elem ) {
10054 var parent = this.parentNode;
10055
10056 if ( jQuery.inArray( this, ignored ) < 0 ) {
10057 jQuery.cleanData( getAll( this ) );
10058 if ( parent ) {
10059 parent.replaceChild( elem, this );
10060 }
10061 }
10062
10063 // Force callback invocation
10064 }, ignored );
10065 }
10066} );
10067
10068jQuery.each( {
10069 appendTo: "append",
10070 prependTo: "prepend",
10071 insertBefore: "before",
10072 insertAfter: "after",
10073 replaceAll: "replaceWith"
10074}, function( name, original ) {
10075 jQuery.fn[ name ] = function( selector ) {
10076 var elems,
10077 ret = [],
10078 insert = jQuery( selector ),
10079 last = insert.length - 1,
10080 i = 0;
10081
10082 for ( ; i <= last; i++ ) {
10083 elems = i === last ? this : this.clone( true );
10084 jQuery( insert[ i ] )[ original ]( elems );
10085
10086 // Support: Android <=4.0 only, PhantomJS 1 only
10087 // .get() because push.apply(_, arraylike) throws on ancient WebKit
10088 push.apply( ret, elems.get() );
10089 }
10090
10091 return this.pushStack( ret );
10092 };
10093} );
10094var rnumnonpx = new RegExp( "^(" + pnum + ")(?!px)[a-z%]+$", "i" );
10095
10096var getStyles = function( elem ) {
10097
10098 // Support: IE <=11 only, Firefox <=30 (#15098, #14150)
10099 // IE throws on elements created in popups
10100 // FF meanwhile throws on frame elements through "defaultView.getComputedStyle"
10101 var view = elem.ownerDocument.defaultView;
10102
10103 if ( !view || !view.opener ) {
10104 view = window;
10105 }
10106
10107 return view.getComputedStyle( elem );
10108 };
10109
10110var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" );
10111
10112
10113
10114( function() {
10115
10116 // Executing both pixelPosition & boxSizingReliable tests require only one layout
10117 // so they're executed at the same time to save the second computation.
10118 function computeStyleTests() {
10119
10120 // This is a singleton, we need to execute it only once
10121 if ( !div ) {
10122 return;
10123 }
10124
10125 container.style.cssText = "position:absolute;left:-11111px;width:60px;" +
10126 "margin-top:1px;padding:0;border:0";
10127 div.style.cssText =
10128 "position:relative;display:block;box-sizing:border-box;overflow:scroll;" +
10129 "margin:auto;border:1px;padding:1px;" +
10130 "width:60%;top:1%";
10131 documentElement.appendChild( container ).appendChild( div );
10132
10133 var divStyle = window.getComputedStyle( div );
10134 pixelPositionVal = divStyle.top !== "1%";
10135
10136 // Support: Android 4.0 - 4.3 only, Firefox <=3 - 44
10137 reliableMarginLeftVal = roundPixelMeasures( divStyle.marginLeft ) === 12;
10138
10139 // Support: Android 4.0 - 4.3 only, Safari <=9.1 - 10.1, iOS <=7.0 - 9.3
10140 // Some styles come back with percentage values, even though they shouldn't
10141 div.style.right = "60%";
10142 pixelBoxStylesVal = roundPixelMeasures( divStyle.right ) === 36;
10143
10144 // Support: IE 9 - 11 only
10145 // Detect misreporting of content dimensions for box-sizing:border-box elements
10146 boxSizingReliableVal = roundPixelMeasures( divStyle.width ) === 36;
10147
10148 // Support: IE 9 only
10149 // Detect overflow:scroll screwiness (gh-3699)
10150 // Support: Chrome <=64
10151 // Don't get tricked when zoom affects offsetWidth (gh-4029)
10152 div.style.position = "absolute";
10153 scrollboxSizeVal = roundPixelMeasures( div.offsetWidth / 3 ) === 12;
10154
10155 documentElement.removeChild( container );
10156
10157 // Nullify the div so it wouldn't be stored in the memory and
10158 // it will also be a sign that checks already performed
10159 div = null;
10160 }
10161
10162 function roundPixelMeasures( measure ) {
10163 return Math.round( parseFloat( measure ) );
10164 }
10165
10166 var pixelPositionVal, boxSizingReliableVal, scrollboxSizeVal, pixelBoxStylesVal,
10167 reliableMarginLeftVal,
10168 container = document.createElement( "div" ),
10169 div = document.createElement( "div" );
10170
10171 // Finish early in limited (non-browser) environments
10172 if ( !div.style ) {
10173 return;
10174 }
10175
10176 // Support: IE <=9 - 11 only
10177 // Style of cloned element affects source element cloned (#8908)
10178 div.style.backgroundClip = "content-box";
10179 div.cloneNode( true ).style.backgroundClip = "";
10180 support.clearCloneStyle = div.style.backgroundClip === "content-box";
10181
10182 jQuery.extend( support, {
10183 boxSizingReliable: function() {
10184 computeStyleTests();
10185 return boxSizingReliableVal;
10186 },
10187 pixelBoxStyles: function() {
10188 computeStyleTests();
10189 return pixelBoxStylesVal;
10190 },
10191 pixelPosition: function() {
10192 computeStyleTests();
10193 return pixelPositionVal;
10194 },
10195 reliableMarginLeft: function() {
10196 computeStyleTests();
10197 return reliableMarginLeftVal;
10198 },
10199 scrollboxSize: function() {
10200 computeStyleTests();
10201 return scrollboxSizeVal;
10202 }
10203 } );
10204} )();
10205
10206
10207function curCSS( elem, name, computed ) {
10208 var width, minWidth, maxWidth, ret,
10209
10210 // Support: Firefox 51+
10211 // Retrieving style before computed somehow
10212 // fixes an issue with getting wrong values
10213 // on detached elements
10214 style = elem.style;
10215
10216 computed = computed || getStyles( elem );
10217
10218 // getPropertyValue is needed for:
10219 // .css('filter') (IE 9 only, #12537)
10220 // .css('--customProperty) (#3144)
10221 if ( computed ) {
10222 ret = computed.getPropertyValue( name ) || computed[ name ];
10223
10224 if ( ret === "" && !isAttached( elem ) ) {
10225 ret = jQuery.style( elem, name );
10226 }
10227
10228 // A tribute to the "awesome hack by Dean Edwards"
10229 // Android Browser returns percentage for some values,
10230 // but width seems to be reliably pixels.
10231 // This is against the CSSOM draft spec:
10232 // https://drafts.csswg.org/cssom/#resolved-values
10233 if ( !support.pixelBoxStyles() && rnumnonpx.test( ret ) && rboxStyle.test( name ) ) {
10234
10235 // Remember the original values
10236 width = style.width;
10237 minWidth = style.minWidth;
10238 maxWidth = style.maxWidth;
10239
10240 // Put in the new values to get a computed value out
10241 style.minWidth = style.maxWidth = style.width = ret;
10242 ret = computed.width;
10243
10244 // Revert the changed values
10245 style.width = width;
10246 style.minWidth = minWidth;
10247 style.maxWidth = maxWidth;
10248 }
10249 }
10250
10251 return ret !== undefined ?
10252
10253 // Support: IE <=9 - 11 only
10254 // IE returns zIndex value as an integer.
10255 ret + "" :
10256 ret;
10257}
10258
10259
10260function addGetHookIf( conditionFn, hookFn ) {
10261
10262 // Define the hook, we'll check on the first run if it's really needed.
10263 return {
10264 get: function() {
10265 if ( conditionFn() ) {
10266
10267 // Hook not needed (or it's not possible to use it due
10268 // to missing dependency), remove it.
10269 delete this.get;
10270 return;
10271 }
10272
10273 // Hook needed; redefine it so that the support test is not executed again.
10274 return ( this.get = hookFn ).apply( this, arguments );
10275 }
10276 };
10277}
10278
10279
10280var cssPrefixes = [ "Webkit", "Moz", "ms" ],
10281 emptyStyle = document.createElement( "div" ).style,
10282 vendorProps = {};
10283
10284// Return a vendor-prefixed property or undefined
10285function vendorPropName( name ) {
10286
10287 // Check for vendor prefixed names
10288 var capName = name[ 0 ].toUpperCase() + name.slice( 1 ),
10289 i = cssPrefixes.length;
10290
10291 while ( i-- ) {
10292 name = cssPrefixes[ i ] + capName;
10293 if ( name in emptyStyle ) {
10294 return name;
10295 }
10296 }
10297}
10298
10299// Return a potentially-mapped jQuery.cssProps or vendor prefixed property
10300function finalPropName( name ) {
10301 var final = jQuery.cssProps[ name ] || vendorProps[ name ];
10302
10303 if ( final ) {
10304 return final;
10305 }
10306 if ( name in emptyStyle ) {
10307 return name;
10308 }
10309 return vendorProps[ name ] = vendorPropName( name ) || name;
10310}
10311
10312
10313var
10314
10315 // Swappable if display is none or starts with table
10316 // except "table", "table-cell", or "table-caption"
10317 // See here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
10318 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
10319 rcustomProp = /^--/,
10320 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
10321 cssNormalTransform = {
10322 letterSpacing: "0",
10323 fontWeight: "400"
10324 };
10325
10326function setPositiveNumber( elem, value, subtract ) {
10327
10328 // Any relative (+/-) values have already been
10329 // normalized at this point
10330 var matches = rcssNum.exec( value );
10331 return matches ?
10332
10333 // Guard against undefined "subtract", e.g., when used as in cssHooks
10334 Math.max( 0, matches[ 2 ] - ( subtract || 0 ) ) + ( matches[ 3 ] || "px" ) :
10335 value;
10336}
10337
10338function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) {
10339 var i = dimension === "width" ? 1 : 0,
10340 extra = 0,
10341 delta = 0;
10342
10343 // Adjustment may not be necessary
10344 if ( box === ( isBorderBox ? "border" : "content" ) ) {
10345 return 0;
10346 }
10347
10348 for ( ; i < 4; i += 2 ) {
10349
10350 // Both box models exclude margin
10351 if ( box === "margin" ) {
10352 delta += jQuery.css( elem, box + cssExpand[ i ], true, styles );
10353 }
10354
10355 // If we get here with a content-box, we're seeking "padding" or "border" or "margin"
10356 if ( !isBorderBox ) {
10357
10358 // Add padding
10359 delta += jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
10360
10361 // For "border" or "margin", add border
10362 if ( box !== "padding" ) {
10363 delta += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
10364
10365 // But still keep track of it otherwise
10366 } else {
10367 extra += jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
10368 }
10369
10370 // If we get here with a border-box (content + padding + border), we're seeking "content" or
10371 // "padding" or "margin"
10372 } else {
10373
10374 // For "content", subtract padding
10375 if ( box === "content" ) {
10376 delta -= jQuery.css( elem, "padding" + cssExpand[ i ], true, styles );
10377 }
10378
10379 // For "content" or "padding", subtract border
10380 if ( box !== "margin" ) {
10381 delta -= jQuery.css( elem, "border" + cssExpand[ i ] + "Width", true, styles );
10382 }
10383 }
10384 }
10385
10386 // Account for positive content-box scroll gutter when requested by providing computedVal
10387 if ( !isBorderBox && computedVal >= 0 ) {
10388
10389 // offsetWidth/offsetHeight is a rounded sum of content, padding, scroll gutter, and border
10390 // Assuming integer scroll gutter, subtract the rest and round down
10391 delta += Math.max( 0, Math.ceil(
10392 elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
10393 computedVal -
10394 delta -
10395 extra -
10396 0.5
10397
10398 // If offsetWidth/offsetHeight is unknown, then we can't determine content-box scroll gutter
10399 // Use an explicit zero to avoid NaN (gh-3964)
10400 ) ) || 0;
10401 }
10402
10403 return delta;
10404}
10405
10406function getWidthOrHeight( elem, dimension, extra ) {
10407
10408 // Start with computed style
10409 var styles = getStyles( elem ),
10410
10411 // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-4322).
10412 // Fake content-box until we know it's needed to know the true value.
10413 boxSizingNeeded = !support.boxSizingReliable() || extra,
10414 isBorderBox = boxSizingNeeded &&
10415 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
10416 valueIsBorderBox = isBorderBox,
10417
10418 val = curCSS( elem, dimension, styles ),
10419 offsetProp = "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 );
10420
10421 // Support: Firefox <=54
10422 // Return a confounding non-pixel value or feign ignorance, as appropriate.
10423 if ( rnumnonpx.test( val ) ) {
10424 if ( !extra ) {
10425 return val;
10426 }
10427 val = "auto";
10428 }
10429
10430
10431 // Fall back to offsetWidth/offsetHeight when value is "auto"
10432 // This happens for inline elements with no explicit setting (gh-3571)
10433 // Support: Android <=4.1 - 4.3 only
10434 // Also use offsetWidth/offsetHeight for misreported inline dimensions (gh-3602)
10435 // Support: IE 9-11 only
10436 // Also use offsetWidth/offsetHeight for when box sizing is unreliable
10437 // We use getClientRects() to check for hidden/disconnected.
10438 // In those cases, the computed value can be trusted to be border-box
10439 if ( ( !support.boxSizingReliable() && isBorderBox ||
10440 val === "auto" ||
10441 !parseFloat( val ) && jQuery.css( elem, "display", false, styles ) === "inline" ) &&
10442 elem.getClientRects().length ) {
10443
10444 isBorderBox = jQuery.css( elem, "boxSizing", false, styles ) === "border-box";
10445
10446 // Where available, offsetWidth/offsetHeight approximate border box dimensions.
10447 // Where not available (e.g., SVG), assume unreliable box-sizing and interpret the
10448 // retrieved value as a content box dimension.
10449 valueIsBorderBox = offsetProp in elem;
10450 if ( valueIsBorderBox ) {
10451 val = elem[ offsetProp ];
10452 }
10453 }
10454
10455 // Normalize "" and auto
10456 val = parseFloat( val ) || 0;
10457
10458 // Adjust for the element's box model
10459 return ( val +
10460 boxModelAdjustment(
10461 elem,
10462 dimension,
10463 extra || ( isBorderBox ? "border" : "content" ),
10464 valueIsBorderBox,
10465 styles,
10466
10467 // Provide the current computed size to request scroll gutter calculation (gh-3589)
10468 val
10469 )
10470 ) + "px";
10471}
10472
10473jQuery.extend( {
10474
10475 // Add in style property hooks for overriding the default
10476 // behavior of getting and setting a style property
10477 cssHooks: {
10478 opacity: {
10479 get: function( elem, computed ) {
10480 if ( computed ) {
10481
10482 // We should always get a number back from opacity
10483 var ret = curCSS( elem, "opacity" );
10484 return ret === "" ? "1" : ret;
10485 }
10486 }
10487 }
10488 },
10489
10490 // Don't automatically add "px" to these possibly-unitless properties
10491 cssNumber: {
10492 "animationIterationCount": true,
10493 "columnCount": true,
10494 "fillOpacity": true,
10495 "flexGrow": true,
10496 "flexShrink": true,
10497 "fontWeight": true,
10498 "gridArea": true,
10499 "gridColumn": true,
10500 "gridColumnEnd": true,
10501 "gridColumnStart": true,
10502 "gridRow": true,
10503 "gridRowEnd": true,
10504 "gridRowStart": true,
10505 "lineHeight": true,
10506 "opacity": true,
10507 "order": true,
10508 "orphans": true,
10509 "widows": true,
10510 "zIndex": true,
10511 "zoom": true
10512 },
10513
10514 // Add in properties whose names you wish to fix before
10515 // setting or getting the value
10516 cssProps: {},
10517
10518 // Get and set the style property on a DOM Node
10519 style: function( elem, name, value, extra ) {
10520
10521 // Don't set styles on text and comment nodes
10522 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
10523 return;
10524 }
10525
10526 // Make sure that we're working with the right name
10527 var ret, type, hooks,
10528 origName = camelCase( name ),
10529 isCustomProp = rcustomProp.test( name ),
10530 style = elem.style;
10531
10532 // Make sure that we're working with the right name. We don't
10533 // want to query the value if it is a CSS custom property
10534 // since they are user-defined.
10535 if ( !isCustomProp ) {
10536 name = finalPropName( origName );
10537 }
10538
10539 // Gets hook for the prefixed version, then unprefixed version
10540 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
10541
10542 // Check if we're setting a value
10543 if ( value !== undefined ) {
10544 type = typeof value;
10545
10546 // Convert "+=" or "-=" to relative numbers (#7345)
10547 if ( type === "string" && ( ret = rcssNum.exec( value ) ) && ret[ 1 ] ) {
10548 value = adjustCSS( elem, name, ret );
10549
10550 // Fixes bug #9237
10551 type = "number";
10552 }
10553
10554 // Make sure that null and NaN values aren't set (#7116)
10555 if ( value == null || value !== value ) {
10556 return;
10557 }
10558
10559 // If a number was passed in, add the unit (except for certain CSS properties)
10560 // The isCustomProp check can be removed in jQuery 4.0 when we only auto-append
10561 // "px" to a few hardcoded values.
10562 if ( type === "number" && !isCustomProp ) {
10563 value += ret && ret[ 3 ] || ( jQuery.cssNumber[ origName ] ? "" : "px" );
10564 }
10565
10566 // background-* props affect original clone's values
10567 if ( !support.clearCloneStyle && value === "" && name.indexOf( "background" ) === 0 ) {
10568 style[ name ] = "inherit";
10569 }
10570
10571 // If a hook was provided, use that value, otherwise just set the specified value
10572 if ( !hooks || !( "set" in hooks ) ||
10573 ( value = hooks.set( elem, value, extra ) ) !== undefined ) {
10574
10575 if ( isCustomProp ) {
10576 style.setProperty( name, value );
10577 } else {
10578 style[ name ] = value;
10579 }
10580 }
10581
10582 } else {
10583
10584 // If a hook was provided get the non-computed value from there
10585 if ( hooks && "get" in hooks &&
10586 ( ret = hooks.get( elem, false, extra ) ) !== undefined ) {
10587
10588 return ret;
10589 }
10590
10591 // Otherwise just get the value from the style object
10592 return style[ name ];
10593 }
10594 },
10595
10596 css: function( elem, name, extra, styles ) {
10597 var val, num, hooks,
10598 origName = camelCase( name ),
10599 isCustomProp = rcustomProp.test( name );
10600
10601 // Make sure that we're working with the right name. We don't
10602 // want to modify the value if it is a CSS custom property
10603 // since they are user-defined.
10604 if ( !isCustomProp ) {
10605 name = finalPropName( origName );
10606 }
10607
10608 // Try prefixed name followed by the unprefixed name
10609 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
10610
10611 // If a hook was provided get the computed value from there
10612 if ( hooks && "get" in hooks ) {
10613 val = hooks.get( elem, true, extra );
10614 }
10615
10616 // Otherwise, if a way to get the computed value exists, use that
10617 if ( val === undefined ) {
10618 val = curCSS( elem, name, styles );
10619 }
10620
10621 // Convert "normal" to computed value
10622 if ( val === "normal" && name in cssNormalTransform ) {
10623 val = cssNormalTransform[ name ];
10624 }
10625
10626 // Make numeric if forced or a qualifier was provided and val looks numeric
10627 if ( extra === "" || extra ) {
10628 num = parseFloat( val );
10629 return extra === true || isFinite( num ) ? num || 0 : val;
10630 }
10631
10632 return val;
10633 }
10634} );
10635
10636jQuery.each( [ "height", "width" ], function( i, dimension ) {
10637 jQuery.cssHooks[ dimension ] = {
10638 get: function( elem, computed, extra ) {
10639 if ( computed ) {
10640
10641 // Certain elements can have dimension info if we invisibly show them
10642 // but it must have a current display style that would benefit
10643 return rdisplayswap.test( jQuery.css( elem, "display" ) ) &&
10644
10645 // Support: Safari 8+
10646 // Table columns in Safari have non-zero offsetWidth & zero
10647 // getBoundingClientRect().width unless display is changed.
10648 // Support: IE <=11 only
10649 // Running getBoundingClientRect on a disconnected node
10650 // in IE throws an error.
10651 ( !elem.getClientRects().length || !elem.getBoundingClientRect().width ) ?
10652 swap( elem, cssShow, function() {
10653 return getWidthOrHeight( elem, dimension, extra );
10654 } ) :
10655 getWidthOrHeight( elem, dimension, extra );
10656 }
10657 },
10658
10659 set: function( elem, value, extra ) {
10660 var matches,
10661 styles = getStyles( elem ),
10662
10663 // Only read styles.position if the test has a chance to fail
10664 // to avoid forcing a reflow.
10665 scrollboxSizeBuggy = !support.scrollboxSize() &&
10666 styles.position === "absolute",
10667
10668 // To avoid forcing a reflow, only fetch boxSizing if we need it (gh-3991)
10669 boxSizingNeeded = scrollboxSizeBuggy || extra,
10670 isBorderBox = boxSizingNeeded &&
10671 jQuery.css( elem, "boxSizing", false, styles ) === "border-box",
10672 subtract = extra ?
10673 boxModelAdjustment(
10674 elem,
10675 dimension,
10676 extra,
10677 isBorderBox,
10678 styles
10679 ) :
10680 0;
10681
10682 // Account for unreliable border-box dimensions by comparing offset* to computed and
10683 // faking a content-box to get border and padding (gh-3699)
10684 if ( isBorderBox && scrollboxSizeBuggy ) {
10685 subtract -= Math.ceil(
10686 elem[ "offset" + dimension[ 0 ].toUpperCase() + dimension.slice( 1 ) ] -
10687 parseFloat( styles[ dimension ] ) -
10688 boxModelAdjustment( elem, dimension, "border", false, styles ) -
10689 0.5
10690 );
10691 }
10692
10693 // Convert to pixels if value adjustment is needed
10694 if ( subtract && ( matches = rcssNum.exec( value ) ) &&
10695 ( matches[ 3 ] || "px" ) !== "px" ) {
10696
10697 elem.style[ dimension ] = value;
10698 value = jQuery.css( elem, dimension );
10699 }
10700
10701 return setPositiveNumber( elem, value, subtract );
10702 }
10703 };
10704} );
10705
10706jQuery.cssHooks.marginLeft = addGetHookIf( support.reliableMarginLeft,
10707 function( elem, computed ) {
10708 if ( computed ) {
10709 return ( parseFloat( curCSS( elem, "marginLeft" ) ) ||
10710 elem.getBoundingClientRect().left -
10711 swap( elem, { marginLeft: 0 }, function() {
10712 return elem.getBoundingClientRect().left;
10713 } )
10714 ) + "px";
10715 }
10716 }
10717);
10718
10719// These hooks are used by animate to expand properties
10720jQuery.each( {
10721 margin: "",
10722 padding: "",
10723 border: "Width"
10724}, function( prefix, suffix ) {
10725 jQuery.cssHooks[ prefix + suffix ] = {
10726 expand: function( value ) {
10727 var i = 0,
10728 expanded = {},
10729
10730 // Assumes a single number if not a string
10731 parts = typeof value === "string" ? value.split( " " ) : [ value ];
10732
10733 for ( ; i < 4; i++ ) {
10734 expanded[ prefix + cssExpand[ i ] + suffix ] =
10735 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
10736 }
10737
10738 return expanded;
10739 }
10740 };
10741
10742 if ( prefix !== "margin" ) {
10743 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
10744 }
10745} );
10746
10747jQuery.fn.extend( {
10748 css: function( name, value ) {
10749 return access( this, function( elem, name, value ) {
10750 var styles, len,
10751 map = {},
10752 i = 0;
10753
10754 if ( Array.isArray( name ) ) {
10755 styles = getStyles( elem );
10756 len = name.length;
10757
10758 for ( ; i < len; i++ ) {
10759 map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles );
10760 }
10761
10762 return map;
10763 }
10764
10765 return value !== undefined ?
10766 jQuery.style( elem, name, value ) :
10767 jQuery.css( elem, name );
10768 }, name, value, arguments.length > 1 );
10769 }
10770} );
10771
10772
10773function Tween( elem, options, prop, end, easing ) {
10774 return new Tween.prototype.init( elem, options, prop, end, easing );
10775}
10776jQuery.Tween = Tween;
10777
10778Tween.prototype = {
10779 constructor: Tween,
10780 init: function( elem, options, prop, end, easing, unit ) {
10781 this.elem = elem;
10782 this.prop = prop;
10783 this.easing = easing || jQuery.easing._default;
10784 this.options = options;
10785 this.start = this.now = this.cur();
10786 this.end = end;
10787 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
10788 },
10789 cur: function() {
10790 var hooks = Tween.propHooks[ this.prop ];
10791
10792 return hooks && hooks.get ?
10793 hooks.get( this ) :
10794 Tween.propHooks._default.get( this );
10795 },
10796 run: function( percent ) {
10797 var eased,
10798 hooks = Tween.propHooks[ this.prop ];
10799
10800 if ( this.options.duration ) {
10801 this.pos = eased = jQuery.easing[ this.easing ](
10802 percent, this.options.duration * percent, 0, 1, this.options.duration
10803 );
10804 } else {
10805 this.pos = eased = percent;
10806 }
10807 this.now = ( this.end - this.start ) * eased + this.start;
10808
10809 if ( this.options.step ) {
10810 this.options.step.call( this.elem, this.now, this );
10811 }
10812
10813 if ( hooks && hooks.set ) {
10814 hooks.set( this );
10815 } else {
10816 Tween.propHooks._default.set( this );
10817 }
10818 return this;
10819 }
10820};
10821
10822Tween.prototype.init.prototype = Tween.prototype;
10823
10824Tween.propHooks = {
10825 _default: {
10826 get: function( tween ) {
10827 var result;
10828
10829 // Use a property on the element directly when it is not a DOM element,
10830 // or when there is no matching style property that exists.
10831 if ( tween.elem.nodeType !== 1 ||
10832 tween.elem[ tween.prop ] != null && tween.elem.style[ tween.prop ] == null ) {
10833 return tween.elem[ tween.prop ];
10834 }
10835
10836 // Passing an empty string as a 3rd parameter to .css will automatically
10837 // attempt a parseFloat and fallback to a string if the parse fails.
10838 // Simple values such as "10px" are parsed to Float;
10839 // complex values such as "rotate(1rad)" are returned as-is.
10840 result = jQuery.css( tween.elem, tween.prop, "" );
10841
10842 // Empty strings, null, undefined and "auto" are converted to 0.
10843 return !result || result === "auto" ? 0 : result;
10844 },
10845 set: function( tween ) {
10846
10847 // Use step hook for back compat.
10848 // Use cssHook if its there.
10849 // Use .style if available and use plain properties where available.
10850 if ( jQuery.fx.step[ tween.prop ] ) {
10851 jQuery.fx.step[ tween.prop ]( tween );
10852 } else if ( tween.elem.nodeType === 1 && (
10853 jQuery.cssHooks[ tween.prop ] ||
10854 tween.elem.style[ finalPropName( tween.prop ) ] != null ) ) {
10855 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
10856 } else {
10857 tween.elem[ tween.prop ] = tween.now;
10858 }
10859 }
10860 }
10861};
10862
10863// Support: IE <=9 only
10864// Panic based approach to setting things on disconnected nodes
10865Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
10866 set: function( tween ) {
10867 if ( tween.elem.nodeType && tween.elem.parentNode ) {
10868 tween.elem[ tween.prop ] = tween.now;
10869 }
10870 }
10871};
10872
10873jQuery.easing = {
10874 linear: function( p ) {
10875 return p;
10876 },
10877 swing: function( p ) {
10878 return 0.5 - Math.cos( p * Math.PI ) / 2;
10879 },
10880 _default: "swing"
10881};
10882
10883jQuery.fx = Tween.prototype.init;
10884
10885// Back compat <1.8 extension point
10886jQuery.fx.step = {};
10887
10888
10889
10890
10891var
10892 fxNow, inProgress,
10893 rfxtypes = /^(?:toggle|show|hide)$/,
10894 rrun = /queueHooks$/;
10895
10896function schedule() {
10897 if ( inProgress ) {
10898 if ( document.hidden === false && window.requestAnimationFrame ) {
10899 window.requestAnimationFrame( schedule );
10900 } else {
10901 window.setTimeout( schedule, jQuery.fx.interval );
10902 }
10903
10904 jQuery.fx.tick();
10905 }
10906}
10907
10908// Animations created synchronously will run synchronously
10909function createFxNow() {
10910 window.setTimeout( function() {
10911 fxNow = undefined;
10912 } );
10913 return ( fxNow = Date.now() );
10914}
10915
10916// Generate parameters to create a standard animation
10917function genFx( type, includeWidth ) {
10918 var which,
10919 i = 0,
10920 attrs = { height: type };
10921
10922 // If we include width, step value is 1 to do all cssExpand values,
10923 // otherwise step value is 2 to skip over Left and Right
10924 includeWidth = includeWidth ? 1 : 0;
10925 for ( ; i < 4; i += 2 - includeWidth ) {
10926 which = cssExpand[ i ];
10927 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
10928 }
10929
10930 if ( includeWidth ) {
10931 attrs.opacity = attrs.width = type;
10932 }
10933
10934 return attrs;
10935}
10936
10937function createTween( value, prop, animation ) {
10938 var tween,
10939 collection = ( Animation.tweeners[ prop ] || [] ).concat( Animation.tweeners[ "*" ] ),
10940 index = 0,
10941 length = collection.length;
10942 for ( ; index < length; index++ ) {
10943 if ( ( tween = collection[ index ].call( animation, prop, value ) ) ) {
10944
10945 // We're done with this property
10946 return tween;
10947 }
10948 }
10949}
10950
10951function defaultPrefilter( elem, props, opts ) {
10952 var prop, value, toggle, hooks, oldfire, propTween, restoreDisplay, display,
10953 isBox = "width" in props || "height" in props,
10954 anim = this,
10955 orig = {},
10956 style = elem.style,
10957 hidden = elem.nodeType && isHiddenWithinTree( elem ),
10958 dataShow = dataPriv.get( elem, "fxshow" );
10959
10960 // Queue-skipping animations hijack the fx hooks
10961 if ( !opts.queue ) {
10962 hooks = jQuery._queueHooks( elem, "fx" );
10963 if ( hooks.unqueued == null ) {
10964 hooks.unqueued = 0;
10965 oldfire = hooks.empty.fire;
10966 hooks.empty.fire = function() {
10967 if ( !hooks.unqueued ) {
10968 oldfire();
10969 }
10970 };
10971 }
10972 hooks.unqueued++;
10973
10974 anim.always( function() {
10975
10976 // Ensure the complete handler is called before this completes
10977 anim.always( function() {
10978 hooks.unqueued--;
10979 if ( !jQuery.queue( elem, "fx" ).length ) {
10980 hooks.empty.fire();
10981 }
10982 } );
10983 } );
10984 }
10985
10986 // Detect show/hide animations
10987 for ( prop in props ) {
10988 value = props[ prop ];
10989 if ( rfxtypes.test( value ) ) {
10990 delete props[ prop ];
10991 toggle = toggle || value === "toggle";
10992 if ( value === ( hidden ? "hide" : "show" ) ) {
10993
10994 // Pretend to be hidden if this is a "show" and
10995 // there is still data from a stopped show/hide
10996 if ( value === "show" && dataShow && dataShow[ prop ] !== undefined ) {
10997 hidden = true;
10998
10999 // Ignore all other no-op show/hide data
11000 } else {
11001 continue;
11002 }
11003 }
11004 orig[ prop ] = dataShow && dataShow[ prop ] || jQuery.style( elem, prop );
11005 }
11006 }
11007
11008 // Bail out if this is a no-op like .hide().hide()
11009 propTween = !jQuery.isEmptyObject( props );
11010 if ( !propTween && jQuery.isEmptyObject( orig ) ) {
11011 return;
11012 }
11013
11014 // Restrict "overflow" and "display" styles during box animations
11015 if ( isBox && elem.nodeType === 1 ) {
11016
11017 // Support: IE <=9 - 11, Edge 12 - 15
11018 // Record all 3 overflow attributes because IE does not infer the shorthand
11019 // from identically-valued overflowX and overflowY and Edge just mirrors
11020 // the overflowX value there.
11021 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
11022
11023 // Identify a display type, preferring old show/hide data over the CSS cascade
11024 restoreDisplay = dataShow && dataShow.display;
11025 if ( restoreDisplay == null ) {
11026 restoreDisplay = dataPriv.get( elem, "display" );
11027 }
11028 display = jQuery.css( elem, "display" );
11029 if ( display === "none" ) {
11030 if ( restoreDisplay ) {
11031 display = restoreDisplay;
11032 } else {
11033
11034 // Get nonempty value(s) by temporarily forcing visibility
11035 showHide( [ elem ], true );
11036 restoreDisplay = elem.style.display || restoreDisplay;
11037 display = jQuery.css( elem, "display" );
11038 showHide( [ elem ] );
11039 }
11040 }
11041
11042 // Animate inline elements as inline-block
11043 if ( display === "inline" || display === "inline-block" && restoreDisplay != null ) {
11044 if ( jQuery.css( elem, "float" ) === "none" ) {
11045
11046 // Restore the original display value at the end of pure show/hide animations
11047 if ( !propTween ) {
11048 anim.done( function() {
11049 style.display = restoreDisplay;
11050 } );
11051 if ( restoreDisplay == null ) {
11052 display = style.display;
11053 restoreDisplay = display === "none" ? "" : display;
11054 }
11055 }
11056 style.display = "inline-block";
11057 }
11058 }
11059 }
11060
11061 if ( opts.overflow ) {
11062 style.overflow = "hidden";
11063 anim.always( function() {
11064 style.overflow = opts.overflow[ 0 ];
11065 style.overflowX = opts.overflow[ 1 ];
11066 style.overflowY = opts.overflow[ 2 ];
11067 } );
11068 }
11069
11070 // Implement show/hide animations
11071 propTween = false;
11072 for ( prop in orig ) {
11073
11074 // General show/hide setup for this element animation
11075 if ( !propTween ) {
11076 if ( dataShow ) {
11077 if ( "hidden" in dataShow ) {
11078 hidden = dataShow.hidden;
11079 }
11080 } else {
11081 dataShow = dataPriv.access( elem, "fxshow", { display: restoreDisplay } );
11082 }
11083
11084 // Store hidden/visible for toggle so `.stop().toggle()` "reverses"
11085 if ( toggle ) {
11086 dataShow.hidden = !hidden;
11087 }
11088
11089 // Show elements before animating them
11090 if ( hidden ) {
11091 showHide( [ elem ], true );
11092 }
11093
11094 /* eslint-disable no-loop-func */
11095
11096 anim.done( function() {
11097
11098 /* eslint-enable no-loop-func */
11099
11100 // The final step of a "hide" animation is actually hiding the element
11101 if ( !hidden ) {
11102 showHide( [ elem ] );
11103 }
11104 dataPriv.remove( elem, "fxshow" );
11105 for ( prop in orig ) {
11106 jQuery.style( elem, prop, orig[ prop ] );
11107 }
11108 } );
11109 }
11110
11111 // Per-property setup
11112 propTween = createTween( hidden ? dataShow[ prop ] : 0, prop, anim );
11113 if ( !( prop in dataShow ) ) {
11114 dataShow[ prop ] = propTween.start;
11115 if ( hidden ) {
11116 propTween.end = propTween.start;
11117 propTween.start = 0;
11118 }
11119 }
11120 }
11121}
11122
11123function propFilter( props, specialEasing ) {
11124 var index, name, easing, value, hooks;
11125
11126 // camelCase, specialEasing and expand cssHook pass
11127 for ( index in props ) {
11128 name = camelCase( index );
11129 easing = specialEasing[ name ];
11130 value = props[ index ];
11131 if ( Array.isArray( value ) ) {
11132 easing = value[ 1 ];
11133 value = props[ index ] = value[ 0 ];
11134 }
11135
11136 if ( index !== name ) {
11137 props[ name ] = value;
11138 delete props[ index ];
11139 }
11140
11141 hooks = jQuery.cssHooks[ name ];
11142 if ( hooks && "expand" in hooks ) {
11143 value = hooks.expand( value );
11144 delete props[ name ];
11145
11146 // Not quite $.extend, this won't overwrite existing keys.
11147 // Reusing 'index' because we have the correct "name"
11148 for ( index in value ) {
11149 if ( !( index in props ) ) {
11150 props[ index ] = value[ index ];
11151 specialEasing[ index ] = easing;
11152 }
11153 }
11154 } else {
11155 specialEasing[ name ] = easing;
11156 }
11157 }
11158}
11159
11160function Animation( elem, properties, options ) {
11161 var result,
11162 stopped,
11163 index = 0,
11164 length = Animation.prefilters.length,
11165 deferred = jQuery.Deferred().always( function() {
11166
11167 // Don't match elem in the :animated selector
11168 delete tick.elem;
11169 } ),
11170 tick = function() {
11171 if ( stopped ) {
11172 return false;
11173 }
11174 var currentTime = fxNow || createFxNow(),
11175 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
11176
11177 // Support: Android 2.3 only
11178 // Archaic crash bug won't allow us to use `1 - ( 0.5 || 0 )` (#12497)
11179 temp = remaining / animation.duration || 0,
11180 percent = 1 - temp,
11181 index = 0,
11182 length = animation.tweens.length;
11183
11184 for ( ; index < length; index++ ) {
11185 animation.tweens[ index ].run( percent );
11186 }
11187
11188 deferred.notifyWith( elem, [ animation, percent, remaining ] );
11189
11190 // If there's more to do, yield
11191 if ( percent < 1 && length ) {
11192 return remaining;
11193 }
11194
11195 // If this was an empty animation, synthesize a final progress notification
11196 if ( !length ) {
11197 deferred.notifyWith( elem, [ animation, 1, 0 ] );
11198 }
11199
11200 // Resolve the animation and report its conclusion
11201 deferred.resolveWith( elem, [ animation ] );
11202 return false;
11203 },
11204 animation = deferred.promise( {
11205 elem: elem,
11206 props: jQuery.extend( {}, properties ),
11207 opts: jQuery.extend( true, {
11208 specialEasing: {},
11209 easing: jQuery.easing._default
11210 }, options ),
11211 originalProperties: properties,
11212 originalOptions: options,
11213 startTime: fxNow || createFxNow(),
11214 duration: options.duration,
11215 tweens: [],
11216 createTween: function( prop, end ) {
11217 var tween = jQuery.Tween( elem, animation.opts, prop, end,
11218 animation.opts.specialEasing[ prop ] || animation.opts.easing );
11219 animation.tweens.push( tween );
11220 return tween;
11221 },
11222 stop: function( gotoEnd ) {
11223 var index = 0,
11224
11225 // If we are going to the end, we want to run all the tweens
11226 // otherwise we skip this part
11227 length = gotoEnd ? animation.tweens.length : 0;
11228 if ( stopped ) {
11229 return this;
11230 }
11231 stopped = true;
11232 for ( ; index < length; index++ ) {
11233 animation.tweens[ index ].run( 1 );
11234 }
11235
11236 // Resolve when we played the last frame; otherwise, reject
11237 if ( gotoEnd ) {
11238 deferred.notifyWith( elem, [ animation, 1, 0 ] );
11239 deferred.resolveWith( elem, [ animation, gotoEnd ] );
11240 } else {
11241 deferred.rejectWith( elem, [ animation, gotoEnd ] );
11242 }
11243 return this;
11244 }
11245 } ),
11246 props = animation.props;
11247
11248 propFilter( props, animation.opts.specialEasing );
11249
11250 for ( ; index < length; index++ ) {
11251 result = Animation.prefilters[ index ].call( animation, elem, props, animation.opts );
11252 if ( result ) {
11253 if ( isFunction( result.stop ) ) {
11254 jQuery._queueHooks( animation.elem, animation.opts.queue ).stop =
11255 result.stop.bind( result );
11256 }
11257 return result;
11258 }
11259 }
11260
11261 jQuery.map( props, createTween, animation );
11262
11263 if ( isFunction( animation.opts.start ) ) {
11264 animation.opts.start.call( elem, animation );
11265 }
11266
11267 // Attach callbacks from options
11268 animation
11269 .progress( animation.opts.progress )
11270 .done( animation.opts.done, animation.opts.complete )
11271 .fail( animation.opts.fail )
11272 .always( animation.opts.always );
11273
11274 jQuery.fx.timer(
11275 jQuery.extend( tick, {
11276 elem: elem,
11277 anim: animation,
11278 queue: animation.opts.queue
11279 } )
11280 );
11281
11282 return animation;
11283}
11284
11285jQuery.Animation = jQuery.extend( Animation, {
11286
11287 tweeners: {
11288 "*": [ function( prop, value ) {
11289 var tween = this.createTween( prop, value );
11290 adjustCSS( tween.elem, prop, rcssNum.exec( value ), tween );
11291 return tween;
11292 } ]
11293 },
11294
11295 tweener: function( props, callback ) {
11296 if ( isFunction( props ) ) {
11297 callback = props;
11298 props = [ "*" ];
11299 } else {
11300 props = props.match( rnothtmlwhite );
11301 }
11302
11303 var prop,
11304 index = 0,
11305 length = props.length;
11306
11307 for ( ; index < length; index++ ) {
11308 prop = props[ index ];
11309 Animation.tweeners[ prop ] = Animation.tweeners[ prop ] || [];
11310 Animation.tweeners[ prop ].unshift( callback );
11311 }
11312 },
11313
11314 prefilters: [ defaultPrefilter ],
11315
11316 prefilter: function( callback, prepend ) {
11317 if ( prepend ) {
11318 Animation.prefilters.unshift( callback );
11319 } else {
11320 Animation.prefilters.push( callback );
11321 }
11322 }
11323} );
11324
11325jQuery.speed = function( speed, easing, fn ) {
11326 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
11327 complete: fn || !fn && easing ||
11328 isFunction( speed ) && speed,
11329 duration: speed,
11330 easing: fn && easing || easing && !isFunction( easing ) && easing
11331 };
11332
11333 // Go to the end state if fx are off
11334 if ( jQuery.fx.off ) {
11335 opt.duration = 0;
11336
11337 } else {
11338 if ( typeof opt.duration !== "number" ) {
11339 if ( opt.duration in jQuery.fx.speeds ) {
11340 opt.duration = jQuery.fx.speeds[ opt.duration ];
11341
11342 } else {
11343 opt.duration = jQuery.fx.speeds._default;
11344 }
11345 }
11346 }
11347
11348 // Normalize opt.queue - true/undefined/null -> "fx"
11349 if ( opt.queue == null || opt.queue === true ) {
11350 opt.queue = "fx";
11351 }
11352
11353 // Queueing
11354 opt.old = opt.complete;
11355
11356 opt.complete = function() {
11357 if ( isFunction( opt.old ) ) {
11358 opt.old.call( this );
11359 }
11360
11361 if ( opt.queue ) {
11362 jQuery.dequeue( this, opt.queue );
11363 }
11364 };
11365
11366 return opt;
11367};
11368
11369jQuery.fn.extend( {
11370 fadeTo: function( speed, to, easing, callback ) {
11371
11372 // Show any hidden elements after setting opacity to 0
11373 return this.filter( isHiddenWithinTree ).css( "opacity", 0 ).show()
11374
11375 // Animate to the value specified
11376 .end().animate( { opacity: to }, speed, easing, callback );
11377 },
11378 animate: function( prop, speed, easing, callback ) {
11379 var empty = jQuery.isEmptyObject( prop ),
11380 optall = jQuery.speed( speed, easing, callback ),
11381 doAnimation = function() {
11382
11383 // Operate on a copy of prop so per-property easing won't be lost
11384 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
11385
11386 // Empty animations, or finishing resolves immediately
11387 if ( empty || dataPriv.get( this, "finish" ) ) {
11388 anim.stop( true );
11389 }
11390 };
11391 doAnimation.finish = doAnimation;
11392
11393 return empty || optall.queue === false ?
11394 this.each( doAnimation ) :
11395 this.queue( optall.queue, doAnimation );
11396 },
11397 stop: function( type, clearQueue, gotoEnd ) {
11398 var stopQueue = function( hooks ) {
11399 var stop = hooks.stop;
11400 delete hooks.stop;
11401 stop( gotoEnd );
11402 };
11403
11404 if ( typeof type !== "string" ) {
11405 gotoEnd = clearQueue;
11406 clearQueue = type;
11407 type = undefined;
11408 }
11409 if ( clearQueue && type !== false ) {
11410 this.queue( type || "fx", [] );
11411 }
11412
11413 return this.each( function() {
11414 var dequeue = true,
11415 index = type != null && type + "queueHooks",
11416 timers = jQuery.timers,
11417 data = dataPriv.get( this );
11418
11419 if ( index ) {
11420 if ( data[ index ] && data[ index ].stop ) {
11421 stopQueue( data[ index ] );
11422 }
11423 } else {
11424 for ( index in data ) {
11425 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
11426 stopQueue( data[ index ] );
11427 }
11428 }
11429 }
11430
11431 for ( index = timers.length; index--; ) {
11432 if ( timers[ index ].elem === this &&
11433 ( type == null || timers[ index ].queue === type ) ) {
11434
11435 timers[ index ].anim.stop( gotoEnd );
11436 dequeue = false;
11437 timers.splice( index, 1 );
11438 }
11439 }
11440
11441 // Start the next in the queue if the last step wasn't forced.
11442 // Timers currently will call their complete callbacks, which
11443 // will dequeue but only if they were gotoEnd.
11444 if ( dequeue || !gotoEnd ) {
11445 jQuery.dequeue( this, type );
11446 }
11447 } );
11448 },
11449 finish: function( type ) {
11450 if ( type !== false ) {
11451 type = type || "fx";
11452 }
11453 return this.each( function() {
11454 var index,
11455 data = dataPriv.get( this ),
11456 queue = data[ type + "queue" ],
11457 hooks = data[ type + "queueHooks" ],
11458 timers = jQuery.timers,
11459 length = queue ? queue.length : 0;
11460
11461 // Enable finishing flag on private data
11462 data.finish = true;
11463
11464 // Empty the queue first
11465 jQuery.queue( this, type, [] );
11466
11467 if ( hooks && hooks.stop ) {
11468 hooks.stop.call( this, true );
11469 }
11470
11471 // Look for any active animations, and finish them
11472 for ( index = timers.length; index--; ) {
11473 if ( timers[ index ].elem === this && timers[ index ].queue === type ) {
11474 timers[ index ].anim.stop( true );
11475 timers.splice( index, 1 );
11476 }
11477 }
11478
11479 // Look for any animations in the old queue and finish them
11480 for ( index = 0; index < length; index++ ) {
11481 if ( queue[ index ] && queue[ index ].finish ) {
11482 queue[ index ].finish.call( this );
11483 }
11484 }
11485
11486 // Turn off finishing flag
11487 delete data.finish;
11488 } );
11489 }
11490} );
11491
11492jQuery.each( [ "toggle", "show", "hide" ], function( i, name ) {
11493 var cssFn = jQuery.fn[ name ];
11494 jQuery.fn[ name ] = function( speed, easing, callback ) {
11495 return speed == null || typeof speed === "boolean" ?
11496 cssFn.apply( this, arguments ) :
11497 this.animate( genFx( name, true ), speed, easing, callback );
11498 };
11499} );
11500
11501// Generate shortcuts for custom animations
11502jQuery.each( {
11503 slideDown: genFx( "show" ),
11504 slideUp: genFx( "hide" ),
11505 slideToggle: genFx( "toggle" ),
11506 fadeIn: { opacity: "show" },
11507 fadeOut: { opacity: "hide" },
11508 fadeToggle: { opacity: "toggle" }
11509}, function( name, props ) {
11510 jQuery.fn[ name ] = function( speed, easing, callback ) {
11511 return this.animate( props, speed, easing, callback );
11512 };
11513} );
11514
11515jQuery.timers = [];
11516jQuery.fx.tick = function() {
11517 var timer,
11518 i = 0,
11519 timers = jQuery.timers;
11520
11521 fxNow = Date.now();
11522
11523 for ( ; i < timers.length; i++ ) {
11524 timer = timers[ i ];
11525
11526 // Run the timer and safely remove it when done (allowing for external removal)
11527 if ( !timer() && timers[ i ] === timer ) {
11528 timers.splice( i--, 1 );
11529 }
11530 }
11531
11532 if ( !timers.length ) {
11533 jQuery.fx.stop();
11534 }
11535 fxNow = undefined;
11536};
11537
11538jQuery.fx.timer = function( timer ) {
11539 jQuery.timers.push( timer );
11540 jQuery.fx.start();
11541};
11542
11543jQuery.fx.interval = 13;
11544jQuery.fx.start = function() {
11545 if ( inProgress ) {
11546 return;
11547 }
11548
11549 inProgress = true;
11550 schedule();
11551};
11552
11553jQuery.fx.stop = function() {
11554 inProgress = null;
11555};
11556
11557jQuery.fx.speeds = {
11558 slow: 600,
11559 fast: 200,
11560
11561 // Default speed
11562 _default: 400
11563};
11564
11565
11566// Based off of the plugin by Clint Helfers, with permission.
11567// https://web.archive.org/web/20100324014747/http://blindsignals.com/index.php/2009/07/jquery-delay/
11568jQuery.fn.delay = function( time, type ) {
11569 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
11570 type = type || "fx";
11571
11572 return this.queue( type, function( next, hooks ) {
11573 var timeout = window.setTimeout( next, time );
11574 hooks.stop = function() {
11575 window.clearTimeout( timeout );
11576 };
11577 } );
11578};
11579
11580
11581( function() {
11582 var input = document.createElement( "input" ),
11583 select = document.createElement( "select" ),
11584 opt = select.appendChild( document.createElement( "option" ) );
11585
11586 input.type = "checkbox";
11587
11588 // Support: Android <=4.3 only
11589 // Default value for a checkbox should be "on"
11590 support.checkOn = input.value !== "";
11591
11592 // Support: IE <=11 only
11593 // Must access selectedIndex to make default options select
11594 support.optSelected = opt.selected;
11595
11596 // Support: IE <=11 only
11597 // An input loses its value after becoming a radio
11598 input = document.createElement( "input" );
11599 input.value = "t";
11600 input.type = "radio";
11601 support.radioValue = input.value === "t";
11602} )();
11603
11604
11605var boolHook,
11606 attrHandle = jQuery.expr.attrHandle;
11607
11608jQuery.fn.extend( {
11609 attr: function( name, value ) {
11610 return access( this, jQuery.attr, name, value, arguments.length > 1 );
11611 },
11612
11613 removeAttr: function( name ) {
11614 return this.each( function() {
11615 jQuery.removeAttr( this, name );
11616 } );
11617 }
11618} );
11619
11620jQuery.extend( {
11621 attr: function( elem, name, value ) {
11622 var ret, hooks,
11623 nType = elem.nodeType;
11624
11625 // Don't get/set attributes on text, comment and attribute nodes
11626 if ( nType === 3 || nType === 8 || nType === 2 ) {
11627 return;
11628 }
11629
11630 // Fallback to prop when attributes are not supported
11631 if ( typeof elem.getAttribute === "undefined" ) {
11632 return jQuery.prop( elem, name, value );
11633 }
11634
11635 // Attribute hooks are determined by the lowercase version
11636 // Grab necessary hook if one is defined
11637 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
11638 hooks = jQuery.attrHooks[ name.toLowerCase() ] ||
11639 ( jQuery.expr.match.bool.test( name ) ? boolHook : undefined );
11640 }
11641
11642 if ( value !== undefined ) {
11643 if ( value === null ) {
11644 jQuery.removeAttr( elem, name );
11645 return;
11646 }
11647
11648 if ( hooks && "set" in hooks &&
11649 ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
11650 return ret;
11651 }
11652
11653 elem.setAttribute( name, value + "" );
11654 return value;
11655 }
11656
11657 if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
11658 return ret;
11659 }
11660
11661 ret = jQuery.find.attr( elem, name );
11662
11663 // Non-existent attributes return null, we normalize to undefined
11664 return ret == null ? undefined : ret;
11665 },
11666
11667 attrHooks: {
11668 type: {
11669 set: function( elem, value ) {
11670 if ( !support.radioValue && value === "radio" &&
11671 nodeName( elem, "input" ) ) {
11672 var val = elem.value;
11673 elem.setAttribute( "type", value );
11674 if ( val ) {
11675 elem.value = val;
11676 }
11677 return value;
11678 }
11679 }
11680 }
11681 },
11682
11683 removeAttr: function( elem, value ) {
11684 var name,
11685 i = 0,
11686
11687 // Attribute names can contain non-HTML whitespace characters
11688 // https://html.spec.whatwg.org/multipage/syntax.html#attributes-2
11689 attrNames = value && value.match( rnothtmlwhite );
11690
11691 if ( attrNames && elem.nodeType === 1 ) {
11692 while ( ( name = attrNames[ i++ ] ) ) {
11693 elem.removeAttribute( name );
11694 }
11695 }
11696 }
11697} );
11698
11699// Hooks for boolean attributes
11700boolHook = {
11701 set: function( elem, value, name ) {
11702 if ( value === false ) {
11703
11704 // Remove boolean attributes when set to false
11705 jQuery.removeAttr( elem, name );
11706 } else {
11707 elem.setAttribute( name, name );
11708 }
11709 return name;
11710 }
11711};
11712
11713jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) {
11714 var getter = attrHandle[ name ] || jQuery.find.attr;
11715
11716 attrHandle[ name ] = function( elem, name, isXML ) {
11717 var ret, handle,
11718 lowercaseName = name.toLowerCase();
11719
11720 if ( !isXML ) {
11721
11722 // Avoid an infinite loop by temporarily removing this function from the getter
11723 handle = attrHandle[ lowercaseName ];
11724 attrHandle[ lowercaseName ] = ret;
11725 ret = getter( elem, name, isXML ) != null ?
11726 lowercaseName :
11727 null;
11728 attrHandle[ lowercaseName ] = handle;
11729 }
11730 return ret;
11731 };
11732} );
11733
11734
11735
11736
11737var rfocusable = /^(?:input|select|textarea|button)$/i,
11738 rclickable = /^(?:a|area)$/i;
11739
11740jQuery.fn.extend( {
11741 prop: function( name, value ) {
11742 return access( this, jQuery.prop, name, value, arguments.length > 1 );
11743 },
11744
11745 removeProp: function( name ) {
11746 return this.each( function() {
11747 delete this[ jQuery.propFix[ name ] || name ];
11748 } );
11749 }
11750} );
11751
11752jQuery.extend( {
11753 prop: function( elem, name, value ) {
11754 var ret, hooks,
11755 nType = elem.nodeType;
11756
11757 // Don't get/set properties on text, comment and attribute nodes
11758 if ( nType === 3 || nType === 8 || nType === 2 ) {
11759 return;
11760 }
11761
11762 if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) {
11763
11764 // Fix name and attach hooks
11765 name = jQuery.propFix[ name ] || name;
11766 hooks = jQuery.propHooks[ name ];
11767 }
11768
11769 if ( value !== undefined ) {
11770 if ( hooks && "set" in hooks &&
11771 ( ret = hooks.set( elem, value, name ) ) !== undefined ) {
11772 return ret;
11773 }
11774
11775 return ( elem[ name ] = value );
11776 }
11777
11778 if ( hooks && "get" in hooks && ( ret = hooks.get( elem, name ) ) !== null ) {
11779 return ret;
11780 }
11781
11782 return elem[ name ];
11783 },
11784
11785 propHooks: {
11786 tabIndex: {
11787 get: function( elem ) {
11788
11789 // Support: IE <=9 - 11 only
11790 // elem.tabIndex doesn't always return the
11791 // correct value when it hasn't been explicitly set
11792 // https://web.archive.org/web/20141116233347/http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
11793 // Use proper attribute retrieval(#12072)
11794 var tabindex = jQuery.find.attr( elem, "tabindex" );
11795
11796 if ( tabindex ) {
11797 return parseInt( tabindex, 10 );
11798 }
11799
11800 if (
11801 rfocusable.test( elem.nodeName ) ||
11802 rclickable.test( elem.nodeName ) &&
11803 elem.href
11804 ) {
11805 return 0;
11806 }
11807
11808 return -1;
11809 }
11810 }
11811 },
11812
11813 propFix: {
11814 "for": "htmlFor",
11815 "class": "className"
11816 }
11817} );
11818
11819// Support: IE <=11 only
11820// Accessing the selectedIndex property
11821// forces the browser to respect setting selected
11822// on the option
11823// The getter ensures a default option is selected
11824// when in an optgroup
11825// eslint rule "no-unused-expressions" is disabled for this code
11826// since it considers such accessions noop
11827if ( !support.optSelected ) {
11828 jQuery.propHooks.selected = {
11829 get: function( elem ) {
11830
11831 /* eslint no-unused-expressions: "off" */
11832
11833 var parent = elem.parentNode;
11834 if ( parent && parent.parentNode ) {
11835 parent.parentNode.selectedIndex;
11836 }
11837 return null;
11838 },
11839 set: function( elem ) {
11840
11841 /* eslint no-unused-expressions: "off" */
11842
11843 var parent = elem.parentNode;
11844 if ( parent ) {
11845 parent.selectedIndex;
11846
11847 if ( parent.parentNode ) {
11848 parent.parentNode.selectedIndex;
11849 }
11850 }
11851 }
11852 };
11853}
11854
11855jQuery.each( [
11856 "tabIndex",
11857 "readOnly",
11858 "maxLength",
11859 "cellSpacing",
11860 "cellPadding",
11861 "rowSpan",
11862 "colSpan",
11863 "useMap",
11864 "frameBorder",
11865 "contentEditable"
11866], function() {
11867 jQuery.propFix[ this.toLowerCase() ] = this;
11868} );
11869
11870
11871
11872
11873 // Strip and collapse whitespace according to HTML spec
11874 // https://infra.spec.whatwg.org/#strip-and-collapse-ascii-whitespace
11875 function stripAndCollapse( value ) {
11876 var tokens = value.match( rnothtmlwhite ) || [];
11877 return tokens.join( " " );
11878 }
11879
11880
11881function getClass( elem ) {
11882 return elem.getAttribute && elem.getAttribute( "class" ) || "";
11883}
11884
11885function classesToArray( value ) {
11886 if ( Array.isArray( value ) ) {
11887 return value;
11888 }
11889 if ( typeof value === "string" ) {
11890 return value.match( rnothtmlwhite ) || [];
11891 }
11892 return [];
11893}
11894
11895jQuery.fn.extend( {
11896 addClass: function( value ) {
11897 var classes, elem, cur, curValue, clazz, j, finalValue,
11898 i = 0;
11899
11900 if ( isFunction( value ) ) {
11901 return this.each( function( j ) {
11902 jQuery( this ).addClass( value.call( this, j, getClass( this ) ) );
11903 } );
11904 }
11905
11906 classes = classesToArray( value );
11907
11908 if ( classes.length ) {
11909 while ( ( elem = this[ i++ ] ) ) {
11910 curValue = getClass( elem );
11911 cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
11912
11913 if ( cur ) {
11914 j = 0;
11915 while ( ( clazz = classes[ j++ ] ) ) {
11916 if ( cur.indexOf( " " + clazz + " " ) < 0 ) {
11917 cur += clazz + " ";
11918 }
11919 }
11920
11921 // Only assign if different to avoid unneeded rendering.
11922 finalValue = stripAndCollapse( cur );
11923 if ( curValue !== finalValue ) {
11924 elem.setAttribute( "class", finalValue );
11925 }
11926 }
11927 }
11928 }
11929
11930 return this;
11931 },
11932
11933 removeClass: function( value ) {
11934 var classes, elem, cur, curValue, clazz, j, finalValue,
11935 i = 0;
11936
11937 if ( isFunction( value ) ) {
11938 return this.each( function( j ) {
11939 jQuery( this ).removeClass( value.call( this, j, getClass( this ) ) );
11940 } );
11941 }
11942
11943 if ( !arguments.length ) {
11944 return this.attr( "class", "" );
11945 }
11946
11947 classes = classesToArray( value );
11948
11949 if ( classes.length ) {
11950 while ( ( elem = this[ i++ ] ) ) {
11951 curValue = getClass( elem );
11952
11953 // This expression is here for better compressibility (see addClass)
11954 cur = elem.nodeType === 1 && ( " " + stripAndCollapse( curValue ) + " " );
11955
11956 if ( cur ) {
11957 j = 0;
11958 while ( ( clazz = classes[ j++ ] ) ) {
11959
11960 // Remove *all* instances
11961 while ( cur.indexOf( " " + clazz + " " ) > -1 ) {
11962 cur = cur.replace( " " + clazz + " ", " " );
11963 }
11964 }
11965
11966 // Only assign if different to avoid unneeded rendering.
11967 finalValue = stripAndCollapse( cur );
11968 if ( curValue !== finalValue ) {
11969 elem.setAttribute( "class", finalValue );
11970 }
11971 }
11972 }
11973 }
11974
11975 return this;
11976 },
11977
11978 toggleClass: function( value, stateVal ) {
11979 var type = typeof value,
11980 isValidValue = type === "string" || Array.isArray( value );
11981
11982 if ( typeof stateVal === "boolean" && isValidValue ) {
11983 return stateVal ? this.addClass( value ) : this.removeClass( value );
11984 }
11985
11986 if ( isFunction( value ) ) {
11987 return this.each( function( i ) {
11988 jQuery( this ).toggleClass(
11989 value.call( this, i, getClass( this ), stateVal ),
11990 stateVal
11991 );
11992 } );
11993 }
11994
11995 return this.each( function() {
11996 var className, i, self, classNames;
11997
11998 if ( isValidValue ) {
11999
12000 // Toggle individual class names
12001 i = 0;
12002 self = jQuery( this );
12003 classNames = classesToArray( value );
12004
12005 while ( ( className = classNames[ i++ ] ) ) {
12006
12007 // Check each className given, space separated list
12008 if ( self.hasClass( className ) ) {
12009 self.removeClass( className );
12010 } else {
12011 self.addClass( className );
12012 }
12013 }
12014
12015 // Toggle whole class name
12016 } else if ( value === undefined || type === "boolean" ) {
12017 className = getClass( this );
12018 if ( className ) {
12019
12020 // Store className if set
12021 dataPriv.set( this, "__className__", className );
12022 }
12023
12024 // If the element has a class name or if we're passed `false`,
12025 // then remove the whole classname (if there was one, the above saved it).
12026 // Otherwise bring back whatever was previously saved (if anything),
12027 // falling back to the empty string if nothing was stored.
12028 if ( this.setAttribute ) {
12029 this.setAttribute( "class",
12030 className || value === false ?
12031 "" :
12032 dataPriv.get( this, "__className__" ) || ""
12033 );
12034 }
12035 }
12036 } );
12037 },
12038
12039 hasClass: function( selector ) {
12040 var className, elem,
12041 i = 0;
12042
12043 className = " " + selector + " ";
12044 while ( ( elem = this[ i++ ] ) ) {
12045 if ( elem.nodeType === 1 &&
12046 ( " " + stripAndCollapse( getClass( elem ) ) + " " ).indexOf( className ) > -1 ) {
12047 return true;
12048 }
12049 }
12050
12051 return false;
12052 }
12053} );
12054
12055
12056
12057
12058var rreturn = /\r/g;
12059
12060jQuery.fn.extend( {
12061 val: function( value ) {
12062 var hooks, ret, valueIsFunction,
12063 elem = this[ 0 ];
12064
12065 if ( !arguments.length ) {
12066 if ( elem ) {
12067 hooks = jQuery.valHooks[ elem.type ] ||
12068 jQuery.valHooks[ elem.nodeName.toLowerCase() ];
12069
12070 if ( hooks &&
12071 "get" in hooks &&
12072 ( ret = hooks.get( elem, "value" ) ) !== undefined
12073 ) {
12074 return ret;
12075 }
12076
12077 ret = elem.value;
12078
12079 // Handle most common string cases
12080 if ( typeof ret === "string" ) {
12081 return ret.replace( rreturn, "" );
12082 }
12083
12084 // Handle cases where value is null/undef or number
12085 return ret == null ? "" : ret;
12086 }
12087
12088 return;
12089 }
12090
12091 valueIsFunction = isFunction( value );
12092
12093 return this.each( function( i ) {
12094 var val;
12095
12096 if ( this.nodeType !== 1 ) {
12097 return;
12098 }
12099
12100 if ( valueIsFunction ) {
12101 val = value.call( this, i, jQuery( this ).val() );
12102 } else {
12103 val = value;
12104 }
12105
12106 // Treat null/undefined as ""; convert numbers to string
12107 if ( val == null ) {
12108 val = "";
12109
12110 } else if ( typeof val === "number" ) {
12111 val += "";
12112
12113 } else if ( Array.isArray( val ) ) {
12114 val = jQuery.map( val, function( value ) {
12115 return value == null ? "" : value + "";
12116 } );
12117 }
12118
12119 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
12120
12121 // If set returns undefined, fall back to normal setting
12122 if ( !hooks || !( "set" in hooks ) || hooks.set( this, val, "value" ) === undefined ) {
12123 this.value = val;
12124 }
12125 } );
12126 }
12127} );
12128
12129jQuery.extend( {
12130 valHooks: {
12131 option: {
12132 get: function( elem ) {
12133
12134 var val = jQuery.find.attr( elem, "value" );
12135 return val != null ?
12136 val :
12137
12138 // Support: IE <=10 - 11 only
12139 // option.text throws exceptions (#14686, #14858)
12140 // Strip and collapse whitespace
12141 // https://html.spec.whatwg.org/#strip-and-collapse-whitespace
12142 stripAndCollapse( jQuery.text( elem ) );
12143 }
12144 },
12145 select: {
12146 get: function( elem ) {
12147 var value, option, i,
12148 options = elem.options,
12149 index = elem.selectedIndex,
12150 one = elem.type === "select-one",
12151 values = one ? null : [],
12152 max = one ? index + 1 : options.length;
12153
12154 if ( index < 0 ) {
12155 i = max;
12156
12157 } else {
12158 i = one ? index : 0;
12159 }
12160
12161 // Loop through all the selected options
12162 for ( ; i < max; i++ ) {
12163 option = options[ i ];
12164
12165 // Support: IE <=9 only
12166 // IE8-9 doesn't update selected after form reset (#2551)
12167 if ( ( option.selected || i === index ) &&
12168
12169 // Don't return options that are disabled or in a disabled optgroup
12170 !option.disabled &&
12171 ( !option.parentNode.disabled ||
12172 !nodeName( option.parentNode, "optgroup" ) ) ) {
12173
12174 // Get the specific value for the option
12175 value = jQuery( option ).val();
12176
12177 // We don't need an array for one selects
12178 if ( one ) {
12179 return value;
12180 }
12181
12182 // Multi-Selects return an array
12183 values.push( value );
12184 }
12185 }
12186
12187 return values;
12188 },
12189
12190 set: function( elem, value ) {
12191 var optionSet, option,
12192 options = elem.options,
12193 values = jQuery.makeArray( value ),
12194 i = options.length;
12195
12196 while ( i-- ) {
12197 option = options[ i ];
12198
12199 /* eslint-disable no-cond-assign */
12200
12201 if ( option.selected =
12202 jQuery.inArray( jQuery.valHooks.option.get( option ), values ) > -1
12203 ) {
12204 optionSet = true;
12205 }
12206
12207 /* eslint-enable no-cond-assign */
12208 }
12209
12210 // Force browsers to behave consistently when non-matching value is set
12211 if ( !optionSet ) {
12212 elem.selectedIndex = -1;
12213 }
12214 return values;
12215 }
12216 }
12217 }
12218} );
12219
12220// Radios and checkboxes getter/setter
12221jQuery.each( [ "radio", "checkbox" ], function() {
12222 jQuery.valHooks[ this ] = {
12223 set: function( elem, value ) {
12224 if ( Array.isArray( value ) ) {
12225 return ( elem.checked = jQuery.inArray( jQuery( elem ).val(), value ) > -1 );
12226 }
12227 }
12228 };
12229 if ( !support.checkOn ) {
12230 jQuery.valHooks[ this ].get = function( elem ) {
12231 return elem.getAttribute( "value" ) === null ? "on" : elem.value;
12232 };
12233 }
12234} );
12235
12236
12237
12238
12239// Return jQuery for attributes-only inclusion
12240
12241
12242support.focusin = "onfocusin" in window;
12243
12244
12245var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
12246 stopPropagationCallback = function( e ) {
12247 e.stopPropagation();
12248 };
12249
12250jQuery.extend( jQuery.event, {
12251
12252 trigger: function( event, data, elem, onlyHandlers ) {
12253
12254 var i, cur, tmp, bubbleType, ontype, handle, special, lastElement,
12255 eventPath = [ elem || document ],
12256 type = hasOwn.call( event, "type" ) ? event.type : event,
12257 namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : [];
12258
12259 cur = lastElement = tmp = elem = elem || document;
12260
12261 // Don't do events on text and comment nodes
12262 if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
12263 return;
12264 }
12265
12266 // focus/blur morphs to focusin/out; ensure we're not firing them right now
12267 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
12268 return;
12269 }
12270
12271 if ( type.indexOf( "." ) > -1 ) {
12272
12273 // Namespaced trigger; create a regexp to match event type in handle()
12274 namespaces = type.split( "." );
12275 type = namespaces.shift();
12276 namespaces.sort();
12277 }
12278 ontype = type.indexOf( ":" ) < 0 && "on" + type;
12279
12280 // Caller can pass in a jQuery.Event object, Object, or just an event type string
12281 event = event[ jQuery.expando ] ?
12282 event :
12283 new jQuery.Event( type, typeof event === "object" && event );
12284
12285 // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true)
12286 event.isTrigger = onlyHandlers ? 2 : 3;
12287 event.namespace = namespaces.join( "." );
12288 event.rnamespace = event.namespace ?
12289 new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) :
12290 null;
12291
12292 // Clean up the event in case it is being reused
12293 event.result = undefined;
12294 if ( !event.target ) {
12295 event.target = elem;
12296 }
12297
12298 // Clone any incoming data and prepend the event, creating the handler arg list
12299 data = data == null ?
12300 [ event ] :
12301 jQuery.makeArray( data, [ event ] );
12302
12303 // Allow special events to draw outside the lines
12304 special = jQuery.event.special[ type ] || {};
12305 if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) {
12306 return;
12307 }
12308
12309 // Determine event propagation path in advance, per W3C events spec (#9951)
12310 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
12311 if ( !onlyHandlers && !special.noBubble && !isWindow( elem ) ) {
12312
12313 bubbleType = special.delegateType || type;
12314 if ( !rfocusMorph.test( bubbleType + type ) ) {
12315 cur = cur.parentNode;
12316 }
12317 for ( ; cur; cur = cur.parentNode ) {
12318 eventPath.push( cur );
12319 tmp = cur;
12320 }
12321
12322 // Only add window if we got to document (e.g., not plain obj or detached DOM)
12323 if ( tmp === ( elem.ownerDocument || document ) ) {
12324 eventPath.push( tmp.defaultView || tmp.parentWindow || window );
12325 }
12326 }
12327
12328 // Fire handlers on the event path
12329 i = 0;
12330 while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) {
12331 lastElement = cur;
12332 event.type = i > 1 ?
12333 bubbleType :
12334 special.bindType || type;
12335
12336 // jQuery handler
12337 handle = ( dataPriv.get( cur, "events" ) || {} )[ event.type ] &&
12338 dataPriv.get( cur, "handle" );
12339 if ( handle ) {
12340 handle.apply( cur, data );
12341 }
12342
12343 // Native handler
12344 handle = ontype && cur[ ontype ];
12345 if ( handle && handle.apply && acceptData( cur ) ) {
12346 event.result = handle.apply( cur, data );
12347 if ( event.result === false ) {
12348 event.preventDefault();
12349 }
12350 }
12351 }
12352 event.type = type;
12353
12354 // If nobody prevented the default action, do it now
12355 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
12356
12357 if ( ( !special._default ||
12358 special._default.apply( eventPath.pop(), data ) === false ) &&
12359 acceptData( elem ) ) {
12360
12361 // Call a native DOM method on the target with the same name as the event.
12362 // Don't do default actions on window, that's where global variables be (#6170)
12363 if ( ontype && isFunction( elem[ type ] ) && !isWindow( elem ) ) {
12364
12365 // Don't re-trigger an onFOO event when we call its FOO() method
12366 tmp = elem[ ontype ];
12367
12368 if ( tmp ) {
12369 elem[ ontype ] = null;
12370 }
12371
12372 // Prevent re-triggering of the same event, since we already bubbled it above
12373 jQuery.event.triggered = type;
12374
12375 if ( event.isPropagationStopped() ) {
12376 lastElement.addEventListener( type, stopPropagationCallback );
12377 }
12378
12379 elem[ type ]();
12380
12381 if ( event.isPropagationStopped() ) {
12382 lastElement.removeEventListener( type, stopPropagationCallback );
12383 }
12384
12385 jQuery.event.triggered = undefined;
12386
12387 if ( tmp ) {
12388 elem[ ontype ] = tmp;
12389 }
12390 }
12391 }
12392 }
12393
12394 return event.result;
12395 },
12396
12397 // Piggyback on a donor event to simulate a different one
12398 // Used only for `focus(in | out)` events
12399 simulate: function( type, elem, event ) {
12400 var e = jQuery.extend(
12401 new jQuery.Event(),
12402 event,
12403 {
12404 type: type,
12405 isSimulated: true
12406 }
12407 );
12408
12409 jQuery.event.trigger( e, null, elem );
12410 }
12411
12412} );
12413
12414jQuery.fn.extend( {
12415
12416 trigger: function( type, data ) {
12417 return this.each( function() {
12418 jQuery.event.trigger( type, data, this );
12419 } );
12420 },
12421 triggerHandler: function( type, data ) {
12422 var elem = this[ 0 ];
12423 if ( elem ) {
12424 return jQuery.event.trigger( type, data, elem, true );
12425 }
12426 }
12427} );
12428
12429
12430// Support: Firefox <=44
12431// Firefox doesn't have focus(in | out) events
12432// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787
12433//
12434// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1
12435// focus(in | out) events fire after focus & blur events,
12436// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order
12437// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857
12438if ( !support.focusin ) {
12439 jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) {
12440
12441 // Attach a single capturing handler on the document while someone wants focusin/focusout
12442 var handler = function( event ) {
12443 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) );
12444 };
12445
12446 jQuery.event.special[ fix ] = {
12447 setup: function() {
12448 var doc = this.ownerDocument || this,
12449 attaches = dataPriv.access( doc, fix );
12450
12451 if ( !attaches ) {
12452 doc.addEventListener( orig, handler, true );
12453 }
12454 dataPriv.access( doc, fix, ( attaches || 0 ) + 1 );
12455 },
12456 teardown: function() {
12457 var doc = this.ownerDocument || this,
12458 attaches = dataPriv.access( doc, fix ) - 1;
12459
12460 if ( !attaches ) {
12461 doc.removeEventListener( orig, handler, true );
12462 dataPriv.remove( doc, fix );
12463
12464 } else {
12465 dataPriv.access( doc, fix, attaches );
12466 }
12467 }
12468 };
12469 } );
12470}
12471var location = window.location;
12472
12473var nonce = Date.now();
12474
12475var rquery = ( /\?/ );
12476
12477
12478
12479// Cross-browser xml parsing
12480jQuery.parseXML = function( data ) {
12481 var xml;
12482 if ( !data || typeof data !== "string" ) {
12483 return null;
12484 }
12485
12486 // Support: IE 9 - 11 only
12487 // IE throws on parseFromString with invalid input.
12488 try {
12489 xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" );
12490 } catch ( e ) {
12491 xml = undefined;
12492 }
12493
12494 if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
12495 jQuery.error( "Invalid XML: " + data );
12496 }
12497 return xml;
12498};
12499
12500
12501var
12502 rbracket = /\[\]$/,
12503 rCRLF = /\r?\n/g,
12504 rsubmitterTypes = /^(?:submit|button|image|reset|file)$/i,
12505 rsubmittable = /^(?:input|select|textarea|keygen)/i;
12506
12507function buildParams( prefix, obj, traditional, add ) {
12508 var name;
12509
12510 if ( Array.isArray( obj ) ) {
12511
12512 // Serialize array item.
12513 jQuery.each( obj, function( i, v ) {
12514 if ( traditional || rbracket.test( prefix ) ) {
12515
12516 // Treat each array item as a scalar.
12517 add( prefix, v );
12518
12519 } else {
12520
12521 // Item is non-scalar (array or object), encode its numeric index.
12522 buildParams(
12523 prefix + "[" + ( typeof v === "object" && v != null ? i : "" ) + "]",
12524 v,
12525 traditional,
12526 add
12527 );
12528 }
12529 } );
12530
12531 } else if ( !traditional && toType( obj ) === "object" ) {
12532
12533 // Serialize object item.
12534 for ( name in obj ) {
12535 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
12536 }
12537
12538 } else {
12539
12540 // Serialize scalar item.
12541 add( prefix, obj );
12542 }
12543}
12544
12545// Serialize an array of form elements or a set of
12546// key/values into a query string
12547jQuery.param = function( a, traditional ) {
12548 var prefix,
12549 s = [],
12550 add = function( key, valueOrFunction ) {
12551
12552 // If value is a function, invoke it and use its return value
12553 var value = isFunction( valueOrFunction ) ?
12554 valueOrFunction() :
12555 valueOrFunction;
12556
12557 s[ s.length ] = encodeURIComponent( key ) + "=" +
12558 encodeURIComponent( value == null ? "" : value );
12559 };
12560
12561 if ( a == null ) {
12562 return "";
12563 }
12564
12565 // If an array was passed in, assume that it is an array of form elements.
12566 if ( Array.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
12567
12568 // Serialize the form elements
12569 jQuery.each( a, function() {
12570 add( this.name, this.value );
12571 } );
12572
12573 } else {
12574
12575 // If traditional, encode the "old" way (the way 1.3.2 or older
12576 // did it), otherwise encode params recursively.
12577 for ( prefix in a ) {
12578 buildParams( prefix, a[ prefix ], traditional, add );
12579 }
12580 }
12581
12582 // Return the resulting serialization
12583 return s.join( "&" );
12584};
12585
12586jQuery.fn.extend( {
12587 serialize: function() {
12588 return jQuery.param( this.serializeArray() );
12589 },
12590 serializeArray: function() {
12591 return this.map( function() {
12592
12593 // Can add propHook for "elements" to filter or add form elements
12594 var elements = jQuery.prop( this, "elements" );
12595 return elements ? jQuery.makeArray( elements ) : this;
12596 } )
12597 .filter( function() {
12598 var type = this.type;
12599
12600 // Use .is( ":disabled" ) so that fieldset[disabled] works
12601 return this.name && !jQuery( this ).is( ":disabled" ) &&
12602 rsubmittable.test( this.nodeName ) && !rsubmitterTypes.test( type ) &&
12603 ( this.checked || !rcheckableType.test( type ) );
12604 } )
12605 .map( function( i, elem ) {
12606 var val = jQuery( this ).val();
12607
12608 if ( val == null ) {
12609 return null;
12610 }
12611
12612 if ( Array.isArray( val ) ) {
12613 return jQuery.map( val, function( val ) {
12614 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
12615 } );
12616 }
12617
12618 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
12619 } ).get();
12620 }
12621} );
12622
12623
12624var
12625 r20 = /%20/g,
12626 rhash = /#.*$/,
12627 rantiCache = /([?&])_=[^&]*/,
12628 rheaders = /^(.*?):[ \t]*([^\r\n]*)$/mg,
12629
12630 // #7653, #8125, #8152: local protocol detection
12631 rlocalProtocol = /^(?:about|app|app-storage|.+-extension|file|res|widget):$/,
12632 rnoContent = /^(?:GET|HEAD)$/,
12633 rprotocol = /^\/\//,
12634
12635 /* Prefilters
12636 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
12637 * 2) These are called:
12638 * - BEFORE asking for a transport
12639 * - AFTER param serialization (s.data is a string if s.processData is true)
12640 * 3) key is the dataType
12641 * 4) the catchall symbol "*" can be used
12642 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
12643 */
12644 prefilters = {},
12645
12646 /* Transports bindings
12647 * 1) key is the dataType
12648 * 2) the catchall symbol "*" can be used
12649 * 3) selection will start with transport dataType and THEN go to "*" if needed
12650 */
12651 transports = {},
12652
12653 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
12654 allTypes = "*/".concat( "*" ),
12655
12656 // Anchor tag for parsing the document origin
12657 originAnchor = document.createElement( "a" );
12658 originAnchor.href = location.href;
12659
12660// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
12661function addToPrefiltersOrTransports( structure ) {
12662
12663 // dataTypeExpression is optional and defaults to "*"
12664 return function( dataTypeExpression, func ) {
12665
12666 if ( typeof dataTypeExpression !== "string" ) {
12667 func = dataTypeExpression;
12668 dataTypeExpression = "*";
12669 }
12670
12671 var dataType,
12672 i = 0,
12673 dataTypes = dataTypeExpression.toLowerCase().match( rnothtmlwhite ) || [];
12674
12675 if ( isFunction( func ) ) {
12676
12677 // For each dataType in the dataTypeExpression
12678 while ( ( dataType = dataTypes[ i++ ] ) ) {
12679
12680 // Prepend if requested
12681 if ( dataType[ 0 ] === "+" ) {
12682 dataType = dataType.slice( 1 ) || "*";
12683 ( structure[ dataType ] = structure[ dataType ] || [] ).unshift( func );
12684
12685 // Otherwise append
12686 } else {
12687 ( structure[ dataType ] = structure[ dataType ] || [] ).push( func );
12688 }
12689 }
12690 }
12691 };
12692}
12693
12694// Base inspection function for prefilters and transports
12695function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR ) {
12696
12697 var inspected = {},
12698 seekingTransport = ( structure === transports );
12699
12700 function inspect( dataType ) {
12701 var selected;
12702 inspected[ dataType ] = true;
12703 jQuery.each( structure[ dataType ] || [], function( _, prefilterOrFactory ) {
12704 var dataTypeOrTransport = prefilterOrFactory( options, originalOptions, jqXHR );
12705 if ( typeof dataTypeOrTransport === "string" &&
12706 !seekingTransport && !inspected[ dataTypeOrTransport ] ) {
12707
12708 options.dataTypes.unshift( dataTypeOrTransport );
12709 inspect( dataTypeOrTransport );
12710 return false;
12711 } else if ( seekingTransport ) {
12712 return !( selected = dataTypeOrTransport );
12713 }
12714 } );
12715 return selected;
12716 }
12717
12718 return inspect( options.dataTypes[ 0 ] ) || !inspected[ "*" ] && inspect( "*" );
12719}
12720
12721// A special extend for ajax options
12722// that takes "flat" options (not to be deep extended)
12723// Fixes #9887
12724function ajaxExtend( target, src ) {
12725 var key, deep,
12726 flatOptions = jQuery.ajaxSettings.flatOptions || {};
12727
12728 for ( key in src ) {
12729 if ( src[ key ] !== undefined ) {
12730 ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
12731 }
12732 }
12733 if ( deep ) {
12734 jQuery.extend( true, target, deep );
12735 }
12736
12737 return target;
12738}
12739
12740/* Handles responses to an ajax request:
12741 * - finds the right dataType (mediates between content-type and expected dataType)
12742 * - returns the corresponding response
12743 */
12744function ajaxHandleResponses( s, jqXHR, responses ) {
12745
12746 var ct, type, finalDataType, firstDataType,
12747 contents = s.contents,
12748 dataTypes = s.dataTypes;
12749
12750 // Remove auto dataType and get content-type in the process
12751 while ( dataTypes[ 0 ] === "*" ) {
12752 dataTypes.shift();
12753 if ( ct === undefined ) {
12754 ct = s.mimeType || jqXHR.getResponseHeader( "Content-Type" );
12755 }
12756 }
12757
12758 // Check if we're dealing with a known content-type
12759 if ( ct ) {
12760 for ( type in contents ) {
12761 if ( contents[ type ] && contents[ type ].test( ct ) ) {
12762 dataTypes.unshift( type );
12763 break;
12764 }
12765 }
12766 }
12767
12768 // Check to see if we have a response for the expected dataType
12769 if ( dataTypes[ 0 ] in responses ) {
12770 finalDataType = dataTypes[ 0 ];
12771 } else {
12772
12773 // Try convertible dataTypes
12774 for ( type in responses ) {
12775 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[ 0 ] ] ) {
12776 finalDataType = type;
12777 break;
12778 }
12779 if ( !firstDataType ) {
12780 firstDataType = type;
12781 }
12782 }
12783
12784 // Or just use first one
12785 finalDataType = finalDataType || firstDataType;
12786 }
12787
12788 // If we found a dataType
12789 // We add the dataType to the list if needed
12790 // and return the corresponding response
12791 if ( finalDataType ) {
12792 if ( finalDataType !== dataTypes[ 0 ] ) {
12793 dataTypes.unshift( finalDataType );
12794 }
12795 return responses[ finalDataType ];
12796 }
12797}
12798
12799/* Chain conversions given the request and the original response
12800 * Also sets the responseXXX fields on the jqXHR instance
12801 */
12802function ajaxConvert( s, response, jqXHR, isSuccess ) {
12803 var conv2, current, conv, tmp, prev,
12804 converters = {},
12805
12806 // Work with a copy of dataTypes in case we need to modify it for conversion
12807 dataTypes = s.dataTypes.slice();
12808
12809 // Create converters map with lowercased keys
12810 if ( dataTypes[ 1 ] ) {
12811 for ( conv in s.converters ) {
12812 converters[ conv.toLowerCase() ] = s.converters[ conv ];
12813 }
12814 }
12815
12816 current = dataTypes.shift();
12817
12818 // Convert to each sequential dataType
12819 while ( current ) {
12820
12821 if ( s.responseFields[ current ] ) {
12822 jqXHR[ s.responseFields[ current ] ] = response;
12823 }
12824
12825 // Apply the dataFilter if provided
12826 if ( !prev && isSuccess && s.dataFilter ) {
12827 response = s.dataFilter( response, s.dataType );
12828 }
12829
12830 prev = current;
12831 current = dataTypes.shift();
12832
12833 if ( current ) {
12834
12835 // There's only work to do if current dataType is non-auto
12836 if ( current === "*" ) {
12837
12838 current = prev;
12839
12840 // Convert response if prev dataType is non-auto and differs from current
12841 } else if ( prev !== "*" && prev !== current ) {
12842
12843 // Seek a direct converter
12844 conv = converters[ prev + " " + current ] || converters[ "* " + current ];
12845
12846 // If none found, seek a pair
12847 if ( !conv ) {
12848 for ( conv2 in converters ) {
12849
12850 // If conv2 outputs current
12851 tmp = conv2.split( " " );
12852 if ( tmp[ 1 ] === current ) {
12853
12854 // If prev can be converted to accepted input
12855 conv = converters[ prev + " " + tmp[ 0 ] ] ||
12856 converters[ "* " + tmp[ 0 ] ];
12857 if ( conv ) {
12858
12859 // Condense equivalence converters
12860 if ( conv === true ) {
12861 conv = converters[ conv2 ];
12862
12863 // Otherwise, insert the intermediate dataType
12864 } else if ( converters[ conv2 ] !== true ) {
12865 current = tmp[ 0 ];
12866 dataTypes.unshift( tmp[ 1 ] );
12867 }
12868 break;
12869 }
12870 }
12871 }
12872 }
12873
12874 // Apply converter (if not an equivalence)
12875 if ( conv !== true ) {
12876
12877 // Unless errors are allowed to bubble, catch and return them
12878 if ( conv && s.throws ) {
12879 response = conv( response );
12880 } else {
12881 try {
12882 response = conv( response );
12883 } catch ( e ) {
12884 return {
12885 state: "parsererror",
12886 error: conv ? e : "No conversion from " + prev + " to " + current
12887 };
12888 }
12889 }
12890 }
12891 }
12892 }
12893 }
12894
12895 return { state: "success", data: response };
12896}
12897
12898jQuery.extend( {
12899
12900 // Counter for holding the number of active queries
12901 active: 0,
12902
12903 // Last-Modified header cache for next request
12904 lastModified: {},
12905 etag: {},
12906
12907 ajaxSettings: {
12908 url: location.href,
12909 type: "GET",
12910 isLocal: rlocalProtocol.test( location.protocol ),
12911 global: true,
12912 processData: true,
12913 async: true,
12914 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
12915
12916 /*
12917 timeout: 0,
12918 data: null,
12919 dataType: null,
12920 username: null,
12921 password: null,
12922 cache: null,
12923 throws: false,
12924 traditional: false,
12925 headers: {},
12926 */
12927
12928 accepts: {
12929 "*": allTypes,
12930 text: "text/plain",
12931 html: "text/html",
12932 xml: "application/xml, text/xml",
12933 json: "application/json, text/javascript"
12934 },
12935
12936 contents: {
12937 xml: /\bxml\b/,
12938 html: /\bhtml/,
12939 json: /\bjson\b/
12940 },
12941
12942 responseFields: {
12943 xml: "responseXML",
12944 text: "responseText",
12945 json: "responseJSON"
12946 },
12947
12948 // Data converters
12949 // Keys separate source (or catchall "*") and destination types with a single space
12950 converters: {
12951
12952 // Convert anything to text
12953 "* text": String,
12954
12955 // Text to html (true = no transformation)
12956 "text html": true,
12957
12958 // Evaluate text as a json expression
12959 "text json": JSON.parse,
12960
12961 // Parse text as xml
12962 "text xml": jQuery.parseXML
12963 },
12964
12965 // For options that shouldn't be deep extended:
12966 // you can add your own custom options here if
12967 // and when you create one that shouldn't be
12968 // deep extended (see ajaxExtend)
12969 flatOptions: {
12970 url: true,
12971 context: true
12972 }
12973 },
12974
12975 // Creates a full fledged settings object into target
12976 // with both ajaxSettings and settings fields.
12977 // If target is omitted, writes into ajaxSettings.
12978 ajaxSetup: function( target, settings ) {
12979 return settings ?
12980
12981 // Building a settings object
12982 ajaxExtend( ajaxExtend( target, jQuery.ajaxSettings ), settings ) :
12983
12984 // Extending ajaxSettings
12985 ajaxExtend( jQuery.ajaxSettings, target );
12986 },
12987
12988 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
12989 ajaxTransport: addToPrefiltersOrTransports( transports ),
12990
12991 // Main method
12992 ajax: function( url, options ) {
12993
12994 // If url is an object, simulate pre-1.5 signature
12995 if ( typeof url === "object" ) {
12996 options = url;
12997 url = undefined;
12998 }
12999
13000 // Force options to be an object
13001 options = options || {};
13002
13003 var transport,
13004
13005 // URL without anti-cache param
13006 cacheURL,
13007
13008 // Response headers
13009 responseHeadersString,
13010 responseHeaders,
13011
13012 // timeout handle
13013 timeoutTimer,
13014
13015 // Url cleanup var
13016 urlAnchor,
13017
13018 // Request state (becomes false upon send and true upon completion)
13019 completed,
13020
13021 // To know if global events are to be dispatched
13022 fireGlobals,
13023
13024 // Loop variable
13025 i,
13026
13027 // uncached part of the url
13028 uncached,
13029
13030 // Create the final options object
13031 s = jQuery.ajaxSetup( {}, options ),
13032
13033 // Callbacks context
13034 callbackContext = s.context || s,
13035
13036 // Context for global events is callbackContext if it is a DOM node or jQuery collection
13037 globalEventContext = s.context &&
13038 ( callbackContext.nodeType || callbackContext.jquery ) ?
13039 jQuery( callbackContext ) :
13040 jQuery.event,
13041
13042 // Deferreds
13043 deferred = jQuery.Deferred(),
13044 completeDeferred = jQuery.Callbacks( "once memory" ),
13045
13046 // Status-dependent callbacks
13047 statusCode = s.statusCode || {},
13048
13049 // Headers (they are sent all at once)
13050 requestHeaders = {},
13051 requestHeadersNames = {},
13052
13053 // Default abort message
13054 strAbort = "canceled",
13055
13056 // Fake xhr
13057 jqXHR = {
13058 readyState: 0,
13059
13060 // Builds headers hashtable if needed
13061 getResponseHeader: function( key ) {
13062 var match;
13063 if ( completed ) {
13064 if ( !responseHeaders ) {
13065 responseHeaders = {};
13066 while ( ( match = rheaders.exec( responseHeadersString ) ) ) {
13067 responseHeaders[ match[ 1 ].toLowerCase() + " " ] =
13068 ( responseHeaders[ match[ 1 ].toLowerCase() + " " ] || [] )
13069 .concat( match[ 2 ] );
13070 }
13071 }
13072 match = responseHeaders[ key.toLowerCase() + " " ];
13073 }
13074 return match == null ? null : match.join( ", " );
13075 },
13076
13077 // Raw string
13078 getAllResponseHeaders: function() {
13079 return completed ? responseHeadersString : null;
13080 },
13081
13082 // Caches the header
13083 setRequestHeader: function( name, value ) {
13084 if ( completed == null ) {
13085 name = requestHeadersNames[ name.toLowerCase() ] =
13086 requestHeadersNames[ name.toLowerCase() ] || name;
13087 requestHeaders[ name ] = value;
13088 }
13089 return this;
13090 },
13091
13092 // Overrides response content-type header
13093 overrideMimeType: function( type ) {
13094 if ( completed == null ) {
13095 s.mimeType = type;
13096 }
13097 return this;
13098 },
13099
13100 // Status-dependent callbacks
13101 statusCode: function( map ) {
13102 var code;
13103 if ( map ) {
13104 if ( completed ) {
13105
13106 // Execute the appropriate callbacks
13107 jqXHR.always( map[ jqXHR.status ] );
13108 } else {
13109
13110 // Lazy-add the new callbacks in a way that preserves old ones
13111 for ( code in map ) {
13112 statusCode[ code ] = [ statusCode[ code ], map[ code ] ];
13113 }
13114 }
13115 }
13116 return this;
13117 },
13118
13119 // Cancel the request
13120 abort: function( statusText ) {
13121 var finalText = statusText || strAbort;
13122 if ( transport ) {
13123 transport.abort( finalText );
13124 }
13125 done( 0, finalText );
13126 return this;
13127 }
13128 };
13129
13130 // Attach deferreds
13131 deferred.promise( jqXHR );
13132
13133 // Add protocol if not provided (prefilters might expect it)
13134 // Handle falsy url in the settings object (#10093: consistency with old signature)
13135 // We also use the url parameter if available
13136 s.url = ( ( url || s.url || location.href ) + "" )
13137 .replace( rprotocol, location.protocol + "//" );
13138
13139 // Alias method option to type as per ticket #12004
13140 s.type = options.method || options.type || s.method || s.type;
13141
13142 // Extract dataTypes list
13143 s.dataTypes = ( s.dataType || "*" ).toLowerCase().match( rnothtmlwhite ) || [ "" ];
13144
13145 // A cross-domain request is in order when the origin doesn't match the current origin.
13146 if ( s.crossDomain == null ) {
13147 urlAnchor = document.createElement( "a" );
13148
13149 // Support: IE <=8 - 11, Edge 12 - 15
13150 // IE throws exception on accessing the href property if url is malformed,
13151 // e.g. http://example.com:80x/
13152 try {
13153 urlAnchor.href = s.url;
13154
13155 // Support: IE <=8 - 11 only
13156 // Anchor's host property isn't correctly set when s.url is relative
13157 urlAnchor.href = urlAnchor.href;
13158 s.crossDomain = originAnchor.protocol + "//" + originAnchor.host !==
13159 urlAnchor.protocol + "//" + urlAnchor.host;
13160 } catch ( e ) {
13161
13162 // If there is an error parsing the URL, assume it is crossDomain,
13163 // it can be rejected by the transport if it is invalid
13164 s.crossDomain = true;
13165 }
13166 }
13167
13168 // Convert data if not already a string
13169 if ( s.data && s.processData && typeof s.data !== "string" ) {
13170 s.data = jQuery.param( s.data, s.traditional );
13171 }
13172
13173 // Apply prefilters
13174 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
13175
13176 // If request was aborted inside a prefilter, stop there
13177 if ( completed ) {
13178 return jqXHR;
13179 }
13180
13181 // We can fire global events as of now if asked to
13182 // Don't fire events if jQuery.event is undefined in an AMD-usage scenario (#15118)
13183 fireGlobals = jQuery.event && s.global;
13184
13185 // Watch for a new set of requests
13186 if ( fireGlobals && jQuery.active++ === 0 ) {
13187 jQuery.event.trigger( "ajaxStart" );
13188 }
13189
13190 // Uppercase the type
13191 s.type = s.type.toUpperCase();
13192
13193 // Determine if request has content
13194 s.hasContent = !rnoContent.test( s.type );
13195
13196 // Save the URL in case we're toying with the If-Modified-Since
13197 // and/or If-None-Match header later on
13198 // Remove hash to simplify url manipulation
13199 cacheURL = s.url.replace( rhash, "" );
13200
13201 // More options handling for requests with no content
13202 if ( !s.hasContent ) {
13203
13204 // Remember the hash so we can put it back
13205 uncached = s.url.slice( cacheURL.length );
13206
13207 // If data is available and should be processed, append data to url
13208 if ( s.data && ( s.processData || typeof s.data === "string" ) ) {
13209 cacheURL += ( rquery.test( cacheURL ) ? "&" : "?" ) + s.data;
13210
13211 // #9682: remove data so that it's not used in an eventual retry
13212 delete s.data;
13213 }
13214
13215 // Add or update anti-cache param if needed
13216 if ( s.cache === false ) {
13217 cacheURL = cacheURL.replace( rantiCache, "$1" );
13218 uncached = ( rquery.test( cacheURL ) ? "&" : "?" ) + "_=" + ( nonce++ ) + uncached;
13219 }
13220
13221 // Put hash and anti-cache on the URL that will be requested (gh-1732)
13222 s.url = cacheURL + uncached;
13223
13224 // Change '%20' to '+' if this is encoded form body content (gh-2658)
13225 } else if ( s.data && s.processData &&
13226 ( s.contentType || "" ).indexOf( "application/x-www-form-urlencoded" ) === 0 ) {
13227 s.data = s.data.replace( r20, "+" );
13228 }
13229
13230 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
13231 if ( s.ifModified ) {
13232 if ( jQuery.lastModified[ cacheURL ] ) {
13233 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ cacheURL ] );
13234 }
13235 if ( jQuery.etag[ cacheURL ] ) {
13236 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ cacheURL ] );
13237 }
13238 }
13239
13240 // Set the correct header, if data is being sent
13241 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
13242 jqXHR.setRequestHeader( "Content-Type", s.contentType );
13243 }
13244
13245 // Set the Accepts header for the server, depending on the dataType
13246 jqXHR.setRequestHeader(
13247 "Accept",
13248 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
13249 s.accepts[ s.dataTypes[ 0 ] ] +
13250 ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
13251 s.accepts[ "*" ]
13252 );
13253
13254 // Check for headers option
13255 for ( i in s.headers ) {
13256 jqXHR.setRequestHeader( i, s.headers[ i ] );
13257 }
13258
13259 // Allow custom headers/mimetypes and early abort
13260 if ( s.beforeSend &&
13261 ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || completed ) ) {
13262
13263 // Abort if not done already and return
13264 return jqXHR.abort();
13265 }
13266
13267 // Aborting is no longer a cancellation
13268 strAbort = "abort";
13269
13270 // Install callbacks on deferreds
13271 completeDeferred.add( s.complete );
13272 jqXHR.done( s.success );
13273 jqXHR.fail( s.error );
13274
13275 // Get transport
13276 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
13277
13278 // If no transport, we auto-abort
13279 if ( !transport ) {
13280 done( -1, "No Transport" );
13281 } else {
13282 jqXHR.readyState = 1;
13283
13284 // Send global event
13285 if ( fireGlobals ) {
13286 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
13287 }
13288
13289 // If request was aborted inside ajaxSend, stop there
13290 if ( completed ) {
13291 return jqXHR;
13292 }
13293
13294 // Timeout
13295 if ( s.async && s.timeout > 0 ) {
13296 timeoutTimer = window.setTimeout( function() {
13297 jqXHR.abort( "timeout" );
13298 }, s.timeout );
13299 }
13300
13301 try {
13302 completed = false;
13303 transport.send( requestHeaders, done );
13304 } catch ( e ) {
13305
13306 // Rethrow post-completion exceptions
13307 if ( completed ) {
13308 throw e;
13309 }
13310
13311 // Propagate others as results
13312 done( -1, e );
13313 }
13314 }
13315
13316 // Callback for when everything is done
13317 function done( status, nativeStatusText, responses, headers ) {
13318 var isSuccess, success, error, response, modified,
13319 statusText = nativeStatusText;
13320
13321 // Ignore repeat invocations
13322 if ( completed ) {
13323 return;
13324 }
13325
13326 completed = true;
13327
13328 // Clear timeout if it exists
13329 if ( timeoutTimer ) {
13330 window.clearTimeout( timeoutTimer );
13331 }
13332
13333 // Dereference transport for early garbage collection
13334 // (no matter how long the jqXHR object will be used)
13335 transport = undefined;
13336
13337 // Cache response headers
13338 responseHeadersString = headers || "";
13339
13340 // Set readyState
13341 jqXHR.readyState = status > 0 ? 4 : 0;
13342
13343 // Determine if successful
13344 isSuccess = status >= 200 && status < 300 || status === 304;
13345
13346 // Get response data
13347 if ( responses ) {
13348 response = ajaxHandleResponses( s, jqXHR, responses );
13349 }
13350
13351 // Convert no matter what (that way responseXXX fields are always set)
13352 response = ajaxConvert( s, response, jqXHR, isSuccess );
13353
13354 // If successful, handle type chaining
13355 if ( isSuccess ) {
13356
13357 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
13358 if ( s.ifModified ) {
13359 modified = jqXHR.getResponseHeader( "Last-Modified" );
13360 if ( modified ) {
13361 jQuery.lastModified[ cacheURL ] = modified;
13362 }
13363 modified = jqXHR.getResponseHeader( "etag" );
13364 if ( modified ) {
13365 jQuery.etag[ cacheURL ] = modified;
13366 }
13367 }
13368
13369 // if no content
13370 if ( status === 204 || s.type === "HEAD" ) {
13371 statusText = "nocontent";
13372
13373 // if not modified
13374 } else if ( status === 304 ) {
13375 statusText = "notmodified";
13376
13377 // If we have data, let's convert it
13378 } else {
13379 statusText = response.state;
13380 success = response.data;
13381 error = response.error;
13382 isSuccess = !error;
13383 }
13384 } else {
13385
13386 // Extract error from statusText and normalize for non-aborts
13387 error = statusText;
13388 if ( status || !statusText ) {
13389 statusText = "error";
13390 if ( status < 0 ) {
13391 status = 0;
13392 }
13393 }
13394 }
13395
13396 // Set data for the fake xhr object
13397 jqXHR.status = status;
13398 jqXHR.statusText = ( nativeStatusText || statusText ) + "";
13399
13400 // Success/Error
13401 if ( isSuccess ) {
13402 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
13403 } else {
13404 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
13405 }
13406
13407 // Status-dependent callbacks
13408 jqXHR.statusCode( statusCode );
13409 statusCode = undefined;
13410
13411 if ( fireGlobals ) {
13412 globalEventContext.trigger( isSuccess ? "ajaxSuccess" : "ajaxError",
13413 [ jqXHR, s, isSuccess ? success : error ] );
13414 }
13415
13416 // Complete
13417 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
13418
13419 if ( fireGlobals ) {
13420 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
13421
13422 // Handle the global AJAX counter
13423 if ( !( --jQuery.active ) ) {
13424 jQuery.event.trigger( "ajaxStop" );
13425 }
13426 }
13427 }
13428
13429 return jqXHR;
13430 },
13431
13432 getJSON: function( url, data, callback ) {
13433 return jQuery.get( url, data, callback, "json" );
13434 },
13435
13436 getScript: function( url, callback ) {
13437 return jQuery.get( url, undefined, callback, "script" );
13438 }
13439} );
13440
13441jQuery.each( [ "get", "post" ], function( i, method ) {
13442 jQuery[ method ] = function( url, data, callback, type ) {
13443
13444 // Shift arguments if data argument was omitted
13445 if ( isFunction( data ) ) {
13446 type = type || callback;
13447 callback = data;
13448 data = undefined;
13449 }
13450
13451 // The url can be an options object (which then must have .url)
13452 return jQuery.ajax( jQuery.extend( {
13453 url: url,
13454 type: method,
13455 dataType: type,
13456 data: data,
13457 success: callback
13458 }, jQuery.isPlainObject( url ) && url ) );
13459 };
13460} );
13461
13462
13463jQuery._evalUrl = function( url, options ) {
13464 return jQuery.ajax( {
13465 url: url,
13466
13467 // Make this explicit, since user can override this through ajaxSetup (#11264)
13468 type: "GET",
13469 dataType: "script",
13470 cache: true,
13471 async: false,
13472 global: false,
13473
13474 // Only evaluate the response if it is successful (gh-4126)
13475 // dataFilter is not invoked for failure responses, so using it instead
13476 // of the default converter is kludgy but it works.
13477 converters: {
13478 "text script": function() {}
13479 },
13480 dataFilter: function( response ) {
13481 jQuery.globalEval( response, options );
13482 }
13483 } );
13484};
13485
13486
13487jQuery.fn.extend( {
13488 wrapAll: function( html ) {
13489 var wrap;
13490
13491 if ( this[ 0 ] ) {
13492 if ( isFunction( html ) ) {
13493 html = html.call( this[ 0 ] );
13494 }
13495
13496 // The elements to wrap the target around
13497 wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true );
13498
13499 if ( this[ 0 ].parentNode ) {
13500 wrap.insertBefore( this[ 0 ] );
13501 }
13502
13503 wrap.map( function() {
13504 var elem = this;
13505
13506 while ( elem.firstElementChild ) {
13507 elem = elem.firstElementChild;
13508 }
13509
13510 return elem;
13511 } ).append( this );
13512 }
13513
13514 return this;
13515 },
13516
13517 wrapInner: function( html ) {
13518 if ( isFunction( html ) ) {
13519 return this.each( function( i ) {
13520 jQuery( this ).wrapInner( html.call( this, i ) );
13521 } );
13522 }
13523
13524 return this.each( function() {
13525 var self = jQuery( this ),
13526 contents = self.contents();
13527
13528 if ( contents.length ) {
13529 contents.wrapAll( html );
13530
13531 } else {
13532 self.append( html );
13533 }
13534 } );
13535 },
13536
13537 wrap: function( html ) {
13538 var htmlIsFunction = isFunction( html );
13539
13540 return this.each( function( i ) {
13541 jQuery( this ).wrapAll( htmlIsFunction ? html.call( this, i ) : html );
13542 } );
13543 },
13544
13545 unwrap: function( selector ) {
13546 this.parent( selector ).not( "body" ).each( function() {
13547 jQuery( this ).replaceWith( this.childNodes );
13548 } );
13549 return this;
13550 }
13551} );
13552
13553
13554jQuery.expr.pseudos.hidden = function( elem ) {
13555 return !jQuery.expr.pseudos.visible( elem );
13556};
13557jQuery.expr.pseudos.visible = function( elem ) {
13558 return !!( elem.offsetWidth || elem.offsetHeight || elem.getClientRects().length );
13559};
13560
13561
13562
13563
13564jQuery.ajaxSettings.xhr = function() {
13565 try {
13566 return new window.XMLHttpRequest();
13567 } catch ( e ) {}
13568};
13569
13570var xhrSuccessStatus = {
13571
13572 // File protocol always yields status code 0, assume 200
13573 0: 200,
13574
13575 // Support: IE <=9 only
13576 // #1450: sometimes IE returns 1223 when it should be 204
13577 1223: 204
13578 },
13579 xhrSupported = jQuery.ajaxSettings.xhr();
13580
13581support.cors = !!xhrSupported && ( "withCredentials" in xhrSupported );
13582support.ajax = xhrSupported = !!xhrSupported;
13583
13584jQuery.ajaxTransport( function( options ) {
13585 var callback, errorCallback;
13586
13587 // Cross domain only allowed if supported through XMLHttpRequest
13588 if ( support.cors || xhrSupported && !options.crossDomain ) {
13589 return {
13590 send: function( headers, complete ) {
13591 var i,
13592 xhr = options.xhr();
13593
13594 xhr.open(
13595 options.type,
13596 options.url,
13597 options.async,
13598 options.username,
13599 options.password
13600 );
13601
13602 // Apply custom fields if provided
13603 if ( options.xhrFields ) {
13604 for ( i in options.xhrFields ) {
13605 xhr[ i ] = options.xhrFields[ i ];
13606 }
13607 }
13608
13609 // Override mime type if needed
13610 if ( options.mimeType && xhr.overrideMimeType ) {
13611 xhr.overrideMimeType( options.mimeType );
13612 }
13613
13614 // X-Requested-With header
13615 // For cross-domain requests, seeing as conditions for a preflight are
13616 // akin to a jigsaw puzzle, we simply never set it to be sure.
13617 // (it can always be set on a per-request basis or even using ajaxSetup)
13618 // For same-domain requests, won't change header if already provided.
13619 if ( !options.crossDomain && !headers[ "X-Requested-With" ] ) {
13620 headers[ "X-Requested-With" ] = "XMLHttpRequest";
13621 }
13622
13623 // Set headers
13624 for ( i in headers ) {
13625 xhr.setRequestHeader( i, headers[ i ] );
13626 }
13627
13628 // Callback
13629 callback = function( type ) {
13630 return function() {
13631 if ( callback ) {
13632 callback = errorCallback = xhr.onload =
13633 xhr.onerror = xhr.onabort = xhr.ontimeout =
13634 xhr.onreadystatechange = null;
13635
13636 if ( type === "abort" ) {
13637 xhr.abort();
13638 } else if ( type === "error" ) {
13639
13640 // Support: IE <=9 only
13641 // On a manual native abort, IE9 throws
13642 // errors on any property access that is not readyState
13643 if ( typeof xhr.status !== "number" ) {
13644 complete( 0, "error" );
13645 } else {
13646 complete(
13647
13648 // File: protocol always yields status 0; see #8605, #14207
13649 xhr.status,
13650 xhr.statusText
13651 );
13652 }
13653 } else {
13654 complete(
13655 xhrSuccessStatus[ xhr.status ] || xhr.status,
13656 xhr.statusText,
13657
13658 // Support: IE <=9 only
13659 // IE9 has no XHR2 but throws on binary (trac-11426)
13660 // For XHR2 non-text, let the caller handle it (gh-2498)
13661 ( xhr.responseType || "text" ) !== "text" ||
13662 typeof xhr.responseText !== "string" ?
13663 { binary: xhr.response } :
13664 { text: xhr.responseText },
13665 xhr.getAllResponseHeaders()
13666 );
13667 }
13668 }
13669 };
13670 };
13671
13672 // Listen to events
13673 xhr.onload = callback();
13674 errorCallback = xhr.onerror = xhr.ontimeout = callback( "error" );
13675
13676 // Support: IE 9 only
13677 // Use onreadystatechange to replace onabort
13678 // to handle uncaught aborts
13679 if ( xhr.onabort !== undefined ) {
13680 xhr.onabort = errorCallback;
13681 } else {
13682 xhr.onreadystatechange = function() {
13683
13684 // Check readyState before timeout as it changes
13685 if ( xhr.readyState === 4 ) {
13686
13687 // Allow onerror to be called first,
13688 // but that will not handle a native abort
13689 // Also, save errorCallback to a variable
13690 // as xhr.onerror cannot be accessed
13691 window.setTimeout( function() {
13692 if ( callback ) {
13693 errorCallback();
13694 }
13695 } );
13696 }
13697 };
13698 }
13699
13700 // Create the abort callback
13701 callback = callback( "abort" );
13702
13703 try {
13704
13705 // Do send the request (this may raise an exception)
13706 xhr.send( options.hasContent && options.data || null );
13707 } catch ( e ) {
13708
13709 // #14683: Only rethrow if this hasn't been notified as an error yet
13710 if ( callback ) {
13711 throw e;
13712 }
13713 }
13714 },
13715
13716 abort: function() {
13717 if ( callback ) {
13718 callback();
13719 }
13720 }
13721 };
13722 }
13723} );
13724
13725
13726
13727
13728// Prevent auto-execution of scripts when no explicit dataType was provided (See gh-2432)
13729jQuery.ajaxPrefilter( function( s ) {
13730 if ( s.crossDomain ) {
13731 s.contents.script = false;
13732 }
13733} );
13734
13735// Install script dataType
13736jQuery.ajaxSetup( {
13737 accepts: {
13738 script: "text/javascript, application/javascript, " +
13739 "application/ecmascript, application/x-ecmascript"
13740 },
13741 contents: {
13742 script: /\b(?:java|ecma)script\b/
13743 },
13744 converters: {
13745 "text script": function( text ) {
13746 jQuery.globalEval( text );
13747 return text;
13748 }
13749 }
13750} );
13751
13752// Handle cache's special case and crossDomain
13753jQuery.ajaxPrefilter( "script", function( s ) {
13754 if ( s.cache === undefined ) {
13755 s.cache = false;
13756 }
13757 if ( s.crossDomain ) {
13758 s.type = "GET";
13759 }
13760} );
13761
13762// Bind script tag hack transport
13763jQuery.ajaxTransport( "script", function( s ) {
13764
13765 // This transport only deals with cross domain or forced-by-attrs requests
13766 if ( s.crossDomain || s.scriptAttrs ) {
13767 var script, callback;
13768 return {
13769 send: function( _, complete ) {
13770 script = jQuery( "<script>" )
13771 .attr( s.scriptAttrs || {} )
13772 .prop( { charset: s.scriptCharset, src: s.url } )
13773 .on( "load error", callback = function( evt ) {
13774 script.remove();
13775 callback = null;
13776 if ( evt ) {
13777 complete( evt.type === "error" ? 404 : 200, evt.type );
13778 }
13779 } );
13780
13781 // Use native DOM manipulation to avoid our domManip AJAX trickery
13782 document.head.appendChild( script[ 0 ] );
13783 },
13784 abort: function() {
13785 if ( callback ) {
13786 callback();
13787 }
13788 }
13789 };
13790 }
13791} );
13792
13793
13794
13795
13796var oldCallbacks = [],
13797 rjsonp = /(=)\?(?=&|$)|\?\?/;
13798
13799// Default jsonp settings
13800jQuery.ajaxSetup( {
13801 jsonp: "callback",
13802 jsonpCallback: function() {
13803 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
13804 this[ callback ] = true;
13805 return callback;
13806 }
13807} );
13808
13809// Detect, normalize options and install callbacks for jsonp requests
13810jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
13811
13812 var callbackName, overwritten, responseContainer,
13813 jsonProp = s.jsonp !== false && ( rjsonp.test( s.url ) ?
13814 "url" :
13815 typeof s.data === "string" &&
13816 ( s.contentType || "" )
13817 .indexOf( "application/x-www-form-urlencoded" ) === 0 &&
13818 rjsonp.test( s.data ) && "data"
13819 );
13820
13821 // Handle iff the expected data type is "jsonp" or we have a parameter to set
13822 if ( jsonProp || s.dataTypes[ 0 ] === "jsonp" ) {
13823
13824 // Get callback name, remembering preexisting value associated with it
13825 callbackName = s.jsonpCallback = isFunction( s.jsonpCallback ) ?
13826 s.jsonpCallback() :
13827 s.jsonpCallback;
13828
13829 // Insert callback into url or form data
13830 if ( jsonProp ) {
13831 s[ jsonProp ] = s[ jsonProp ].replace( rjsonp, "$1" + callbackName );
13832 } else if ( s.jsonp !== false ) {
13833 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
13834 }
13835
13836 // Use data converter to retrieve json after script execution
13837 s.converters[ "script json" ] = function() {
13838 if ( !responseContainer ) {
13839 jQuery.error( callbackName + " was not called" );
13840 }
13841 return responseContainer[ 0 ];
13842 };
13843
13844 // Force json dataType
13845 s.dataTypes[ 0 ] = "json";
13846
13847 // Install callback
13848 overwritten = window[ callbackName ];
13849 window[ callbackName ] = function() {
13850 responseContainer = arguments;
13851 };
13852
13853 // Clean-up function (fires after converters)
13854 jqXHR.always( function() {
13855
13856 // If previous value didn't exist - remove it
13857 if ( overwritten === undefined ) {
13858 jQuery( window ).removeProp( callbackName );
13859
13860 // Otherwise restore preexisting value
13861 } else {
13862 window[ callbackName ] = overwritten;
13863 }
13864
13865 // Save back as free
13866 if ( s[ callbackName ] ) {
13867
13868 // Make sure that re-using the options doesn't screw things around
13869 s.jsonpCallback = originalSettings.jsonpCallback;
13870
13871 // Save the callback name for future use
13872 oldCallbacks.push( callbackName );
13873 }
13874
13875 // Call if it was a function and we have a response
13876 if ( responseContainer && isFunction( overwritten ) ) {
13877 overwritten( responseContainer[ 0 ] );
13878 }
13879
13880 responseContainer = overwritten = undefined;
13881 } );
13882
13883 // Delegate to script
13884 return "script";
13885 }
13886} );
13887
13888
13889
13890
13891// Support: Safari 8 only
13892// In Safari 8 documents created via document.implementation.createHTMLDocument
13893// collapse sibling forms: the second one becomes a child of the first one.
13894// Because of that, this security measure has to be disabled in Safari 8.
13895// https://bugs.webkit.org/show_bug.cgi?id=137337
13896support.createHTMLDocument = ( function() {
13897 var body = document.implementation.createHTMLDocument( "" ).body;
13898 body.innerHTML = "<form></form><form></form>";
13899 return body.childNodes.length === 2;
13900} )();
13901
13902
13903// Argument "data" should be string of html
13904// context (optional): If specified, the fragment will be created in this context,
13905// defaults to document
13906// keepScripts (optional): If true, will include scripts passed in the html string
13907jQuery.parseHTML = function( data, context, keepScripts ) {
13908 if ( typeof data !== "string" ) {
13909 return [];
13910 }
13911 if ( typeof context === "boolean" ) {
13912 keepScripts = context;
13913 context = false;
13914 }
13915
13916 var base, parsed, scripts;
13917
13918 if ( !context ) {
13919
13920 // Stop scripts or inline event handlers from being executed immediately
13921 // by using document.implementation
13922 if ( support.createHTMLDocument ) {
13923 context = document.implementation.createHTMLDocument( "" );
13924
13925 // Set the base href for the created document
13926 // so any parsed elements with URLs
13927 // are based on the document's URL (gh-2965)
13928 base = context.createElement( "base" );
13929 base.href = document.location.href;
13930 context.head.appendChild( base );
13931 } else {
13932 context = document;
13933 }
13934 }
13935
13936 parsed = rsingleTag.exec( data );
13937 scripts = !keepScripts && [];
13938
13939 // Single tag
13940 if ( parsed ) {
13941 return [ context.createElement( parsed[ 1 ] ) ];
13942 }
13943
13944 parsed = buildFragment( [ data ], context, scripts );
13945
13946 if ( scripts && scripts.length ) {
13947 jQuery( scripts ).remove();
13948 }
13949
13950 return jQuery.merge( [], parsed.childNodes );
13951};
13952
13953
13954/**
13955 * Load a url into a page
13956 */
13957jQuery.fn.load = function( url, params, callback ) {
13958 var selector, type, response,
13959 self = this,
13960 off = url.indexOf( " " );
13961
13962 if ( off > -1 ) {
13963 selector = stripAndCollapse( url.slice( off ) );
13964 url = url.slice( 0, off );
13965 }
13966
13967 // If it's a function
13968 if ( isFunction( params ) ) {
13969
13970 // We assume that it's the callback
13971 callback = params;
13972 params = undefined;
13973
13974 // Otherwise, build a param string
13975 } else if ( params && typeof params === "object" ) {
13976 type = "POST";
13977 }
13978
13979 // If we have elements to modify, make the request
13980 if ( self.length > 0 ) {
13981 jQuery.ajax( {
13982 url: url,
13983
13984 // If "type" variable is undefined, then "GET" method will be used.
13985 // Make value of this field explicit since
13986 // user can override it through ajaxSetup method
13987 type: type || "GET",
13988 dataType: "html",
13989 data: params
13990 } ).done( function( responseText ) {
13991
13992 // Save response for use in complete callback
13993 response = arguments;
13994
13995 self.html( selector ?
13996
13997 // If a selector was specified, locate the right elements in a dummy div
13998 // Exclude scripts to avoid IE 'Permission Denied' errors
13999 jQuery( "<div>" ).append( jQuery.parseHTML( responseText ) ).find( selector ) :
14000
14001 // Otherwise use the full result
14002 responseText );
14003
14004 // If the request succeeds, this function gets "data", "status", "jqXHR"
14005 // but they are ignored because response was set above.
14006 // If it fails, this function gets "jqXHR", "status", "error"
14007 } ).always( callback && function( jqXHR, status ) {
14008 self.each( function() {
14009 callback.apply( this, response || [ jqXHR.responseText, status, jqXHR ] );
14010 } );
14011 } );
14012 }
14013
14014 return this;
14015};
14016
14017
14018
14019
14020// Attach a bunch of functions for handling common AJAX events
14021jQuery.each( [
14022 "ajaxStart",
14023 "ajaxStop",
14024 "ajaxComplete",
14025 "ajaxError",
14026 "ajaxSuccess",
14027 "ajaxSend"
14028], function( i, type ) {
14029 jQuery.fn[ type ] = function( fn ) {
14030 return this.on( type, fn );
14031 };
14032} );
14033
14034
14035
14036
14037jQuery.expr.pseudos.animated = function( elem ) {
14038 return jQuery.grep( jQuery.timers, function( fn ) {
14039 return elem === fn.elem;
14040 } ).length;
14041};
14042
14043
14044
14045
14046jQuery.offset = {
14047 setOffset: function( elem, options, i ) {
14048 var curPosition, curLeft, curCSSTop, curTop, curOffset, curCSSLeft, calculatePosition,
14049 position = jQuery.css( elem, "position" ),
14050 curElem = jQuery( elem ),
14051 props = {};
14052
14053 // Set position first, in-case top/left are set even on static elem
14054 if ( position === "static" ) {
14055 elem.style.position = "relative";
14056 }
14057
14058 curOffset = curElem.offset();
14059 curCSSTop = jQuery.css( elem, "top" );
14060 curCSSLeft = jQuery.css( elem, "left" );
14061 calculatePosition = ( position === "absolute" || position === "fixed" ) &&
14062 ( curCSSTop + curCSSLeft ).indexOf( "auto" ) > -1;
14063
14064 // Need to be able to calculate position if either
14065 // top or left is auto and position is either absolute or fixed
14066 if ( calculatePosition ) {
14067 curPosition = curElem.position();
14068 curTop = curPosition.top;
14069 curLeft = curPosition.left;
14070
14071 } else {
14072 curTop = parseFloat( curCSSTop ) || 0;
14073 curLeft = parseFloat( curCSSLeft ) || 0;
14074 }
14075
14076 if ( isFunction( options ) ) {
14077
14078 // Use jQuery.extend here to allow modification of coordinates argument (gh-1848)
14079 options = options.call( elem, i, jQuery.extend( {}, curOffset ) );
14080 }
14081
14082 if ( options.top != null ) {
14083 props.top = ( options.top - curOffset.top ) + curTop;
14084 }
14085 if ( options.left != null ) {
14086 props.left = ( options.left - curOffset.left ) + curLeft;
14087 }
14088
14089 if ( "using" in options ) {
14090 options.using.call( elem, props );
14091
14092 } else {
14093 curElem.css( props );
14094 }
14095 }
14096};
14097
14098jQuery.fn.extend( {
14099
14100 // offset() relates an element's border box to the document origin
14101 offset: function( options ) {
14102
14103 // Preserve chaining for setter
14104 if ( arguments.length ) {
14105 return options === undefined ?
14106 this :
14107 this.each( function( i ) {
14108 jQuery.offset.setOffset( this, options, i );
14109 } );
14110 }
14111
14112 var rect, win,
14113 elem = this[ 0 ];
14114
14115 if ( !elem ) {
14116 return;
14117 }
14118
14119 // Return zeros for disconnected and hidden (display: none) elements (gh-2310)
14120 // Support: IE <=11 only
14121 // Running getBoundingClientRect on a
14122 // disconnected node in IE throws an error
14123 if ( !elem.getClientRects().length ) {
14124 return { top: 0, left: 0 };
14125 }
14126
14127 // Get document-relative position by adding viewport scroll to viewport-relative gBCR
14128 rect = elem.getBoundingClientRect();
14129 win = elem.ownerDocument.defaultView;
14130 return {
14131 top: rect.top + win.pageYOffset,
14132 left: rect.left + win.pageXOffset
14133 };
14134 },
14135
14136 // position() relates an element's margin box to its offset parent's padding box
14137 // This corresponds to the behavior of CSS absolute positioning
14138 position: function() {
14139 if ( !this[ 0 ] ) {
14140 return;
14141 }
14142
14143 var offsetParent, offset, doc,
14144 elem = this[ 0 ],
14145 parentOffset = { top: 0, left: 0 };
14146
14147 // position:fixed elements are offset from the viewport, which itself always has zero offset
14148 if ( jQuery.css( elem, "position" ) === "fixed" ) {
14149
14150 // Assume position:fixed implies availability of getBoundingClientRect
14151 offset = elem.getBoundingClientRect();
14152
14153 } else {
14154 offset = this.offset();
14155
14156 // Account for the *real* offset parent, which can be the document or its root element
14157 // when a statically positioned element is identified
14158 doc = elem.ownerDocument;
14159 offsetParent = elem.offsetParent || doc.documentElement;
14160 while ( offsetParent &&
14161 ( offsetParent === doc.body || offsetParent === doc.documentElement ) &&
14162 jQuery.css( offsetParent, "position" ) === "static" ) {
14163
14164 offsetParent = offsetParent.parentNode;
14165 }
14166 if ( offsetParent && offsetParent !== elem && offsetParent.nodeType === 1 ) {
14167
14168 // Incorporate borders into its offset, since they are outside its content origin
14169 parentOffset = jQuery( offsetParent ).offset();
14170 parentOffset.top += jQuery.css( offsetParent, "borderTopWidth", true );
14171 parentOffset.left += jQuery.css( offsetParent, "borderLeftWidth", true );
14172 }
14173 }
14174
14175 // Subtract parent offsets and element margins
14176 return {
14177 top: offset.top - parentOffset.top - jQuery.css( elem, "marginTop", true ),
14178 left: offset.left - parentOffset.left - jQuery.css( elem, "marginLeft", true )
14179 };
14180 },
14181
14182 // This method will return documentElement in the following cases:
14183 // 1) For the element inside the iframe without offsetParent, this method will return
14184 // documentElement of the parent window
14185 // 2) For the hidden or detached element
14186 // 3) For body or html element, i.e. in case of the html node - it will return itself
14187 //
14188 // but those exceptions were never presented as a real life use-cases
14189 // and might be considered as more preferable results.
14190 //
14191 // This logic, however, is not guaranteed and can change at any point in the future
14192 offsetParent: function() {
14193 return this.map( function() {
14194 var offsetParent = this.offsetParent;
14195
14196 while ( offsetParent && jQuery.css( offsetParent, "position" ) === "static" ) {
14197 offsetParent = offsetParent.offsetParent;
14198 }
14199
14200 return offsetParent || documentElement;
14201 } );
14202 }
14203} );
14204
14205// Create scrollLeft and scrollTop methods
14206jQuery.each( { scrollLeft: "pageXOffset", scrollTop: "pageYOffset" }, function( method, prop ) {
14207 var top = "pageYOffset" === prop;
14208
14209 jQuery.fn[ method ] = function( val ) {
14210 return access( this, function( elem, method, val ) {
14211
14212 // Coalesce documents and windows
14213 var win;
14214 if ( isWindow( elem ) ) {
14215 win = elem;
14216 } else if ( elem.nodeType === 9 ) {
14217 win = elem.defaultView;
14218 }
14219
14220 if ( val === undefined ) {
14221 return win ? win[ prop ] : elem[ method ];
14222 }
14223
14224 if ( win ) {
14225 win.scrollTo(
14226 !top ? val : win.pageXOffset,
14227 top ? val : win.pageYOffset
14228 );
14229
14230 } else {
14231 elem[ method ] = val;
14232 }
14233 }, method, val, arguments.length );
14234 };
14235} );
14236
14237// Support: Safari <=7 - 9.1, Chrome <=37 - 49
14238// Add the top/left cssHooks using jQuery.fn.position
14239// Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
14240// Blink bug: https://bugs.chromium.org/p/chromium/issues/detail?id=589347
14241// getComputedStyle returns percent when specified for top/left/bottom/right;
14242// rather than make the css module depend on the offset module, just check for it here
14243jQuery.each( [ "top", "left" ], function( i, prop ) {
14244 jQuery.cssHooks[ prop ] = addGetHookIf( support.pixelPosition,
14245 function( elem, computed ) {
14246 if ( computed ) {
14247 computed = curCSS( elem, prop );
14248
14249 // If curCSS returns percentage, fallback to offset
14250 return rnumnonpx.test( computed ) ?
14251 jQuery( elem ).position()[ prop ] + "px" :
14252 computed;
14253 }
14254 }
14255 );
14256} );
14257
14258
14259// Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
14260jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
14261 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name },
14262 function( defaultExtra, funcName ) {
14263
14264 // Margin is only for outerHeight, outerWidth
14265 jQuery.fn[ funcName ] = function( margin, value ) {
14266 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
14267 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
14268
14269 return access( this, function( elem, type, value ) {
14270 var doc;
14271
14272 if ( isWindow( elem ) ) {
14273
14274 // $( window ).outerWidth/Height return w/h including scrollbars (gh-1729)
14275 return funcName.indexOf( "outer" ) === 0 ?
14276 elem[ "inner" + name ] :
14277 elem.document.documentElement[ "client" + name ];
14278 }
14279
14280 // Get document width or height
14281 if ( elem.nodeType === 9 ) {
14282 doc = elem.documentElement;
14283
14284 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height],
14285 // whichever is greatest
14286 return Math.max(
14287 elem.body[ "scroll" + name ], doc[ "scroll" + name ],
14288 elem.body[ "offset" + name ], doc[ "offset" + name ],
14289 doc[ "client" + name ]
14290 );
14291 }
14292
14293 return value === undefined ?
14294
14295 // Get width or height on the element, requesting but not forcing parseFloat
14296 jQuery.css( elem, type, extra ) :
14297
14298 // Set width or height on the element
14299 jQuery.style( elem, type, value, extra );
14300 }, type, chainable ? margin : undefined, chainable );
14301 };
14302 } );
14303} );
14304
14305
14306jQuery.each( ( "blur focus focusin focusout resize scroll click dblclick " +
14307 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
14308 "change select submit keydown keypress keyup contextmenu" ).split( " " ),
14309 function( i, name ) {
14310
14311 // Handle event binding
14312 jQuery.fn[ name ] = function( data, fn ) {
14313 return arguments.length > 0 ?
14314 this.on( name, null, data, fn ) :
14315 this.trigger( name );
14316 };
14317} );
14318
14319jQuery.fn.extend( {
14320 hover: function( fnOver, fnOut ) {
14321 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
14322 }
14323} );
14324
14325
14326
14327
14328jQuery.fn.extend( {
14329
14330 bind: function( types, data, fn ) {
14331 return this.on( types, null, data, fn );
14332 },
14333 unbind: function( types, fn ) {
14334 return this.off( types, null, fn );
14335 },
14336
14337 delegate: function( selector, types, data, fn ) {
14338 return this.on( types, selector, data, fn );
14339 },
14340 undelegate: function( selector, types, fn ) {
14341
14342 // ( namespace ) or ( selector, types [, fn] )
14343 return arguments.length === 1 ?
14344 this.off( selector, "**" ) :
14345 this.off( types, selector || "**", fn );
14346 }
14347} );
14348
14349// Bind a function to a context, optionally partially applying any
14350// arguments.
14351// jQuery.proxy is deprecated to promote standards (specifically Function#bind)
14352// However, it is not slated for removal any time soon
14353jQuery.proxy = function( fn, context ) {
14354 var tmp, args, proxy;
14355
14356 if ( typeof context === "string" ) {
14357 tmp = fn[ context ];
14358 context = fn;
14359 fn = tmp;
14360 }
14361
14362 // Quick check to determine if target is callable, in the spec
14363 // this throws a TypeError, but we will just return undefined.
14364 if ( !isFunction( fn ) ) {
14365 return undefined;
14366 }
14367
14368 // Simulated bind
14369 args = slice.call( arguments, 2 );
14370 proxy = function() {
14371 return fn.apply( context || this, args.concat( slice.call( arguments ) ) );
14372 };
14373
14374 // Set the guid of unique handler to the same of original handler, so it can be removed
14375 proxy.guid = fn.guid = fn.guid || jQuery.guid++;
14376
14377 return proxy;
14378};
14379
14380jQuery.holdReady = function( hold ) {
14381 if ( hold ) {
14382 jQuery.readyWait++;
14383 } else {
14384 jQuery.ready( true );
14385 }
14386};
14387jQuery.isArray = Array.isArray;
14388jQuery.parseJSON = JSON.parse;
14389jQuery.nodeName = nodeName;
14390jQuery.isFunction = isFunction;
14391jQuery.isWindow = isWindow;
14392jQuery.camelCase = camelCase;
14393jQuery.type = toType;
14394
14395jQuery.now = Date.now;
14396
14397jQuery.isNumeric = function( obj ) {
14398
14399 // As of jQuery 3.0, isNumeric is limited to
14400 // strings and numbers (primitives or objects)
14401 // that can be coerced to finite numbers (gh-2662)
14402 var type = jQuery.type( obj );
14403 return ( type === "number" || type === "string" ) &&
14404
14405 // parseFloat NaNs numeric-cast false positives ("")
14406 // ...but misinterprets leading-number strings, particularly hex literals ("0x...")
14407 // subtraction forces infinities to NaN
14408 !isNaN( obj - parseFloat( obj ) );
14409};
14410
14411
14412
14413
14414// Register as a named AMD module, since jQuery can be concatenated with other
14415// files that may use define, but not via a proper concatenation script that
14416// understands anonymous AMD modules. A named AMD is safest and most robust
14417// way to register. Lowercase jquery is used because AMD module names are
14418// derived from file names, and jQuery is normally delivered in a lowercase
14419// file name. Do this after creating the global so that if an AMD module wants
14420// to call noConflict to hide this version of jQuery, it will work.
14421
14422// Note that for maximum portability, libraries that are not jQuery should
14423// declare themselves as anonymous modules, and avoid setting a global if an
14424// AMD loader is present. jQuery is a special case. For more information, see
14425// https://github.com/jrburke/requirejs/wiki/Updating-existing-libraries#wiki-anon
14426
14427if ( typeof define === "function" && define.amd ) {
14428 define( "jquery", [], function() {
14429 return jQuery;
14430 } );
14431}
14432
14433
14434
14435
14436var
14437
14438 // Map over jQuery in case of overwrite
14439 _jQuery = window.jQuery,
14440
14441 // Map over the $ in case of overwrite
14442 _$ = window.$;
14443
14444jQuery.noConflict = function( deep ) {
14445 if ( window.$ === jQuery ) {
14446 window.$ = _$;
14447 }
14448
14449 if ( deep && window.jQuery === jQuery ) {
14450 window.jQuery = _jQuery;
14451 }
14452
14453 return jQuery;
14454};
14455
14456// Expose jQuery and $ identifiers, even in AMD
14457// (#7102#comment:10, https://github.com/jquery/jquery/pull/557)
14458// and CommonJS for browser emulators (#13566)
14459if ( !noGlobal ) {
14460 window.jQuery = window.$ = jQuery;
14461}
14462
14463
14464
14465
14466return jQuery;
14467} );
14468
14469},{}],3:[function(require,module,exports){
14470"use strict";
14471
14472console.log("Extension loading...");
14473const jQuery = require("jquery");
14474const $ = jQuery;
14475const GmailFactory = require("gmail-js");
14476const gmail = new GmailFactory.Gmail($);
14477window.gmail = gmail;
14478
14479gmail.observe.on("load", () => {
14480 const userEmail = gmail.get.user_email();
14481 console.log("Hello, " + userEmail + ". This is your extension talking!");
14482
14483 gmail.observe.on("view_email", (domEmail) => {
14484 console.log("Looking at email:", domEmail);
14485 const emailData = gmail.new.get.email_data(domEmail);
14486 console.log("Email data:", emailData);
14487 });
14488});
14489
14490},{"gmail-js":1,"jquery":2}]},{},[3]);