Codebase list python-faraday / a96b468
Merge tag 'upstream/1.0.11' into kali/master Upstream version 1.0.11 Sophie Brun 8 years ago
88 changed file(s) with 14651 addition(s) and 2443 deletion(s). Raw diff Collapse all Expand all
99 New features in the latest update
1010 =====================================
1111
12 Jun 30, 2015:
13 ---
14
15 * Added hosts CRUD
16 * Added services CRUD
17 * Fix ubuntu 15.04 installation bug
18 * Small bug in burp plugin "Import new vulnerabilities" checkbox issue
19 * Added an interactive visualization to calculate the value of a Workspace
20 * Fixed several bugs in WEB UI
21 * Added a URL filter functionality to the status report, allowing searches by fields
1222
1323
1424 Apr 17, 2015:
0 1.0.10
0 1.0.11
5353 elif [ "$os" = "Ubuntu 13.04" ]; then
5454 version="ubuntu13-04-$arch"
5555 down=1
56 elif [[ "$os" =~ "Ubuntu 14.04".*|"Ubuntu 14.10".*|"Ubuntu Vivid Vervet (development branch)"|"Debian 8.0".* ]]; then
56 elif [[ "$os" =~ "Ubuntu 14.04".*|"Ubuntu 14.10".*|"Ubuntu Vivid Vervet (development branch)"|"Debian 8.0".*|"Ubuntu 15".* ]]; then
5757 version="ubuntu13-10-$arch"
5858 down=1
5959 # Install pip from github.
165165 #@stdout.println('Menu TYPE: %s\n' % ctx)
166166 if ctx == 5 or ctx == 6 or ctx == 2 or ctx == 7
167167
168 faradayMenu = JMenuItem.new("Send to Faraday", nil)
169
170 faradayMenu.addActionListener do |e|
171 eventScan(invocation, ctx)
172 end
173
174 menu.push(faradayMenu)
168 faradayMenu = JMenuItem.new("Send to Faraday", nil)
169
170 faradayMenu.addActionListener do |e|
171 eventScan(invocation, ctx)
172 end
173
174 menu.push(faradayMenu)
175175 end
176176
177177 return menu
203203 #
204204 def newScanIssue(issue, ctx=nil, import=nil)
205205
206 if import == nil && @import_new_vulns.isSelected() == 0
206 if import == nil && @import_new_vulns.isSelected() == false
207207 #ignore new issues
208208 return
209209 end
128128 for v in t.vulns:
129129
130130 desc=""
131 desc+=v.get('description') if v.get('description') else ""
131 desc+=v.get('description').encode("ascii", errors="backslashreplace") if v.get('description') else ""
132132 resolution = ""
133 resolution = v.get('solution') if v.get('solution') else ""
134 desc+="\nOutput: "+v.get('plugin_output') if v.get('plugin_output') else ""
133 resolution = v.get('solution').encode("ascii", errors="backslashreplace") if v.get('solution') else ""
134 desc+="\nOutput: "+v.get('plugin_output').encode("ascii", errors="backslashreplace") if v.get('plugin_output') else ""
135135 ref=[]
136136 if v.get('cve'):
137137 ref.append(", ".join(v.get('cve')))
33 argparse==1.1
44 IPy==0.75
55 restkit==4.2.2
6 requests==1.2.3
6 requests==2.7.0
77 tornado==3.2
88 flask==0.10.1
99 colorama==0.3.2
6060 margin: 10px 5px 0px;
6161 float: left;
6262 }
63 .cursor{cursor: pointer;}
6364 #compound .columna{
6465 width: 492px;
6566 margin-top: -42px;
338339 display: inline;
339340 }
340341
341 .label-list span.label {
342 .label-list span.label, .label-list a span.label {
342343 font-size: 12px !important;
344 margin: 5px;
343345 }
344346 table thead th a {
345347 color: #bbb;
405407 }
406408
407409 div.modal-body .col-md-6.vuln_table{width: 63%}
410 div.modal-button .btn.btn-success{float: right}
408411 div.modal-footer button.btn{bottom: 1%;right: 1%;}
409412 div.modal-footer button.btn.btn-success{right: 10%}
410413 div.modal-footer button.btn-footer{position: absolute;}
509512 float: right;
510513 }
511514 #compound article.panel-default input.btn.btn-danger{width: 43px}
515 div.col-md-6.left-big-box{width: 45%;margin: -200px 0 5px}
516 div.col-md-6.left-big-box table.table-striped.last-vuln td:nth-child(4){word-break: break-all;}
512517
513518 .table-striped tbody tr:nth-child(odd){background: #f9f9f9;}
514519 .table-striped tbody tr:nth-child(even){background: #FFF;}
602607 #treemap, #bar, #cake{height:195px}
603608 #vulns .columna{margin-left:2%}
604609 #vulns .main{width:100%;height:105px}
610 #vulns-by-price .main{width:100% !important;height: 180px !important;}
605611 #vulns .columna.unsexto{margin: auto;}
606612 #summarized .main{height:197px}
607613 #byservices .main{height:197px}
637643 #byservices .col-lg-3, #summarized .col-lg-3{width: 29%;margin:0px -2% 0px 0%;}
638644 section .col-lg-2{margin: 0px -2% 0px 2px;padding-right: 1.9%;}
639645 .col-lg-6 .panel{margin-bottom: 10px}
640 #list .col-lg-6, #vulns .col-lg-6{width: 55%;float: right;margin-bottom: 5px}
641 #compound .col-lg-6{width: 45%;margin-top: -2%;}
646 #list .col-lg-6, #vulns .col-lg-6, #vulns-by-price .col-lg-6{width: 55%;float: right;margin-bottom: 5px}
647 #compound .col-lg-6{width: 45%;}
642648
643649 .panel{margin-bottom: 1%!important}
644650 article.panel > span{
684690 .col-lg-2{width: 100%;margin: auto;}
685691 section#main{height: 100%!important}
686692 .treemap{left:256px!important;}
687 #compound .col-lg-6, #list .col-lg-6, #vulns .col-lg-6, #byservices .col-lg-3, #summarized .col-lg-3{
693 #compound .col-lg-6, #list .col-lg-6, #vulns .col-lg-6, #vulns-by-price .col-lg-6, #byservices .col-lg-3, #summarized .col-lg-3{
688694 width: 100%;margin-top: 0%
689695 }
690696 }
693699 .ws-name {
694700 font: 250%/3 'Ubuntu',Helvetica,Arial,sans-serif !important;
695701 font-size: 100%;
696 margin: 5px;
697702 }
698703 .ws-link {
699704 float: left;
717722 a.ng-binding.ng-scope{padding: 10px;}
718723 div.panel-heading a i.glyphicon{margin: -25px 10px;}
719724 div.modal-body .alert-danger.target_not_selected{font-size: 15px}
725 div.modal-body .form-group:first-child button{float: right}
720726 /* Pagination Modal New */
721727 .showPagination ul.pagination{font-size: 15px;margin:auto;padding-left: 2%}
722728 div.showPagination .col-md-2, div.showPagination .btn.btn-default{float:right;}
727733 input#vuln-refs{border-radius: 5px 0 0 5px}
728734 i.fa.fa-plus-circle{color: green;}
729735 i.fa.fa-minus-circle{color: red;cursor: pointer;}
730 span.input-group-addon.add-reference{
731 border-radius: 4px;
736 span.input-group-addon.button-radius{
737 border-radius: 4px!important;
732738 border-left: 1px solid #ccc;
733739 width: 20px;
734740 cursor: pointer;
735741 }
742 input#port
736743 span#cakeText{width: 95%}
737744 span#treemapTextModel{font-size: 14px}
738745 .normal-size {
739746 font-size: 14px !important;
740747 }
748 div.input-margin{margin: 4px 0;}
749 div.col-md-8.protocol input#protocol{margin-top: 35px}
741750 div#treemap_container{margin: 5% auto 0}
742751 div#compound div.showPagination{margin-bottom: 30px}
743752 div#compound .showPagination input.form-control{width:110px;}
841850 .label-high .badge {
842851 color: #DF3936;
843852 background-color: #ffffff;
844 }
853 }
854 .btn-small-margin {
855 margin-bottom: 15px !important;
856 }
857 .glyphicon-btn {
858 top: 0 !important;
859 }
860 #tooltip-stacked-bar {
861 position: absolute;
862 text-align: center;
863 width: 40px;
864 height: auto;
865 padding: 10px;
866 background-color: white;
867 -webkit-border-radius: 10px;
868 -moz-border-radius: 10px;
869 border-radius: 10px;
870 -webkit-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
871 -moz-box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
872 box-shadow: 4px 4px 10px rgba(0, 0, 0, 0.4);
873 pointer-events: none;
874 }
875 #tooltip-stacked-bar.hidden {
876 display: none;
877 }
878 #tooltip-stacked-bar p {
879 margin: 0;
880 font-family: sans-serif;
881 font-size: 16px;
882 line-height: 20px;
883 }
884 .center-lg-6 {
885 margin: 0 auto;
886 text-align: center;
887 width: 500px;
888 }
3030 <link href="images/site_preview.jpg" rel="image_src" />
3131
3232 <!-- Scripts -->
33 <script type="text/javascript" src="/_utils/script/jquery.js"></script>
34 <script type="text/javascript" src="/_utils/script/jquery.couch.js"></script>
33 <script type="text/javascript" src="script/jquery.js"></script>
34 <script type="text/javascript" src="script/jquery.couch.js"></script>
3535 <script type="text/javascript" src="script/bootstrap.min.js"></script>
3636 <script type="text/javascript" src="script/angular.js"></script>
3737 <script type="text/javascript" src="script/angular-cookies.js"></script>
6161
6262 <script type="text/javascript" src="scripts/app.js"></script>
6363 <script type="text/javascript" src="scripts/attachments/providers/attachments.js"></script>
64 <script type="text/javascript" src="scripts/commons/directives/contenteditable.js"></script>
65 <script type="text/javascript" src="scripts/commons/controllers/modal.js"></script>
6466 <script type="text/javascript" src="scripts/commons/providers/commons.js"></script>
6567 <script type="text/javascript" src="scripts/commons/filters/orderObjectBy.js"></script>
68 <script type="text/javascript" src="scripts/commons/filters/startFrom.js"></script>
6669 <script type="text/javascript" src="scripts/fileExporter/directives/download.js"></script>
6770 <script type="text/javascript" src="scripts/fileExporter/services/blob.js"></script>
6871 <script type="text/javascript" src="scripts/fileExporter/services/click.js"></script>
72 <script type="text/javascript" src="scripts/hosts/controllers/host.js"></script>
73 <script type="text/javascript" src="scripts/hosts/controllers/hosts.js"></script>
74 <script type="text/javascript" src="scripts/hosts/controllers/hostsModalEdit.js"></script>
75 <script type="text/javascript" src="scripts/hosts/controllers/hostsModalNew.js"></script>
76 <script type="text/javascript" src="scripts/hosts/services/host.js"></script>
6977 <script type="text/javascript" src="scripts/hosts/services/hosts.js"></script>
70 <script type="text/javascript" src="scripts/modal/filter/Filter.js"></script>
71 <script type="text/javascript" src="scripts/modal/controllers/modalDelete.js"></script>
72 <script type="text/javascript" src="scripts/modal/controllers/modalEdit.js"></script>
73 <script type="text/javascript" src="scripts/modal/controllers/modalNew.js"></script>
74 <script type="text/javascript" src="scripts/modal/controllers/modalKO.js"></script>
7578 <script type="text/javascript" src="scripts/navigation/controllers/navigationCtrl.js"></script>
7679 <script type="text/javascript" src="scripts/notes/services/notes.js"></script>
80 <script type="text/javascript" src="scripts/services/controllers/serviceModalNew.js"></script>
81 <script type="text/javascript" src="scripts/services/controllers/serviceModalEdit.js"></script>
82 <script type="text/javascript" src="scripts/services/providers/services.js"></script>
83 <script type="text/javascript" src="scripts/services/providers/service.js"></script>
7784 <script type="text/javascript" src="scripts/statusReport/controllers/statusReport.js"></script>
85 <script type="text/javascript" src="scripts/statusReport/controllers/modalEdit.js"></script>
86 <script type="text/javascript" src="scripts/statusReport/controllers/modalNew.js"></script>
7887 <script type="text/javascript" src="scripts/statusReport/directives/textCollapse.js"></script>
7988 <script type="text/javascript" src="scripts/statusReport/services/statusReport.js"></script>
8089 <script type="text/javascript" src="scripts/statusReport/services/target.js"></script>
8594 <script type="text/javascript" src="scripts/dashboard/controllers/dashboard.js"></script>
8695 <script type="text/javascript" src="scripts/dashboard/controllers/graphicsBarCtrl.js"></script>
8796 <script type="text/javascript" src="scripts/dashboard/controllers/summarizedCtrl.js"></script>
97 <script type="text/javascript" src="scripts/dashboard/controllers/vulnsbyprice.js"></script>
8898 <script type="text/javascript" src="scripts/dashboard/services/dashboard.js"></script>
8999 <script type="text/javascript" src="scripts/d3/services/d3.js"></script>
90100 <script type="text/javascript" src="scripts/d3/directives/treemap.js"></script>
91101 <script type="text/javascript" src="scripts/d3/directives/bar.js"></script>
92102 <script type="text/javascript" src="scripts/d3/directives/cake.js"></script>
103 <script type="text/javascript" src="scripts/d3/directives/stackedbar.js"></script>
93104 </body>
94105 </html>
+0
-277
views/reports/_attachments/index2.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <!DOCTYPE html>
4 <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
5 <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
6 <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
7 <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
8 <!--[if (gt IE 9)|!(IE)]><!-->
9 <html id="no-overflow" lang="en" class="no-js"> <!--<![endif]-->
10 <head>
11 <meta charset="utf-8"/>
12 <!--[if IE]><![endif]-->
13 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
14 <title>Dashboard | Faraday</title>
15 <meta name="description" content=""/>
16 <meta name="keywords" content=""/>
17 <meta name="author" content=""/>
18
19 <!-- !CSS -->
20 <link rel="stylesheet" type="text/css" href="normalize.css" />
21 <link rel="stylesheet" type="text/css" href="estilos.css" />
22 <link rel="stylesheet" type="text/css" href="script/jquery.qtip.css" />
23 <link rel="stylesheet" href="script/bootstrap.min.css">
24 <link rel="stylesheet" href="script/bootstrap-theme.min.css">
25 <link rel="stylesheet" type="text/css" href="styles/font-awesome.css" />
26 <link href="favicon.ico" rel="shortcut icon">
27 <link href="favicon.ico" type="image/vnd.microsoft.icon" rel="icon" />
28 <link href="images/site_preview.jpg" rel="image_src" />
29
30 <script type="text/javascript" src="script/couch.js"></script>
31 <script type="text/javascript" src="script/common.js"></script>
32 <script type="text/javascript" src="/_utils/script/sha1.js"></script>
33 <script type="text/javascript" src="script/app.js"></script>
34 <script type="text/javascript" src="script/d3_summarized.js"></script>
35 <script type="text/javascript" src="script/reports_list.js"></script>
36 <script type="text/javascript" src="/_utils/script/json2.js"></script>
37 <script type="text/javascript" src="/_utils/script/jquery.js"></script>
38 <script type="text/javascript" src="/_utils/script/jquery.couch.js"></script>
39 <script type="text/javascript" src="script/jquery.tablesorter.min.js"></script>
40 <script type="text/javascript" src="script/bootstrap.min.js"></script>
41 <script type="text/javascript" src="script/jquery.qtip.js"></script>
42 <script type="text/javascript" src="script/d3.min.js"></script>
43 </head>
44
45 <body>
46 <div id="cont">
47 <div class="wrapper">
48 <header class="head">
49 <a href="#" class="ws-dashboard"><img class="logo" src="images/logo-faraday.png" alt="Faraday home | WS Dashboard"/></a>
50 </header>
51
52 <section id="main" class="seccion clearfix">
53 <aside>
54 <nav class="left-nav">
55 <ul>
56 <li>
57 <a href="#" class="ws-dashboard" style="color: #ffffff !important" title="WS Dashboard">
58 <h2><span class="fa fa-pie-chart" title="Dashboard"></span></h2>
59 </a>
60 </li>
61 <li>
62 <a href="#" class="status-report" style="color: #ffffff !important" title="Status Report">
63 <h2><span class="fa fa-list" title="Status Report"></span></h2>
64 </a>
65 </li>
66 <li>
67 <a href="#" class="workspaces" style="color: #ffffff !important" title="Workspaces">
68 <h2><span class="fa fa-folder-open-o" title="Workspaces"></span></h2>
69 </a>
70 </li>
71 </ul>
72 </nav>
73 </aside>
74
75 <div class="right-main">
76 <div id="reports-main" class="fila clearfix">
77 <h2 class="ws-label">
78 <span id="ws-name" class="label label-default" title="Current workspace"></span><!-- WS name -->
79
80 <div id="ws-control" class="btn-group">
81 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace">
82 <span class="glyphicon glyphicon-refresh"></span>
83 </button>
84 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
85 Change workspace <span class="caret"></span>
86 </button>
87 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu"></ul><!-- WS navigation -->
88 </div><!-- #ws-control -->
89 </h2><!-- .ws-label -->
90 <div class="reports">
91 </div><!-- .reports -->
92 </div><!-- #reports-main -->
93 </div><!-- .right-main -->
94 </section><!-- #main -->
95 <footer>
96 </footer>
97 </div><!--!/#wrapper -->
98 </div><!--!/#container -->
99
100 <script type="text/javascript">
101 $(document).ready(function() {
102 var counter = 0;
103 var h = 0;
104 $.couch.allDbs({
105 success : function(dbs) {
106 dbs.filter(function(db_name){
107 return db_name.search(/^_/) < 0 && db_name.search("reports") < 0;
108 })
109 .forEach(function(db) {
110 // WSs list
111 $("#ws-control #nav").append('<li><a href="#'+db+'" class="ws" >'+db+'</a></li>');
112 // navigation between WSs
113 if(window.location.hash === "" && counter === 0) {
114 window.location.hash = db;
115 location.reload();
116 }
117 counter +=1;
118 });
119 if(counter < 1) {
120 $("#reports-main").empty();
121 $("#reports-main").append("<div id=\"no-workspace\" class=\"workspace\">No Workspaces installed, please start working to get some data</div><!-- .workspace -->");
122 }
123 }
124 });
125 // navegacion entre WSs en el dropdown
126 $(document).on("click", "a.ws", function(e) {
127 e.preventDefault();
128 var hash = $(this).attr("href").substr(1);
129 window.location.hash = hash;
130 location.reload();
131 });
132 $(document).on("click", "#refresh", function() {
133 location.reload();
134 });
135 $(document).on("click", "a.ws-dashboard", function(e) {
136 e.preventDefault();
137 var url = "../././reports/index.html#" + workspace;
138 window.location.href = url;
139 });
140 $(document).on("click", "a.status-report", function(e) {
141 e.preventDefault();
142 var url = "../././reports/faraday.html#/status/ws/" + workspace;
143 window.location.href = url;
144 });
145 $(document).on("click", "a.workspaces", function(e) {
146 e.preventDefault();
147 var url = "../././reports/faraday.html#/workspaces";
148 window.location.href = url;
149 });
150 $(document).on("click", "a.treemap-view", function(e) {
151 e.preventDefault();
152 var url = "../././reports/treemap.html?workspace=" + workspace +"&design=hosts&view=byservices#"+ workspace;
153 window.location.href = url;
154 });
155
156 //get workspace
157 domain = location.href;
158 space = domain.split("#");
159 workspace = space[1];
160
161 var ret = "Dashboard for "+ workspace;
162 $('#reports-main .ws-label span#ws-name').text(ret);
163
164 rutas = [
165 "reports/compound.html?workspace="+workspace+"&design=hosts&view=compound",
166 "reports/byservices.html?workspace="+workspace+"&design=hosts&view=byservices",
167 "reports/summarized.html?workspace="+workspace+"&design=hosts&view=summarized",
168 "reports/vulns.html?workspace="+workspace+"&design=hosts&view=vulns",
169 "reports/commands.html?workspace="+workspace+"&design=commands&view=list"
170 ];
171 var content_cake = "<div id='sequence'></div><div id='chart'><div id='explanation' style='visibility: hidden;'></div></div>";
172
173 $("#reports-main").append("<div class='col-lg-2'><article id='treemap' class='panel panel-default'><header>"+
174 "<h2><a class=\"treemap-view\" href=\"\">Top Services</a> <span class=\"glyphicon glyphicon-info-sign faraday-qtips\" title=\"Top 5 services with the largest quantity of hosts\">"+
175 "</span></h2></header></article></div>"+
176 "<div class='col-lg-2'><article id='bar' class='panel panel-default'><header><h2>Top Hosts "+
177 "<span class=\"glyphicon glyphicon-info-sign faraday-qtips\" title=\"Top 3 hosts with the largest quantity of services\"></span></h2></header></article></div>"+
178 "<div class='col-lg-2'><article id='cake' class='panel panel-default'><header><h2>Vulnerabilities "+
179 "<span class=\"glyphicon glyphicon-info-sign faraday-qtips\" title=\"Vulnerabilty distribution for current WS\"></span></h2>"+
180 "</header>"+ content_cake +"</article></div>"+
181 "<div id='byservices'></div><div id='summarized'></div>"+
182 "<div id='compound'></div><div id='vulns'></div><div id='list'></div>");
183
184 design = "hosts";
185 view = "byservices";
186 treemap(workspace, design, view);
187 bar(workspace, design, view);
188 view = "vulns";
189 cake(workspace, design, view);
190 for (i = 0; i < rutas.length; i++) {
191 domain = rutas[i];
192 var div = load(domain);
193 }
194
195 var navegador = navigator.userAgent;
196 if (navigator.userAgent.indexOf('MSIE') !=-1) {
197 $("#main.seccion").height(screen.height - 100);
198 } else if (navigator.userAgent.indexOf('Firefox') !=-1) {
199 $("#main.seccion").height(screen.height - 100);
200 } else if (navigator.userAgent.indexOf('Chromium') !=-1) {
201 $("#main.seccion").height(screen.height - 100);
202 } else if (navigator.userAgent.indexOf('Chrome') !=-1) {
203 $("#main.seccion").height(screen.height - 100);
204 } else if (navigator.userAgent.indexOf('Opera') !=-1) {
205 $("#main.seccion").height(screen.height - 100);
206 }
207
208 $('#cont').on('mouseenter', '.faraday-qtips', function (event) {
209 $(this).qtip({
210 overwrite: false, // Don't overwrite tooltips already bound
211 show: {
212 event: event.type, // Use the same event type as above
213 ready: true // Show immediately - important!
214 },
215 hide: {
216 fixed: true,
217 delay: 300
218 },
219 content:{
220 text: function(event, api) {
221 var res = "<div id=\"contenido\">"+$(this).attr("title")+"</div>";
222 return res;
223 }
224 },
225 position:{
226 my: 'top center',
227 at: 'bottom center',
228 adjust: {
229 method: 'shift'
230 }
231 }
232 });
233 });
234 });
235
236 function load(domain){
237 $(".reports").load(domain, function(){
238 var parametros = domains(domain);
239 var div = get_report_div(parametros[0],parametros[1],parametros[2]);
240 if(parametros[1] == "byservices"){
241 $("#byservices").append(div);
242 }
243 if(parametros[1] == "summarized"){
244 $("#summarized").append(div);
245 }
246 if(parametros[1] == "vulns"){
247 $("#vulns").append(div);
248 }
249 if(parametros[1] == "list"){
250 $("#list").append(div);
251 }
252 if(parametros[1] == "compound"){
253 $("#compound").append(div);
254 }
255 });
256 }
257
258 function domains(domain){
259 //get the link parameters
260 space = domain.split("=");
261 workspace = space[1].split("&");
262 workspace = workspace[0];
263 design = space[2].split("&");
264 design = design[0];
265 view = space[3];
266 parametros = [workspace, view, design];
267 return parametros;
268 }
269
270 setTimeout(function() {
271 //order of compound when reload
272 sorter(0);
273 }, 1000);
274 </script>
275 </body>
276 </html>
+0
-268
views/reports/_attachments/maqueta.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <!DOCTYPE html>
4 <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
5 <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
6 <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
7 <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
8 <!--[if (gt IE 9)|!(IE)]><!--> <html lang="en" class="no-js"> <!--<![endif]-->
9 <head>
10 <meta charset="utf-8"/>
11 <!--[if IE]><![endif]-->
12 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
13 <title>Dashboard | Faraday</title>
14 <meta name="description" content=""/>
15 <meta name="keywords" content=""/>
16 <meta name="author" content=""/>
17
18 <!-- !CSS -->
19 <link rel="stylesheet" type="text/css" href="estilos.css" />
20
21 <link href="favicon.ico" rel="shortcut icon">
22 <link href="favicon.ico" type="image/vnd.microsoft.icon" rel="icon" />
23 <link href="images/site_preview.jpg" rel="image_src" />
24 </head>
25
26 <body>
27 <div id="cont">
28 <div class="wrapper">
29 <header class="head">
30 <a href="#"><img class="logo" src="images/logo-faraday.png"/></a>
31 <nav>
32 <ul class="menu">
33 <li>
34 <a href="#">Opción 1</a>
35 </li>
36 <li>
37 <a href="#">Opción 1</a>
38 </li>
39 <li>
40 <a href="#" class="activo">Username</a>
41 <ul>
42 <li><a href="#">Sub opción 1</a></li>
43 <li><a href="#">Sub opción 2</a></li>
44 </ul>
45 </li>
46 </ul>
47 </nav>
48 </header>
49
50 <aside>
51 <nav>
52 <ul>
53 <li>
54 <a href="#"><img src="images/ico-settings.png" alt="Settings"/></a>
55 <a href="#" class="activo"><img src="images/ico-graph.png" alt=""/></a>
56 <a href="#"><img src="images/ico-stopwatch.png" alt=""/></a>
57 </li>
58 </ul>
59 </nav>
60 </aside>
61
62 <section id="reportes" class="seccion clearfix">
63 <div class="fila clearfix">
64 <div class="columna full">
65 <article>
66 <section class="sin_padding">
67 <table id="myTable" class="tablesorter">
68 <thead>
69 <tr>
70 <th>Last Name</th>
71 <th>First Name</th>
72 <th>Email</th>
73 <th>Due</th>
74 <th>Web Site</th>
75 </tr>
76 </thead>
77 <tbody>
78 <tr>
79 <td>Smith</td>
80 <td>John</td>
81 <td>[email protected]</td>
82 <td>$50.00</td>
83 <td>http://www.jsmith.com</td>
84 </tr>
85 <tr>
86 <td>Bach</td>
87 <td>Frank</td>
88 <td>[email protected]</td>
89 <td>$50.00</td>
90 <td>http://www.frank.com</td>
91 </tr>
92 </tbody>
93 </table>
94 </section>
95 </article>
96 </div>
97 </div>
98
99 <div class="fila clearfix">
100 <div class="columna unmedio">
101 <article>
102 <header>
103 <h2>Summary report con texto muy largo</h2>
104 </header>
105 <section>
106 <img src="images/grafico.png"/>
107 </section>
108 </article>
109 </div>
110 <div class="columna unmedio last">
111 <article>
112 <header>
113 <h2>Summary</h2>
114 </header>
115 <section>
116
117 </section>
118 </article>
119 </div>
120 </div><!-- fin fila -->
121
122 <div class="fila clearfix">
123 <div class="columna untercio">
124 <article>
125 <header>
126
127 </header>
128 <section>
129
130 </section>
131 </article>
132 </div>
133 <div class="columna dostercios last">
134 <article>
135 <header>
136
137 </header>
138 <section>
139 <img src="images/grafico.png"/>
140 </section>
141 </article>
142 </div>
143 </div><!-- fin fila -->
144
145 <div class="fila clearfix">
146 <div class="columna uncuarto">
147 <article class="dato1">
148 <section>
149 <div class="tit">Título</div>
150 <div class="nro">9.871,3</div>
151 <p>Alguna aclaración con texto largo de tres líneas</p>
152 </section>
153 </article>
154 </div>
155 <div class="columna uncuarto">
156 <article>
157 <section>
158
159 </section>
160 </article>
161 </div>
162 <div class="columna uncuarto">
163 <article>
164 <section>
165 <p>sdjfhsd</p>
166 </section>
167 </article>
168 </div>
169 <div class="columna uncuarto last">
170 <article>
171 <section>
172
173 </section>
174 </article>
175 </div>
176 </div><!-- fin fila -->
177
178 <div class="fila clearfix">
179 <div class="columna unquinto">
180 <article class="dato2 fondo-negro">
181 <section>
182 <div class="nro texto-rojo">574</div>
183 <div class="txt texto-blanco">Algún dato</div>
184 </section>
185 </article>
186 </div>
187 <div class="columna unquinto">
188 <article class="dato2 fondo-rojo">
189 <section>
190 <div class="nro texto-negro">574</div>
191 <div class="txt texto-blanco">Algún dato</div>
192 </section>
193 </article>
194 </div>
195 <div class="columna unquinto">
196 <article>
197 <section>
198
199 </section>
200 </article>
201 </div>
202 <div class="columna unquinto">
203 <article>
204 <section>
205
206 </section>
207 </article>
208 </div>
209 </div>
210
211 </section>
212 <footer>
213 </footer>
214 </div><!--!/#wrapper -->
215 </div><!--!/#container -->
216 <script type="text/javascript" src="js/jquery.tablesorter.min"></script>
217
218 <script src="/_utils/script/sha1.js"></script>
219 <script src="script/app.js"></script>
220 <script src="script/reports_list.js"></script>
221
222 <script src="/_utils/script/json2.js"></script>
223 <script src="/_utils/script/jquery.js"></script>
224 <script src="/_utils/script/jquery.couch.js"></script>
225
226 <script type="text/javascript">
227 $.couch.allDbs({
228 success : function(dbs) {
229 dbs.filter(function(db_name){
230 return db_name.search(/^_/) < 0 && db_name.search("reports") < 0;})
231 .forEach(function(db) {
232 rep_links = [];
233 reports.forEach( function(reps) {
234 json_url = "/" + db + "/_design/" + designs[reps] + "/_view/" + views[reps] + "?group=true";
235
236 $.getJSON(json_url, function(data) {
237 console.log(data);
238 var thead = "<table id=\"myTable\" class=\"tablesorter\"><thead><tr><th>Service</th><th>Quantity</th></tr></thead><tbody>";
239 var tfoot = "</tbody></table>";
240 var tbody = [];
241 $.each(data.rows, function(n, obj){
242 tbody.push("<tr> <td>"+obj.key+"</td> <td>"+obj.value+"</td> </tr>");
243 console.log(obj.value);
244
245 });
246 $("#reportes").append("<div class=\"fila clearfix\"><div class=\"columna uncuarto\"><article><section class=\"sin_padding\">"+thead+tbody.join("")+tfoot+"</section></article></div></div>");
247 });
248 });
249 //$("#reportes").append('<div class="fila clearfix"><div class="columna full"><article><section class="sin_padding"><li><a href="/_utils/database.html?'+db+'">'+db+'</a>' + rep_links.join(' ') + '</li></section></article></div></div>');
250 });
251 }
252 });
253 </script>
254
255 <script type="text/javascript">
256 $(function(){
257
258 var h = $('section.seccion').height();
259 $('aside').height(h);
260
261 // Table sorter
262 $("#myTable").tablesorter();
263 //
264 });
265 </script>
266 </body>
267 </html>
+0
-53
views/reports/_attachments/reportes.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <!DOCTYPE html>
4 <html>
5 <head>
6 <title>Example CouchApp</title>
7 <link rel="stylesheet" href="style/main.css" type="text/css">
8 </head>
9 <body>
10 <!-- <div id="account"></div> -->
11
12 <h1>Welcome to Faraday Reports!</h1>
13
14 <div id="profile">
15 Have you wonder what's your infraestructure status?
16 </div>
17 <div id="content">
18 Available Workspaces
19 </div>
20
21 <div id="sidebar">
22 <p>Here you can find full visualization on your Security Assets.</p>
23 </div>
24 </body>
25 <script src="/_utils/script/sha1.js"></script>
26 <script src="script/app.js"></script>
27 <script src="script/reports_list.js"></script>
28
29 <script src="/_utils/script/json2.js"></script>
30 <script src="/_utils/script/jquery.js"></script>
31 <script src="/_utils/script/jquery.couch.js"></script>
32
33 <script >
34 $.couch.allDbs({
35 success : function(dbs) {
36 dbs.filter(function(db_name){
37 return db_name.search(/^_/) < 0 && db_name.search("reports") < 0;})
38 .forEach(function(db) {
39 rep_links = [];
40 reports.forEach( function(reps) {
41 var qs = ["workspace=" + db, 'design=' + designs[reps], 'view=' + views[reps]];
42 rep_links.push('<a href="/reports/_design/reports/' + reps + '.html?' + qs.join('&') + '">[' + reps + ']</a>');
43 });
44 $(".databases").append('<li><a href="/_utils/database.html?'+db+'">'+db+'</a>' + rep_links.join(' ') + '</li>');
45 });
46 }
47 });
48 </script>
49
50 <div id="content" class="databases"/>
51
52 </html>
0 // Licensed under the Apache License, Version 2.0 (the "License"); you may not
1 // use this file except in compliance with the License. You may obtain a copy of
2 // the License at
3 //
4 // http://www.apache.org/licenses/LICENSE-2.0
5 //
6 // Unless required by applicable law or agreed to in writing, software
7 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
8 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
9 // License for the specific language governing permissions and limitations under
10 // the License.
11
12 /**
13 * @namespace
14 * $.couch is used to communicate with a CouchDB server, the server methods can
15 * be called directly without creating an instance. Typically all methods are
16 * passed an <code>options</code> object which defines a success callback which
17 * is called with the data returned from the http request to CouchDB, you can
18 * find the other settings that can be used in the <code>options</code> object
19 * from <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
20 * jQuery.ajax settings</a>
21 * <pre><code>$.couch.activeTasks({
22 * success: function (data) {
23 * console.log(data);
24 * }
25 * });</code></pre>
26 * Outputs (for example):
27 * <pre><code>[
28 * {
29 * "pid" : "<0.11599.0>",
30 * "status" : "Copied 0 of 18369 changes (0%)",
31 * "task" : "recipes",
32 * "type" : "Database Compaction"
33 * }
34 *]</code></pre>
35 */
36 (function($) {
37
38 $.couch = $.couch || {};
39 /** @lends $.couch */
40
41 /**
42 * @private
43 */
44 function encodeDocId(docID) {
45 var parts = docID.split("/");
46 if (parts[0] == "_design") {
47 parts.shift();
48 return "_design/" + encodeURIComponent(parts.join('/'));
49 }
50 return encodeURIComponent(docID);
51 }
52
53 /**
54 * @private
55 */
56
57 var uuidCache = [];
58
59 $.extend($.couch, {
60 urlPrefix: '',
61
62 /**
63 * You can obtain a list of active tasks by using the /_active_tasks URL.
64 * The result is a JSON array of the currently running tasks, with each task
65 * being described with a single object.
66 * @see <a href="http://docs.couchdb.org/en/latest/api/server/common.html#
67 * active-tasks">docs for /_active_tasks</a>
68 * @param {ajaxSettings} options <a href="http://api.jquery.com/jQuery.ajax
69 * /#jQuery-ajax-settings">jQuery ajax settings</a>
70 */
71 activeTasks: function(options) {
72 return ajax(
73 {url: this.urlPrefix + "/_active_tasks"},
74 options,
75 "Active task status could not be retrieved"
76 );
77 },
78
79 /**
80 * Returns a list of all the databases in the CouchDB instance
81 * @see <a href="http://docs.couchdb.org/en/latest/api/server/common.html
82 * #all-dbs">docs for /_all_dbs</a>
83 * @param {ajaxSettings} options <a href="http://api.jquery.com/jQuery.ajax
84 * /#jQuery-ajax-settings">jQuery ajax settings</a>
85 */
86 allDbs: function(options) {
87 return ajax(
88 {url: this.urlPrefix + "/_all_dbs"},
89 options,
90 "An error occurred retrieving the list of all databases"
91 );
92 },
93
94 /**
95 * View and edit the CouchDB configuration, called with just the options
96 * parameter the entire config is returned, you can be more specific by
97 * passing the section and option parameters, if you specify a value that
98 * value will be stored in the configuration.
99 * @see <a href="http://docs.couchdb.org/en/latest/api/server
100 * /configuration.html#config-section-key">docs for /_config</a>
101 * @param {ajaxSettings} options
102 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
103 * jQuery ajax settings</a>
104 * @param {String} [section] the section of the config
105 * @param {String} [option] the particular config option
106 * @param {String} [value] value to be set
107 */
108 config: function(options, section, option, value) {
109 var req = {url: this.urlPrefix + "/_config/"};
110 if (section) {
111 req.url += encodeURIComponent(section) + "/";
112 if (option) {
113 req.url += encodeURIComponent(option);
114 }
115 }
116 if (value === null) {
117 req.type = "DELETE";
118 } else if (value !== undefined) {
119 req.type = "PUT";
120 req.data = toJSON(value);
121 req.contentType = "application/json";
122 req.processData = false
123 }
124
125 return ajax(req, options,
126 "An error occurred retrieving/updating the server configuration"
127 );
128 },
129
130 /**
131 * Returns the session information for the currently logged in user.
132 * @see <a href="http://docs.couchdb.org/en/latest/api/server/authn.html
133 * #get--_session">docs for GET /_session</a>
134 * @param {ajaxSettings} options
135 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
136 * jQuery ajax settings</a>
137 */
138 session: function(options) {
139 options = options || {};
140 return ajax({
141 type: "GET", url: this.urlPrefix + "/_session",
142 beforeSend: function(xhr) {
143 xhr.setRequestHeader('Accept', 'application/json');
144 },
145 complete: function(req) {
146 var resp = $.parseJSON(req.responseText);
147 if (req.status == 200) {
148 if (options.success) options.success(resp);
149 } else if (options.error) {
150 options.error(req.status, resp.error, resp.reason);
151 } else {
152 throw "An error occurred getting session info: " + resp.reason;
153 }
154 }
155 });
156 },
157
158 /**
159 * @private
160 */
161 userDb : function(callback) {
162 return $.couch.session({
163 success : function(resp) {
164 var userDb = $.couch.db(resp.info.authentication_db);
165 callback(userDb);
166 }
167 });
168 },
169
170 /**
171 * Create a new user on the CouchDB server, <code>user_doc</code> is an
172 * object with a <code>name</code> field and other information you want
173 * to store relating to that user, for example
174 * <code>{"name": "daleharvey"}</code>
175 * @param {Object} user_doc Users details
176 * @param {String} password Users password
177 * @param {ajaxSettings} options
178 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
179 * jQuery ajax settings</a>
180 */
181 signup: function(user_doc, password, options) {
182 options = options || {};
183 user_doc.password = password;
184 user_doc.roles = user_doc.roles || [];
185 user_doc.type = "user";
186 var user_prefix = "org.couchdb.user:";
187 user_doc._id = user_doc._id || user_prefix + user_doc.name;
188
189 return $.couch.userDb(function(db) {
190 db.saveDoc(user_doc, options);
191 });
192 },
193
194 /**
195 * Authenticate against CouchDB, the <code>options</code> parameter is
196 *expected to have <code>name</code> and <code>password</code> fields.
197 * @see <a href="http://docs.couchdb.org/en/latest/api/server/authn.html
198 * #post--_session">docs for POST /_session</a>
199 * @param {ajaxSettings} options
200 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
201 * jQuery ajax settings</a>
202 */
203 login: function(options) {
204 options = options || {};
205 return $.ajax({
206 type: "POST", url: this.urlPrefix + "/_session", dataType: "json",
207 data: {name: options.name, password: options.password},
208 beforeSend: function(xhr) {
209 xhr.setRequestHeader('Accept', 'application/json');
210 },
211 complete: function(req) {
212 var resp = $.parseJSON(req.responseText);
213 if (req.status == 200) {
214 if (options.success) options.success(resp);
215 } else if (options.error) {
216 options.error(req.status, resp.error, resp.reason);
217 } else {
218 throw 'An error occurred logging in: ' + resp.reason;
219 }
220 }
221 });
222 },
223
224
225 /**
226 * Delete your current CouchDB user session
227 * @see <a href="http://docs.couchdb.org/en/latest/api/server/authn.html
228 * #delete--_session">docs for DELETE /_session</a>
229 * @param {ajaxSettings} options
230 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
231 * jQuery ajax settings</a>
232 */
233 logout: function(options) {
234 options = options || {};
235 return $.ajax({
236 type: "DELETE", url: this.urlPrefix + "/_session", dataType: "json",
237 username : "_", password : "_",
238 beforeSend: function(xhr) {
239 xhr.setRequestHeader('Accept', 'application/json');
240 },
241 complete: function(req) {
242 var resp = $.parseJSON(req.responseText);
243 if (req.status == 200) {
244 if (options.success) options.success(resp);
245 } else if (options.error) {
246 options.error(req.status, resp.error, resp.reason);
247 } else {
248 throw 'An error occurred logging out: ' + resp.reason;
249 }
250 }
251 });
252 },
253
254 /**
255 * @namespace
256 * $.couch.db is used to communicate with a specific CouchDB database
257 * <pre><code>var $db = $.couch.db("mydatabase");
258 *$db.allApps({
259 * success: function (data) {
260 * ... process data ...
261 * }
262 *});
263 * </code></pre>
264 */
265 db: function(name, db_opts) {
266 db_opts = db_opts || {};
267 var rawDocs = {};
268 function maybeApplyVersion(doc) {
269 if (doc._id && doc._rev && rawDocs[doc._id] &&
270 rawDocs[doc._id].rev == doc._rev) {
271 // todo: can we use commonjs require here?
272 if (typeof Base64 == "undefined") {
273 throw 'Base64 support not found.';
274 } else {
275 doc._attachments = doc._attachments || {};
276 doc._attachments["rev-"+doc._rev.split("-")[0]] = {
277 content_type :"application/json",
278 data : Base64.encode(rawDocs[doc._id].raw)
279 };
280 return true;
281 }
282 }
283 }
284 return /** @lends $.couch.db */{
285 name: name,
286 uri: this.urlPrefix + "/" + encodeURIComponent(name) + "/",
287
288 /**
289 * Request compaction of the specified database.
290 * @see <a href="http://docs.couchdb.org/en/latest/api/database
291 * /compact.html#db-compact">docs for /db/_compact</a>
292 * @param {ajaxSettings} options
293 * <a href="http://api.jquery.com/jQuery.ajax/#jQuery-ajax-settings">
294 * jQuery ajax settings</a>
295 */
296 compact: function(options) {
297 $.extend(options, {successStatus: 202});
298 return ajax({
299 type: "POST", url: this.uri + "_compact",
300 data: "", processData: false
301 },
302 options,
303 "The database could not be compacted"
304 );
305 },
306
307 /**
308 * Cleans up the cached view output on disk for a given view.
309 * @see <a href="http://docs.couchdb.org/en/latest/api/database
310 * /compact.html#db-view-cleanup">docs for /db/_view_cleanup</a>
311 * @param {ajaxSettings} options <a href="http://api.jquery.com/
312 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
313 */
314 viewCleanup: function(options) {
315 $.extend(options, {successStatus: 202});
316 return ajax({
317 type: "POST", url: this.uri + "_view_cleanup",
318 data: "", processData: false
319 },
320 options,
321 "The views could not be cleaned up"
322 );
323 },
324
325 /**
326 * Compacts the view indexes associated with the specified design
327 * document. You can use this in place of the full database compaction
328 * if you know a specific set of view indexes have been affected by a
329 * recent database change.
330 * @see <a href="http://docs.couchdb.org/en/latest/api/database
331 * /compact.html#db-compact-design-doc">
332 * docs for /db/_compact/design-doc</a>
333 * @param {String} groupname Name of design-doc to compact
334 * @param {ajaxSettings} options <a href="http://api.jquery.com/
335 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
336 */
337 compactView: function(groupname, options) {
338 $.extend(options, {successStatus: 202});
339 return ajax({
340 type: "POST", url: this.uri + "_compact/" + groupname,
341 data: "", processData: false
342 },
343 options,
344 "The view could not be compacted"
345 );
346 },
347
348 /**
349 * Create a new database
350 * @see <a href="http://docs.couchdb.org/en/latest/api/database
351 * /common.html#put--db">docs for PUT /db/</a>
352 * @param {ajaxSettings} options <a href="http://api.jquery.com/
353 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
354 */
355 create: function(options) {
356 $.extend(options, {successStatus: 201});
357 return ajax({
358 type: "PUT", url: this.uri, contentType: "application/json",
359 data: "", processData: false
360 },
361 options,
362 "The database could not be created"
363 );
364 },
365
366 /**
367 * Deletes the specified database, and all the documents and
368 * attachments contained within it.
369 * @see <a href="http://docs.couchdb.org/en/latest/api/database
370 * /common.html#delete--db">docs for DELETE /db/</a>
371 * @param {ajaxSettings} options <a href="http://api.jquery.com/
372 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
373 */
374 drop: function(options) {
375 return ajax(
376 {type: "DELETE", url: this.uri},
377 options,
378 "The database could not be deleted"
379 );
380 },
381
382 /**
383 * Gets information about the specified database.
384 * @see <a href="http://docs.couchdb.org/en/latest/api/database
385 * /common.html#get--db">docs for GET /db/</a>
386 * @param {ajaxSettings} options <a href="http://api.jquery.com/
387 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
388 */
389 info: function(options) {
390 return ajax(
391 {url: this.uri},
392 options,
393 "Database information could not be retrieved"
394 );
395 },
396
397 /**
398 * @namespace
399 * $.couch.db.changes provides an API for subscribing to the changes
400 * feed
401 * <pre><code>var $changes = $.couch.db("mydatabase").changes();
402 *$changes.onChange = function (data) {
403 * ... process data ...
404 * }
405 * $changes.stop();
406 * </code></pre>
407 */
408 changes: function(since, options) {
409
410 options = options || {};
411 // set up the promise object within a closure for this handler
412 var timeout = 100, db = this, active = true,
413 listeners = [],
414 xhr = null,
415 promise = /** @lends $.couch.db.changes */ {
416 /**
417 * Add a listener callback
418 * @see <a href="http://docs.couchdb.org/en/latest/api/database
419 * /changes.html#db-changes">docs for /db/_changes</a>
420 * @param {Function} fun Callback function to run when
421 * notified of changes.
422 */
423 onChange : function(fun) {
424 listeners.push(fun);
425 },
426 /**
427 * Stop subscribing to the changes feed
428 */
429 stop : function() {
430 active = false;
431 if (xhr){
432 xhr.abort();
433 }
434 }
435 };
436 // call each listener when there is a change
437 function triggerListeners(resp) {
438 $.each(listeners, function() {
439 this(resp);
440 });
441 }
442 // when there is a change, call any listeners, then check for
443 // another change
444 options.success = function(resp) {
445 timeout = 100;
446 if (active) {
447 since = resp.last_seq;
448 triggerListeners(resp);
449 getChangesSince();
450 }
451 };
452 options.error = function() {
453 if (active) {
454 setTimeout(getChangesSince, timeout);
455 timeout = timeout * 2;
456 }
457 };
458 // actually make the changes request
459 function getChangesSince() {
460 var opts = $.extend({heartbeat : 10 * 1000}, options, {
461 feed : "longpoll",
462 since : since
463 });
464 xhr = ajax(
465 {url: db.uri + "_changes"+encodeOptions(opts)},
466 options,
467 "Error connecting to "+db.uri+"/_changes."
468 );
469 }
470 // start the first request
471 if (since) {
472 getChangesSince();
473 } else {
474 db.info({
475 success : function(info) {
476 since = info.update_seq;
477 getChangesSince();
478 }
479 });
480 }
481 return promise;
482 },
483
484 /**
485 * Fetch all the docs in this db, you can specify an array of keys to
486 * fetch by passing the <code>keys</code> field in the
487 * <code>options</code>
488 * parameter.
489 * @see <a href="http://docs.couchdb.org/en/latest/api/database
490 * /bulk-api.html#db-all-docs">docs for /db/all_docs/</a>
491 * @param {ajaxSettings} options <a href="http://api.jquery.com/
492 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
493 */
494 allDocs: function(options) {
495 var type = "GET";
496 var data = null;
497 if (options["keys"]) {
498 type = "POST";
499 var keys = options["keys"];
500 delete options["keys"];
501 data = toJSON({ "keys": keys });
502 }
503 return ajax({
504 type: type,
505 data: data,
506 url: this.uri + "_all_docs" + encodeOptions(options)
507 },
508 options,
509 "An error occurred retrieving a list of all documents"
510 );
511 },
512
513 /**
514 * Fetch all the design docs in this db
515 * @param {ajaxSettings} options <a href="http://api.jquery.com/
516 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
517 */
518 allDesignDocs: function(options) {
519 return this.allDocs($.extend(
520 {startkey:"_design", endkey:"_design0"}, options));
521 },
522
523 /**
524 * Fetch all the design docs with an index.html, <code>options</code>
525 * parameter expects an <code>eachApp</code> field which is a callback
526 * called on each app found.
527 * @param {ajaxSettings} options <a href="http://api.jquery.com/
528 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
529 */
530 allApps: function(options) {
531 options = options || {};
532 var self = this;
533 if (options.eachApp) {
534 return this.allDesignDocs({
535 success: function(resp) {
536 $.each(resp.rows, function() {
537 self.openDoc(this.id, {
538 success: function(ddoc) {
539 var index, appPath, appName = ddoc._id.split('/');
540 appName.shift();
541 appName = appName.join('/');
542 index = ddoc.couchapp && ddoc.couchapp.index;
543 if (index) {
544 appPath = ['', name, ddoc._id, index].join('/');
545 } else if (ddoc._attachments &&
546 ddoc._attachments["index.html"]) {
547 appPath = ['', name, ddoc._id, "index.html"].join('/');
548 }
549 if (appPath) options.eachApp(appName, appPath, ddoc);
550 }
551 });
552 });
553 }
554 });
555 } else {
556 throw 'Please provide an eachApp function for allApps()';
557 }
558 },
559
560 /**
561 * Returns the specified doc from the specified db.
562 * @see <a href="http://docs.couchdb.org/en/latest/api/document
563 * /common.html#get--db-docid">docs for GET /db/doc</a>
564 * @param {String} docId id of document to fetch
565 * @param {ajaxSettings} options <a href="http://api.jquery.com/
566 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
567 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
568 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
569 */
570 openDoc: function(docId, options, ajaxOptions) {
571 options = options || {};
572 if (db_opts.attachPrevRev || options.attachPrevRev) {
573 $.extend(options, {
574 beforeSuccess : function(req, doc) {
575 rawDocs[doc._id] = {
576 rev : doc._rev,
577 raw : req.responseText
578 };
579 }
580 });
581 } else {
582 $.extend(options, {
583 beforeSuccess : function(req, doc) {
584 if (doc["jquery.couch.attachPrevRev"]) {
585 rawDocs[doc._id] = {
586 rev : doc._rev,
587 raw : req.responseText
588 };
589 }
590 }
591 });
592 }
593 return ajax(
594 {url: this.uri + encodeDocId(docId) + encodeOptions(options)},
595 options,
596 "The document could not be retrieved",
597 ajaxOptions
598 );
599 },
600
601 /**
602 * Create a new document in the specified database, using the supplied
603 * JSON document structure. If the JSON structure includes the _id
604 * field, then the document will be created with the specified document
605 * ID. If the _id field is not specified, a new unique ID will be
606 * generated.
607 * @see <a href="http://docs.couchdb.org/en/latest/api/document
608 * /common.html#put--db-docid">docs for PUT /db/doc</a>
609 * @param {String} doc document to save
610 * @param {ajaxSettings} options <a href="http://api.jquery.com/
611 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
612 */
613 saveDoc: function(doc, options) {
614 options = options || {};
615 var db = this;
616 var beforeSend = fullCommit(options);
617 if (doc._id === undefined) {
618 var method = "POST";
619 var uri = this.uri;
620 } else {
621 var method = "PUT";
622 var uri = this.uri + encodeDocId(doc._id);
623 }
624 var versioned = maybeApplyVersion(doc);
625 return $.ajax({
626 type: method, url: uri + encodeOptions(options),
627 contentType: "application/json",
628 dataType: "json", data: toJSON(doc),
629 beforeSend : beforeSend,
630 complete: function(req) {
631 var resp = $.parseJSON(req.responseText);
632 if (req.status == 200 || req.status == 201 || req.status == 202) {
633 doc._id = resp.id;
634 doc._rev = resp.rev;
635 if (versioned) {
636 db.openDoc(doc._id, {
637 attachPrevRev : true,
638 success : function(d) {
639 doc._attachments = d._attachments;
640 if (options.success) options.success(resp);
641 }
642 });
643 } else {
644 if (options.success) options.success(resp);
645 }
646 } else if (options.error) {
647 options.error(req.status, resp.error, resp.reason);
648 } else {
649 throw "The document could not be saved: " + resp.reason;
650 }
651 }
652 });
653 },
654
655 /**
656 * Save a list of documents
657 * @see <a href="http://docs.couchdb.org/en/latest/api/database
658 * /bulk-api.html#db-bulk-docs">docs for /db/_bulk_docs</a>
659 * @param {Object[]} docs List of documents to save
660 * @param {ajaxSettings} options <a href="http://api.jquery.com/
661 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
662 */
663 bulkSave: function(docs, options) {
664 var beforeSend = fullCommit(options);
665 $.extend(options, {successStatus: 201, beforeSend : beforeSend});
666 return ajax({
667 type: "POST",
668 url: this.uri + "_bulk_docs" + encodeOptions(options),
669 contentType: "application/json", data: toJSON(docs)
670 },
671 options,
672 "The documents could not be saved"
673 );
674 },
675
676 /**
677 * Deletes the specified document from the database. You must supply
678 * the current (latest) revision and <code>id</code> of the document
679 * to delete eg <code>removeDoc({_id:"mydoc", _rev: "1-2345"})</code>
680 * @see <a href="http://docs.couchdb.org/en/latest/api/document
681 * /common.html#delete--db-docid">docs for DELETE /db/doc</a>
682 * @param {Object} doc Document to delete
683 * @param {ajaxSettings} options <a href="http://api.jquery.com/
684 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
685 */
686 removeDoc: function(doc, options) {
687 return ajax({
688 type: "DELETE",
689 url: this.uri +
690 encodeDocId(doc._id) +
691 encodeOptions({rev: doc._rev})
692 },
693 options,
694 "The document could not be deleted"
695 );
696 },
697
698 /**
699 * Remove a set of documents
700 * @see <a href="http://docs.couchdb.org/en/latest/api/database
701 * /bulk-api.html#db-bulk-docs">docs for /db/_bulk_docs</a>
702 * @param {String[]} docs List of document id's to remove
703 * @param {ajaxSettings} options <a href="http://api.jquery.com/
704 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
705 */
706 bulkRemove: function(docs, options){
707 docs.docs = $.each(
708 docs.docs, function(i, doc){
709 doc._deleted = true;
710 }
711 );
712 $.extend(options, {successStatus: 201});
713 return ajax({
714 type: "POST",
715 url: this.uri + "_bulk_docs" + encodeOptions(options),
716 data: toJSON(docs)
717 },
718 options,
719 "The documents could not be deleted"
720 );
721 },
722
723 /**
724 * The COPY command (which is non-standard HTTP) copies an existing
725 * document to a new or existing document.
726 * @see <a href="http://docs.couchdb.org/en/latest/api/document
727 * /common.html#copy--db-docid">docs for COPY /db/doc</a>
728 * @param {String[]} docId document id to copy
729 * @param {ajaxSettings} options <a href="http://api.jquery.com/
730 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
731 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
732 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
733 */
734 copyDoc: function(docId, options, ajaxOptions) {
735 ajaxOptions = $.extend(ajaxOptions, {
736 complete: function(req) {
737 var resp = $.parseJSON(req.responseText);
738 if (req.status == 201) {
739 if (options.success) options.success(resp);
740 } else if (options.error) {
741 options.error(req.status, resp.error, resp.reason);
742 } else {
743 throw "The document could not be copied: " + resp.reason;
744 }
745 }
746 });
747 return ajax({
748 type: "COPY",
749 url: this.uri + encodeDocId(docId)
750 },
751 options,
752 "The document could not be copied",
753 ajaxOptions
754 );
755 },
756
757 /**
758 * Creates (and executes) a temporary view based on the view function
759 * supplied in the JSON request.
760 * @see <a href="http://docs.couchdb.org/en/latest/api/database
761 * /temp-views.html#db-temp-view">docs for /db/_temp_view</a>
762 * @param {Function} mapFun Map function
763 * @param {Function} reduceFun Reduce function
764 * @param {String} language Language the map / reduce funs are
765 * implemented in
766 * @param {ajaxSettings} options <a href="http://api.jquery.com/
767 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
768 */
769 query: function(mapFun, reduceFun, language, options) {
770 language = language || "javascript";
771 if (typeof(mapFun) !== "string") {
772 mapFun = mapFun.toSource ? mapFun.toSource()
773 : "(" + mapFun.toString() + ")";
774 }
775 var body = {language: language, map: mapFun};
776 if (reduceFun != null) {
777 if (typeof(reduceFun) !== "string")
778 reduceFun = reduceFun.toSource ? reduceFun.toSource()
779 : "(" + reduceFun.toString() + ")";
780 body.reduce = reduceFun;
781 }
782 return ajax({
783 type: "POST",
784 url: this.uri + "_temp_view" + encodeOptions(options),
785 contentType: "application/json", data: toJSON(body)
786 },
787 options,
788 "An error occurred querying the database"
789 );
790 },
791
792 /**
793 * Fetch a _list view output, you can specify a list of
794 * <code>keys</code> in the options object to receive only those keys.
795 * @see <a href="http://docs.couchdb.org/en/latest/api/ddoc/render.html
796 * #db-design-design-doc-list-list-name-view-name">
797 * docs for /db/_design/design-doc/_list/list/view</a>
798 * @param {String} list Listname in the form of ddoc/listname
799 * @param {String} view View to run list against
800 * @param {Object} options CouchDB <a href="http://docs.couchdb.org/en
801 * /latest/api/ddoc/views.html#get--db-_design-ddoc-_view-view">
802 * View Options</a>
803 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
804 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
805 */
806 list: function(list, view, options, ajaxOptions) {
807 var list = list.split('/');
808 var options = options || {};
809 var type = 'GET';
810 var data = null;
811 if (options['keys']) {
812 type = 'POST';
813 var keys = options['keys'];
814 delete options['keys'];
815 data = toJSON({'keys': keys });
816 }
817 return ajax({
818 type: type,
819 data: data,
820 url: this.uri + '_design/' + list[0] +
821 '/_list/' + list[1] + '/' + view + encodeOptions(options)
822 },
823 ajaxOptions, 'An error occurred accessing the list'
824 );
825 },
826
827 /**
828 * Executes the specified view-name from the specified design-doc
829 * design document, you can specify a list of <code>keys</code>
830 * in the options object to receive only those keys.
831 * @see <a href="http://docs.couchdb.org/en/latest/api/ddoc/views.html
832 * #db-design-design-doc-view-view-name">docs for /db/
833 * _design/design-doc/_view/name</a>
834 * @param {String} name View to run list against (string should have
835 * the design-doc name followed by a slash and the view name)
836 * @param {ajaxSettings} options <a href="http://api.jquery.com/
837 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
838 */
839 view: function(name, options) {
840 var name = name.split('/');
841 var options = options || {};
842 var type = "GET";
843 var data= null;
844 if (options["keys"]) {
845 type = "POST";
846 var keys = options["keys"];
847 delete options["keys"];
848 data = toJSON({ "keys": keys });
849 }
850 return ajax({
851 type: type,
852 data: data,
853 url: this.uri + "_design/" + name[0] +
854 "/_view/" + name[1] + encodeOptions(options)
855 },
856 options, "An error occurred accessing the view"
857 );
858 },
859
860 /**
861 * Fetch an arbitrary CouchDB database property
862 * @param {String} propName Property name to fetch
863 * @param {ajaxSettings} options <a href="http://api.jquery.com/
864 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
865 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
866 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
867 */
868 getDbProperty: function(propName, options, ajaxOptions) {
869 return ajax({url: this.uri + propName + encodeOptions(options)},
870 options,
871 "The property could not be retrieved",
872 ajaxOptions
873 );
874 },
875
876 /**
877 * Set an arbitrary CouchDB database property
878 * @param {String} propName Property name to fetch
879 * @param {String} propValue Property value to set
880 * @param {ajaxSettings} options <a href="http://api.jquery.com/
881 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
882 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
883 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
884 */
885 setDbProperty: function(propName, propValue, options, ajaxOptions) {
886 return ajax({
887 type: "PUT",
888 url: this.uri + propName + encodeOptions(options),
889 data : JSON.stringify(propValue)
890 },
891 options,
892 "The property could not be updated",
893 ajaxOptions
894 );
895 }
896 };
897 },
898
899 encodeDocId: encodeDocId,
900
901 /**
902 * Accessing the root of a CouchDB instance returns meta information about
903 * the instance. The response is a JSON structure containing information
904 * about the server, including a welcome message and the version of the
905 * server.
906 * @see <a href="http://docs.couchdb.org/en/latest/api/server/common.html
907 * #api-server-root">
908 * docs for GET /</a>
909 * @param {ajaxSettings} options <a href="http://api.jquery.com/
910 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
911 */
912 info: function(options) {
913 return ajax(
914 {url: this.urlPrefix + "/"},
915 options,
916 "Server information could not be retrieved"
917 );
918 },
919
920 /**
921 * Request, configure, or stop, a replication operation.
922 * @see <a href="http://docs.couchdb.org/en/latest/api/server/common.html
923 * #replicate">docs for POST /_replicate</a>
924 * @param {String} source Path or url to source database
925 * @param {String} target Path or url to target database
926 * @param {ajaxSettings} ajaxOptions <a href="http://api.jquery.com/
927 * jQuery.ajax/#jQuery-ajax-settings">jQuery ajax settings</a>
928 * @param {Object} repOpts Additional replication options
929 */
930 replicate: function(source, target, ajaxOptions, repOpts) {
931 repOpts = $.extend({source: source, target: target}, repOpts);
932 if (repOpts.continuous && !repOpts.cancel) {
933 ajaxOptions.successStatus = 202;
934 }
935 return ajax({
936 type: "POST", url: this.urlPrefix + "/_replicate",
937 data: JSON.stringify(repOpts),
938 contentType: "application/json"
939 },
940 ajaxOptions,
941 "Replication failed"
942 );
943 },
944
945 /**
946 * Fetch a new UUID
947 * @see <a href="http://docs.couchdb.org/en/latest/api/server/common.html
948 * #uuids">docs for /_uuids</a>
949 * @param {Integer} cacheNum Number of uuids to keep cached for future use
950 */
951 newUUID: function(cacheNum) {
952 if (cacheNum === undefined) {
953 cacheNum = 1;
954 }
955 if (!uuidCache.length) {
956 ajax({url: this.urlPrefix + "/_uuids", data: {count: cacheNum}, async:
957 false}, {
958 success: function(resp) {
959 uuidCache = resp.uuids;
960 }
961 },
962 "Failed to retrieve UUID batch."
963 );
964 }
965 return uuidCache.shift();
966 }
967 });
968
969 /**
970 * @private
971 */
972 function ajax(obj, options, errorMessage, ajaxOptions) {
973 var timeStart;
974 var defaultAjaxOpts = {
975 contentType: "application/json",
976 headers:{"Accept": "application/json"}
977 };
978
979 options = $.extend({successStatus: 200}, options);
980 ajaxOptions = $.extend(defaultAjaxOpts, ajaxOptions);
981 errorMessage = errorMessage || "Unknown error";
982 timeStart = (new Date()).getTime();
983 return $.ajax($.extend($.extend({
984 type: "GET", dataType: "json", cache : maybeUseCache(),
985 beforeSend: function(xhr){
986 if(ajaxOptions && ajaxOptions.headers){
987 for (var header in ajaxOptions.headers){
988 xhr.setRequestHeader(header, ajaxOptions.headers[header]);
989 }
990 }
991 },
992 complete: function(req) {
993 var reqDuration = (new Date()).getTime() - timeStart;
994 try {
995 var resp = $.parseJSON(req.responseText);
996 } catch(e) {
997 if (options.error) {
998 options.error(req.status, req, e);
999 } else {
1000 throw errorMessage + ': ' + e;
1001 }
1002 return;
1003 }
1004 if (options.ajaxStart) {
1005 options.ajaxStart(resp);
1006 }
1007 if (req.status == options.successStatus) {
1008 if (options.beforeSuccess) options.beforeSuccess(req, resp, reqDuration);
1009 if (options.success) options.success(resp, reqDuration);
1010 } else if (options.error) {
1011 options.error(req.status, resp && resp.error ||
1012 errorMessage, resp && resp.reason || "no response",
1013 reqDuration);
1014 } else {
1015 throw errorMessage + ": " + resp.reason;
1016 }
1017 }
1018 }, obj), ajaxOptions));
1019 }
1020
1021 /**
1022 * @private
1023 */
1024 function fullCommit(options) {
1025 var options = options || {};
1026 if (typeof options.ensure_full_commit !== "undefined") {
1027 var commit = options.ensure_full_commit;
1028 delete options.ensure_full_commit;
1029 return function(xhr) {
1030 xhr.setRequestHeader('Accept', 'application/json');
1031 xhr.setRequestHeader("X-Couch-Full-Commit", commit.toString());
1032 };
1033 }
1034 }
1035
1036 /**
1037 * @private
1038 */
1039 // Convert a options object to an url query string.
1040 // ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
1041 function encodeOptions(options) {
1042 var buf = [];
1043 if (typeof(options) === "object" && options !== null) {
1044 for (var name in options) {
1045 if ($.inArray(name,
1046 ["error", "success", "beforeSuccess", "ajaxStart"]) >= 0)
1047 continue;
1048 var value = options[name];
1049 if ($.inArray(name, ["key", "startkey", "endkey"]) >= 0) {
1050 value = toJSON(value);
1051 }
1052 buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
1053 }
1054 }
1055 return buf.length ? "?" + buf.join("&") : "";
1056 }
1057
1058 /**
1059 * @private
1060 */
1061 function toJSON(obj) {
1062 return obj !== null ? JSON.stringify(obj) : null;
1063 }
1064
1065 /**
1066 * @private
1067 */
1068 function maybeUseCache() {
1069 if (!navigator){
1070 return true;
1071 }
1072 else if (/(MSIE|Trident)/.test(navigator.userAgent)){
1073 return false;
1074 }
1075 return true;
1076 }
1077
1078 })(jQuery);
0 /*!
1 * jQuery JavaScript Library v1.8.3
2 * http://jquery.com/
3 *
4 * Includes Sizzle.js
5 * http://sizzlejs.com/
6 *
7 * Copyright 2012 jQuery Foundation and other contributors
8 * Released under the MIT license
9 * http://jquery.org/license
10 *
11 * Date: Tue Nov 13 2012 08:20:33 GMT-0500 (Eastern Standard Time)
12 */
13 (function( window, undefined ) {
14 var
15 // A central reference to the root jQuery(document)
16 rootjQuery,
17
18 // The deferred used on DOM ready
19 readyList,
20
21 // Use the correct document accordingly with window argument (sandbox)
22 document = window.document,
23 location = window.location,
24 navigator = window.navigator,
25
26 // Map over jQuery in case of overwrite
27 _jQuery = window.jQuery,
28
29 // Map over the $ in case of overwrite
30 _$ = window.$,
31
32 // Save a reference to some core methods
33 core_push = Array.prototype.push,
34 core_slice = Array.prototype.slice,
35 core_indexOf = Array.prototype.indexOf,
36 core_toString = Object.prototype.toString,
37 core_hasOwn = Object.prototype.hasOwnProperty,
38 core_trim = String.prototype.trim,
39
40 // Define a local copy of jQuery
41 jQuery = function( selector, context ) {
42 // The jQuery object is actually just the init constructor 'enhanced'
43 return new jQuery.fn.init( selector, context, rootjQuery );
44 },
45
46 // Used for matching numbers
47 core_pnum = /[\-+]?(?:\d*\.|)\d+(?:[eE][\-+]?\d+|)/.source,
48
49 // Used for detecting and trimming whitespace
50 core_rnotwhite = /\S/,
51 core_rspace = /\s+/,
52
53 // Make sure we trim BOM and NBSP (here's looking at you, Safari 5.0 and IE)
54 rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,
55
56 // A simple way to check for HTML strings
57 // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
58 rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
59
60 // Match a standalone tag
61 rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/,
62
63 // JSON RegExp
64 rvalidchars = /^[\],:{}\s]*$/,
65 rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
66 rvalidescape = /\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,
67 rvalidtokens = /"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,
68
69 // Matches dashed string for camelizing
70 rmsPrefix = /^-ms-/,
71 rdashAlpha = /-([\da-z])/gi,
72
73 // Used by jQuery.camelCase as callback to replace()
74 fcamelCase = function( all, letter ) {
75 return ( letter + "" ).toUpperCase();
76 },
77
78 // The ready event handler and self cleanup method
79 DOMContentLoaded = function() {
80 if ( document.addEventListener ) {
81 document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
82 jQuery.ready();
83 } else if ( document.readyState === "complete" ) {
84 // we're here because readyState === "complete" in oldIE
85 // which is good enough for us to call the dom ready!
86 document.detachEvent( "onreadystatechange", DOMContentLoaded );
87 jQuery.ready();
88 }
89 },
90
91 // [[Class]] -> type pairs
92 class2type = {};
93
94 jQuery.fn = jQuery.prototype = {
95 constructor: jQuery,
96 init: function( selector, context, rootjQuery ) {
97 var match, elem, ret, doc;
98
99 // Handle $(""), $(null), $(undefined), $(false)
100 if ( !selector ) {
101 return this;
102 }
103
104 // Handle $(DOMElement)
105 if ( selector.nodeType ) {
106 this.context = this[0] = selector;
107 this.length = 1;
108 return this;
109 }
110
111 // Handle HTML strings
112 if ( typeof selector === "string" ) {
113 if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
114 // Assume that strings that start and end with <> are HTML and skip the regex check
115 match = [ null, selector, null ];
116
117 } else {
118 match = rquickExpr.exec( selector );
119 }
120
121 // Match html or make sure no context is specified for #id
122 if ( match && (match[1] || !context) ) {
123
124 // HANDLE: $(html) -> $(array)
125 if ( match[1] ) {
126 context = context instanceof jQuery ? context[0] : context;
127 doc = ( context && context.nodeType ? context.ownerDocument || context : document );
128
129 // scripts is true for back-compat
130 selector = jQuery.parseHTML( match[1], doc, true );
131 if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) {
132 this.attr.call( selector, context, true );
133 }
134
135 return jQuery.merge( this, selector );
136
137 // HANDLE: $(#id)
138 } else {
139 elem = document.getElementById( match[2] );
140
141 // Check parentNode to catch when Blackberry 4.6 returns
142 // nodes that are no longer in the document #6963
143 if ( elem && elem.parentNode ) {
144 // Handle the case where IE and Opera return items
145 // by name instead of ID
146 if ( elem.id !== match[2] ) {
147 return rootjQuery.find( selector );
148 }
149
150 // Otherwise, we inject the element directly into the jQuery object
151 this.length = 1;
152 this[0] = elem;
153 }
154
155 this.context = document;
156 this.selector = selector;
157 return this;
158 }
159
160 // HANDLE: $(expr, $(...))
161 } else if ( !context || context.jquery ) {
162 return ( context || rootjQuery ).find( selector );
163
164 // HANDLE: $(expr, context)
165 // (which is just equivalent to: $(context).find(expr)
166 } else {
167 return this.constructor( context ).find( selector );
168 }
169
170 // HANDLE: $(function)
171 // Shortcut for document ready
172 } else if ( jQuery.isFunction( selector ) ) {
173 return rootjQuery.ready( selector );
174 }
175
176 if ( selector.selector !== undefined ) {
177 this.selector = selector.selector;
178 this.context = selector.context;
179 }
180
181 return jQuery.makeArray( selector, this );
182 },
183
184 // Start with an empty selector
185 selector: "",
186
187 // The current version of jQuery being used
188 jquery: "1.8.3",
189
190 // The default length of a jQuery object is 0
191 length: 0,
192
193 // The number of elements contained in the matched element set
194 size: function() {
195 return this.length;
196 },
197
198 toArray: function() {
199 return core_slice.call( this );
200 },
201
202 // Get the Nth element in the matched element set OR
203 // Get the whole matched element set as a clean array
204 get: function( num ) {
205 return num == null ?
206
207 // Return a 'clean' array
208 this.toArray() :
209
210 // Return just the object
211 ( num < 0 ? this[ this.length + num ] : this[ num ] );
212 },
213
214 // Take an array of elements and push it onto the stack
215 // (returning the new matched element set)
216 pushStack: function( elems, name, selector ) {
217
218 // Build a new jQuery matched element set
219 var ret = jQuery.merge( this.constructor(), elems );
220
221 // Add the old object onto the stack (as a reference)
222 ret.prevObject = this;
223
224 ret.context = this.context;
225
226 if ( name === "find" ) {
227 ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
228 } else if ( name ) {
229 ret.selector = this.selector + "." + name + "(" + selector + ")";
230 }
231
232 // Return the newly-formed element set
233 return ret;
234 },
235
236 // Execute a callback for every element in the matched set.
237 // (You can seed the arguments with an array of args, but this is
238 // only used internally.)
239 each: function( callback, args ) {
240 return jQuery.each( this, callback, args );
241 },
242
243 ready: function( fn ) {
244 // Add the callback
245 jQuery.ready.promise().done( fn );
246
247 return this;
248 },
249
250 eq: function( i ) {
251 i = +i;
252 return i === -1 ?
253 this.slice( i ) :
254 this.slice( i, i + 1 );
255 },
256
257 first: function() {
258 return this.eq( 0 );
259 },
260
261 last: function() {
262 return this.eq( -1 );
263 },
264
265 slice: function() {
266 return this.pushStack( core_slice.apply( this, arguments ),
267 "slice", core_slice.call(arguments).join(",") );
268 },
269
270 map: function( callback ) {
271 return this.pushStack( jQuery.map(this, function( elem, i ) {
272 return callback.call( elem, i, elem );
273 }));
274 },
275
276 end: function() {
277 return this.prevObject || this.constructor(null);
278 },
279
280 // For internal use only.
281 // Behaves like an Array's method, not like a jQuery method.
282 push: core_push,
283 sort: [].sort,
284 splice: [].splice
285 };
286
287 // Give the init function the jQuery prototype for later instantiation
288 jQuery.fn.init.prototype = jQuery.fn;
289
290 jQuery.extend = jQuery.fn.extend = function() {
291 var options, name, src, copy, copyIsArray, clone,
292 target = arguments[0] || {},
293 i = 1,
294 length = arguments.length,
295 deep = false;
296
297 // Handle a deep copy situation
298 if ( typeof target === "boolean" ) {
299 deep = target;
300 target = arguments[1] || {};
301 // skip the boolean and the target
302 i = 2;
303 }
304
305 // Handle case when target is a string or something (possible in deep copy)
306 if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
307 target = {};
308 }
309
310 // extend jQuery itself if only one argument is passed
311 if ( length === i ) {
312 target = this;
313 --i;
314 }
315
316 for ( ; i < length; i++ ) {
317 // Only deal with non-null/undefined values
318 if ( (options = arguments[ i ]) != null ) {
319 // Extend the base object
320 for ( name in options ) {
321 src = target[ name ];
322 copy = options[ name ];
323
324 // Prevent never-ending loop
325 if ( target === copy ) {
326 continue;
327 }
328
329 // Recurse if we're merging plain objects or arrays
330 if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
331 if ( copyIsArray ) {
332 copyIsArray = false;
333 clone = src && jQuery.isArray(src) ? src : [];
334
335 } else {
336 clone = src && jQuery.isPlainObject(src) ? src : {};
337 }
338
339 // Never move original objects, clone them
340 target[ name ] = jQuery.extend( deep, clone, copy );
341
342 // Don't bring in undefined values
343 } else if ( copy !== undefined ) {
344 target[ name ] = copy;
345 }
346 }
347 }
348 }
349
350 // Return the modified object
351 return target;
352 };
353
354 jQuery.extend({
355 noConflict: function( deep ) {
356 if ( window.$ === jQuery ) {
357 window.$ = _$;
358 }
359
360 if ( deep && window.jQuery === jQuery ) {
361 window.jQuery = _jQuery;
362 }
363
364 return jQuery;
365 },
366
367 // Is the DOM ready to be used? Set to true once it occurs.
368 isReady: false,
369
370 // A counter to track how many items to wait for before
371 // the ready event fires. See #6781
372 readyWait: 1,
373
374 // Hold (or release) the ready event
375 holdReady: function( hold ) {
376 if ( hold ) {
377 jQuery.readyWait++;
378 } else {
379 jQuery.ready( true );
380 }
381 },
382
383 // Handle when the DOM is ready
384 ready: function( wait ) {
385
386 // Abort if there are pending holds or we're already ready
387 if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
388 return;
389 }
390
391 // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
392 if ( !document.body ) {
393 return setTimeout( jQuery.ready, 1 );
394 }
395
396 // Remember that the DOM is ready
397 jQuery.isReady = true;
398
399 // If a normal DOM Ready event fired, decrement, and wait if need be
400 if ( wait !== true && --jQuery.readyWait > 0 ) {
401 return;
402 }
403
404 // If there are functions bound, to execute
405 readyList.resolveWith( document, [ jQuery ] );
406
407 // Trigger any bound ready events
408 if ( jQuery.fn.trigger ) {
409 jQuery( document ).trigger("ready").off("ready");
410 }
411 },
412
413 // See test/unit/core.js for details concerning isFunction.
414 // Since version 1.3, DOM methods and functions like alert
415 // aren't supported. They return false on IE (#2968).
416 isFunction: function( obj ) {
417 return jQuery.type(obj) === "function";
418 },
419
420 isArray: Array.isArray || function( obj ) {
421 return jQuery.type(obj) === "array";
422 },
423
424 isWindow: function( obj ) {
425 return obj != null && obj == obj.window;
426 },
427
428 isNumeric: function( obj ) {
429 return !isNaN( parseFloat(obj) ) && isFinite( obj );
430 },
431
432 type: function( obj ) {
433 return obj == null ?
434 String( obj ) :
435 class2type[ core_toString.call(obj) ] || "object";
436 },
437
438 isPlainObject: function( obj ) {
439 // Must be an Object.
440 // Because of IE, we also have to check the presence of the constructor property.
441 // Make sure that DOM nodes and window objects don't pass through, as well
442 if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
443 return false;
444 }
445
446 try {
447 // Not own constructor property must be Object
448 if ( obj.constructor &&
449 !core_hasOwn.call(obj, "constructor") &&
450 !core_hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
451 return false;
452 }
453 } catch ( e ) {
454 // IE8,9 Will throw exceptions on certain host objects #9897
455 return false;
456 }
457
458 // Own properties are enumerated firstly, so to speed up,
459 // if last one is own, then all properties are own.
460
461 var key;
462 for ( key in obj ) {}
463
464 return key === undefined || core_hasOwn.call( obj, key );
465 },
466
467 isEmptyObject: function( obj ) {
468 var name;
469 for ( name in obj ) {
470 return false;
471 }
472 return true;
473 },
474
475 error: function( msg ) {
476 throw new Error( msg );
477 },
478
479 // data: string of html
480 // context (optional): If specified, the fragment will be created in this context, defaults to document
481 // scripts (optional): If true, will include scripts passed in the html string
482 parseHTML: function( data, context, scripts ) {
483 var parsed;
484 if ( !data || typeof data !== "string" ) {
485 return null;
486 }
487 if ( typeof context === "boolean" ) {
488 scripts = context;
489 context = 0;
490 }
491 context = context || document;
492
493 // Single tag
494 if ( (parsed = rsingleTag.exec( data )) ) {
495 return [ context.createElement( parsed[1] ) ];
496 }
497
498 parsed = jQuery.buildFragment( [ data ], context, scripts ? null : [] );
499 return jQuery.merge( [],
500 (parsed.cacheable ? jQuery.clone( parsed.fragment ) : parsed.fragment).childNodes );
501 },
502
503 parseJSON: function( data ) {
504 if ( !data || typeof data !== "string") {
505 return null;
506 }
507
508 // Make sure leading/trailing whitespace is removed (IE can't handle it)
509 data = jQuery.trim( data );
510
511 // Attempt to parse using the native JSON parser first
512 if ( window.JSON && window.JSON.parse ) {
513 return window.JSON.parse( data );
514 }
515
516 // Make sure the incoming data is actual JSON
517 // Logic borrowed from http://json.org/json2.js
518 if ( rvalidchars.test( data.replace( rvalidescape, "@" )
519 .replace( rvalidtokens, "]" )
520 .replace( rvalidbraces, "")) ) {
521
522 return ( new Function( "return " + data ) )();
523
524 }
525 jQuery.error( "Invalid JSON: " + data );
526 },
527
528 // Cross-browser xml parsing
529 parseXML: function( data ) {
530 var xml, tmp;
531 if ( !data || typeof data !== "string" ) {
532 return null;
533 }
534 try {
535 if ( window.DOMParser ) { // Standard
536 tmp = new DOMParser();
537 xml = tmp.parseFromString( data , "text/xml" );
538 } else { // IE
539 xml = new ActiveXObject( "Microsoft.XMLDOM" );
540 xml.async = "false";
541 xml.loadXML( data );
542 }
543 } catch( e ) {
544 xml = undefined;
545 }
546 if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
547 jQuery.error( "Invalid XML: " + data );
548 }
549 return xml;
550 },
551
552 noop: function() {},
553
554 // Evaluates a script in a global context
555 // Workarounds based on findings by Jim Driscoll
556 // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
557 globalEval: function( data ) {
558 if ( data && core_rnotwhite.test( data ) ) {
559 // We use execScript on Internet Explorer
560 // We use an anonymous function so that context is window
561 // rather than jQuery in Firefox
562 ( window.execScript || function( data ) {
563 window[ "eval" ].call( window, data );
564 } )( data );
565 }
566 },
567
568 // Convert dashed to camelCase; used by the css and data modules
569 // Microsoft forgot to hump their vendor prefix (#9572)
570 camelCase: function( string ) {
571 return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
572 },
573
574 nodeName: function( elem, name ) {
575 return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
576 },
577
578 // args is for internal usage only
579 each: function( obj, callback, args ) {
580 var name,
581 i = 0,
582 length = obj.length,
583 isObj = length === undefined || jQuery.isFunction( obj );
584
585 if ( args ) {
586 if ( isObj ) {
587 for ( name in obj ) {
588 if ( callback.apply( obj[ name ], args ) === false ) {
589 break;
590 }
591 }
592 } else {
593 for ( ; i < length; ) {
594 if ( callback.apply( obj[ i++ ], args ) === false ) {
595 break;
596 }
597 }
598 }
599
600 // A special, fast, case for the most common use of each
601 } else {
602 if ( isObj ) {
603 for ( name in obj ) {
604 if ( callback.call( obj[ name ], name, obj[ name ] ) === false ) {
605 break;
606 }
607 }
608 } else {
609 for ( ; i < length; ) {
610 if ( callback.call( obj[ i ], i, obj[ i++ ] ) === false ) {
611 break;
612 }
613 }
614 }
615 }
616
617 return obj;
618 },
619
620 // Use native String.trim function wherever possible
621 trim: core_trim && !core_trim.call("\uFEFF\xA0") ?
622 function( text ) {
623 return text == null ?
624 "" :
625 core_trim.call( text );
626 } :
627
628 // Otherwise use our own trimming functionality
629 function( text ) {
630 return text == null ?
631 "" :
632 ( text + "" ).replace( rtrim, "" );
633 },
634
635 // results is for internal usage only
636 makeArray: function( arr, results ) {
637 var type,
638 ret = results || [];
639
640 if ( arr != null ) {
641 // The window, strings (and functions) also have 'length'
642 // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
643 type = jQuery.type( arr );
644
645 if ( arr.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( arr ) ) {
646 core_push.call( ret, arr );
647 } else {
648 jQuery.merge( ret, arr );
649 }
650 }
651
652 return ret;
653 },
654
655 inArray: function( elem, arr, i ) {
656 var len;
657
658 if ( arr ) {
659 if ( core_indexOf ) {
660 return core_indexOf.call( arr, elem, i );
661 }
662
663 len = arr.length;
664 i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
665
666 for ( ; i < len; i++ ) {
667 // Skip accessing in sparse arrays
668 if ( i in arr && arr[ i ] === elem ) {
669 return i;
670 }
671 }
672 }
673
674 return -1;
675 },
676
677 merge: function( first, second ) {
678 var l = second.length,
679 i = first.length,
680 j = 0;
681
682 if ( typeof l === "number" ) {
683 for ( ; j < l; j++ ) {
684 first[ i++ ] = second[ j ];
685 }
686
687 } else {
688 while ( second[j] !== undefined ) {
689 first[ i++ ] = second[ j++ ];
690 }
691 }
692
693 first.length = i;
694
695 return first;
696 },
697
698 grep: function( elems, callback, inv ) {
699 var retVal,
700 ret = [],
701 i = 0,
702 length = elems.length;
703 inv = !!inv;
704
705 // Go through the array, only saving the items
706 // that pass the validator function
707 for ( ; i < length; i++ ) {
708 retVal = !!callback( elems[ i ], i );
709 if ( inv !== retVal ) {
710 ret.push( elems[ i ] );
711 }
712 }
713
714 return ret;
715 },
716
717 // arg is for internal usage only
718 map: function( elems, callback, arg ) {
719 var value, key,
720 ret = [],
721 i = 0,
722 length = elems.length,
723 // jquery objects are treated as arrays
724 isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
725
726 // Go through the array, translating each of the items to their
727 if ( isArray ) {
728 for ( ; i < length; i++ ) {
729 value = callback( elems[ i ], i, arg );
730
731 if ( value != null ) {
732 ret[ ret.length ] = value;
733 }
734 }
735
736 // Go through every key on the object,
737 } else {
738 for ( key in elems ) {
739 value = callback( elems[ key ], key, arg );
740
741 if ( value != null ) {
742 ret[ ret.length ] = value;
743 }
744 }
745 }
746
747 // Flatten any nested arrays
748 return ret.concat.apply( [], ret );
749 },
750
751 // A global GUID counter for objects
752 guid: 1,
753
754 // Bind a function to a context, optionally partially applying any
755 // arguments.
756 proxy: function( fn, context ) {
757 var tmp, args, proxy;
758
759 if ( typeof context === "string" ) {
760 tmp = fn[ context ];
761 context = fn;
762 fn = tmp;
763 }
764
765 // Quick check to determine if target is callable, in the spec
766 // this throws a TypeError, but we will just return undefined.
767 if ( !jQuery.isFunction( fn ) ) {
768 return undefined;
769 }
770
771 // Simulated bind
772 args = core_slice.call( arguments, 2 );
773 proxy = function() {
774 return fn.apply( context, args.concat( core_slice.call( arguments ) ) );
775 };
776
777 // Set the guid of unique handler to the same of original handler, so it can be removed
778 proxy.guid = fn.guid = fn.guid || jQuery.guid++;
779
780 return proxy;
781 },
782
783 // Multifunctional method to get and set values of a collection
784 // The value/s can optionally be executed if it's a function
785 access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
786 var exec,
787 bulk = key == null,
788 i = 0,
789 length = elems.length;
790
791 // Sets many values
792 if ( key && typeof key === "object" ) {
793 for ( i in key ) {
794 jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
795 }
796 chainable = 1;
797
798 // Sets one value
799 } else if ( value !== undefined ) {
800 // Optionally, function values get executed if exec is true
801 exec = pass === undefined && jQuery.isFunction( value );
802
803 if ( bulk ) {
804 // Bulk operations only iterate when executing function values
805 if ( exec ) {
806 exec = fn;
807 fn = function( elem, key, value ) {
808 return exec.call( jQuery( elem ), value );
809 };
810
811 // Otherwise they run against the entire set
812 } else {
813 fn.call( elems, value );
814 fn = null;
815 }
816 }
817
818 if ( fn ) {
819 for (; i < length; i++ ) {
820 fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
821 }
822 }
823
824 chainable = 1;
825 }
826
827 return chainable ?
828 elems :
829
830 // Gets
831 bulk ?
832 fn.call( elems ) :
833 length ? fn( elems[0], key ) : emptyGet;
834 },
835
836 now: function() {
837 return ( new Date() ).getTime();
838 }
839 });
840
841 jQuery.ready.promise = function( obj ) {
842 if ( !readyList ) {
843
844 readyList = jQuery.Deferred();
845
846 // Catch cases where $(document).ready() is called after the browser event has already occurred.
847 // we once tried to use readyState "interactive" here, but it caused issues like the one
848 // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15
849 if ( document.readyState === "complete" ) {
850 // Handle it asynchronously to allow scripts the opportunity to delay ready
851 setTimeout( jQuery.ready, 1 );
852
853 // Standards-based browsers support DOMContentLoaded
854 } else if ( document.addEventListener ) {
855 // Use the handy event callback
856 document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
857
858 // A fallback to window.onload, that will always work
859 window.addEventListener( "load", jQuery.ready, false );
860
861 // If IE event model is used
862 } else {
863 // Ensure firing before onload, maybe late but safe also for iframes
864 document.attachEvent( "onreadystatechange", DOMContentLoaded );
865
866 // A fallback to window.onload, that will always work
867 window.attachEvent( "onload", jQuery.ready );
868
869 // If IE and not a frame
870 // continually check to see if the document is ready
871 var top = false;
872
873 try {
874 top = window.frameElement == null && document.documentElement;
875 } catch(e) {}
876
877 if ( top && top.doScroll ) {
878 (function doScrollCheck() {
879 if ( !jQuery.isReady ) {
880
881 try {
882 // Use the trick by Diego Perini
883 // http://javascript.nwbox.com/IEContentLoaded/
884 top.doScroll("left");
885 } catch(e) {
886 return setTimeout( doScrollCheck, 50 );
887 }
888
889 // and execute any waiting functions
890 jQuery.ready();
891 }
892 })();
893 }
894 }
895 }
896 return readyList.promise( obj );
897 };
898
899 // Populate the class2type map
900 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
901 class2type[ "[object " + name + "]" ] = name.toLowerCase();
902 });
903
904 // All jQuery objects should point back to these
905 rootjQuery = jQuery(document);
906 // String to Object options format cache
907 var optionsCache = {};
908
909 // Convert String-formatted options into Object-formatted ones and store in cache
910 function createOptions( options ) {
911 var object = optionsCache[ options ] = {};
912 jQuery.each( options.split( core_rspace ), function( _, flag ) {
913 object[ flag ] = true;
914 });
915 return object;
916 }
917
918 /*
919 * Create a callback list using the following parameters:
920 *
921 * options: an optional list of space-separated options that will change how
922 * the callback list behaves or a more traditional option object
923 *
924 * By default a callback list will act like an event callback list and can be
925 * "fired" multiple times.
926 *
927 * Possible options:
928 *
929 * once: will ensure the callback list can only be fired once (like a Deferred)
930 *
931 * memory: will keep track of previous values and will call any callback added
932 * after the list has been fired right away with the latest "memorized"
933 * values (like a Deferred)
934 *
935 * unique: will ensure a callback can only be added once (no duplicate in the list)
936 *
937 * stopOnFalse: interrupt callings when a callback returns false
938 *
939 */
940 jQuery.Callbacks = function( options ) {
941
942 // Convert options from String-formatted to Object-formatted if needed
943 // (we check in cache first)
944 options = typeof options === "string" ?
945 ( optionsCache[ options ] || createOptions( options ) ) :
946 jQuery.extend( {}, options );
947
948 var // Last fire value (for non-forgettable lists)
949 memory,
950 // Flag to know if list was already fired
951 fired,
952 // Flag to know if list is currently firing
953 firing,
954 // First callback to fire (used internally by add and fireWith)
955 firingStart,
956 // End of the loop when firing
957 firingLength,
958 // Index of currently firing callback (modified by remove if needed)
959 firingIndex,
960 // Actual callback list
961 list = [],
962 // Stack of fire calls for repeatable lists
963 stack = !options.once && [],
964 // Fire callbacks
965 fire = function( data ) {
966 memory = options.memory && data;
967 fired = true;
968 firingIndex = firingStart || 0;
969 firingStart = 0;
970 firingLength = list.length;
971 firing = true;
972 for ( ; list && firingIndex < firingLength; firingIndex++ ) {
973 if ( list[ firingIndex ].apply( data[ 0 ], data[ 1 ] ) === false && options.stopOnFalse ) {
974 memory = false; // To prevent further calls using add
975 break;
976 }
977 }
978 firing = false;
979 if ( list ) {
980 if ( stack ) {
981 if ( stack.length ) {
982 fire( stack.shift() );
983 }
984 } else if ( memory ) {
985 list = [];
986 } else {
987 self.disable();
988 }
989 }
990 },
991 // Actual Callbacks object
992 self = {
993 // Add a callback or a collection of callbacks to the list
994 add: function() {
995 if ( list ) {
996 // First, we save the current length
997 var start = list.length;
998 (function add( args ) {
999 jQuery.each( args, function( _, arg ) {
1000 var type = jQuery.type( arg );
1001 if ( type === "function" ) {
1002 if ( !options.unique || !self.has( arg ) ) {
1003 list.push( arg );
1004 }
1005 } else if ( arg && arg.length && type !== "string" ) {
1006 // Inspect recursively
1007 add( arg );
1008 }
1009 });
1010 })( arguments );
1011 // Do we need to add the callbacks to the
1012 // current firing batch?
1013 if ( firing ) {
1014 firingLength = list.length;
1015 // With memory, if we're not firing then
1016 // we should call right away
1017 } else if ( memory ) {
1018 firingStart = start;
1019 fire( memory );
1020 }
1021 }
1022 return this;
1023 },
1024 // Remove a callback from the list
1025 remove: function() {
1026 if ( list ) {
1027 jQuery.each( arguments, function( _, arg ) {
1028 var index;
1029 while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
1030 list.splice( index, 1 );
1031 // Handle firing indexes
1032 if ( firing ) {
1033 if ( index <= firingLength ) {
1034 firingLength--;
1035 }
1036 if ( index <= firingIndex ) {
1037 firingIndex--;
1038 }
1039 }
1040 }
1041 });
1042 }
1043 return this;
1044 },
1045 // Control if a given callback is in the list
1046 has: function( fn ) {
1047 return jQuery.inArray( fn, list ) > -1;
1048 },
1049 // Remove all callbacks from the list
1050 empty: function() {
1051 list = [];
1052 return this;
1053 },
1054 // Have the list do nothing anymore
1055 disable: function() {
1056 list = stack = memory = undefined;
1057 return this;
1058 },
1059 // Is it disabled?
1060 disabled: function() {
1061 return !list;
1062 },
1063 // Lock the list in its current state
1064 lock: function() {
1065 stack = undefined;
1066 if ( !memory ) {
1067 self.disable();
1068 }
1069 return this;
1070 },
1071 // Is it locked?
1072 locked: function() {
1073 return !stack;
1074 },
1075 // Call all callbacks with the given context and arguments
1076 fireWith: function( context, args ) {
1077 args = args || [];
1078 args = [ context, args.slice ? args.slice() : args ];
1079 if ( list && ( !fired || stack ) ) {
1080 if ( firing ) {
1081 stack.push( args );
1082 } else {
1083 fire( args );
1084 }
1085 }
1086 return this;
1087 },
1088 // Call all the callbacks with the given arguments
1089 fire: function() {
1090 self.fireWith( this, arguments );
1091 return this;
1092 },
1093 // To know if the callbacks have already been called at least once
1094 fired: function() {
1095 return !!fired;
1096 }
1097 };
1098
1099 return self;
1100 };
1101 jQuery.extend({
1102
1103 Deferred: function( func ) {
1104 var tuples = [
1105 // action, add listener, listener list, final state
1106 [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
1107 [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
1108 [ "notify", "progress", jQuery.Callbacks("memory") ]
1109 ],
1110 state = "pending",
1111 promise = {
1112 state: function() {
1113 return state;
1114 },
1115 always: function() {
1116 deferred.done( arguments ).fail( arguments );
1117 return this;
1118 },
1119 then: function( /* fnDone, fnFail, fnProgress */ ) {
1120 var fns = arguments;
1121 return jQuery.Deferred(function( newDefer ) {
1122 jQuery.each( tuples, function( i, tuple ) {
1123 var action = tuple[ 0 ],
1124 fn = fns[ i ];
1125 // deferred[ done | fail | progress ] for forwarding actions to newDefer
1126 deferred[ tuple[1] ]( jQuery.isFunction( fn ) ?
1127 function() {
1128 var returned = fn.apply( this, arguments );
1129 if ( returned && jQuery.isFunction( returned.promise ) ) {
1130 returned.promise()
1131 .done( newDefer.resolve )
1132 .fail( newDefer.reject )
1133 .progress( newDefer.notify );
1134 } else {
1135 newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
1136 }
1137 } :
1138 newDefer[ action ]
1139 );
1140 });
1141 fns = null;
1142 }).promise();
1143 },
1144 // Get a promise for this deferred
1145 // If obj is provided, the promise aspect is added to the object
1146 promise: function( obj ) {
1147 return obj != null ? jQuery.extend( obj, promise ) : promise;
1148 }
1149 },
1150 deferred = {};
1151
1152 // Keep pipe for back-compat
1153 promise.pipe = promise.then;
1154
1155 // Add list-specific methods
1156 jQuery.each( tuples, function( i, tuple ) {
1157 var list = tuple[ 2 ],
1158 stateString = tuple[ 3 ];
1159
1160 // promise[ done | fail | progress ] = list.add
1161 promise[ tuple[1] ] = list.add;
1162
1163 // Handle state
1164 if ( stateString ) {
1165 list.add(function() {
1166 // state = [ resolved | rejected ]
1167 state = stateString;
1168
1169 // [ reject_list | resolve_list ].disable; progress_list.lock
1170 }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
1171 }
1172
1173 // deferred[ resolve | reject | notify ] = list.fire
1174 deferred[ tuple[0] ] = list.fire;
1175 deferred[ tuple[0] + "With" ] = list.fireWith;
1176 });
1177
1178 // Make the deferred a promise
1179 promise.promise( deferred );
1180
1181 // Call given func if any
1182 if ( func ) {
1183 func.call( deferred, deferred );
1184 }
1185
1186 // All done!
1187 return deferred;
1188 },
1189
1190 // Deferred helper
1191 when: function( subordinate /* , ..., subordinateN */ ) {
1192 var i = 0,
1193 resolveValues = core_slice.call( arguments ),
1194 length = resolveValues.length,
1195
1196 // the count of uncompleted subordinates
1197 remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
1198
1199 // the master Deferred. If resolveValues consist of only a single Deferred, just use that.
1200 deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
1201
1202 // Update function for both resolve and progress values
1203 updateFunc = function( i, contexts, values ) {
1204 return function( value ) {
1205 contexts[ i ] = this;
1206 values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
1207 if( values === progressValues ) {
1208 deferred.notifyWith( contexts, values );
1209 } else if ( !( --remaining ) ) {
1210 deferred.resolveWith( contexts, values );
1211 }
1212 };
1213 },
1214
1215 progressValues, progressContexts, resolveContexts;
1216
1217 // add listeners to Deferred subordinates; treat others as resolved
1218 if ( length > 1 ) {
1219 progressValues = new Array( length );
1220 progressContexts = new Array( length );
1221 resolveContexts = new Array( length );
1222 for ( ; i < length; i++ ) {
1223 if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
1224 resolveValues[ i ].promise()
1225 .done( updateFunc( i, resolveContexts, resolveValues ) )
1226 .fail( deferred.reject )
1227 .progress( updateFunc( i, progressContexts, progressValues ) );
1228 } else {
1229 --remaining;
1230 }
1231 }
1232 }
1233
1234 // if we're not waiting on anything, resolve the master
1235 if ( !remaining ) {
1236 deferred.resolveWith( resolveContexts, resolveValues );
1237 }
1238
1239 return deferred.promise();
1240 }
1241 });
1242 jQuery.support = (function() {
1243
1244 var support,
1245 all,
1246 a,
1247 select,
1248 opt,
1249 input,
1250 fragment,
1251 eventName,
1252 i,
1253 isSupported,
1254 clickFn,
1255 div = document.createElement("div");
1256
1257 // Setup
1258 div.setAttribute( "className", "t" );
1259 div.innerHTML = " <link/><table></table><a href='/a'>a</a><input type='checkbox'/>";
1260
1261 // Support tests won't run in some limited or non-browser environments
1262 all = div.getElementsByTagName("*");
1263 a = div.getElementsByTagName("a")[ 0 ];
1264 if ( !all || !a || !all.length ) {
1265 return {};
1266 }
1267
1268 // First batch of tests
1269 select = document.createElement("select");
1270 opt = select.appendChild( document.createElement("option") );
1271 input = div.getElementsByTagName("input")[ 0 ];
1272
1273 a.style.cssText = "top:1px;float:left;opacity:.5";
1274 support = {
1275 // IE strips leading whitespace when .innerHTML is used
1276 leadingWhitespace: ( div.firstChild.nodeType === 3 ),
1277
1278 // Make sure that tbody elements aren't automatically inserted
1279 // IE will insert them into empty tables
1280 tbody: !div.getElementsByTagName("tbody").length,
1281
1282 // Make sure that link elements get serialized correctly by innerHTML
1283 // This requires a wrapper element in IE
1284 htmlSerialize: !!div.getElementsByTagName("link").length,
1285
1286 // Get the style information from getAttribute
1287 // (IE uses .cssText instead)
1288 style: /top/.test( a.getAttribute("style") ),
1289
1290 // Make sure that URLs aren't manipulated
1291 // (IE normalizes it by default)
1292 hrefNormalized: ( a.getAttribute("href") === "/a" ),
1293
1294 // Make sure that element opacity exists
1295 // (IE uses filter instead)
1296 // Use a regex to work around a WebKit issue. See #5145
1297 opacity: /^0.5/.test( a.style.opacity ),
1298
1299 // Verify style float existence
1300 // (IE uses styleFloat instead of cssFloat)
1301 cssFloat: !!a.style.cssFloat,
1302
1303 // Make sure that if no value is specified for a checkbox
1304 // that it defaults to "on".
1305 // (WebKit defaults to "" instead)
1306 checkOn: ( input.value === "on" ),
1307
1308 // Make sure that a selected-by-default option has a working selected property.
1309 // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
1310 optSelected: opt.selected,
1311
1312 // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
1313 getSetAttribute: div.className !== "t",
1314
1315 // Tests for enctype support on a form (#6743)
1316 enctype: !!document.createElement("form").enctype,
1317
1318 // Makes sure cloning an html5 element does not cause problems
1319 // Where outerHTML is undefined, this still works
1320 html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
1321
1322 // jQuery.support.boxModel DEPRECATED in 1.8 since we don't support Quirks Mode
1323 boxModel: ( document.compatMode === "CSS1Compat" ),
1324
1325 // Will be defined later
1326 submitBubbles: true,
1327 changeBubbles: true,
1328 focusinBubbles: false,
1329 deleteExpando: true,
1330 noCloneEvent: true,
1331 inlineBlockNeedsLayout: false,
1332 shrinkWrapBlocks: false,
1333 reliableMarginRight: true,
1334 boxSizingReliable: true,
1335 pixelPosition: false
1336 };
1337
1338 // Make sure checked status is properly cloned
1339 input.checked = true;
1340 support.noCloneChecked = input.cloneNode( true ).checked;
1341
1342 // Make sure that the options inside disabled selects aren't marked as disabled
1343 // (WebKit marks them as disabled)
1344 select.disabled = true;
1345 support.optDisabled = !opt.disabled;
1346
1347 // Test to see if it's possible to delete an expando from an element
1348 // Fails in Internet Explorer
1349 try {
1350 delete div.test;
1351 } catch( e ) {
1352 support.deleteExpando = false;
1353 }
1354
1355 if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
1356 div.attachEvent( "onclick", clickFn = function() {
1357 // Cloning a node shouldn't copy over any
1358 // bound event handlers (IE does this)
1359 support.noCloneEvent = false;
1360 });
1361 div.cloneNode( true ).fireEvent("onclick");
1362 div.detachEvent( "onclick", clickFn );
1363 }
1364
1365 // Check if a radio maintains its value
1366 // after being appended to the DOM
1367 input = document.createElement("input");
1368 input.value = "t";
1369 input.setAttribute( "type", "radio" );
1370 support.radioValue = input.value === "t";
1371
1372 input.setAttribute( "checked", "checked" );
1373
1374 // #11217 - WebKit loses check when the name is after the checked attribute
1375 input.setAttribute( "name", "t" );
1376
1377 div.appendChild( input );
1378 fragment = document.createDocumentFragment();
1379 fragment.appendChild( div.lastChild );
1380
1381 // WebKit doesn't clone checked state correctly in fragments
1382 support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
1383
1384 // Check if a disconnected checkbox will retain its checked
1385 // value of true after appended to the DOM (IE6/7)
1386 support.appendChecked = input.checked;
1387
1388 fragment.removeChild( input );
1389 fragment.appendChild( div );
1390
1391 // Technique from Juriy Zaytsev
1392 // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
1393 // We only care about the case where non-standard event systems
1394 // are used, namely in IE. Short-circuiting here helps us to
1395 // avoid an eval call (in setAttribute) which can cause CSP
1396 // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
1397 if ( div.attachEvent ) {
1398 for ( i in {
1399 submit: true,
1400 change: true,
1401 focusin: true
1402 }) {
1403 eventName = "on" + i;
1404 isSupported = ( eventName in div );
1405 if ( !isSupported ) {
1406 div.setAttribute( eventName, "return;" );
1407 isSupported = ( typeof div[ eventName ] === "function" );
1408 }
1409 support[ i + "Bubbles" ] = isSupported;
1410 }
1411 }
1412
1413 // Run tests that need a body at doc ready
1414 jQuery(function() {
1415 var container, div, tds, marginDiv,
1416 divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;",
1417 body = document.getElementsByTagName("body")[0];
1418
1419 if ( !body ) {
1420 // Return for frameset docs that don't have a body
1421 return;
1422 }
1423
1424 container = document.createElement("div");
1425 container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
1426 body.insertBefore( container, body.firstChild );
1427
1428 // Construct the test element
1429 div = document.createElement("div");
1430 container.appendChild( div );
1431
1432 // Check if table cells still have offsetWidth/Height when they are set
1433 // to display:none and there are still other visible table cells in a
1434 // table row; if so, offsetWidth/Height are not reliable for use when
1435 // determining if an element has been hidden directly using
1436 // display:none (it is still safe to use offsets if a parent element is
1437 // hidden; don safety goggles and see bug #4512 for more information).
1438 // (only IE 8 fails this test)
1439 div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
1440 tds = div.getElementsByTagName("td");
1441 tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
1442 isSupported = ( tds[ 0 ].offsetHeight === 0 );
1443
1444 tds[ 0 ].style.display = "";
1445 tds[ 1 ].style.display = "none";
1446
1447 // Check if empty table cells still have offsetWidth/Height
1448 // (IE <= 8 fail this test)
1449 support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
1450
1451 // Check box-sizing and margin behavior
1452 div.innerHTML = "";
1453 div.style.cssText = "box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;";
1454 support.boxSizing = ( div.offsetWidth === 4 );
1455 support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );
1456
1457 // NOTE: To any future maintainer, we've window.getComputedStyle
1458 // because jsdom on node.js will break without it.
1459 if ( window.getComputedStyle ) {
1460 support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
1461 support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";
1462
1463 // Check if div with explicit width and no margin-right incorrectly
1464 // gets computed margin-right based on width of container. For more
1465 // info see bug #3333
1466 // Fails in WebKit before Feb 2011 nightlies
1467 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
1468 marginDiv = document.createElement("div");
1469 marginDiv.style.cssText = div.style.cssText = divReset;
1470 marginDiv.style.marginRight = marginDiv.style.width = "0";
1471 div.style.width = "1px";
1472 div.appendChild( marginDiv );
1473 support.reliableMarginRight =
1474 !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
1475 }
1476
1477 if ( typeof div.style.zoom !== "undefined" ) {
1478 // Check if natively block-level elements act like inline-block
1479 // elements when setting their display to 'inline' and giving
1480 // them layout
1481 // (IE < 8 does this)
1482 div.innerHTML = "";
1483 div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
1484 support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
1485
1486 // Check if elements with layout shrink-wrap their children
1487 // (IE 6 does this)
1488 div.style.display = "block";
1489 div.style.overflow = "visible";
1490 div.innerHTML = "<div></div>";
1491 div.firstChild.style.width = "5px";
1492 support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
1493
1494 container.style.zoom = 1;
1495 }
1496
1497 // Null elements to avoid leaks in IE
1498 body.removeChild( container );
1499 container = div = tds = marginDiv = null;
1500 });
1501
1502 // Null elements to avoid leaks in IE
1503 fragment.removeChild( div );
1504 all = a = select = opt = input = fragment = div = null;
1505
1506 return support;
1507 })();
1508 var rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/,
1509 rmultiDash = /([A-Z])/g;
1510
1511 jQuery.extend({
1512 cache: {},
1513
1514 deletedIds: [],
1515
1516 // Remove at next major release (1.9/2.0)
1517 uuid: 0,
1518
1519 // Unique for each copy of jQuery on the page
1520 // Non-digits removed to match rinlinejQuery
1521 expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
1522
1523 // The following elements throw uncatchable exceptions if you
1524 // attempt to add expando properties to them.
1525 noData: {
1526 "embed": true,
1527 // Ban all objects except for Flash (which handle expandos)
1528 "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
1529 "applet": true
1530 },
1531
1532 hasData: function( elem ) {
1533 elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
1534 return !!elem && !isEmptyDataObject( elem );
1535 },
1536
1537 data: function( elem, name, data, pvt /* Internal Use Only */ ) {
1538 if ( !jQuery.acceptData( elem ) ) {
1539 return;
1540 }
1541
1542 var thisCache, ret,
1543 internalKey = jQuery.expando,
1544 getByName = typeof name === "string",
1545
1546 // We have to handle DOM nodes and JS objects differently because IE6-7
1547 // can't GC object references properly across the DOM-JS boundary
1548 isNode = elem.nodeType,
1549
1550 // Only DOM nodes need the global jQuery cache; JS object data is
1551 // attached directly to the object so GC can occur automatically
1552 cache = isNode ? jQuery.cache : elem,
1553
1554 // Only defining an ID for JS objects if its cache already exists allows
1555 // the code to shortcut on the same path as a DOM node with no cache
1556 id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey;
1557
1558 // Avoid doing any more work than we need to when trying to get data on an
1559 // object that has no data at all
1560 if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && getByName && data === undefined ) {
1561 return;
1562 }
1563
1564 if ( !id ) {
1565 // Only DOM nodes need a new unique ID for each element since their data
1566 // ends up in the global cache
1567 if ( isNode ) {
1568 elem[ internalKey ] = id = jQuery.deletedIds.pop() || jQuery.guid++;
1569 } else {
1570 id = internalKey;
1571 }
1572 }
1573
1574 if ( !cache[ id ] ) {
1575 cache[ id ] = {};
1576
1577 // Avoids exposing jQuery metadata on plain JS objects when the object
1578 // is serialized using JSON.stringify
1579 if ( !isNode ) {
1580 cache[ id ].toJSON = jQuery.noop;
1581 }
1582 }
1583
1584 // An object can be passed to jQuery.data instead of a key/value pair; this gets
1585 // shallow copied over onto the existing cache
1586 if ( typeof name === "object" || typeof name === "function" ) {
1587 if ( pvt ) {
1588 cache[ id ] = jQuery.extend( cache[ id ], name );
1589 } else {
1590 cache[ id ].data = jQuery.extend( cache[ id ].data, name );
1591 }
1592 }
1593
1594 thisCache = cache[ id ];
1595
1596 // jQuery data() is stored in a separate object inside the object's internal data
1597 // cache in order to avoid key collisions between internal data and user-defined
1598 // data.
1599 if ( !pvt ) {
1600 if ( !thisCache.data ) {
1601 thisCache.data = {};
1602 }
1603
1604 thisCache = thisCache.data;
1605 }
1606
1607 if ( data !== undefined ) {
1608 thisCache[ jQuery.camelCase( name ) ] = data;
1609 }
1610
1611 // Check for both converted-to-camel and non-converted data property names
1612 // If a data property was specified
1613 if ( getByName ) {
1614
1615 // First Try to find as-is property data
1616 ret = thisCache[ name ];
1617
1618 // Test for null|undefined property data
1619 if ( ret == null ) {
1620
1621 // Try to find the camelCased property
1622 ret = thisCache[ jQuery.camelCase( name ) ];
1623 }
1624 } else {
1625 ret = thisCache;
1626 }
1627
1628 return ret;
1629 },
1630
1631 removeData: function( elem, name, pvt /* Internal Use Only */ ) {
1632 if ( !jQuery.acceptData( elem ) ) {
1633 return;
1634 }
1635
1636 var thisCache, i, l,
1637
1638 isNode = elem.nodeType,
1639
1640 // See jQuery.data for more information
1641 cache = isNode ? jQuery.cache : elem,
1642 id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
1643
1644 // If there is already no cache entry for this object, there is no
1645 // purpose in continuing
1646 if ( !cache[ id ] ) {
1647 return;
1648 }
1649
1650 if ( name ) {
1651
1652 thisCache = pvt ? cache[ id ] : cache[ id ].data;
1653
1654 if ( thisCache ) {
1655
1656 // Support array or space separated string names for data keys
1657 if ( !jQuery.isArray( name ) ) {
1658
1659 // try the string as a key before any manipulation
1660 if ( name in thisCache ) {
1661 name = [ name ];
1662 } else {
1663
1664 // split the camel cased version by spaces unless a key with the spaces exists
1665 name = jQuery.camelCase( name );
1666 if ( name in thisCache ) {
1667 name = [ name ];
1668 } else {
1669 name = name.split(" ");
1670 }
1671 }
1672 }
1673
1674 for ( i = 0, l = name.length; i < l; i++ ) {
1675 delete thisCache[ name[i] ];
1676 }
1677
1678 // If there is no data left in the cache, we want to continue
1679 // and let the cache object itself get destroyed
1680 if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
1681 return;
1682 }
1683 }
1684 }
1685
1686 // See jQuery.data for more information
1687 if ( !pvt ) {
1688 delete cache[ id ].data;
1689
1690 // Don't destroy the parent cache unless the internal data object
1691 // had been the only thing left in it
1692 if ( !isEmptyDataObject( cache[ id ] ) ) {
1693 return;
1694 }
1695 }
1696
1697 // Destroy the cache
1698 if ( isNode ) {
1699 jQuery.cleanData( [ elem ], true );
1700
1701 // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080)
1702 } else if ( jQuery.support.deleteExpando || cache != cache.window ) {
1703 delete cache[ id ];
1704
1705 // When all else fails, null
1706 } else {
1707 cache[ id ] = null;
1708 }
1709 },
1710
1711 // For internal use only.
1712 _data: function( elem, name, data ) {
1713 return jQuery.data( elem, name, data, true );
1714 },
1715
1716 // A method for determining if a DOM node can handle the data expando
1717 acceptData: function( elem ) {
1718 var noData = elem.nodeName && jQuery.noData[ elem.nodeName.toLowerCase() ];
1719
1720 // nodes accept data unless otherwise specified; rejection can be conditional
1721 return !noData || noData !== true && elem.getAttribute("classid") === noData;
1722 }
1723 });
1724
1725 jQuery.fn.extend({
1726 data: function( key, value ) {
1727 var parts, part, attr, name, l,
1728 elem = this[0],
1729 i = 0,
1730 data = null;
1731
1732 // Gets all values
1733 if ( key === undefined ) {
1734 if ( this.length ) {
1735 data = jQuery.data( elem );
1736
1737 if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
1738 attr = elem.attributes;
1739 for ( l = attr.length; i < l; i++ ) {
1740 name = attr[i].name;
1741
1742 if ( !name.indexOf( "data-" ) ) {
1743 name = jQuery.camelCase( name.substring(5) );
1744
1745 dataAttr( elem, name, data[ name ] );
1746 }
1747 }
1748 jQuery._data( elem, "parsedAttrs", true );
1749 }
1750 }
1751
1752 return data;
1753 }
1754
1755 // Sets multiple values
1756 if ( typeof key === "object" ) {
1757 return this.each(function() {
1758 jQuery.data( this, key );
1759 });
1760 }
1761
1762 parts = key.split( ".", 2 );
1763 parts[1] = parts[1] ? "." + parts[1] : "";
1764 part = parts[1] + "!";
1765
1766 return jQuery.access( this, function( value ) {
1767
1768 if ( value === undefined ) {
1769 data = this.triggerHandler( "getData" + part, [ parts[0] ] );
1770
1771 // Try to fetch any internally stored data first
1772 if ( data === undefined && elem ) {
1773 data = jQuery.data( elem, key );
1774 data = dataAttr( elem, key, data );
1775 }
1776
1777 return data === undefined && parts[1] ?
1778 this.data( parts[0] ) :
1779 data;
1780 }
1781
1782 parts[1] = value;
1783 this.each(function() {
1784 var self = jQuery( this );
1785
1786 self.triggerHandler( "setData" + part, parts );
1787 jQuery.data( this, key, value );
1788 self.triggerHandler( "changeData" + part, parts );
1789 });
1790 }, null, value, arguments.length > 1, null, false );
1791 },
1792
1793 removeData: function( key ) {
1794 return this.each(function() {
1795 jQuery.removeData( this, key );
1796 });
1797 }
1798 });
1799
1800 function dataAttr( elem, key, data ) {
1801 // If nothing was found internally, try to fetch any
1802 // data from the HTML5 data-* attribute
1803 if ( data === undefined && elem.nodeType === 1 ) {
1804
1805 var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
1806
1807 data = elem.getAttribute( name );
1808
1809 if ( typeof data === "string" ) {
1810 try {
1811 data = data === "true" ? true :
1812 data === "false" ? false :
1813 data === "null" ? null :
1814 // Only convert to a number if it doesn't change the string
1815 +data + "" === data ? +data :
1816 rbrace.test( data ) ? jQuery.parseJSON( data ) :
1817 data;
1818 } catch( e ) {}
1819
1820 // Make sure we set the data so it isn't changed later
1821 jQuery.data( elem, key, data );
1822
1823 } else {
1824 data = undefined;
1825 }
1826 }
1827
1828 return data;
1829 }
1830
1831 // checks a cache object for emptiness
1832 function isEmptyDataObject( obj ) {
1833 var name;
1834 for ( name in obj ) {
1835
1836 // if the public data object is empty, the private is still empty
1837 if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
1838 continue;
1839 }
1840 if ( name !== "toJSON" ) {
1841 return false;
1842 }
1843 }
1844
1845 return true;
1846 }
1847 jQuery.extend({
1848 queue: function( elem, type, data ) {
1849 var queue;
1850
1851 if ( elem ) {
1852 type = ( type || "fx" ) + "queue";
1853 queue = jQuery._data( elem, type );
1854
1855 // Speed up dequeue by getting out quickly if this is just a lookup
1856 if ( data ) {
1857 if ( !queue || jQuery.isArray(data) ) {
1858 queue = jQuery._data( elem, type, jQuery.makeArray(data) );
1859 } else {
1860 queue.push( data );
1861 }
1862 }
1863 return queue || [];
1864 }
1865 },
1866
1867 dequeue: function( elem, type ) {
1868 type = type || "fx";
1869
1870 var queue = jQuery.queue( elem, type ),
1871 startLength = queue.length,
1872 fn = queue.shift(),
1873 hooks = jQuery._queueHooks( elem, type ),
1874 next = function() {
1875 jQuery.dequeue( elem, type );
1876 };
1877
1878 // If the fx queue is dequeued, always remove the progress sentinel
1879 if ( fn === "inprogress" ) {
1880 fn = queue.shift();
1881 startLength--;
1882 }
1883
1884 if ( fn ) {
1885
1886 // Add a progress sentinel to prevent the fx queue from being
1887 // automatically dequeued
1888 if ( type === "fx" ) {
1889 queue.unshift( "inprogress" );
1890 }
1891
1892 // clear up the last queue stop function
1893 delete hooks.stop;
1894 fn.call( elem, next, hooks );
1895 }
1896
1897 if ( !startLength && hooks ) {
1898 hooks.empty.fire();
1899 }
1900 },
1901
1902 // not intended for public consumption - generates a queueHooks object, or returns the current one
1903 _queueHooks: function( elem, type ) {
1904 var key = type + "queueHooks";
1905 return jQuery._data( elem, key ) || jQuery._data( elem, key, {
1906 empty: jQuery.Callbacks("once memory").add(function() {
1907 jQuery.removeData( elem, type + "queue", true );
1908 jQuery.removeData( elem, key, true );
1909 })
1910 });
1911 }
1912 });
1913
1914 jQuery.fn.extend({
1915 queue: function( type, data ) {
1916 var setter = 2;
1917
1918 if ( typeof type !== "string" ) {
1919 data = type;
1920 type = "fx";
1921 setter--;
1922 }
1923
1924 if ( arguments.length < setter ) {
1925 return jQuery.queue( this[0], type );
1926 }
1927
1928 return data === undefined ?
1929 this :
1930 this.each(function() {
1931 var queue = jQuery.queue( this, type, data );
1932
1933 // ensure a hooks for this queue
1934 jQuery._queueHooks( this, type );
1935
1936 if ( type === "fx" && queue[0] !== "inprogress" ) {
1937 jQuery.dequeue( this, type );
1938 }
1939 });
1940 },
1941 dequeue: function( type ) {
1942 return this.each(function() {
1943 jQuery.dequeue( this, type );
1944 });
1945 },
1946 // Based off of the plugin by Clint Helfers, with permission.
1947 // http://blindsignals.com/index.php/2009/07/jquery-delay/
1948 delay: function( time, type ) {
1949 time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
1950 type = type || "fx";
1951
1952 return this.queue( type, function( next, hooks ) {
1953 var timeout = setTimeout( next, time );
1954 hooks.stop = function() {
1955 clearTimeout( timeout );
1956 };
1957 });
1958 },
1959 clearQueue: function( type ) {
1960 return this.queue( type || "fx", [] );
1961 },
1962 // Get a promise resolved when queues of a certain type
1963 // are emptied (fx is the type by default)
1964 promise: function( type, obj ) {
1965 var tmp,
1966 count = 1,
1967 defer = jQuery.Deferred(),
1968 elements = this,
1969 i = this.length,
1970 resolve = function() {
1971 if ( !( --count ) ) {
1972 defer.resolveWith( elements, [ elements ] );
1973 }
1974 };
1975
1976 if ( typeof type !== "string" ) {
1977 obj = type;
1978 type = undefined;
1979 }
1980 type = type || "fx";
1981
1982 while( i-- ) {
1983 tmp = jQuery._data( elements[ i ], type + "queueHooks" );
1984 if ( tmp && tmp.empty ) {
1985 count++;
1986 tmp.empty.add( resolve );
1987 }
1988 }
1989 resolve();
1990 return defer.promise( obj );
1991 }
1992 });
1993 var nodeHook, boolHook, fixSpecified,
1994 rclass = /[\t\r\n]/g,
1995 rreturn = /\r/g,
1996 rtype = /^(?:button|input)$/i,
1997 rfocusable = /^(?:button|input|object|select|textarea)$/i,
1998 rclickable = /^a(?:rea|)$/i,
1999 rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
2000 getSetAttribute = jQuery.support.getSetAttribute;
2001
2002 jQuery.fn.extend({
2003 attr: function( name, value ) {
2004 return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
2005 },
2006
2007 removeAttr: function( name ) {
2008 return this.each(function() {
2009 jQuery.removeAttr( this, name );
2010 });
2011 },
2012
2013 prop: function( name, value ) {
2014 return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
2015 },
2016
2017 removeProp: function( name ) {
2018 name = jQuery.propFix[ name ] || name;
2019 return this.each(function() {
2020 // try/catch handles cases where IE balks (such as removing a property on window)
2021 try {
2022 this[ name ] = undefined;
2023 delete this[ name ];
2024 } catch( e ) {}
2025 });
2026 },
2027
2028 addClass: function( value ) {
2029 var classNames, i, l, elem,
2030 setClass, c, cl;
2031
2032 if ( jQuery.isFunction( value ) ) {
2033 return this.each(function( j ) {
2034 jQuery( this ).addClass( value.call(this, j, this.className) );
2035 });
2036 }
2037
2038 if ( value && typeof value === "string" ) {
2039 classNames = value.split( core_rspace );
2040
2041 for ( i = 0, l = this.length; i < l; i++ ) {
2042 elem = this[ i ];
2043
2044 if ( elem.nodeType === 1 ) {
2045 if ( !elem.className && classNames.length === 1 ) {
2046 elem.className = value;
2047
2048 } else {
2049 setClass = " " + elem.className + " ";
2050
2051 for ( c = 0, cl = classNames.length; c < cl; c++ ) {
2052 if ( setClass.indexOf( " " + classNames[ c ] + " " ) < 0 ) {
2053 setClass += classNames[ c ] + " ";
2054 }
2055 }
2056 elem.className = jQuery.trim( setClass );
2057 }
2058 }
2059 }
2060 }
2061
2062 return this;
2063 },
2064
2065 removeClass: function( value ) {
2066 var removes, className, elem, c, cl, i, l;
2067
2068 if ( jQuery.isFunction( value ) ) {
2069 return this.each(function( j ) {
2070 jQuery( this ).removeClass( value.call(this, j, this.className) );
2071 });
2072 }
2073 if ( (value && typeof value === "string") || value === undefined ) {
2074 removes = ( value || "" ).split( core_rspace );
2075
2076 for ( i = 0, l = this.length; i < l; i++ ) {
2077 elem = this[ i ];
2078 if ( elem.nodeType === 1 && elem.className ) {
2079
2080 className = (" " + elem.className + " ").replace( rclass, " " );
2081
2082 // loop over each item in the removal list
2083 for ( c = 0, cl = removes.length; c < cl; c++ ) {
2084 // Remove until there is nothing to remove,
2085 while ( className.indexOf(" " + removes[ c ] + " ") >= 0 ) {
2086 className = className.replace( " " + removes[ c ] + " " , " " );
2087 }
2088 }
2089 elem.className = value ? jQuery.trim( className ) : "";
2090 }
2091 }
2092 }
2093
2094 return this;
2095 },
2096
2097 toggleClass: function( value, stateVal ) {
2098 var type = typeof value,
2099 isBool = typeof stateVal === "boolean";
2100
2101 if ( jQuery.isFunction( value ) ) {
2102 return this.each(function( i ) {
2103 jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
2104 });
2105 }
2106
2107 return this.each(function() {
2108 if ( type === "string" ) {
2109 // toggle individual class names
2110 var className,
2111 i = 0,
2112 self = jQuery( this ),
2113 state = stateVal,
2114 classNames = value.split( core_rspace );
2115
2116 while ( (className = classNames[ i++ ]) ) {
2117 // check each className given, space separated list
2118 state = isBool ? state : !self.hasClass( className );
2119 self[ state ? "addClass" : "removeClass" ]( className );
2120 }
2121
2122 } else if ( type === "undefined" || type === "boolean" ) {
2123 if ( this.className ) {
2124 // store className if set
2125 jQuery._data( this, "__className__", this.className );
2126 }
2127
2128 // toggle whole className
2129 this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
2130 }
2131 });
2132 },
2133
2134 hasClass: function( selector ) {
2135 var className = " " + selector + " ",
2136 i = 0,
2137 l = this.length;
2138 for ( ; i < l; i++ ) {
2139 if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) {
2140 return true;
2141 }
2142 }
2143
2144 return false;
2145 },
2146
2147 val: function( value ) {
2148 var hooks, ret, isFunction,
2149 elem = this[0];
2150
2151 if ( !arguments.length ) {
2152 if ( elem ) {
2153 hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
2154
2155 if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
2156 return ret;
2157 }
2158
2159 ret = elem.value;
2160
2161 return typeof ret === "string" ?
2162 // handle most common string cases
2163 ret.replace(rreturn, "") :
2164 // handle cases where value is null/undef or number
2165 ret == null ? "" : ret;
2166 }
2167
2168 return;
2169 }
2170
2171 isFunction = jQuery.isFunction( value );
2172
2173 return this.each(function( i ) {
2174 var val,
2175 self = jQuery(this);
2176
2177 if ( this.nodeType !== 1 ) {
2178 return;
2179 }
2180
2181 if ( isFunction ) {
2182 val = value.call( this, i, self.val() );
2183 } else {
2184 val = value;
2185 }
2186
2187 // Treat null/undefined as ""; convert numbers to string
2188 if ( val == null ) {
2189 val = "";
2190 } else if ( typeof val === "number" ) {
2191 val += "";
2192 } else if ( jQuery.isArray( val ) ) {
2193 val = jQuery.map(val, function ( value ) {
2194 return value == null ? "" : value + "";
2195 });
2196 }
2197
2198 hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
2199
2200 // If set returns undefined, fall back to normal setting
2201 if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
2202 this.value = val;
2203 }
2204 });
2205 }
2206 });
2207
2208 jQuery.extend({
2209 valHooks: {
2210 option: {
2211 get: function( elem ) {
2212 // attributes.value is undefined in Blackberry 4.7 but
2213 // uses .value. See #6932
2214 var val = elem.attributes.value;
2215 return !val || val.specified ? elem.value : elem.text;
2216 }
2217 },
2218 select: {
2219 get: function( elem ) {
2220 var value, option,
2221 options = elem.options,
2222 index = elem.selectedIndex,
2223 one = elem.type === "select-one" || index < 0,
2224 values = one ? null : [],
2225 max = one ? index + 1 : options.length,
2226 i = index < 0 ?
2227 max :
2228 one ? index : 0;
2229
2230 // Loop through all the selected options
2231 for ( ; i < max; i++ ) {
2232 option = options[ i ];
2233
2234 // oldIE doesn't update selected after form reset (#2551)
2235 if ( ( option.selected || i === index ) &&
2236 // Don't return options that are disabled or in a disabled optgroup
2237 ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) &&
2238 ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) {
2239
2240 // Get the specific value for the option
2241 value = jQuery( option ).val();
2242
2243 // We don't need an array for one selects
2244 if ( one ) {
2245 return value;
2246 }
2247
2248 // Multi-Selects return an array
2249 values.push( value );
2250 }
2251 }
2252
2253 return values;
2254 },
2255
2256 set: function( elem, value ) {
2257 var values = jQuery.makeArray( value );
2258
2259 jQuery(elem).find("option").each(function() {
2260 this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
2261 });
2262
2263 if ( !values.length ) {
2264 elem.selectedIndex = -1;
2265 }
2266 return values;
2267 }
2268 }
2269 },
2270
2271 // Unused in 1.8, left in so attrFn-stabbers won't die; remove in 1.9
2272 attrFn: {},
2273
2274 attr: function( elem, name, value, pass ) {
2275 var ret, hooks, notxml,
2276 nType = elem.nodeType;
2277
2278 // don't get/set attributes on text, comment and attribute nodes
2279 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2280 return;
2281 }
2282
2283 if ( pass && jQuery.isFunction( jQuery.fn[ name ] ) ) {
2284 return jQuery( elem )[ name ]( value );
2285 }
2286
2287 // Fallback to prop when attributes are not supported
2288 if ( typeof elem.getAttribute === "undefined" ) {
2289 return jQuery.prop( elem, name, value );
2290 }
2291
2292 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2293
2294 // All attributes are lowercase
2295 // Grab necessary hook if one is defined
2296 if ( notxml ) {
2297 name = name.toLowerCase();
2298 hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
2299 }
2300
2301 if ( value !== undefined ) {
2302
2303 if ( value === null ) {
2304 jQuery.removeAttr( elem, name );
2305 return;
2306
2307 } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
2308 return ret;
2309
2310 } else {
2311 elem.setAttribute( name, value + "" );
2312 return value;
2313 }
2314
2315 } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
2316 return ret;
2317
2318 } else {
2319
2320 ret = elem.getAttribute( name );
2321
2322 // Non-existent attributes return null, we normalize to undefined
2323 return ret === null ?
2324 undefined :
2325 ret;
2326 }
2327 },
2328
2329 removeAttr: function( elem, value ) {
2330 var propName, attrNames, name, isBool,
2331 i = 0;
2332
2333 if ( value && elem.nodeType === 1 ) {
2334
2335 attrNames = value.split( core_rspace );
2336
2337 for ( ; i < attrNames.length; i++ ) {
2338 name = attrNames[ i ];
2339
2340 if ( name ) {
2341 propName = jQuery.propFix[ name ] || name;
2342 isBool = rboolean.test( name );
2343
2344 // See #9699 for explanation of this approach (setting first, then removal)
2345 // Do not do this for boolean attributes (see #10870)
2346 if ( !isBool ) {
2347 jQuery.attr( elem, name, "" );
2348 }
2349 elem.removeAttribute( getSetAttribute ? name : propName );
2350
2351 // Set corresponding property to false for boolean attributes
2352 if ( isBool && propName in elem ) {
2353 elem[ propName ] = false;
2354 }
2355 }
2356 }
2357 }
2358 },
2359
2360 attrHooks: {
2361 type: {
2362 set: function( elem, value ) {
2363 // We can't allow the type property to be changed (since it causes problems in IE)
2364 if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
2365 jQuery.error( "type property can't be changed" );
2366 } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
2367 // Setting the type on a radio button after the value resets the value in IE6-9
2368 // Reset value to it's default in case type is set after value
2369 // This is for element creation
2370 var val = elem.value;
2371 elem.setAttribute( "type", value );
2372 if ( val ) {
2373 elem.value = val;
2374 }
2375 return value;
2376 }
2377 }
2378 },
2379 // Use the value property for back compat
2380 // Use the nodeHook for button elements in IE6/7 (#1954)
2381 value: {
2382 get: function( elem, name ) {
2383 if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
2384 return nodeHook.get( elem, name );
2385 }
2386 return name in elem ?
2387 elem.value :
2388 null;
2389 },
2390 set: function( elem, value, name ) {
2391 if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
2392 return nodeHook.set( elem, value, name );
2393 }
2394 // Does not return so that setAttribute is also used
2395 elem.value = value;
2396 }
2397 }
2398 },
2399
2400 propFix: {
2401 tabindex: "tabIndex",
2402 readonly: "readOnly",
2403 "for": "htmlFor",
2404 "class": "className",
2405 maxlength: "maxLength",
2406 cellspacing: "cellSpacing",
2407 cellpadding: "cellPadding",
2408 rowspan: "rowSpan",
2409 colspan: "colSpan",
2410 usemap: "useMap",
2411 frameborder: "frameBorder",
2412 contenteditable: "contentEditable"
2413 },
2414
2415 prop: function( elem, name, value ) {
2416 var ret, hooks, notxml,
2417 nType = elem.nodeType;
2418
2419 // don't get/set properties on text, comment and attribute nodes
2420 if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
2421 return;
2422 }
2423
2424 notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
2425
2426 if ( notxml ) {
2427 // Fix name and attach hooks
2428 name = jQuery.propFix[ name ] || name;
2429 hooks = jQuery.propHooks[ name ];
2430 }
2431
2432 if ( value !== undefined ) {
2433 if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
2434 return ret;
2435
2436 } else {
2437 return ( elem[ name ] = value );
2438 }
2439
2440 } else {
2441 if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
2442 return ret;
2443
2444 } else {
2445 return elem[ name ];
2446 }
2447 }
2448 },
2449
2450 propHooks: {
2451 tabIndex: {
2452 get: function( elem ) {
2453 // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
2454 // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
2455 var attributeNode = elem.getAttributeNode("tabindex");
2456
2457 return attributeNode && attributeNode.specified ?
2458 parseInt( attributeNode.value, 10 ) :
2459 rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
2460 0 :
2461 undefined;
2462 }
2463 }
2464 }
2465 });
2466
2467 // Hook for boolean attributes
2468 boolHook = {
2469 get: function( elem, name ) {
2470 // Align boolean attributes with corresponding properties
2471 // Fall back to attribute presence where some booleans are not supported
2472 var attrNode,
2473 property = jQuery.prop( elem, name );
2474 return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
2475 name.toLowerCase() :
2476 undefined;
2477 },
2478 set: function( elem, value, name ) {
2479 var propName;
2480 if ( value === false ) {
2481 // Remove boolean attributes when set to false
2482 jQuery.removeAttr( elem, name );
2483 } else {
2484 // value is true since we know at this point it's type boolean and not false
2485 // Set boolean attributes to the same name and set the DOM property
2486 propName = jQuery.propFix[ name ] || name;
2487 if ( propName in elem ) {
2488 // Only set the IDL specifically if it already exists on the element
2489 elem[ propName ] = true;
2490 }
2491
2492 elem.setAttribute( name, name.toLowerCase() );
2493 }
2494 return name;
2495 }
2496 };
2497
2498 // IE6/7 do not support getting/setting some attributes with get/setAttribute
2499 if ( !getSetAttribute ) {
2500
2501 fixSpecified = {
2502 name: true,
2503 id: true,
2504 coords: true
2505 };
2506
2507 // Use this for any attribute in IE6/7
2508 // This fixes almost every IE6/7 issue
2509 nodeHook = jQuery.valHooks.button = {
2510 get: function( elem, name ) {
2511 var ret;
2512 ret = elem.getAttributeNode( name );
2513 return ret && ( fixSpecified[ name ] ? ret.value !== "" : ret.specified ) ?
2514 ret.value :
2515 undefined;
2516 },
2517 set: function( elem, value, name ) {
2518 // Set the existing or create a new attribute node
2519 var ret = elem.getAttributeNode( name );
2520 if ( !ret ) {
2521 ret = document.createAttribute( name );
2522 elem.setAttributeNode( ret );
2523 }
2524 return ( ret.value = value + "" );
2525 }
2526 };
2527
2528 // Set width and height to auto instead of 0 on empty string( Bug #8150 )
2529 // This is for removals
2530 jQuery.each([ "width", "height" ], function( i, name ) {
2531 jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2532 set: function( elem, value ) {
2533 if ( value === "" ) {
2534 elem.setAttribute( name, "auto" );
2535 return value;
2536 }
2537 }
2538 });
2539 });
2540
2541 // Set contenteditable to false on removals(#10429)
2542 // Setting to empty string throws an error as an invalid value
2543 jQuery.attrHooks.contenteditable = {
2544 get: nodeHook.get,
2545 set: function( elem, value, name ) {
2546 if ( value === "" ) {
2547 value = "false";
2548 }
2549 nodeHook.set( elem, value, name );
2550 }
2551 };
2552 }
2553
2554
2555 // Some attributes require a special call on IE
2556 if ( !jQuery.support.hrefNormalized ) {
2557 jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
2558 jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
2559 get: function( elem ) {
2560 var ret = elem.getAttribute( name, 2 );
2561 return ret === null ? undefined : ret;
2562 }
2563 });
2564 });
2565 }
2566
2567 if ( !jQuery.support.style ) {
2568 jQuery.attrHooks.style = {
2569 get: function( elem ) {
2570 // Return undefined in the case of empty string
2571 // Normalize to lowercase since IE uppercases css property names
2572 return elem.style.cssText.toLowerCase() || undefined;
2573 },
2574 set: function( elem, value ) {
2575 return ( elem.style.cssText = value + "" );
2576 }
2577 };
2578 }
2579
2580 // Safari mis-reports the default selected property of an option
2581 // Accessing the parent's selectedIndex property fixes it
2582 if ( !jQuery.support.optSelected ) {
2583 jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
2584 get: function( elem ) {
2585 var parent = elem.parentNode;
2586
2587 if ( parent ) {
2588 parent.selectedIndex;
2589
2590 // Make sure that it also works with optgroups, see #5701
2591 if ( parent.parentNode ) {
2592 parent.parentNode.selectedIndex;
2593 }
2594 }
2595 return null;
2596 }
2597 });
2598 }
2599
2600 // IE6/7 call enctype encoding
2601 if ( !jQuery.support.enctype ) {
2602 jQuery.propFix.enctype = "encoding";
2603 }
2604
2605 // Radios and checkboxes getter/setter
2606 if ( !jQuery.support.checkOn ) {
2607 jQuery.each([ "radio", "checkbox" ], function() {
2608 jQuery.valHooks[ this ] = {
2609 get: function( elem ) {
2610 // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
2611 return elem.getAttribute("value") === null ? "on" : elem.value;
2612 }
2613 };
2614 });
2615 }
2616 jQuery.each([ "radio", "checkbox" ], function() {
2617 jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
2618 set: function( elem, value ) {
2619 if ( jQuery.isArray( value ) ) {
2620 return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
2621 }
2622 }
2623 });
2624 });
2625 var rformElems = /^(?:textarea|input|select)$/i,
2626 rtypenamespace = /^([^\.]*|)(?:\.(.+)|)$/,
2627 rhoverHack = /(?:^|\s)hover(\.\S+|)\b/,
2628 rkeyEvent = /^key/,
2629 rmouseEvent = /^(?:mouse|contextmenu)|click/,
2630 rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
2631 hoverHack = function( events ) {
2632 return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
2633 };
2634
2635 /*
2636 * Helper functions for managing events -- not part of the public interface.
2637 * Props to Dean Edwards' addEvent library for many of the ideas.
2638 */
2639 jQuery.event = {
2640
2641 add: function( elem, types, handler, data, selector ) {
2642
2643 var elemData, eventHandle, events,
2644 t, tns, type, namespaces, handleObj,
2645 handleObjIn, handlers, special;
2646
2647 // Don't attach events to noData or text/comment nodes (allow plain objects tho)
2648 if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
2649 return;
2650 }
2651
2652 // Caller can pass in an object of custom data in lieu of the handler
2653 if ( handler.handler ) {
2654 handleObjIn = handler;
2655 handler = handleObjIn.handler;
2656 selector = handleObjIn.selector;
2657 }
2658
2659 // Make sure that the handler has a unique ID, used to find/remove it later
2660 if ( !handler.guid ) {
2661 handler.guid = jQuery.guid++;
2662 }
2663
2664 // Init the element's event structure and main handler, if this is the first
2665 events = elemData.events;
2666 if ( !events ) {
2667 elemData.events = events = {};
2668 }
2669 eventHandle = elemData.handle;
2670 if ( !eventHandle ) {
2671 elemData.handle = eventHandle = function( e ) {
2672 // Discard the second event of a jQuery.event.trigger() and
2673 // when an event is called after a page has unloaded
2674 return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
2675 jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
2676 undefined;
2677 };
2678 // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
2679 eventHandle.elem = elem;
2680 }
2681
2682 // Handle multiple events separated by a space
2683 // jQuery(...).bind("mouseover mouseout", fn);
2684 types = jQuery.trim( hoverHack(types) ).split( " " );
2685 for ( t = 0; t < types.length; t++ ) {
2686
2687 tns = rtypenamespace.exec( types[t] ) || [];
2688 type = tns[1];
2689 namespaces = ( tns[2] || "" ).split( "." ).sort();
2690
2691 // If event changes its type, use the special event handlers for the changed type
2692 special = jQuery.event.special[ type ] || {};
2693
2694 // If selector defined, determine special event api type, otherwise given type
2695 type = ( selector ? special.delegateType : special.bindType ) || type;
2696
2697 // Update special based on newly reset type
2698 special = jQuery.event.special[ type ] || {};
2699
2700 // handleObj is passed to all event handlers
2701 handleObj = jQuery.extend({
2702 type: type,
2703 origType: tns[1],
2704 data: data,
2705 handler: handler,
2706 guid: handler.guid,
2707 selector: selector,
2708 needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
2709 namespace: namespaces.join(".")
2710 }, handleObjIn );
2711
2712 // Init the event handler queue if we're the first
2713 handlers = events[ type ];
2714 if ( !handlers ) {
2715 handlers = events[ type ] = [];
2716 handlers.delegateCount = 0;
2717
2718 // Only use addEventListener/attachEvent if the special events handler returns false
2719 if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
2720 // Bind the global event handler to the element
2721 if ( elem.addEventListener ) {
2722 elem.addEventListener( type, eventHandle, false );
2723
2724 } else if ( elem.attachEvent ) {
2725 elem.attachEvent( "on" + type, eventHandle );
2726 }
2727 }
2728 }
2729
2730 if ( special.add ) {
2731 special.add.call( elem, handleObj );
2732
2733 if ( !handleObj.handler.guid ) {
2734 handleObj.handler.guid = handler.guid;
2735 }
2736 }
2737
2738 // Add to the element's handler list, delegates in front
2739 if ( selector ) {
2740 handlers.splice( handlers.delegateCount++, 0, handleObj );
2741 } else {
2742 handlers.push( handleObj );
2743 }
2744
2745 // Keep track of which events have ever been used, for event optimization
2746 jQuery.event.global[ type ] = true;
2747 }
2748
2749 // Nullify elem to prevent memory leaks in IE
2750 elem = null;
2751 },
2752
2753 global: {},
2754
2755 // Detach an event or set of events from an element
2756 remove: function( elem, types, handler, selector, mappedTypes ) {
2757
2758 var t, tns, type, origType, namespaces, origCount,
2759 j, events, special, eventType, handleObj,
2760 elemData = jQuery.hasData( elem ) && jQuery._data( elem );
2761
2762 if ( !elemData || !(events = elemData.events) ) {
2763 return;
2764 }
2765
2766 // Once for each type.namespace in types; type may be omitted
2767 types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
2768 for ( t = 0; t < types.length; t++ ) {
2769 tns = rtypenamespace.exec( types[t] ) || [];
2770 type = origType = tns[1];
2771 namespaces = tns[2];
2772
2773 // Unbind all events (on this namespace, if provided) for the element
2774 if ( !type ) {
2775 for ( type in events ) {
2776 jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
2777 }
2778 continue;
2779 }
2780
2781 special = jQuery.event.special[ type ] || {};
2782 type = ( selector? special.delegateType : special.bindType ) || type;
2783 eventType = events[ type ] || [];
2784 origCount = eventType.length;
2785 namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
2786
2787 // Remove matching events
2788 for ( j = 0; j < eventType.length; j++ ) {
2789 handleObj = eventType[ j ];
2790
2791 if ( ( mappedTypes || origType === handleObj.origType ) &&
2792 ( !handler || handler.guid === handleObj.guid ) &&
2793 ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
2794 ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
2795 eventType.splice( j--, 1 );
2796
2797 if ( handleObj.selector ) {
2798 eventType.delegateCount--;
2799 }
2800 if ( special.remove ) {
2801 special.remove.call( elem, handleObj );
2802 }
2803 }
2804 }
2805
2806 // Remove generic event handler if we removed something and no more handlers exist
2807 // (avoids potential for endless recursion during removal of special event handlers)
2808 if ( eventType.length === 0 && origCount !== eventType.length ) {
2809 if ( !special.teardown || special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
2810 jQuery.removeEvent( elem, type, elemData.handle );
2811 }
2812
2813 delete events[ type ];
2814 }
2815 }
2816
2817 // Remove the expando if it's no longer used
2818 if ( jQuery.isEmptyObject( events ) ) {
2819 delete elemData.handle;
2820
2821 // removeData also checks for emptiness and clears the expando if empty
2822 // so use it instead of delete
2823 jQuery.removeData( elem, "events", true );
2824 }
2825 },
2826
2827 // Events that are safe to short-circuit if no handlers are attached.
2828 // Native DOM events should not be added, they may have inline handlers.
2829 customEvent: {
2830 "getData": true,
2831 "setData": true,
2832 "changeData": true
2833 },
2834
2835 trigger: function( event, data, elem, onlyHandlers ) {
2836 // Don't do events on text and comment nodes
2837 if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
2838 return;
2839 }
2840
2841 // Event object or event type
2842 var cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType,
2843 type = event.type || event,
2844 namespaces = [];
2845
2846 // focus/blur morphs to focusin/out; ensure we're not firing them right now
2847 if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
2848 return;
2849 }
2850
2851 if ( type.indexOf( "!" ) >= 0 ) {
2852 // Exclusive events trigger only for the exact event (no namespaces)
2853 type = type.slice(0, -1);
2854 exclusive = true;
2855 }
2856
2857 if ( type.indexOf( "." ) >= 0 ) {
2858 // Namespaced trigger; create a regexp to match event type in handle()
2859 namespaces = type.split(".");
2860 type = namespaces.shift();
2861 namespaces.sort();
2862 }
2863
2864 if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
2865 // No jQuery handlers for this event type, and it can't have inline handlers
2866 return;
2867 }
2868
2869 // Caller can pass in an Event, Object, or just an event type string
2870 event = typeof event === "object" ?
2871 // jQuery.Event object
2872 event[ jQuery.expando ] ? event :
2873 // Object literal
2874 new jQuery.Event( type, event ) :
2875 // Just the event type (string)
2876 new jQuery.Event( type );
2877
2878 event.type = type;
2879 event.isTrigger = true;
2880 event.exclusive = exclusive;
2881 event.namespace = namespaces.join( "." );
2882 event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.|)") + "(\\.|$)") : null;
2883 ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
2884
2885 // Handle a global trigger
2886 if ( !elem ) {
2887
2888 // TODO: Stop taunting the data cache; remove global events and always attach to document
2889 cache = jQuery.cache;
2890 for ( i in cache ) {
2891 if ( cache[ i ].events && cache[ i ].events[ type ] ) {
2892 jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
2893 }
2894 }
2895 return;
2896 }
2897
2898 // Clean up the event in case it is being reused
2899 event.result = undefined;
2900 if ( !event.target ) {
2901 event.target = elem;
2902 }
2903
2904 // Clone any incoming data and prepend the event, creating the handler arg list
2905 data = data != null ? jQuery.makeArray( data ) : [];
2906 data.unshift( event );
2907
2908 // Allow special events to draw outside the lines
2909 special = jQuery.event.special[ type ] || {};
2910 if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
2911 return;
2912 }
2913
2914 // Determine event propagation path in advance, per W3C events spec (#9951)
2915 // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
2916 eventPath = [[ elem, special.bindType || type ]];
2917 if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
2918
2919 bubbleType = special.delegateType || type;
2920 cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
2921 for ( old = elem; cur; cur = cur.parentNode ) {
2922 eventPath.push([ cur, bubbleType ]);
2923 old = cur;
2924 }
2925
2926 // Only add window if we got to document (e.g., not plain obj or detached DOM)
2927 if ( old === (elem.ownerDocument || document) ) {
2928 eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
2929 }
2930 }
2931
2932 // Fire handlers on the event path
2933 for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
2934
2935 cur = eventPath[i][0];
2936 event.type = eventPath[i][1];
2937
2938 handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
2939 if ( handle ) {
2940 handle.apply( cur, data );
2941 }
2942 // Note that this is a bare JS function and not a jQuery handler
2943 handle = ontype && cur[ ontype ];
2944 if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) {
2945 event.preventDefault();
2946 }
2947 }
2948 event.type = type;
2949
2950 // If nobody prevented the default action, do it now
2951 if ( !onlyHandlers && !event.isDefaultPrevented() ) {
2952
2953 if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
2954 !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
2955
2956 // Call a native DOM method on the target with the same name name as the event.
2957 // Can't use an .isFunction() check here because IE6/7 fails that test.
2958 // Don't do default actions on window, that's where global variables be (#6170)
2959 // IE<9 dies on focus/blur to hidden element (#1486)
2960 if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
2961
2962 // Don't re-trigger an onFOO event when we call its FOO() method
2963 old = elem[ ontype ];
2964
2965 if ( old ) {
2966 elem[ ontype ] = null;
2967 }
2968
2969 // Prevent re-triggering of the same event, since we already bubbled it above
2970 jQuery.event.triggered = type;
2971 elem[ type ]();
2972 jQuery.event.triggered = undefined;
2973
2974 if ( old ) {
2975 elem[ ontype ] = old;
2976 }
2977 }
2978 }
2979 }
2980
2981 return event.result;
2982 },
2983
2984 dispatch: function( event ) {
2985
2986 // Make a writable jQuery.Event from the native event object
2987 event = jQuery.event.fix( event || window.event );
2988
2989 var i, j, cur, ret, selMatch, matched, matches, handleObj, sel, related,
2990 handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
2991 delegateCount = handlers.delegateCount,
2992 args = core_slice.call( arguments ),
2993 run_all = !event.exclusive && !event.namespace,
2994 special = jQuery.event.special[ event.type ] || {},
2995 handlerQueue = [];
2996
2997 // Use the fix-ed jQuery.Event rather than the (read-only) native event
2998 args[0] = event;
2999 event.delegateTarget = this;
3000
3001 // Call the preDispatch hook for the mapped type, and let it bail if desired
3002 if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
3003 return;
3004 }
3005
3006 // Determine handlers that should run if there are delegated events
3007 // Avoid non-left-click bubbling in Firefox (#3861)
3008 if ( delegateCount && !(event.button && event.type === "click") ) {
3009
3010 for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
3011
3012 // Don't process clicks (ONLY) on disabled elements (#6911, #8165, #11382, #11764)
3013 if ( cur.disabled !== true || event.type !== "click" ) {
3014 selMatch = {};
3015 matches = [];
3016 for ( i = 0; i < delegateCount; i++ ) {
3017 handleObj = handlers[ i ];
3018 sel = handleObj.selector;
3019
3020 if ( selMatch[ sel ] === undefined ) {
3021 selMatch[ sel ] = handleObj.needsContext ?
3022 jQuery( sel, this ).index( cur ) >= 0 :
3023 jQuery.find( sel, this, null, [ cur ] ).length;
3024 }
3025 if ( selMatch[ sel ] ) {
3026 matches.push( handleObj );
3027 }
3028 }
3029 if ( matches.length ) {
3030 handlerQueue.push({ elem: cur, matches: matches });
3031 }
3032 }
3033 }
3034 }
3035
3036 // Add the remaining (directly-bound) handlers
3037 if ( handlers.length > delegateCount ) {
3038 handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
3039 }
3040
3041 // Run delegates first; they may want to stop propagation beneath us
3042 for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
3043 matched = handlerQueue[ i ];
3044 event.currentTarget = matched.elem;
3045
3046 for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
3047 handleObj = matched.matches[ j ];
3048
3049 // Triggered event must either 1) be non-exclusive and have no namespace, or
3050 // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
3051 if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
3052
3053 event.data = handleObj.data;
3054 event.handleObj = handleObj;
3055
3056 ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
3057 .apply( matched.elem, args );
3058
3059 if ( ret !== undefined ) {
3060 event.result = ret;
3061 if ( ret === false ) {
3062 event.preventDefault();
3063 event.stopPropagation();
3064 }
3065 }
3066 }
3067 }
3068 }
3069
3070 // Call the postDispatch hook for the mapped type
3071 if ( special.postDispatch ) {
3072 special.postDispatch.call( this, event );
3073 }
3074
3075 return event.result;
3076 },
3077
3078 // Includes some event props shared by KeyEvent and MouseEvent
3079 // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
3080 props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
3081
3082 fixHooks: {},
3083
3084 keyHooks: {
3085 props: "char charCode key keyCode".split(" "),
3086 filter: function( event, original ) {
3087
3088 // Add which for key events
3089 if ( event.which == null ) {
3090 event.which = original.charCode != null ? original.charCode : original.keyCode;
3091 }
3092
3093 return event;
3094 }
3095 },
3096
3097 mouseHooks: {
3098 props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
3099 filter: function( event, original ) {
3100 var eventDoc, doc, body,
3101 button = original.button,
3102 fromElement = original.fromElement;
3103
3104 // Calculate pageX/Y if missing and clientX/Y available
3105 if ( event.pageX == null && original.clientX != null ) {
3106 eventDoc = event.target.ownerDocument || document;
3107 doc = eventDoc.documentElement;
3108 body = eventDoc.body;
3109
3110 event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
3111 event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
3112 }
3113
3114 // Add relatedTarget, if necessary
3115 if ( !event.relatedTarget && fromElement ) {
3116 event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
3117 }
3118
3119 // Add which for click: 1 === left; 2 === middle; 3 === right
3120 // Note: button is not normalized, so don't use it
3121 if ( !event.which && button !== undefined ) {
3122 event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
3123 }
3124
3125 return event;
3126 }
3127 },
3128
3129 fix: function( event ) {
3130 if ( event[ jQuery.expando ] ) {
3131 return event;
3132 }
3133
3134 // Create a writable copy of the event object and normalize some properties
3135 var i, prop,
3136 originalEvent = event,
3137 fixHook = jQuery.event.fixHooks[ event.type ] || {},
3138 copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
3139
3140 event = jQuery.Event( originalEvent );
3141
3142 for ( i = copy.length; i; ) {
3143 prop = copy[ --i ];
3144 event[ prop ] = originalEvent[ prop ];
3145 }
3146
3147 // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
3148 if ( !event.target ) {
3149 event.target = originalEvent.srcElement || document;
3150 }
3151
3152 // Target should not be a text node (#504, Safari)
3153 if ( event.target.nodeType === 3 ) {
3154 event.target = event.target.parentNode;
3155 }
3156
3157 // For mouse/key events, metaKey==false if it's undefined (#3368, #11328; IE6/7/8)
3158 event.metaKey = !!event.metaKey;
3159
3160 return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
3161 },
3162
3163 special: {
3164 load: {
3165 // Prevent triggered image.load events from bubbling to window.load
3166 noBubble: true
3167 },
3168
3169 focus: {
3170 delegateType: "focusin"
3171 },
3172 blur: {
3173 delegateType: "focusout"
3174 },
3175
3176 beforeunload: {
3177 setup: function( data, namespaces, eventHandle ) {
3178 // We only want to do this special case on windows
3179 if ( jQuery.isWindow( this ) ) {
3180 this.onbeforeunload = eventHandle;
3181 }
3182 },
3183
3184 teardown: function( namespaces, eventHandle ) {
3185 if ( this.onbeforeunload === eventHandle ) {
3186 this.onbeforeunload = null;
3187 }
3188 }
3189 }
3190 },
3191
3192 simulate: function( type, elem, event, bubble ) {
3193 // Piggyback on a donor event to simulate a different one.
3194 // Fake originalEvent to avoid donor's stopPropagation, but if the
3195 // simulated event prevents default then we do the same on the donor.
3196 var e = jQuery.extend(
3197 new jQuery.Event(),
3198 event,
3199 { type: type,
3200 isSimulated: true,
3201 originalEvent: {}
3202 }
3203 );
3204 if ( bubble ) {
3205 jQuery.event.trigger( e, null, elem );
3206 } else {
3207 jQuery.event.dispatch.call( elem, e );
3208 }
3209 if ( e.isDefaultPrevented() ) {
3210 event.preventDefault();
3211 }
3212 }
3213 };
3214
3215 // Some plugins are using, but it's undocumented/deprecated and will be removed.
3216 // The 1.7 special event interface should provide all the hooks needed now.
3217 jQuery.event.handle = jQuery.event.dispatch;
3218
3219 jQuery.removeEvent = document.removeEventListener ?
3220 function( elem, type, handle ) {
3221 if ( elem.removeEventListener ) {
3222 elem.removeEventListener( type, handle, false );
3223 }
3224 } :
3225 function( elem, type, handle ) {
3226 var name = "on" + type;
3227
3228 if ( elem.detachEvent ) {
3229
3230 // #8545, #7054, preventing memory leaks for custom events in IE6-8
3231 // detachEvent needed property on element, by name of that event, to properly expose it to GC
3232 if ( typeof elem[ name ] === "undefined" ) {
3233 elem[ name ] = null;
3234 }
3235
3236 elem.detachEvent( name, handle );
3237 }
3238 };
3239
3240 jQuery.Event = function( src, props ) {
3241 // Allow instantiation without the 'new' keyword
3242 if ( !(this instanceof jQuery.Event) ) {
3243 return new jQuery.Event( src, props );
3244 }
3245
3246 // Event object
3247 if ( src && src.type ) {
3248 this.originalEvent = src;
3249 this.type = src.type;
3250
3251 // Events bubbling up the document may have been marked as prevented
3252 // by a handler lower down the tree; reflect the correct value.
3253 this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
3254 src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
3255
3256 // Event type
3257 } else {
3258 this.type = src;
3259 }
3260
3261 // Put explicitly provided properties onto the event object
3262 if ( props ) {
3263 jQuery.extend( this, props );
3264 }
3265
3266 // Create a timestamp if incoming event doesn't have one
3267 this.timeStamp = src && src.timeStamp || jQuery.now();
3268
3269 // Mark it as fixed
3270 this[ jQuery.expando ] = true;
3271 };
3272
3273 function returnFalse() {
3274 return false;
3275 }
3276 function returnTrue() {
3277 return true;
3278 }
3279
3280 // jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
3281 // http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
3282 jQuery.Event.prototype = {
3283 preventDefault: function() {
3284 this.isDefaultPrevented = returnTrue;
3285
3286 var e = this.originalEvent;
3287 if ( !e ) {
3288 return;
3289 }
3290
3291 // if preventDefault exists run it on the original event
3292 if ( e.preventDefault ) {
3293 e.preventDefault();
3294
3295 // otherwise set the returnValue property of the original event to false (IE)
3296 } else {
3297 e.returnValue = false;
3298 }
3299 },
3300 stopPropagation: function() {
3301 this.isPropagationStopped = returnTrue;
3302
3303 var e = this.originalEvent;
3304 if ( !e ) {
3305 return;
3306 }
3307 // if stopPropagation exists run it on the original event
3308 if ( e.stopPropagation ) {
3309 e.stopPropagation();
3310 }
3311 // otherwise set the cancelBubble property of the original event to true (IE)
3312 e.cancelBubble = true;
3313 },
3314 stopImmediatePropagation: function() {
3315 this.isImmediatePropagationStopped = returnTrue;
3316 this.stopPropagation();
3317 },
3318 isDefaultPrevented: returnFalse,
3319 isPropagationStopped: returnFalse,
3320 isImmediatePropagationStopped: returnFalse
3321 };
3322
3323 // Create mouseenter/leave events using mouseover/out and event-time checks
3324 jQuery.each({
3325 mouseenter: "mouseover",
3326 mouseleave: "mouseout"
3327 }, function( orig, fix ) {
3328 jQuery.event.special[ orig ] = {
3329 delegateType: fix,
3330 bindType: fix,
3331
3332 handle: function( event ) {
3333 var ret,
3334 target = this,
3335 related = event.relatedTarget,
3336 handleObj = event.handleObj,
3337 selector = handleObj.selector;
3338
3339 // For mousenter/leave call the handler if related is outside the target.
3340 // NB: No relatedTarget if the mouse left/entered the browser window
3341 if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
3342 event.type = handleObj.origType;
3343 ret = handleObj.handler.apply( this, arguments );
3344 event.type = fix;
3345 }
3346 return ret;
3347 }
3348 };
3349 });
3350
3351 // IE submit delegation
3352 if ( !jQuery.support.submitBubbles ) {
3353
3354 jQuery.event.special.submit = {
3355 setup: function() {
3356 // Only need this for delegated form submit events
3357 if ( jQuery.nodeName( this, "form" ) ) {
3358 return false;
3359 }
3360
3361 // Lazy-add a submit handler when a descendant form may potentially be submitted
3362 jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
3363 // Node name check avoids a VML-related crash in IE (#9807)
3364 var elem = e.target,
3365 form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
3366 if ( form && !jQuery._data( form, "_submit_attached" ) ) {
3367 jQuery.event.add( form, "submit._submit", function( event ) {
3368 event._submit_bubble = true;
3369 });
3370 jQuery._data( form, "_submit_attached", true );
3371 }
3372 });
3373 // return undefined since we don't need an event listener
3374 },
3375
3376 postDispatch: function( event ) {
3377 // If form was submitted by the user, bubble the event up the tree
3378 if ( event._submit_bubble ) {
3379 delete event._submit_bubble;
3380 if ( this.parentNode && !event.isTrigger ) {
3381 jQuery.event.simulate( "submit", this.parentNode, event, true );
3382 }
3383 }
3384 },
3385
3386 teardown: function() {
3387 // Only need this for delegated form submit events
3388 if ( jQuery.nodeName( this, "form" ) ) {
3389 return false;
3390 }
3391
3392 // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
3393 jQuery.event.remove( this, "._submit" );
3394 }
3395 };
3396 }
3397
3398 // IE change delegation and checkbox/radio fix
3399 if ( !jQuery.support.changeBubbles ) {
3400
3401 jQuery.event.special.change = {
3402
3403 setup: function() {
3404
3405 if ( rformElems.test( this.nodeName ) ) {
3406 // IE doesn't fire change on a check/radio until blur; trigger it on click
3407 // after a propertychange. Eat the blur-change in special.change.handle.
3408 // This still fires onchange a second time for check/radio after blur.
3409 if ( this.type === "checkbox" || this.type === "radio" ) {
3410 jQuery.event.add( this, "propertychange._change", function( event ) {
3411 if ( event.originalEvent.propertyName === "checked" ) {
3412 this._just_changed = true;
3413 }
3414 });
3415 jQuery.event.add( this, "click._change", function( event ) {
3416 if ( this._just_changed && !event.isTrigger ) {
3417 this._just_changed = false;
3418 }
3419 // Allow triggered, simulated change events (#11500)
3420 jQuery.event.simulate( "change", this, event, true );
3421 });
3422 }
3423 return false;
3424 }
3425 // Delegated event; lazy-add a change handler on descendant inputs
3426 jQuery.event.add( this, "beforeactivate._change", function( e ) {
3427 var elem = e.target;
3428
3429 if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "_change_attached" ) ) {
3430 jQuery.event.add( elem, "change._change", function( event ) {
3431 if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
3432 jQuery.event.simulate( "change", this.parentNode, event, true );
3433 }
3434 });
3435 jQuery._data( elem, "_change_attached", true );
3436 }
3437 });
3438 },
3439
3440 handle: function( event ) {
3441 var elem = event.target;
3442
3443 // Swallow native change events from checkbox/radio, we already triggered them above
3444 if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
3445 return event.handleObj.handler.apply( this, arguments );
3446 }
3447 },
3448
3449 teardown: function() {
3450 jQuery.event.remove( this, "._change" );
3451
3452 return !rformElems.test( this.nodeName );
3453 }
3454 };
3455 }
3456
3457 // Create "bubbling" focus and blur events
3458 if ( !jQuery.support.focusinBubbles ) {
3459 jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
3460
3461 // Attach a single capturing handler while someone wants focusin/focusout
3462 var attaches = 0,
3463 handler = function( event ) {
3464 jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
3465 };
3466
3467 jQuery.event.special[ fix ] = {
3468 setup: function() {
3469 if ( attaches++ === 0 ) {
3470 document.addEventListener( orig, handler, true );
3471 }
3472 },
3473 teardown: function() {
3474 if ( --attaches === 0 ) {
3475 document.removeEventListener( orig, handler, true );
3476 }
3477 }
3478 };
3479 });
3480 }
3481
3482 jQuery.fn.extend({
3483
3484 on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
3485 var origFn, type;
3486
3487 // Types can be a map of types/handlers
3488 if ( typeof types === "object" ) {
3489 // ( types-Object, selector, data )
3490 if ( typeof selector !== "string" ) { // && selector != null
3491 // ( types-Object, data )
3492 data = data || selector;
3493 selector = undefined;
3494 }
3495 for ( type in types ) {
3496 this.on( type, selector, data, types[ type ], one );
3497 }
3498 return this;
3499 }
3500
3501 if ( data == null && fn == null ) {
3502 // ( types, fn )
3503 fn = selector;
3504 data = selector = undefined;
3505 } else if ( fn == null ) {
3506 if ( typeof selector === "string" ) {
3507 // ( types, selector, fn )
3508 fn = data;
3509 data = undefined;
3510 } else {
3511 // ( types, data, fn )
3512 fn = data;
3513 data = selector;
3514 selector = undefined;
3515 }
3516 }
3517 if ( fn === false ) {
3518 fn = returnFalse;
3519 } else if ( !fn ) {
3520 return this;
3521 }
3522
3523 if ( one === 1 ) {
3524 origFn = fn;
3525 fn = function( event ) {
3526 // Can use an empty set, since event contains the info
3527 jQuery().off( event );
3528 return origFn.apply( this, arguments );
3529 };
3530 // Use same guid so caller can remove using origFn
3531 fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
3532 }
3533 return this.each( function() {
3534 jQuery.event.add( this, types, fn, data, selector );
3535 });
3536 },
3537 one: function( types, selector, data, fn ) {
3538 return this.on( types, selector, data, fn, 1 );
3539 },
3540 off: function( types, selector, fn ) {
3541 var handleObj, type;
3542 if ( types && types.preventDefault && types.handleObj ) {
3543 // ( event ) dispatched jQuery.Event
3544 handleObj = types.handleObj;
3545 jQuery( types.delegateTarget ).off(
3546 handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
3547 handleObj.selector,
3548 handleObj.handler
3549 );
3550 return this;
3551 }
3552 if ( typeof types === "object" ) {
3553 // ( types-object [, selector] )
3554 for ( type in types ) {
3555 this.off( type, selector, types[ type ] );
3556 }
3557 return this;
3558 }
3559 if ( selector === false || typeof selector === "function" ) {
3560 // ( types [, fn] )
3561 fn = selector;
3562 selector = undefined;
3563 }
3564 if ( fn === false ) {
3565 fn = returnFalse;
3566 }
3567 return this.each(function() {
3568 jQuery.event.remove( this, types, fn, selector );
3569 });
3570 },
3571
3572 bind: function( types, data, fn ) {
3573 return this.on( types, null, data, fn );
3574 },
3575 unbind: function( types, fn ) {
3576 return this.off( types, null, fn );
3577 },
3578
3579 live: function( types, data, fn ) {
3580 jQuery( this.context ).on( types, this.selector, data, fn );
3581 return this;
3582 },
3583 die: function( types, fn ) {
3584 jQuery( this.context ).off( types, this.selector || "**", fn );
3585 return this;
3586 },
3587
3588 delegate: function( selector, types, data, fn ) {
3589 return this.on( types, selector, data, fn );
3590 },
3591 undelegate: function( selector, types, fn ) {
3592 // ( namespace ) or ( selector, types [, fn] )
3593 return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
3594 },
3595
3596 trigger: function( type, data ) {
3597 return this.each(function() {
3598 jQuery.event.trigger( type, data, this );
3599 });
3600 },
3601 triggerHandler: function( type, data ) {
3602 if ( this[0] ) {
3603 return jQuery.event.trigger( type, data, this[0], true );
3604 }
3605 },
3606
3607 toggle: function( fn ) {
3608 // Save reference to arguments for access in closure
3609 var args = arguments,
3610 guid = fn.guid || jQuery.guid++,
3611 i = 0,
3612 toggler = function( event ) {
3613 // Figure out which function to execute
3614 var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
3615 jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
3616
3617 // Make sure that clicks stop
3618 event.preventDefault();
3619
3620 // and execute the function
3621 return args[ lastToggle ].apply( this, arguments ) || false;
3622 };
3623
3624 // link all the functions, so any of them can unbind this click handler
3625 toggler.guid = guid;
3626 while ( i < args.length ) {
3627 args[ i++ ].guid = guid;
3628 }
3629
3630 return this.click( toggler );
3631 },
3632
3633 hover: function( fnOver, fnOut ) {
3634 return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
3635 }
3636 });
3637
3638 jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
3639 "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
3640 "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
3641
3642 // Handle event binding
3643 jQuery.fn[ name ] = function( data, fn ) {
3644 if ( fn == null ) {
3645 fn = data;
3646 data = null;
3647 }
3648
3649 return arguments.length > 0 ?
3650 this.on( name, null, data, fn ) :
3651 this.trigger( name );
3652 };
3653
3654 if ( rkeyEvent.test( name ) ) {
3655 jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
3656 }
3657
3658 if ( rmouseEvent.test( name ) ) {
3659 jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
3660 }
3661 });
3662 /*!
3663 * Sizzle CSS Selector Engine
3664 * Copyright 2012 jQuery Foundation and other contributors
3665 * Released under the MIT license
3666 * http://sizzlejs.com/
3667 */
3668 (function( window, undefined ) {
3669
3670 var cachedruns,
3671 assertGetIdNotName,
3672 Expr,
3673 getText,
3674 isXML,
3675 contains,
3676 compile,
3677 sortOrder,
3678 hasDuplicate,
3679 outermostContext,
3680
3681 baseHasDuplicate = true,
3682 strundefined = "undefined",
3683
3684 expando = ( "sizcache" + Math.random() ).replace( ".", "" ),
3685
3686 Token = String,
3687 document = window.document,
3688 docElem = document.documentElement,
3689 dirruns = 0,
3690 done = 0,
3691 pop = [].pop,
3692 push = [].push,
3693 slice = [].slice,
3694 // Use a stripped-down indexOf if a native one is unavailable
3695 indexOf = [].indexOf || function( elem ) {
3696 var i = 0,
3697 len = this.length;
3698 for ( ; i < len; i++ ) {
3699 if ( this[i] === elem ) {
3700 return i;
3701 }
3702 }
3703 return -1;
3704 },
3705
3706 // Augment a function for special use by Sizzle
3707 markFunction = function( fn, value ) {
3708 fn[ expando ] = value == null || value;
3709 return fn;
3710 },
3711
3712 createCache = function() {
3713 var cache = {},
3714 keys = [];
3715
3716 return markFunction(function( key, value ) {
3717 // Only keep the most recent entries
3718 if ( keys.push( key ) > Expr.cacheLength ) {
3719 delete cache[ keys.shift() ];
3720 }
3721
3722 // Retrieve with (key + " ") to avoid collision with native Object.prototype properties (see Issue #157)
3723 return (cache[ key + " " ] = value);
3724 }, cache );
3725 },
3726
3727 classCache = createCache(),
3728 tokenCache = createCache(),
3729 compilerCache = createCache(),
3730
3731 // Regex
3732
3733 // Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
3734 whitespace = "[\\x20\\t\\r\\n\\f]",
3735 // http://www.w3.org/TR/css3-syntax/#characters
3736 characterEncoding = "(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",
3737
3738 // Loosely modeled on CSS identifier characters
3739 // An unquoted value should be a CSS identifier (http://www.w3.org/TR/css3-selectors/#attribute-selectors)
3740 // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
3741 identifier = characterEncoding.replace( "w", "w#" ),
3742
3743 // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors
3744 operators = "([*^$|!~]?=)",
3745 attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace +
3746 "*(?:" + operators + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]",
3747
3748 // Prefer arguments not in parens/brackets,
3749 // then attribute selectors and non-pseudos (denoted by :),
3750 // then anything else
3751 // These preferences are here to reduce the number of selectors
3752 // needing tokenize in the PSEUDO preFilter
3753 pseudos = ":(" + characterEncoding + ")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:" + attributes + ")|[^:]|\\\\.)*|.*))\\)|)",
3754
3755 // For matchExpr.POS and matchExpr.needsContext
3756 pos = ":(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
3757 "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)",
3758
3759 // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
3760 rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ),
3761
3762 rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
3763 rcombinators = new RegExp( "^" + whitespace + "*([\\x20\\t\\r\\n\\f>+~])" + whitespace + "*" ),
3764 rpseudo = new RegExp( pseudos ),
3765
3766 // Easily-parseable/retrievable ID or TAG or CLASS selectors
3767 rquickExpr = /^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,
3768
3769 rnot = /^:not/,
3770 rsibling = /[\x20\t\r\n\f]*[+~]/,
3771 rendsWithNot = /:not\($/,
3772
3773 rheader = /h\d/i,
3774 rinputs = /input|select|textarea|button/i,
3775
3776 rbackslash = /\\(?!\\)/g,
3777
3778 matchExpr = {
3779 "ID": new RegExp( "^#(" + characterEncoding + ")" ),
3780 "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ),
3781 "NAME": new RegExp( "^\\[name=['\"]?(" + characterEncoding + ")['\"]?\\]" ),
3782 "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ),
3783 "ATTR": new RegExp( "^" + attributes ),
3784 "PSEUDO": new RegExp( "^" + pseudos ),
3785 "POS": new RegExp( pos, "i" ),
3786 "CHILD": new RegExp( "^:(only|nth|first|last)-child(?:\\(" + whitespace +
3787 "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
3788 "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
3789 // For use in libraries implementing .is()
3790 "needsContext": new RegExp( "^" + whitespace + "*[>+~]|" + pos, "i" )
3791 },
3792
3793 // Support
3794
3795 // Used for testing something on an element
3796 assert = function( fn ) {
3797 var div = document.createElement("div");
3798
3799 try {
3800 return fn( div );
3801 } catch (e) {
3802 return false;
3803 } finally {
3804 // release memory in IE
3805 div = null;
3806 }
3807 },
3808
3809 // Check if getElementsByTagName("*") returns only elements
3810 assertTagNameNoComments = assert(function( div ) {
3811 div.appendChild( document.createComment("") );
3812 return !div.getElementsByTagName("*").length;
3813 }),
3814
3815 // Check if getAttribute returns normalized href attributes
3816 assertHrefNotNormalized = assert(function( div ) {
3817 div.innerHTML = "<a href='#'></a>";
3818 return div.firstChild && typeof div.firstChild.getAttribute !== strundefined &&
3819 div.firstChild.getAttribute("href") === "#";
3820 }),
3821
3822 // Check if attributes should be retrieved by attribute nodes
3823 assertAttributes = assert(function( div ) {
3824 div.innerHTML = "<select></select>";
3825 var type = typeof div.lastChild.getAttribute("multiple");
3826 // IE8 returns a string for some attributes even when not present
3827 return type !== "boolean" && type !== "string";
3828 }),
3829
3830 // Check if getElementsByClassName can be trusted
3831 assertUsableClassName = assert(function( div ) {
3832 // Opera can't find a second classname (in 9.6)
3833 div.innerHTML = "<div class='hidden e'></div><div class='hidden'></div>";
3834 if ( !div.getElementsByClassName || !div.getElementsByClassName("e").length ) {
3835 return false;
3836 }
3837
3838 // Safari 3.2 caches class attributes and doesn't catch changes
3839 div.lastChild.className = "e";
3840 return div.getElementsByClassName("e").length === 2;
3841 }),
3842
3843 // Check if getElementById returns elements by name
3844 // Check if getElementsByName privileges form controls or returns elements by ID
3845 assertUsableName = assert(function( div ) {
3846 // Inject content
3847 div.id = expando + 0;
3848 div.innerHTML = "<a name='" + expando + "'></a><div name='" + expando + "'></div>";
3849 docElem.insertBefore( div, docElem.firstChild );
3850
3851 // Test
3852 var pass = document.getElementsByName &&
3853 // buggy browsers will return fewer than the correct 2
3854 document.getElementsByName( expando ).length === 2 +
3855 // buggy browsers will return more than the correct 0
3856 document.getElementsByName( expando + 0 ).length;
3857 assertGetIdNotName = !document.getElementById( expando );
3858
3859 // Cleanup
3860 docElem.removeChild( div );
3861
3862 return pass;
3863 });
3864
3865 // If slice is not available, provide a backup
3866 try {
3867 slice.call( docElem.childNodes, 0 )[0].nodeType;
3868 } catch ( e ) {
3869 slice = function( i ) {
3870 var elem,
3871 results = [];
3872 for ( ; (elem = this[i]); i++ ) {
3873 results.push( elem );
3874 }
3875 return results;
3876 };
3877 }
3878
3879 function Sizzle( selector, context, results, seed ) {
3880 results = results || [];
3881 context = context || document;
3882 var match, elem, xml, m,
3883 nodeType = context.nodeType;
3884
3885 if ( !selector || typeof selector !== "string" ) {
3886 return results;
3887 }
3888
3889 if ( nodeType !== 1 && nodeType !== 9 ) {
3890 return [];
3891 }
3892
3893 xml = isXML( context );
3894
3895 if ( !xml && !seed ) {
3896 if ( (match = rquickExpr.exec( selector )) ) {
3897 // Speed-up: Sizzle("#ID")
3898 if ( (m = match[1]) ) {
3899 if ( nodeType === 9 ) {
3900 elem = context.getElementById( m );
3901 // Check parentNode to catch when Blackberry 4.6 returns
3902 // nodes that are no longer in the document #6963
3903 if ( elem && elem.parentNode ) {
3904 // Handle the case where IE, Opera, and Webkit return items
3905 // by name instead of ID
3906 if ( elem.id === m ) {
3907 results.push( elem );
3908 return results;
3909 }
3910 } else {
3911 return results;
3912 }
3913 } else {
3914 // Context is not a document
3915 if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
3916 contains( context, elem ) && elem.id === m ) {
3917 results.push( elem );
3918 return results;
3919 }
3920 }
3921
3922 // Speed-up: Sizzle("TAG")
3923 } else if ( match[2] ) {
3924 push.apply( results, slice.call(context.getElementsByTagName( selector ), 0) );
3925 return results;
3926
3927 // Speed-up: Sizzle(".CLASS")
3928 } else if ( (m = match[3]) && assertUsableClassName && context.getElementsByClassName ) {
3929 push.apply( results, slice.call(context.getElementsByClassName( m ), 0) );
3930 return results;
3931 }
3932 }
3933 }
3934
3935 // All others
3936 return select( selector.replace( rtrim, "$1" ), context, results, seed, xml );
3937 }
3938
3939 Sizzle.matches = function( expr, elements ) {
3940 return Sizzle( expr, null, null, elements );
3941 };
3942
3943 Sizzle.matchesSelector = function( elem, expr ) {
3944 return Sizzle( expr, null, null, [ elem ] ).length > 0;
3945 };
3946
3947 // Returns a function to use in pseudos for input types
3948 function createInputPseudo( type ) {
3949 return function( elem ) {
3950 var name = elem.nodeName.toLowerCase();
3951 return name === "input" && elem.type === type;
3952 };
3953 }
3954
3955 // Returns a function to use in pseudos for buttons
3956 function createButtonPseudo( type ) {
3957 return function( elem ) {
3958 var name = elem.nodeName.toLowerCase();
3959 return (name === "input" || name === "button") && elem.type === type;
3960 };
3961 }
3962
3963 // Returns a function to use in pseudos for positionals
3964 function createPositionalPseudo( fn ) {
3965 return markFunction(function( argument ) {
3966 argument = +argument;
3967 return markFunction(function( seed, matches ) {
3968 var j,
3969 matchIndexes = fn( [], seed.length, argument ),
3970 i = matchIndexes.length;
3971
3972 // Match elements found at the specified indexes
3973 while ( i-- ) {
3974 if ( seed[ (j = matchIndexes[i]) ] ) {
3975 seed[j] = !(matches[j] = seed[j]);
3976 }
3977 }
3978 });
3979 });
3980 }
3981
3982 /**
3983 * Utility function for retrieving the text value of an array of DOM nodes
3984 * @param {Array|Element} elem
3985 */
3986 getText = Sizzle.getText = function( elem ) {
3987 var node,
3988 ret = "",
3989 i = 0,
3990 nodeType = elem.nodeType;
3991
3992 if ( nodeType ) {
3993 if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
3994 // Use textContent for elements
3995 // innerText usage removed for consistency of new lines (see #11153)
3996 if ( typeof elem.textContent === "string" ) {
3997 return elem.textContent;
3998 } else {
3999 // Traverse its children
4000 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
4001 ret += getText( elem );
4002 }
4003 }
4004 } else if ( nodeType === 3 || nodeType === 4 ) {
4005 return elem.nodeValue;
4006 }
4007 // Do not include comment or processing instruction nodes
4008 } else {
4009
4010 // If no nodeType, this is expected to be an array
4011 for ( ; (node = elem[i]); i++ ) {
4012 // Do not traverse comment nodes
4013 ret += getText( node );
4014 }
4015 }
4016 return ret;
4017 };
4018
4019 isXML = Sizzle.isXML = function( elem ) {
4020 // documentElement is verified for cases where it doesn't yet exist
4021 // (such as loading iframes in IE - #4833)
4022 var documentElement = elem && (elem.ownerDocument || elem).documentElement;
4023 return documentElement ? documentElement.nodeName !== "HTML" : false;
4024 };
4025
4026 // Element contains another
4027 contains = Sizzle.contains = docElem.contains ?
4028 function( a, b ) {
4029 var adown = a.nodeType === 9 ? a.documentElement : a,
4030 bup = b && b.parentNode;
4031 return a === bup || !!( bup && bup.nodeType === 1 && adown.contains && adown.contains(bup) );
4032 } :
4033 docElem.compareDocumentPosition ?
4034 function( a, b ) {
4035 return b && !!( a.compareDocumentPosition( b ) & 16 );
4036 } :
4037 function( a, b ) {
4038 while ( (b = b.parentNode) ) {
4039 if ( b === a ) {
4040 return true;
4041 }
4042 }
4043 return false;
4044 };
4045
4046 Sizzle.attr = function( elem, name ) {
4047 var val,
4048 xml = isXML( elem );
4049
4050 if ( !xml ) {
4051 name = name.toLowerCase();
4052 }
4053 if ( (val = Expr.attrHandle[ name ]) ) {
4054 return val( elem );
4055 }
4056 if ( xml || assertAttributes ) {
4057 return elem.getAttribute( name );
4058 }
4059 val = elem.getAttributeNode( name );
4060 return val ?
4061 typeof elem[ name ] === "boolean" ?
4062 elem[ name ] ? name : null :
4063 val.specified ? val.value : null :
4064 null;
4065 };
4066
4067 Expr = Sizzle.selectors = {
4068
4069 // Can be adjusted by the user
4070 cacheLength: 50,
4071
4072 createPseudo: markFunction,
4073
4074 match: matchExpr,
4075
4076 // IE6/7 return a modified href
4077 attrHandle: assertHrefNotNormalized ?
4078 {} :
4079 {
4080 "href": function( elem ) {
4081 return elem.getAttribute( "href", 2 );
4082 },
4083 "type": function( elem ) {
4084 return elem.getAttribute("type");
4085 }
4086 },
4087
4088 find: {
4089 "ID": assertGetIdNotName ?
4090 function( id, context, xml ) {
4091 if ( typeof context.getElementById !== strundefined && !xml ) {
4092 var m = context.getElementById( id );
4093 // Check parentNode to catch when Blackberry 4.6 returns
4094 // nodes that are no longer in the document #6963
4095 return m && m.parentNode ? [m] : [];
4096 }
4097 } :
4098 function( id, context, xml ) {
4099 if ( typeof context.getElementById !== strundefined && !xml ) {
4100 var m = context.getElementById( id );
4101
4102 return m ?
4103 m.id === id || typeof m.getAttributeNode !== strundefined && m.getAttributeNode("id").value === id ?
4104 [m] :
4105 undefined :
4106 [];
4107 }
4108 },
4109
4110 "TAG": assertTagNameNoComments ?
4111 function( tag, context ) {
4112 if ( typeof context.getElementsByTagName !== strundefined ) {
4113 return context.getElementsByTagName( tag );
4114 }
4115 } :
4116 function( tag, context ) {
4117 var results = context.getElementsByTagName( tag );
4118
4119 // Filter out possible comments
4120 if ( tag === "*" ) {
4121 var elem,
4122 tmp = [],
4123 i = 0;
4124
4125 for ( ; (elem = results[i]); i++ ) {
4126 if ( elem.nodeType === 1 ) {
4127 tmp.push( elem );
4128 }
4129 }
4130
4131 return tmp;
4132 }
4133 return results;
4134 },
4135
4136 "NAME": assertUsableName && function( tag, context ) {
4137 if ( typeof context.getElementsByName !== strundefined ) {
4138 return context.getElementsByName( name );
4139 }
4140 },
4141
4142 "CLASS": assertUsableClassName && function( className, context, xml ) {
4143 if ( typeof context.getElementsByClassName !== strundefined && !xml ) {
4144 return context.getElementsByClassName( className );
4145 }
4146 }
4147 },
4148
4149 relative: {
4150 ">": { dir: "parentNode", first: true },
4151 " ": { dir: "parentNode" },
4152 "+": { dir: "previousSibling", first: true },
4153 "~": { dir: "previousSibling" }
4154 },
4155
4156 preFilter: {
4157 "ATTR": function( match ) {
4158 match[1] = match[1].replace( rbackslash, "" );
4159
4160 // Move the given value to match[3] whether quoted or unquoted
4161 match[3] = ( match[4] || match[5] || "" ).replace( rbackslash, "" );
4162
4163 if ( match[2] === "~=" ) {
4164 match[3] = " " + match[3] + " ";
4165 }
4166
4167 return match.slice( 0, 4 );
4168 },
4169
4170 "CHILD": function( match ) {
4171 /* matches from matchExpr["CHILD"]
4172 1 type (only|nth|...)
4173 2 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4174 3 xn-component of xn+y argument ([+-]?\d*n|)
4175 4 sign of xn-component
4176 5 x of xn-component
4177 6 sign of y-component
4178 7 y of y-component
4179 */
4180 match[1] = match[1].toLowerCase();
4181
4182 if ( match[1] === "nth" ) {
4183 // nth-child requires argument
4184 if ( !match[2] ) {
4185 Sizzle.error( match[0] );
4186 }
4187
4188 // numeric x and y parameters for Expr.filter.CHILD
4189 // remember that false/true cast respectively to 0/1
4190 match[3] = +( match[3] ? match[4] + (match[5] || 1) : 2 * ( match[2] === "even" || match[2] === "odd" ) );
4191 match[4] = +( ( match[6] + match[7] ) || match[2] === "odd" );
4192
4193 // other types prohibit arguments
4194 } else if ( match[2] ) {
4195 Sizzle.error( match[0] );
4196 }
4197
4198 return match;
4199 },
4200
4201 "PSEUDO": function( match ) {
4202 var unquoted, excess;
4203 if ( matchExpr["CHILD"].test( match[0] ) ) {
4204 return null;
4205 }
4206
4207 if ( match[3] ) {
4208 match[2] = match[3];
4209 } else if ( (unquoted = match[4]) ) {
4210 // Only check arguments that contain a pseudo
4211 if ( rpseudo.test(unquoted) &&
4212 // Get excess from tokenize (recursively)
4213 (excess = tokenize( unquoted, true )) &&
4214 // advance to the next closing parenthesis
4215 (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) {
4216
4217 // excess is a negative index
4218 unquoted = unquoted.slice( 0, excess );
4219 match[0] = match[0].slice( 0, excess );
4220 }
4221 match[2] = unquoted;
4222 }
4223
4224 // Return only captures needed by the pseudo filter method (type and argument)
4225 return match.slice( 0, 3 );
4226 }
4227 },
4228
4229 filter: {
4230 "ID": assertGetIdNotName ?
4231 function( id ) {
4232 id = id.replace( rbackslash, "" );
4233 return function( elem ) {
4234 return elem.getAttribute("id") === id;
4235 };
4236 } :
4237 function( id ) {
4238 id = id.replace( rbackslash, "" );
4239 return function( elem ) {
4240 var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
4241 return node && node.value === id;
4242 };
4243 },
4244
4245 "TAG": function( nodeName ) {
4246 if ( nodeName === "*" ) {
4247 return function() { return true; };
4248 }
4249 nodeName = nodeName.replace( rbackslash, "" ).toLowerCase();
4250
4251 return function( elem ) {
4252 return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
4253 };
4254 },
4255
4256 "CLASS": function( className ) {
4257 var pattern = classCache[ expando ][ className + " " ];
4258
4259 return pattern ||
4260 (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) &&
4261 classCache( className, function( elem ) {
4262 return pattern.test( elem.className || (typeof elem.getAttribute !== strundefined && elem.getAttribute("class")) || "" );
4263 });
4264 },
4265
4266 "ATTR": function( name, operator, check ) {
4267 return function( elem, context ) {
4268 var result = Sizzle.attr( elem, name );
4269
4270 if ( result == null ) {
4271 return operator === "!=";
4272 }
4273 if ( !operator ) {
4274 return true;
4275 }
4276
4277 result += "";
4278
4279 return operator === "=" ? result === check :
4280 operator === "!=" ? result !== check :
4281 operator === "^=" ? check && result.indexOf( check ) === 0 :
4282 operator === "*=" ? check && result.indexOf( check ) > -1 :
4283 operator === "$=" ? check && result.substr( result.length - check.length ) === check :
4284 operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 :
4285 operator === "|=" ? result === check || result.substr( 0, check.length + 1 ) === check + "-" :
4286 false;
4287 };
4288 },
4289
4290 "CHILD": function( type, argument, first, last ) {
4291
4292 if ( type === "nth" ) {
4293 return function( elem ) {
4294 var node, diff,
4295 parent = elem.parentNode;
4296
4297 if ( first === 1 && last === 0 ) {
4298 return true;
4299 }
4300
4301 if ( parent ) {
4302 diff = 0;
4303 for ( node = parent.firstChild; node; node = node.nextSibling ) {
4304 if ( node.nodeType === 1 ) {
4305 diff++;
4306 if ( elem === node ) {
4307 break;
4308 }
4309 }
4310 }
4311 }
4312
4313 // Incorporate the offset (or cast to NaN), then check against cycle size
4314 diff -= last;
4315 return diff === first || ( diff % first === 0 && diff / first >= 0 );
4316 };
4317 }
4318
4319 return function( elem ) {
4320 var node = elem;
4321
4322 switch ( type ) {
4323 case "only":
4324 case "first":
4325 while ( (node = node.previousSibling) ) {
4326 if ( node.nodeType === 1 ) {
4327 return false;
4328 }
4329 }
4330
4331 if ( type === "first" ) {
4332 return true;
4333 }
4334
4335 node = elem;
4336
4337 /* falls through */
4338 case "last":
4339 while ( (node = node.nextSibling) ) {
4340 if ( node.nodeType === 1 ) {
4341 return false;
4342 }
4343 }
4344
4345 return true;
4346 }
4347 };
4348 },
4349
4350 "PSEUDO": function( pseudo, argument ) {
4351 // pseudo-class names are case-insensitive
4352 // http://www.w3.org/TR/selectors/#pseudo-classes
4353 // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
4354 // Remember that setFilters inherits from pseudos
4355 var args,
4356 fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
4357 Sizzle.error( "unsupported pseudo: " + pseudo );
4358
4359 // The user may use createPseudo to indicate that
4360 // arguments are needed to create the filter function
4361 // just as Sizzle does
4362 if ( fn[ expando ] ) {
4363 return fn( argument );
4364 }
4365
4366 // But maintain support for old signatures
4367 if ( fn.length > 1 ) {
4368 args = [ pseudo, pseudo, "", argument ];
4369 return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
4370 markFunction(function( seed, matches ) {
4371 var idx,
4372 matched = fn( seed, argument ),
4373 i = matched.length;
4374 while ( i-- ) {
4375 idx = indexOf.call( seed, matched[i] );
4376 seed[ idx ] = !( matches[ idx ] = matched[i] );
4377 }
4378 }) :
4379 function( elem ) {
4380 return fn( elem, 0, args );
4381 };
4382 }
4383
4384 return fn;
4385 }
4386 },
4387
4388 pseudos: {
4389 "not": markFunction(function( selector ) {
4390 // Trim the selector passed to compile
4391 // to avoid treating leading and trailing
4392 // spaces as combinators
4393 var input = [],
4394 results = [],
4395 matcher = compile( selector.replace( rtrim, "$1" ) );
4396
4397 return matcher[ expando ] ?
4398 markFunction(function( seed, matches, context, xml ) {
4399 var elem,
4400 unmatched = matcher( seed, null, xml, [] ),
4401 i = seed.length;
4402
4403 // Match elements unmatched by `matcher`
4404 while ( i-- ) {
4405 if ( (elem = unmatched[i]) ) {
4406 seed[i] = !(matches[i] = elem);
4407 }
4408 }
4409 }) :
4410 function( elem, context, xml ) {
4411 input[0] = elem;
4412 matcher( input, null, xml, results );
4413 return !results.pop();
4414 };
4415 }),
4416
4417 "has": markFunction(function( selector ) {
4418 return function( elem ) {
4419 return Sizzle( selector, elem ).length > 0;
4420 };
4421 }),
4422
4423 "contains": markFunction(function( text ) {
4424 return function( elem ) {
4425 return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1;
4426 };
4427 }),
4428
4429 "enabled": function( elem ) {
4430 return elem.disabled === false;
4431 },
4432
4433 "disabled": function( elem ) {
4434 return elem.disabled === true;
4435 },
4436
4437 "checked": function( elem ) {
4438 // In CSS3, :checked should return both checked and selected elements
4439 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
4440 var nodeName = elem.nodeName.toLowerCase();
4441 return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
4442 },
4443
4444 "selected": function( elem ) {
4445 // Accessing this property makes selected-by-default
4446 // options in Safari work properly
4447 if ( elem.parentNode ) {
4448 elem.parentNode.selectedIndex;
4449 }
4450
4451 return elem.selected === true;
4452 },
4453
4454 "parent": function( elem ) {
4455 return !Expr.pseudos["empty"]( elem );
4456 },
4457
4458 "empty": function( elem ) {
4459 // http://www.w3.org/TR/selectors/#empty-pseudo
4460 // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
4461 // not comment, processing instructions, or others
4462 // Thanks to Diego Perini for the nodeName shortcut
4463 // Greater than "@" means alpha characters (specifically not starting with "#" or "?")
4464 var nodeType;
4465 elem = elem.firstChild;
4466 while ( elem ) {
4467 if ( elem.nodeName > "@" || (nodeType = elem.nodeType) === 3 || nodeType === 4 ) {
4468 return false;
4469 }
4470 elem = elem.nextSibling;
4471 }
4472 return true;
4473 },
4474
4475 "header": function( elem ) {
4476 return rheader.test( elem.nodeName );
4477 },
4478
4479 "text": function( elem ) {
4480 var type, attr;
4481 // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
4482 // use getAttribute instead to test this case
4483 return elem.nodeName.toLowerCase() === "input" &&
4484 (type = elem.type) === "text" &&
4485 ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === type );
4486 },
4487
4488 // Input types
4489 "radio": createInputPseudo("radio"),
4490 "checkbox": createInputPseudo("checkbox"),
4491 "file": createInputPseudo("file"),
4492 "password": createInputPseudo("password"),
4493 "image": createInputPseudo("image"),
4494
4495 "submit": createButtonPseudo("submit"),
4496 "reset": createButtonPseudo("reset"),
4497
4498 "button": function( elem ) {
4499 var name = elem.nodeName.toLowerCase();
4500 return name === "input" && elem.type === "button" || name === "button";
4501 },
4502
4503 "input": function( elem ) {
4504 return rinputs.test( elem.nodeName );
4505 },
4506
4507 "focus": function( elem ) {
4508 var doc = elem.ownerDocument;
4509 return elem === doc.activeElement && (!doc.hasFocus || doc.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
4510 },
4511
4512 "active": function( elem ) {
4513 return elem === elem.ownerDocument.activeElement;
4514 },
4515
4516 // Positional types
4517 "first": createPositionalPseudo(function() {
4518 return [ 0 ];
4519 }),
4520
4521 "last": createPositionalPseudo(function( matchIndexes, length ) {
4522 return [ length - 1 ];
4523 }),
4524
4525 "eq": createPositionalPseudo(function( matchIndexes, length, argument ) {
4526 return [ argument < 0 ? argument + length : argument ];
4527 }),
4528
4529 "even": createPositionalPseudo(function( matchIndexes, length ) {
4530 for ( var i = 0; i < length; i += 2 ) {
4531 matchIndexes.push( i );
4532 }
4533 return matchIndexes;
4534 }),
4535
4536 "odd": createPositionalPseudo(function( matchIndexes, length ) {
4537 for ( var i = 1; i < length; i += 2 ) {
4538 matchIndexes.push( i );
4539 }
4540 return matchIndexes;
4541 }),
4542
4543 "lt": createPositionalPseudo(function( matchIndexes, length, argument ) {
4544 for ( var i = argument < 0 ? argument + length : argument; --i >= 0; ) {
4545 matchIndexes.push( i );
4546 }
4547 return matchIndexes;
4548 }),
4549
4550 "gt": createPositionalPseudo(function( matchIndexes, length, argument ) {
4551 for ( var i = argument < 0 ? argument + length : argument; ++i < length; ) {
4552 matchIndexes.push( i );
4553 }
4554 return matchIndexes;
4555 })
4556 }
4557 };
4558
4559 function siblingCheck( a, b, ret ) {
4560 if ( a === b ) {
4561 return ret;
4562 }
4563
4564 var cur = a.nextSibling;
4565
4566 while ( cur ) {
4567 if ( cur === b ) {
4568 return -1;
4569 }
4570
4571 cur = cur.nextSibling;
4572 }
4573
4574 return 1;
4575 }
4576
4577 sortOrder = docElem.compareDocumentPosition ?
4578 function( a, b ) {
4579 if ( a === b ) {
4580 hasDuplicate = true;
4581 return 0;
4582 }
4583
4584 return ( !a.compareDocumentPosition || !b.compareDocumentPosition ?
4585 a.compareDocumentPosition :
4586 a.compareDocumentPosition(b) & 4
4587 ) ? -1 : 1;
4588 } :
4589 function( a, b ) {
4590 // The nodes are identical, we can exit early
4591 if ( a === b ) {
4592 hasDuplicate = true;
4593 return 0;
4594
4595 // Fallback to using sourceIndex (in IE) if it's available on both nodes
4596 } else if ( a.sourceIndex && b.sourceIndex ) {
4597 return a.sourceIndex - b.sourceIndex;
4598 }
4599
4600 var al, bl,
4601 ap = [],
4602 bp = [],
4603 aup = a.parentNode,
4604 bup = b.parentNode,
4605 cur = aup;
4606
4607 // If the nodes are siblings (or identical) we can do a quick check
4608 if ( aup === bup ) {
4609 return siblingCheck( a, b );
4610
4611 // If no parents were found then the nodes are disconnected
4612 } else if ( !aup ) {
4613 return -1;
4614
4615 } else if ( !bup ) {
4616 return 1;
4617 }
4618
4619 // Otherwise they're somewhere else in the tree so we need
4620 // to build up a full list of the parentNodes for comparison
4621 while ( cur ) {
4622 ap.unshift( cur );
4623 cur = cur.parentNode;
4624 }
4625
4626 cur = bup;
4627
4628 while ( cur ) {
4629 bp.unshift( cur );
4630 cur = cur.parentNode;
4631 }
4632
4633 al = ap.length;
4634 bl = bp.length;
4635
4636 // Start walking down the tree looking for a discrepancy
4637 for ( var i = 0; i < al && i < bl; i++ ) {
4638 if ( ap[i] !== bp[i] ) {
4639 return siblingCheck( ap[i], bp[i] );
4640 }
4641 }
4642
4643 // We ended someplace up the tree so do a sibling check
4644 return i === al ?
4645 siblingCheck( a, bp[i], -1 ) :
4646 siblingCheck( ap[i], b, 1 );
4647 };
4648
4649 // Always assume the presence of duplicates if sort doesn't
4650 // pass them to our comparison function (as in Google Chrome).
4651 [0, 0].sort( sortOrder );
4652 baseHasDuplicate = !hasDuplicate;
4653
4654 // Document sorting and removing duplicates
4655 Sizzle.uniqueSort = function( results ) {
4656 var elem,
4657 duplicates = [],
4658 i = 1,
4659 j = 0;
4660
4661 hasDuplicate = baseHasDuplicate;
4662 results.sort( sortOrder );
4663
4664 if ( hasDuplicate ) {
4665 for ( ; (elem = results[i]); i++ ) {
4666 if ( elem === results[ i - 1 ] ) {
4667 j = duplicates.push( i );
4668 }
4669 }
4670 while ( j-- ) {
4671 results.splice( duplicates[ j ], 1 );
4672 }
4673 }
4674
4675 return results;
4676 };
4677
4678 Sizzle.error = function( msg ) {
4679 throw new Error( "Syntax error, unrecognized expression: " + msg );
4680 };
4681
4682 function tokenize( selector, parseOnly ) {
4683 var matched, match, tokens, type,
4684 soFar, groups, preFilters,
4685 cached = tokenCache[ expando ][ selector + " " ];
4686
4687 if ( cached ) {
4688 return parseOnly ? 0 : cached.slice( 0 );
4689 }
4690
4691 soFar = selector;
4692 groups = [];
4693 preFilters = Expr.preFilter;
4694
4695 while ( soFar ) {
4696
4697 // Comma and first run
4698 if ( !matched || (match = rcomma.exec( soFar )) ) {
4699 if ( match ) {
4700 // Don't consume trailing commas as valid
4701 soFar = soFar.slice( match[0].length ) || soFar;
4702 }
4703 groups.push( tokens = [] );
4704 }
4705
4706 matched = false;
4707
4708 // Combinators
4709 if ( (match = rcombinators.exec( soFar )) ) {
4710 tokens.push( matched = new Token( match.shift() ) );
4711 soFar = soFar.slice( matched.length );
4712
4713 // Cast descendant combinators to space
4714 matched.type = match[0].replace( rtrim, " " );
4715 }
4716
4717 // Filters
4718 for ( type in Expr.filter ) {
4719 if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] ||
4720 (match = preFilters[ type ]( match ))) ) {
4721
4722 tokens.push( matched = new Token( match.shift() ) );
4723 soFar = soFar.slice( matched.length );
4724 matched.type = type;
4725 matched.matches = match;
4726 }
4727 }
4728
4729 if ( !matched ) {
4730 break;
4731 }
4732 }
4733
4734 // Return the length of the invalid excess
4735 // if we're just parsing
4736 // Otherwise, throw an error or return tokens
4737 return parseOnly ?
4738 soFar.length :
4739 soFar ?
4740 Sizzle.error( selector ) :
4741 // Cache the tokens
4742 tokenCache( selector, groups ).slice( 0 );
4743 }
4744
4745 function addCombinator( matcher, combinator, base ) {
4746 var dir = combinator.dir,
4747 checkNonElements = base && combinator.dir === "parentNode",
4748 doneName = done++;
4749
4750 return combinator.first ?
4751 // Check against closest ancestor/preceding element
4752 function( elem, context, xml ) {
4753 while ( (elem = elem[ dir ]) ) {
4754 if ( checkNonElements || elem.nodeType === 1 ) {
4755 return matcher( elem, context, xml );
4756 }
4757 }
4758 } :
4759
4760 // Check against all ancestor/preceding elements
4761 function( elem, context, xml ) {
4762 // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
4763 if ( !xml ) {
4764 var cache,
4765 dirkey = dirruns + " " + doneName + " ",
4766 cachedkey = dirkey + cachedruns;
4767 while ( (elem = elem[ dir ]) ) {
4768 if ( checkNonElements || elem.nodeType === 1 ) {
4769 if ( (cache = elem[ expando ]) === cachedkey ) {
4770 return elem.sizset;
4771 } else if ( typeof cache === "string" && cache.indexOf(dirkey) === 0 ) {
4772 if ( elem.sizset ) {
4773 return elem;
4774 }
4775 } else {
4776 elem[ expando ] = cachedkey;
4777 if ( matcher( elem, context, xml ) ) {
4778 elem.sizset = true;
4779 return elem;
4780 }
4781 elem.sizset = false;
4782 }
4783 }
4784 }
4785 } else {
4786 while ( (elem = elem[ dir ]) ) {
4787 if ( checkNonElements || elem.nodeType === 1 ) {
4788 if ( matcher( elem, context, xml ) ) {
4789 return elem;
4790 }
4791 }
4792 }
4793 }
4794 };
4795 }
4796
4797 function elementMatcher( matchers ) {
4798 return matchers.length > 1 ?
4799 function( elem, context, xml ) {
4800 var i = matchers.length;
4801 while ( i-- ) {
4802 if ( !matchers[i]( elem, context, xml ) ) {
4803 return false;
4804 }
4805 }
4806 return true;
4807 } :
4808 matchers[0];
4809 }
4810
4811 function condense( unmatched, map, filter, context, xml ) {
4812 var elem,
4813 newUnmatched = [],
4814 i = 0,
4815 len = unmatched.length,
4816 mapped = map != null;
4817
4818 for ( ; i < len; i++ ) {
4819 if ( (elem = unmatched[i]) ) {
4820 if ( !filter || filter( elem, context, xml ) ) {
4821 newUnmatched.push( elem );
4822 if ( mapped ) {
4823 map.push( i );
4824 }
4825 }
4826 }
4827 }
4828
4829 return newUnmatched;
4830 }
4831
4832 function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
4833 if ( postFilter && !postFilter[ expando ] ) {
4834 postFilter = setMatcher( postFilter );
4835 }
4836 if ( postFinder && !postFinder[ expando ] ) {
4837 postFinder = setMatcher( postFinder, postSelector );
4838 }
4839 return markFunction(function( seed, results, context, xml ) {
4840 var temp, i, elem,
4841 preMap = [],
4842 postMap = [],
4843 preexisting = results.length,
4844
4845 // Get initial elements from seed or context
4846 elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ),
4847
4848 // Prefilter to get matcher input, preserving a map for seed-results synchronization
4849 matcherIn = preFilter && ( seed || !selector ) ?
4850 condense( elems, preMap, preFilter, context, xml ) :
4851 elems,
4852
4853 matcherOut = matcher ?
4854 // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
4855 postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
4856
4857 // ...intermediate processing is necessary
4858 [] :
4859
4860 // ...otherwise use results directly
4861 results :
4862 matcherIn;
4863
4864 // Find primary matches
4865 if ( matcher ) {
4866 matcher( matcherIn, matcherOut, context, xml );
4867 }
4868
4869 // Apply postFilter
4870 if ( postFilter ) {
4871 temp = condense( matcherOut, postMap );
4872 postFilter( temp, [], context, xml );
4873
4874 // Un-match failing elements by moving them back to matcherIn
4875 i = temp.length;
4876 while ( i-- ) {
4877 if ( (elem = temp[i]) ) {
4878 matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem);
4879 }
4880 }
4881 }
4882
4883 if ( seed ) {
4884 if ( postFinder || preFilter ) {
4885 if ( postFinder ) {
4886 // Get the final matcherOut by condensing this intermediate into postFinder contexts
4887 temp = [];
4888 i = matcherOut.length;
4889 while ( i-- ) {
4890 if ( (elem = matcherOut[i]) ) {
4891 // Restore matcherIn since elem is not yet a final match
4892 temp.push( (matcherIn[i] = elem) );
4893 }
4894 }
4895 postFinder( null, (matcherOut = []), temp, xml );
4896 }
4897
4898 // Move matched elements from seed to results to keep them synchronized
4899 i = matcherOut.length;
4900 while ( i-- ) {
4901 if ( (elem = matcherOut[i]) &&
4902 (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) {
4903
4904 seed[temp] = !(results[temp] = elem);
4905 }
4906 }
4907 }
4908
4909 // Add elements to results, through postFinder if defined
4910 } else {
4911 matcherOut = condense(
4912 matcherOut === results ?
4913 matcherOut.splice( preexisting, matcherOut.length ) :
4914 matcherOut
4915 );
4916 if ( postFinder ) {
4917 postFinder( null, results, matcherOut, xml );
4918 } else {
4919 push.apply( results, matcherOut );
4920 }
4921 }
4922 });
4923 }
4924
4925 function matcherFromTokens( tokens ) {
4926 var checkContext, matcher, j,
4927 len = tokens.length,
4928 leadingRelative = Expr.relative[ tokens[0].type ],
4929 implicitRelative = leadingRelative || Expr.relative[" "],
4930 i = leadingRelative ? 1 : 0,
4931
4932 // The foundational matcher ensures that elements are reachable from top-level context(s)
4933 matchContext = addCombinator( function( elem ) {
4934 return elem === checkContext;
4935 }, implicitRelative, true ),
4936 matchAnyContext = addCombinator( function( elem ) {
4937 return indexOf.call( checkContext, elem ) > -1;
4938 }, implicitRelative, true ),
4939 matchers = [ function( elem, context, xml ) {
4940 return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
4941 (checkContext = context).nodeType ?
4942 matchContext( elem, context, xml ) :
4943 matchAnyContext( elem, context, xml ) );
4944 } ];
4945
4946 for ( ; i < len; i++ ) {
4947 if ( (matcher = Expr.relative[ tokens[i].type ]) ) {
4948 matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
4949 } else {
4950 matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches );
4951
4952 // Return special upon seeing a positional matcher
4953 if ( matcher[ expando ] ) {
4954 // Find the next relative operator (if any) for proper handling
4955 j = ++i;
4956 for ( ; j < len; j++ ) {
4957 if ( Expr.relative[ tokens[j].type ] ) {
4958 break;
4959 }
4960 }
4961 return setMatcher(
4962 i > 1 && elementMatcher( matchers ),
4963 i > 1 && tokens.slice( 0, i - 1 ).join("").replace( rtrim, "$1" ),
4964 matcher,
4965 i < j && matcherFromTokens( tokens.slice( i, j ) ),
4966 j < len && matcherFromTokens( (tokens = tokens.slice( j )) ),
4967 j < len && tokens.join("")
4968 );
4969 }
4970 matchers.push( matcher );
4971 }
4972 }
4973
4974 return elementMatcher( matchers );
4975 }
4976
4977 function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
4978 var bySet = setMatchers.length > 0,
4979 byElement = elementMatchers.length > 0,
4980 superMatcher = function( seed, context, xml, results, expandContext ) {
4981 var elem, j, matcher,
4982 setMatched = [],
4983 matchedCount = 0,
4984 i = "0",
4985 unmatched = seed && [],
4986 outermost = expandContext != null,
4987 contextBackup = outermostContext,
4988 // We must always have either seed elements or context
4989 elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ),
4990 // Nested matchers should use non-integer dirruns
4991 dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.E);
4992
4993 if ( outermost ) {
4994 outermostContext = context !== document && context;
4995 cachedruns = superMatcher.el;
4996 }
4997
4998 // Add elements passing elementMatchers directly to results
4999 for ( ; (elem = elems[i]) != null; i++ ) {
5000 if ( byElement && elem ) {
5001 for ( j = 0; (matcher = elementMatchers[j]); j++ ) {
5002 if ( matcher( elem, context, xml ) ) {
5003 results.push( elem );
5004 break;
5005 }
5006 }
5007 if ( outermost ) {
5008 dirruns = dirrunsUnique;
5009 cachedruns = ++superMatcher.el;
5010 }
5011 }
5012
5013 // Track unmatched elements for set filters
5014 if ( bySet ) {
5015 // They will have gone through all possible matchers
5016 if ( (elem = !matcher && elem) ) {
5017 matchedCount--;
5018 }
5019
5020 // Lengthen the array for every element, matched or not
5021 if ( seed ) {
5022 unmatched.push( elem );
5023 }
5024 }
5025 }
5026
5027 // Apply set filters to unmatched elements
5028 matchedCount += i;
5029 if ( bySet && i !== matchedCount ) {
5030 for ( j = 0; (matcher = setMatchers[j]); j++ ) {
5031 matcher( unmatched, setMatched, context, xml );
5032 }
5033
5034 if ( seed ) {
5035 // Reintegrate element matches to eliminate the need for sorting
5036 if ( matchedCount > 0 ) {
5037 while ( i-- ) {
5038 if ( !(unmatched[i] || setMatched[i]) ) {
5039 setMatched[i] = pop.call( results );
5040 }
5041 }
5042 }
5043
5044 // Discard index placeholder values to get only actual matches
5045 setMatched = condense( setMatched );
5046 }
5047
5048 // Add matches to results
5049 push.apply( results, setMatched );
5050
5051 // Seedless set matches succeeding multiple successful matchers stipulate sorting
5052 if ( outermost && !seed && setMatched.length > 0 &&
5053 ( matchedCount + setMatchers.length ) > 1 ) {
5054
5055 Sizzle.uniqueSort( results );
5056 }
5057 }
5058
5059 // Override manipulation of globals by nested matchers
5060 if ( outermost ) {
5061 dirruns = dirrunsUnique;
5062 outermostContext = contextBackup;
5063 }
5064
5065 return unmatched;
5066 };
5067
5068 superMatcher.el = 0;
5069 return bySet ?
5070 markFunction( superMatcher ) :
5071 superMatcher;
5072 }
5073
5074 compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) {
5075 var i,
5076 setMatchers = [],
5077 elementMatchers = [],
5078 cached = compilerCache[ expando ][ selector + " " ];
5079
5080 if ( !cached ) {
5081 // Generate a function of recursive functions that can be used to check each element
5082 if ( !group ) {
5083 group = tokenize( selector );
5084 }
5085 i = group.length;
5086 while ( i-- ) {
5087 cached = matcherFromTokens( group[i] );
5088 if ( cached[ expando ] ) {
5089 setMatchers.push( cached );
5090 } else {
5091 elementMatchers.push( cached );
5092 }
5093 }
5094
5095 // Cache the compiled function
5096 cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) );
5097 }
5098 return cached;
5099 };
5100
5101 function multipleContexts( selector, contexts, results ) {
5102 var i = 0,
5103 len = contexts.length;
5104 for ( ; i < len; i++ ) {
5105 Sizzle( selector, contexts[i], results );
5106 }
5107 return results;
5108 }
5109
5110 function select( selector, context, results, seed, xml ) {
5111 var i, tokens, token, type, find,
5112 match = tokenize( selector ),
5113 j = match.length;
5114
5115 if ( !seed ) {
5116 // Try to minimize operations if there is only one group
5117 if ( match.length === 1 ) {
5118
5119 // Take a shortcut and set the context if the root selector is an ID
5120 tokens = match[0] = match[0].slice( 0 );
5121 if ( tokens.length > 2 && (token = tokens[0]).type === "ID" &&
5122 context.nodeType === 9 && !xml &&
5123 Expr.relative[ tokens[1].type ] ) {
5124
5125 context = Expr.find["ID"]( token.matches[0].replace( rbackslash, "" ), context, xml )[0];
5126 if ( !context ) {
5127 return results;
5128 }
5129
5130 selector = selector.slice( tokens.shift().length );
5131 }
5132
5133 // Fetch a seed set for right-to-left matching
5134 for ( i = matchExpr["POS"].test( selector ) ? -1 : tokens.length - 1; i >= 0; i-- ) {
5135 token = tokens[i];
5136
5137 // Abort if we hit a combinator
5138 if ( Expr.relative[ (type = token.type) ] ) {
5139 break;
5140 }
5141 if ( (find = Expr.find[ type ]) ) {
5142 // Search, expanding context for leading sibling combinators
5143 if ( (seed = find(
5144 token.matches[0].replace( rbackslash, "" ),
5145 rsibling.test( tokens[0].type ) && context.parentNode || context,
5146 xml
5147 )) ) {
5148
5149 // If seed is empty or no tokens remain, we can return early
5150 tokens.splice( i, 1 );
5151 selector = seed.length && tokens.join("");
5152 if ( !selector ) {
5153 push.apply( results, slice.call( seed, 0 ) );
5154 return results;
5155 }
5156
5157 break;
5158 }
5159 }
5160 }
5161 }
5162 }
5163
5164 // Compile and execute a filtering function
5165 // Provide `match` to avoid retokenization if we modified the selector above
5166 compile( selector, match )(
5167 seed,
5168 context,
5169 xml,
5170 results,
5171 rsibling.test( selector )
5172 );
5173 return results;
5174 }
5175
5176 if ( document.querySelectorAll ) {
5177 (function() {
5178 var disconnectedMatch,
5179 oldSelect = select,
5180 rescape = /'|\\/g,
5181 rattributeQuotes = /\=[\x20\t\r\n\f]*([^'"\]]*)[\x20\t\r\n\f]*\]/g,
5182
5183 // qSa(:focus) reports false when true (Chrome 21), no need to also add to buggyMatches since matches checks buggyQSA
5184 // A support test would require too much code (would include document ready)
5185 rbuggyQSA = [ ":focus" ],
5186
5187 // matchesSelector(:active) reports false when true (IE9/Opera 11.5)
5188 // A support test would require too much code (would include document ready)
5189 // just skip matchesSelector for :active
5190 rbuggyMatches = [ ":active" ],
5191 matches = docElem.matchesSelector ||
5192 docElem.mozMatchesSelector ||
5193 docElem.webkitMatchesSelector ||
5194 docElem.oMatchesSelector ||
5195 docElem.msMatchesSelector;
5196
5197 // Build QSA regex
5198 // Regex strategy adopted from Diego Perini
5199 assert(function( div ) {
5200 // Select is set to empty string on purpose
5201 // This is to test IE's treatment of not explictly
5202 // setting a boolean content attribute,
5203 // since its presence should be enough
5204 // http://bugs.jquery.com/ticket/12359
5205 div.innerHTML = "<select><option selected=''></option></select>";
5206
5207 // IE8 - Some boolean attributes are not treated correctly
5208 if ( !div.querySelectorAll("[selected]").length ) {
5209 rbuggyQSA.push( "\\[" + whitespace + "*(?:checked|disabled|ismap|multiple|readonly|selected|value)" );
5210 }
5211
5212 // Webkit/Opera - :checked should return selected option elements
5213 // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
5214 // IE8 throws error here (do not put tests after this one)
5215 if ( !div.querySelectorAll(":checked").length ) {
5216 rbuggyQSA.push(":checked");
5217 }
5218 });
5219
5220 assert(function( div ) {
5221
5222 // Opera 10-12/IE9 - ^= $= *= and empty values
5223 // Should not select anything
5224 div.innerHTML = "<p test=''></p>";
5225 if ( div.querySelectorAll("[test^='']").length ) {
5226 rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:\"\"|'')" );
5227 }
5228
5229 // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
5230 // IE8 throws error here (do not put tests after this one)
5231 div.innerHTML = "<input type='hidden'/>";
5232 if ( !div.querySelectorAll(":enabled").length ) {
5233 rbuggyQSA.push(":enabled", ":disabled");
5234 }
5235 });
5236
5237 // rbuggyQSA always contains :focus, so no need for a length check
5238 rbuggyQSA = /* rbuggyQSA.length && */ new RegExp( rbuggyQSA.join("|") );
5239
5240 select = function( selector, context, results, seed, xml ) {
5241 // Only use querySelectorAll when not filtering,
5242 // when this is not xml,
5243 // and when no QSA bugs apply
5244 if ( !seed && !xml && !rbuggyQSA.test( selector ) ) {
5245 var groups, i,
5246 old = true,
5247 nid = expando,
5248 newContext = context,
5249 newSelector = context.nodeType === 9 && selector;
5250
5251 // qSA works strangely on Element-rooted queries
5252 // We can work around this by specifying an extra ID on the root
5253 // and working up from there (Thanks to Andrew Dupont for the technique)
5254 // IE 8 doesn't work on object elements
5255 if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
5256 groups = tokenize( selector );
5257
5258 if ( (old = context.getAttribute("id")) ) {
5259 nid = old.replace( rescape, "\\$&" );
5260 } else {
5261 context.setAttribute( "id", nid );
5262 }
5263 nid = "[id='" + nid + "'] ";
5264
5265 i = groups.length;
5266 while ( i-- ) {
5267 groups[i] = nid + groups[i].join("");
5268 }
5269 newContext = rsibling.test( selector ) && context.parentNode || context;
5270 newSelector = groups.join(",");
5271 }
5272
5273 if ( newSelector ) {
5274 try {
5275 push.apply( results, slice.call( newContext.querySelectorAll(
5276 newSelector
5277 ), 0 ) );
5278 return results;
5279 } catch(qsaError) {
5280 } finally {
5281 if ( !old ) {
5282 context.removeAttribute("id");
5283 }
5284 }
5285 }
5286 }
5287
5288 return oldSelect( selector, context, results, seed, xml );
5289 };
5290
5291 if ( matches ) {
5292 assert(function( div ) {
5293 // Check to see if it's possible to do matchesSelector
5294 // on a disconnected node (IE 9)
5295 disconnectedMatch = matches.call( div, "div" );
5296
5297 // This should fail with an exception
5298 // Gecko does not error, returns false instead
5299 try {
5300 matches.call( div, "[test!='']:sizzle" );
5301 rbuggyMatches.push( "!=", pseudos );
5302 } catch ( e ) {}
5303 });
5304
5305 // rbuggyMatches always contains :active and :focus, so no need for a length check
5306 rbuggyMatches = /* rbuggyMatches.length && */ new RegExp( rbuggyMatches.join("|") );
5307
5308 Sizzle.matchesSelector = function( elem, expr ) {
5309 // Make sure that attribute selectors are quoted
5310 expr = expr.replace( rattributeQuotes, "='$1']" );
5311
5312 // rbuggyMatches always contains :active, so no need for an existence check
5313 if ( !isXML( elem ) && !rbuggyMatches.test( expr ) && !rbuggyQSA.test( expr ) ) {
5314 try {
5315 var ret = matches.call( elem, expr );
5316
5317 // IE 9's matchesSelector returns false on disconnected nodes
5318 if ( ret || disconnectedMatch ||
5319 // As well, disconnected nodes are said to be in a document
5320 // fragment in IE 9
5321 elem.document && elem.document.nodeType !== 11 ) {
5322 return ret;
5323 }
5324 } catch(e) {}
5325 }
5326
5327 return Sizzle( expr, null, null, [ elem ] ).length > 0;
5328 };
5329 }
5330 })();
5331 }
5332
5333 // Deprecated
5334 Expr.pseudos["nth"] = Expr.pseudos["eq"];
5335
5336 // Back-compat
5337 function setFilters() {}
5338 Expr.filters = setFilters.prototype = Expr.pseudos;
5339 Expr.setFilters = new setFilters();
5340
5341 // Override sizzle attribute retrieval
5342 Sizzle.attr = jQuery.attr;
5343 jQuery.find = Sizzle;
5344 jQuery.expr = Sizzle.selectors;
5345 jQuery.expr[":"] = jQuery.expr.pseudos;
5346 jQuery.unique = Sizzle.uniqueSort;
5347 jQuery.text = Sizzle.getText;
5348 jQuery.isXMLDoc = Sizzle.isXML;
5349 jQuery.contains = Sizzle.contains;
5350
5351
5352 })( window );
5353 var runtil = /Until$/,
5354 rparentsprev = /^(?:parents|prev(?:Until|All))/,
5355 isSimple = /^.[^:#\[\.,]*$/,
5356 rneedsContext = jQuery.expr.match.needsContext,
5357 // methods guaranteed to produce a unique set when starting from a unique set
5358 guaranteedUnique = {
5359 children: true,
5360 contents: true,
5361 next: true,
5362 prev: true
5363 };
5364
5365 jQuery.fn.extend({
5366 find: function( selector ) {
5367 var i, l, length, n, r, ret,
5368 self = this;
5369
5370 if ( typeof selector !== "string" ) {
5371 return jQuery( selector ).filter(function() {
5372 for ( i = 0, l = self.length; i < l; i++ ) {
5373 if ( jQuery.contains( self[ i ], this ) ) {
5374 return true;
5375 }
5376 }
5377 });
5378 }
5379
5380 ret = this.pushStack( "", "find", selector );
5381
5382 for ( i = 0, l = this.length; i < l; i++ ) {
5383 length = ret.length;
5384 jQuery.find( selector, this[i], ret );
5385
5386 if ( i > 0 ) {
5387 // Make sure that the results are unique
5388 for ( n = length; n < ret.length; n++ ) {
5389 for ( r = 0; r < length; r++ ) {
5390 if ( ret[r] === ret[n] ) {
5391 ret.splice(n--, 1);
5392 break;
5393 }
5394 }
5395 }
5396 }
5397 }
5398
5399 return ret;
5400 },
5401
5402 has: function( target ) {
5403 var i,
5404 targets = jQuery( target, this ),
5405 len = targets.length;
5406
5407 return this.filter(function() {
5408 for ( i = 0; i < len; i++ ) {
5409 if ( jQuery.contains( this, targets[i] ) ) {
5410 return true;
5411 }
5412 }
5413 });
5414 },
5415
5416 not: function( selector ) {
5417 return this.pushStack( winnow(this, selector, false), "not", selector);
5418 },
5419
5420 filter: function( selector ) {
5421 return this.pushStack( winnow(this, selector, true), "filter", selector );
5422 },
5423
5424 is: function( selector ) {
5425 return !!selector && (
5426 typeof selector === "string" ?
5427 // If this is a positional/relative selector, check membership in the returned set
5428 // so $("p:first").is("p:last") won't return true for a doc with two "p".
5429 rneedsContext.test( selector ) ?
5430 jQuery( selector, this.context ).index( this[0] ) >= 0 :
5431 jQuery.filter( selector, this ).length > 0 :
5432 this.filter( selector ).length > 0 );
5433 },
5434
5435 closest: function( selectors, context ) {
5436 var cur,
5437 i = 0,
5438 l = this.length,
5439 ret = [],
5440 pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ?
5441 jQuery( selectors, context || this.context ) :
5442 0;
5443
5444 for ( ; i < l; i++ ) {
5445 cur = this[i];
5446
5447 while ( cur && cur.ownerDocument && cur !== context && cur.nodeType !== 11 ) {
5448 if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
5449 ret.push( cur );
5450 break;
5451 }
5452 cur = cur.parentNode;
5453 }
5454 }
5455
5456 ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
5457
5458 return this.pushStack( ret, "closest", selectors );
5459 },
5460
5461 // Determine the position of an element within
5462 // the matched set of elements
5463 index: function( elem ) {
5464
5465 // No argument, return index in parent
5466 if ( !elem ) {
5467 return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
5468 }
5469
5470 // index in selector
5471 if ( typeof elem === "string" ) {
5472 return jQuery.inArray( this[0], jQuery( elem ) );
5473 }
5474
5475 // Locate the position of the desired element
5476 return jQuery.inArray(
5477 // If it receives a jQuery object, the first element is used
5478 elem.jquery ? elem[0] : elem, this );
5479 },
5480
5481 add: function( selector, context ) {
5482 var set = typeof selector === "string" ?
5483 jQuery( selector, context ) :
5484 jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
5485 all = jQuery.merge( this.get(), set );
5486
5487 return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
5488 all :
5489 jQuery.unique( all ) );
5490 },
5491
5492 addBack: function( selector ) {
5493 return this.add( selector == null ?
5494 this.prevObject : this.prevObject.filter(selector)
5495 );
5496 }
5497 });
5498
5499 jQuery.fn.andSelf = jQuery.fn.addBack;
5500
5501 // A painfully simple check to see if an element is disconnected
5502 // from a document (should be improved, where feasible).
5503 function isDisconnected( node ) {
5504 return !node || !node.parentNode || node.parentNode.nodeType === 11;
5505 }
5506
5507 function sibling( cur, dir ) {
5508 do {
5509 cur = cur[ dir ];
5510 } while ( cur && cur.nodeType !== 1 );
5511
5512 return cur;
5513 }
5514
5515 jQuery.each({
5516 parent: function( elem ) {
5517 var parent = elem.parentNode;
5518 return parent && parent.nodeType !== 11 ? parent : null;
5519 },
5520 parents: function( elem ) {
5521 return jQuery.dir( elem, "parentNode" );
5522 },
5523 parentsUntil: function( elem, i, until ) {
5524 return jQuery.dir( elem, "parentNode", until );
5525 },
5526 next: function( elem ) {
5527 return sibling( elem, "nextSibling" );
5528 },
5529 prev: function( elem ) {
5530 return sibling( elem, "previousSibling" );
5531 },
5532 nextAll: function( elem ) {
5533 return jQuery.dir( elem, "nextSibling" );
5534 },
5535 prevAll: function( elem ) {
5536 return jQuery.dir( elem, "previousSibling" );
5537 },
5538 nextUntil: function( elem, i, until ) {
5539 return jQuery.dir( elem, "nextSibling", until );
5540 },
5541 prevUntil: function( elem, i, until ) {
5542 return jQuery.dir( elem, "previousSibling", until );
5543 },
5544 siblings: function( elem ) {
5545 return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
5546 },
5547 children: function( elem ) {
5548 return jQuery.sibling( elem.firstChild );
5549 },
5550 contents: function( elem ) {
5551 return jQuery.nodeName( elem, "iframe" ) ?
5552 elem.contentDocument || elem.contentWindow.document :
5553 jQuery.merge( [], elem.childNodes );
5554 }
5555 }, function( name, fn ) {
5556 jQuery.fn[ name ] = function( until, selector ) {
5557 var ret = jQuery.map( this, fn, until );
5558
5559 if ( !runtil.test( name ) ) {
5560 selector = until;
5561 }
5562
5563 if ( selector && typeof selector === "string" ) {
5564 ret = jQuery.filter( selector, ret );
5565 }
5566
5567 ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
5568
5569 if ( this.length > 1 && rparentsprev.test( name ) ) {
5570 ret = ret.reverse();
5571 }
5572
5573 return this.pushStack( ret, name, core_slice.call( arguments ).join(",") );
5574 };
5575 });
5576
5577 jQuery.extend({
5578 filter: function( expr, elems, not ) {
5579 if ( not ) {
5580 expr = ":not(" + expr + ")";
5581 }
5582
5583 return elems.length === 1 ?
5584 jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
5585 jQuery.find.matches(expr, elems);
5586 },
5587
5588 dir: function( elem, dir, until ) {
5589 var matched = [],
5590 cur = elem[ dir ];
5591
5592 while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
5593 if ( cur.nodeType === 1 ) {
5594 matched.push( cur );
5595 }
5596 cur = cur[dir];
5597 }
5598 return matched;
5599 },
5600
5601 sibling: function( n, elem ) {
5602 var r = [];
5603
5604 for ( ; n; n = n.nextSibling ) {
5605 if ( n.nodeType === 1 && n !== elem ) {
5606 r.push( n );
5607 }
5608 }
5609
5610 return r;
5611 }
5612 });
5613
5614 // Implement the identical functionality for filter and not
5615 function winnow( elements, qualifier, keep ) {
5616
5617 // Can't pass null or undefined to indexOf in Firefox 4
5618 // Set to 0 to skip string check
5619 qualifier = qualifier || 0;
5620
5621 if ( jQuery.isFunction( qualifier ) ) {
5622 return jQuery.grep(elements, function( elem, i ) {
5623 var retVal = !!qualifier.call( elem, i, elem );
5624 return retVal === keep;
5625 });
5626
5627 } else if ( qualifier.nodeType ) {
5628 return jQuery.grep(elements, function( elem, i ) {
5629 return ( elem === qualifier ) === keep;
5630 });
5631
5632 } else if ( typeof qualifier === "string" ) {
5633 var filtered = jQuery.grep(elements, function( elem ) {
5634 return elem.nodeType === 1;
5635 });
5636
5637 if ( isSimple.test( qualifier ) ) {
5638 return jQuery.filter(qualifier, filtered, !keep);
5639 } else {
5640 qualifier = jQuery.filter( qualifier, filtered );
5641 }
5642 }
5643
5644 return jQuery.grep(elements, function( elem, i ) {
5645 return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
5646 });
5647 }
5648 function createSafeFragment( document ) {
5649 var list = nodeNames.split( "|" ),
5650 safeFrag = document.createDocumentFragment();
5651
5652 if ( safeFrag.createElement ) {
5653 while ( list.length ) {
5654 safeFrag.createElement(
5655 list.pop()
5656 );
5657 }
5658 }
5659 return safeFrag;
5660 }
5661
5662 var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
5663 "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
5664 rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g,
5665 rleadingWhitespace = /^\s+/,
5666 rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
5667 rtagName = /<([\w:]+)/,
5668 rtbody = /<tbody/i,
5669 rhtml = /<|&#?\w+;/,
5670 rnoInnerhtml = /<(?:script|style|link)/i,
5671 rnocache = /<(?:script|object|embed|option|style)/i,
5672 rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"),
5673 rcheckableType = /^(?:checkbox|radio)$/,
5674 // checked="checked" or checked
5675 rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
5676 rscriptType = /\/(java|ecma)script/i,
5677 rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)|[\]\-]{2}>\s*$/g,
5678 wrapMap = {
5679 option: [ 1, "<select multiple='multiple'>", "</select>" ],
5680 legend: [ 1, "<fieldset>", "</fieldset>" ],
5681 thead: [ 1, "<table>", "</table>" ],
5682 tr: [ 2, "<table><tbody>", "</tbody></table>" ],
5683 td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
5684 col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
5685 area: [ 1, "<map>", "</map>" ],
5686 _default: [ 0, "", "" ]
5687 },
5688 safeFragment = createSafeFragment( document ),
5689 fragmentDiv = safeFragment.appendChild( document.createElement("div") );
5690
5691 wrapMap.optgroup = wrapMap.option;
5692 wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
5693 wrapMap.th = wrapMap.td;
5694
5695 // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags,
5696 // unless wrapped in a div with non-breaking characters in front of it.
5697 if ( !jQuery.support.htmlSerialize ) {
5698 wrapMap._default = [ 1, "X<div>", "</div>" ];
5699 }
5700
5701 jQuery.fn.extend({
5702 text: function( value ) {
5703 return jQuery.access( this, function( value ) {
5704 return value === undefined ?
5705 jQuery.text( this ) :
5706 this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) );
5707 }, null, value, arguments.length );
5708 },
5709
5710 wrapAll: function( html ) {
5711 if ( jQuery.isFunction( html ) ) {
5712 return this.each(function(i) {
5713 jQuery(this).wrapAll( html.call(this, i) );
5714 });
5715 }
5716
5717 if ( this[0] ) {
5718 // The elements to wrap the target around
5719 var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
5720
5721 if ( this[0].parentNode ) {
5722 wrap.insertBefore( this[0] );
5723 }
5724
5725 wrap.map(function() {
5726 var elem = this;
5727
5728 while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
5729 elem = elem.firstChild;
5730 }
5731
5732 return elem;
5733 }).append( this );
5734 }
5735
5736 return this;
5737 },
5738
5739 wrapInner: function( html ) {
5740 if ( jQuery.isFunction( html ) ) {
5741 return this.each(function(i) {
5742 jQuery(this).wrapInner( html.call(this, i) );
5743 });
5744 }
5745
5746 return this.each(function() {
5747 var self = jQuery( this ),
5748 contents = self.contents();
5749
5750 if ( contents.length ) {
5751 contents.wrapAll( html );
5752
5753 } else {
5754 self.append( html );
5755 }
5756 });
5757 },
5758
5759 wrap: function( html ) {
5760 var isFunction = jQuery.isFunction( html );
5761
5762 return this.each(function(i) {
5763 jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html );
5764 });
5765 },
5766
5767 unwrap: function() {
5768 return this.parent().each(function() {
5769 if ( !jQuery.nodeName( this, "body" ) ) {
5770 jQuery( this ).replaceWith( this.childNodes );
5771 }
5772 }).end();
5773 },
5774
5775 append: function() {
5776 return this.domManip(arguments, true, function( elem ) {
5777 if ( this.nodeType === 1 || this.nodeType === 11 ) {
5778 this.appendChild( elem );
5779 }
5780 });
5781 },
5782
5783 prepend: function() {
5784 return this.domManip(arguments, true, function( elem ) {
5785 if ( this.nodeType === 1 || this.nodeType === 11 ) {
5786 this.insertBefore( elem, this.firstChild );
5787 }
5788 });
5789 },
5790
5791 before: function() {
5792 if ( !isDisconnected( this[0] ) ) {
5793 return this.domManip(arguments, false, function( elem ) {
5794 this.parentNode.insertBefore( elem, this );
5795 });
5796 }
5797
5798 if ( arguments.length ) {
5799 var set = jQuery.clean( arguments );
5800 return this.pushStack( jQuery.merge( set, this ), "before", this.selector );
5801 }
5802 },
5803
5804 after: function() {
5805 if ( !isDisconnected( this[0] ) ) {
5806 return this.domManip(arguments, false, function( elem ) {
5807 this.parentNode.insertBefore( elem, this.nextSibling );
5808 });
5809 }
5810
5811 if ( arguments.length ) {
5812 var set = jQuery.clean( arguments );
5813 return this.pushStack( jQuery.merge( this, set ), "after", this.selector );
5814 }
5815 },
5816
5817 // keepData is for internal use only--do not document
5818 remove: function( selector, keepData ) {
5819 var elem,
5820 i = 0;
5821
5822 for ( ; (elem = this[i]) != null; i++ ) {
5823 if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
5824 if ( !keepData && elem.nodeType === 1 ) {
5825 jQuery.cleanData( elem.getElementsByTagName("*") );
5826 jQuery.cleanData( [ elem ] );
5827 }
5828
5829 if ( elem.parentNode ) {
5830 elem.parentNode.removeChild( elem );
5831 }
5832 }
5833 }
5834
5835 return this;
5836 },
5837
5838 empty: function() {
5839 var elem,
5840 i = 0;
5841
5842 for ( ; (elem = this[i]) != null; i++ ) {
5843 // Remove element nodes and prevent memory leaks
5844 if ( elem.nodeType === 1 ) {
5845 jQuery.cleanData( elem.getElementsByTagName("*") );
5846 }
5847
5848 // Remove any remaining nodes
5849 while ( elem.firstChild ) {
5850 elem.removeChild( elem.firstChild );
5851 }
5852 }
5853
5854 return this;
5855 },
5856
5857 clone: function( dataAndEvents, deepDataAndEvents ) {
5858 dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
5859 deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;
5860
5861 return this.map( function () {
5862 return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
5863 });
5864 },
5865
5866 html: function( value ) {
5867 return jQuery.access( this, function( value ) {
5868 var elem = this[0] || {},
5869 i = 0,
5870 l = this.length;
5871
5872 if ( value === undefined ) {
5873 return elem.nodeType === 1 ?
5874 elem.innerHTML.replace( rinlinejQuery, "" ) :
5875 undefined;
5876 }
5877
5878 // See if we can take a shortcut and just use innerHTML
5879 if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
5880 ( jQuery.support.htmlSerialize || !rnoshimcache.test( value ) ) &&
5881 ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) &&
5882 !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) {
5883
5884 value = value.replace( rxhtmlTag, "<$1></$2>" );
5885
5886 try {
5887 for (; i < l; i++ ) {
5888 // Remove element nodes and prevent memory leaks
5889 elem = this[i] || {};
5890 if ( elem.nodeType === 1 ) {
5891 jQuery.cleanData( elem.getElementsByTagName( "*" ) );
5892 elem.innerHTML = value;
5893 }
5894 }
5895
5896 elem = 0;
5897
5898 // If using innerHTML throws an exception, use the fallback method
5899 } catch(e) {}
5900 }
5901
5902 if ( elem ) {
5903 this.empty().append( value );
5904 }
5905 }, null, value, arguments.length );
5906 },
5907
5908 replaceWith: function( value ) {
5909 if ( !isDisconnected( this[0] ) ) {
5910 // Make sure that the elements are removed from the DOM before they are inserted
5911 // this can help fix replacing a parent with child elements
5912 if ( jQuery.isFunction( value ) ) {
5913 return this.each(function(i) {
5914 var self = jQuery(this), old = self.html();
5915 self.replaceWith( value.call( this, i, old ) );
5916 });
5917 }
5918
5919 if ( typeof value !== "string" ) {
5920 value = jQuery( value ).detach();
5921 }
5922
5923 return this.each(function() {
5924 var next = this.nextSibling,
5925 parent = this.parentNode;
5926
5927 jQuery( this ).remove();
5928
5929 if ( next ) {
5930 jQuery(next).before( value );
5931 } else {
5932 jQuery(parent).append( value );
5933 }
5934 });
5935 }
5936
5937 return this.length ?
5938 this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) :
5939 this;
5940 },
5941
5942 detach: function( selector ) {
5943 return this.remove( selector, true );
5944 },
5945
5946 domManip: function( args, table, callback ) {
5947
5948 // Flatten any nested arrays
5949 args = [].concat.apply( [], args );
5950
5951 var results, first, fragment, iNoClone,
5952 i = 0,
5953 value = args[0],
5954 scripts = [],
5955 l = this.length;
5956
5957 // We can't cloneNode fragments that contain checked, in WebKit
5958 if ( !jQuery.support.checkClone && l > 1 && typeof value === "string" && rchecked.test( value ) ) {
5959 return this.each(function() {
5960 jQuery(this).domManip( args, table, callback );
5961 });
5962 }
5963
5964 if ( jQuery.isFunction(value) ) {
5965 return this.each(function(i) {
5966 var self = jQuery(this);
5967 args[0] = value.call( this, i, table ? self.html() : undefined );
5968 self.domManip( args, table, callback );
5969 });
5970 }
5971
5972 if ( this[0] ) {
5973 results = jQuery.buildFragment( args, this, scripts );
5974 fragment = results.fragment;
5975 first = fragment.firstChild;
5976
5977 if ( fragment.childNodes.length === 1 ) {
5978 fragment = first;
5979 }
5980
5981 if ( first ) {
5982 table = table && jQuery.nodeName( first, "tr" );
5983
5984 // Use the original fragment for the last item instead of the first because it can end up
5985 // being emptied incorrectly in certain situations (#8070).
5986 // Fragments from the fragment cache must always be cloned and never used in place.
5987 for ( iNoClone = results.cacheable || l - 1; i < l; i++ ) {
5988 callback.call(
5989 table && jQuery.nodeName( this[i], "table" ) ?
5990 findOrAppend( this[i], "tbody" ) :
5991 this[i],
5992 i === iNoClone ?
5993 fragment :
5994 jQuery.clone( fragment, true, true )
5995 );
5996 }
5997 }
5998
5999 // Fix #11809: Avoid leaking memory
6000 fragment = first = null;
6001
6002 if ( scripts.length ) {
6003 jQuery.each( scripts, function( i, elem ) {
6004 if ( elem.src ) {
6005 if ( jQuery.ajax ) {
6006 jQuery.ajax({
6007 url: elem.src,
6008 type: "GET",
6009 dataType: "script",
6010 async: false,
6011 global: false,
6012 "throws": true
6013 });
6014 } else {
6015 jQuery.error("no ajax");
6016 }
6017 } else {
6018 jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "" ) );
6019 }
6020
6021 if ( elem.parentNode ) {
6022 elem.parentNode.removeChild( elem );
6023 }
6024 });
6025 }
6026 }
6027
6028 return this;
6029 }
6030 });
6031
6032 function findOrAppend( elem, tag ) {
6033 return elem.getElementsByTagName( tag )[0] || elem.appendChild( elem.ownerDocument.createElement( tag ) );
6034 }
6035
6036 function cloneCopyEvent( src, dest ) {
6037
6038 if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) {
6039 return;
6040 }
6041
6042 var type, i, l,
6043 oldData = jQuery._data( src ),
6044 curData = jQuery._data( dest, oldData ),
6045 events = oldData.events;
6046
6047 if ( events ) {
6048 delete curData.handle;
6049 curData.events = {};
6050
6051 for ( type in events ) {
6052 for ( i = 0, l = events[ type ].length; i < l; i++ ) {
6053 jQuery.event.add( dest, type, events[ type ][ i ] );
6054 }
6055 }
6056 }
6057
6058 // make the cloned public data object a copy from the original
6059 if ( curData.data ) {
6060 curData.data = jQuery.extend( {}, curData.data );
6061 }
6062 }
6063
6064 function cloneFixAttributes( src, dest ) {
6065 var nodeName;
6066
6067 // We do not need to do anything for non-Elements
6068 if ( dest.nodeType !== 1 ) {
6069 return;
6070 }
6071
6072 // clearAttributes removes the attributes, which we don't want,
6073 // but also removes the attachEvent events, which we *do* want
6074 if ( dest.clearAttributes ) {
6075 dest.clearAttributes();
6076 }
6077
6078 // mergeAttributes, in contrast, only merges back on the
6079 // original attributes, not the events
6080 if ( dest.mergeAttributes ) {
6081 dest.mergeAttributes( src );
6082 }
6083
6084 nodeName = dest.nodeName.toLowerCase();
6085
6086 if ( nodeName === "object" ) {
6087 // IE6-10 improperly clones children of object elements using classid.
6088 // IE10 throws NoModificationAllowedError if parent is null, #12132.
6089 if ( dest.parentNode ) {
6090 dest.outerHTML = src.outerHTML;
6091 }
6092
6093 // This path appears unavoidable for IE9. When cloning an object
6094 // element in IE9, the outerHTML strategy above is not sufficient.
6095 // If the src has innerHTML and the destination does not,
6096 // copy the src.innerHTML into the dest.innerHTML. #10324
6097 if ( jQuery.support.html5Clone && (src.innerHTML && !jQuery.trim(dest.innerHTML)) ) {
6098 dest.innerHTML = src.innerHTML;
6099 }
6100
6101 } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) {
6102 // IE6-8 fails to persist the checked state of a cloned checkbox
6103 // or radio button. Worse, IE6-7 fail to give the cloned element
6104 // a checked appearance if the defaultChecked value isn't also set
6105
6106 dest.defaultChecked = dest.checked = src.checked;
6107
6108 // IE6-7 get confused and end up setting the value of a cloned
6109 // checkbox/radio button to an empty string instead of "on"
6110 if ( dest.value !== src.value ) {
6111 dest.value = src.value;
6112 }
6113
6114 // IE6-8 fails to return the selected option to the default selected
6115 // state when cloning options
6116 } else if ( nodeName === "option" ) {
6117 dest.selected = src.defaultSelected;
6118
6119 // IE6-8 fails to set the defaultValue to the correct value when
6120 // cloning other types of input fields
6121 } else if ( nodeName === "input" || nodeName === "textarea" ) {
6122 dest.defaultValue = src.defaultValue;
6123
6124 // IE blanks contents when cloning scripts
6125 } else if ( nodeName === "script" && dest.text !== src.text ) {
6126 dest.text = src.text;
6127 }
6128
6129 // Event data gets referenced instead of copied if the expando
6130 // gets copied too
6131 dest.removeAttribute( jQuery.expando );
6132 }
6133
6134 jQuery.buildFragment = function( args, context, scripts ) {
6135 var fragment, cacheable, cachehit,
6136 first = args[ 0 ];
6137
6138 // Set context from what may come in as undefined or a jQuery collection or a node
6139 // Updated to fix #12266 where accessing context[0] could throw an exception in IE9/10 &
6140 // also doubles as fix for #8950 where plain objects caused createDocumentFragment exception
6141 context = context || document;
6142 context = !context.nodeType && context[0] || context;
6143 context = context.ownerDocument || context;
6144
6145 // Only cache "small" (1/2 KB) HTML strings that are associated with the main document
6146 // Cloning options loses the selected state, so don't cache them
6147 // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
6148 // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
6149 // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501
6150 if ( args.length === 1 && typeof first === "string" && first.length < 512 && context === document &&
6151 first.charAt(0) === "<" && !rnocache.test( first ) &&
6152 (jQuery.support.checkClone || !rchecked.test( first )) &&
6153 (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) {
6154
6155 // Mark cacheable and look for a hit
6156 cacheable = true;
6157 fragment = jQuery.fragments[ first ];
6158 cachehit = fragment !== undefined;
6159 }
6160
6161 if ( !fragment ) {
6162 fragment = context.createDocumentFragment();
6163 jQuery.clean( args, context, fragment, scripts );
6164
6165 // Update the cache, but only store false
6166 // unless this is a second parsing of the same content
6167 if ( cacheable ) {
6168 jQuery.fragments[ first ] = cachehit && fragment;
6169 }
6170 }
6171
6172 return { fragment: fragment, cacheable: cacheable };
6173 };
6174
6175 jQuery.fragments = {};
6176
6177 jQuery.each({
6178 appendTo: "append",
6179 prependTo: "prepend",
6180 insertBefore: "before",
6181 insertAfter: "after",
6182 replaceAll: "replaceWith"
6183 }, function( name, original ) {
6184 jQuery.fn[ name ] = function( selector ) {
6185 var elems,
6186 i = 0,
6187 ret = [],
6188 insert = jQuery( selector ),
6189 l = insert.length,
6190 parent = this.length === 1 && this[0].parentNode;
6191
6192 if ( (parent == null || parent && parent.nodeType === 11 && parent.childNodes.length === 1) && l === 1 ) {
6193 insert[ original ]( this[0] );
6194 return this;
6195 } else {
6196 for ( ; i < l; i++ ) {
6197 elems = ( i > 0 ? this.clone(true) : this ).get();
6198 jQuery( insert[i] )[ original ]( elems );
6199 ret = ret.concat( elems );
6200 }
6201
6202 return this.pushStack( ret, name, insert.selector );
6203 }
6204 };
6205 });
6206
6207 function getAll( elem ) {
6208 if ( typeof elem.getElementsByTagName !== "undefined" ) {
6209 return elem.getElementsByTagName( "*" );
6210
6211 } else if ( typeof elem.querySelectorAll !== "undefined" ) {
6212 return elem.querySelectorAll( "*" );
6213
6214 } else {
6215 return [];
6216 }
6217 }
6218
6219 // Used in clean, fixes the defaultChecked property
6220 function fixDefaultChecked( elem ) {
6221 if ( rcheckableType.test( elem.type ) ) {
6222 elem.defaultChecked = elem.checked;
6223 }
6224 }
6225
6226 jQuery.extend({
6227 clone: function( elem, dataAndEvents, deepDataAndEvents ) {
6228 var srcElements,
6229 destElements,
6230 i,
6231 clone;
6232
6233 if ( jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) {
6234 clone = elem.cloneNode( true );
6235
6236 // IE<=8 does not properly clone detached, unknown element nodes
6237 } else {
6238 fragmentDiv.innerHTML = elem.outerHTML;
6239 fragmentDiv.removeChild( clone = fragmentDiv.firstChild );
6240 }
6241
6242 if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) &&
6243 (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) {
6244 // IE copies events bound via attachEvent when using cloneNode.
6245 // Calling detachEvent on the clone will also remove the events
6246 // from the original. In order to get around this, we use some
6247 // proprietary methods to clear the events. Thanks to MooTools
6248 // guys for this hotness.
6249
6250 cloneFixAttributes( elem, clone );
6251
6252 // Using Sizzle here is crazy slow, so we use getElementsByTagName instead
6253 srcElements = getAll( elem );
6254 destElements = getAll( clone );
6255
6256 // Weird iteration because IE will replace the length property
6257 // with an element if you are cloning the body and one of the
6258 // elements on the page has a name or id of "length"
6259 for ( i = 0; srcElements[i]; ++i ) {
6260 // Ensure that the destination node is not null; Fixes #9587
6261 if ( destElements[i] ) {
6262 cloneFixAttributes( srcElements[i], destElements[i] );
6263 }
6264 }
6265 }
6266
6267 // Copy the events from the original to the clone
6268 if ( dataAndEvents ) {
6269 cloneCopyEvent( elem, clone );
6270
6271 if ( deepDataAndEvents ) {
6272 srcElements = getAll( elem );
6273 destElements = getAll( clone );
6274
6275 for ( i = 0; srcElements[i]; ++i ) {
6276 cloneCopyEvent( srcElements[i], destElements[i] );
6277 }
6278 }
6279 }
6280
6281 srcElements = destElements = null;
6282
6283 // Return the cloned set
6284 return clone;
6285 },
6286
6287 clean: function( elems, context, fragment, scripts ) {
6288 var i, j, elem, tag, wrap, depth, div, hasBody, tbody, len, handleScript, jsTags,
6289 safe = context === document && safeFragment,
6290 ret = [];
6291
6292 // Ensure that context is a document
6293 if ( !context || typeof context.createDocumentFragment === "undefined" ) {
6294 context = document;
6295 }
6296
6297 // Use the already-created safe fragment if context permits
6298 for ( i = 0; (elem = elems[i]) != null; i++ ) {
6299 if ( typeof elem === "number" ) {
6300 elem += "";
6301 }
6302
6303 if ( !elem ) {
6304 continue;
6305 }
6306
6307 // Convert html string into DOM nodes
6308 if ( typeof elem === "string" ) {
6309 if ( !rhtml.test( elem ) ) {
6310 elem = context.createTextNode( elem );
6311 } else {
6312 // Ensure a safe container in which to render the html
6313 safe = safe || createSafeFragment( context );
6314 div = context.createElement("div");
6315 safe.appendChild( div );
6316
6317 // Fix "XHTML"-style tags in all browsers
6318 elem = elem.replace(rxhtmlTag, "<$1></$2>");
6319
6320 // Go to html and back, then peel off extra wrappers
6321 tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase();
6322 wrap = wrapMap[ tag ] || wrapMap._default;
6323 depth = wrap[0];
6324 div.innerHTML = wrap[1] + elem + wrap[2];
6325
6326 // Move to the right depth
6327 while ( depth-- ) {
6328 div = div.lastChild;
6329 }
6330
6331 // Remove IE's autoinserted <tbody> from table fragments
6332 if ( !jQuery.support.tbody ) {
6333
6334 // String was a <table>, *may* have spurious <tbody>
6335 hasBody = rtbody.test(elem);
6336 tbody = tag === "table" && !hasBody ?
6337 div.firstChild && div.firstChild.childNodes :
6338
6339 // String was a bare <thead> or <tfoot>
6340 wrap[1] === "<table>" && !hasBody ?
6341 div.childNodes :
6342 [];
6343
6344 for ( j = tbody.length - 1; j >= 0 ; --j ) {
6345 if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
6346 tbody[ j ].parentNode.removeChild( tbody[ j ] );
6347 }
6348 }
6349 }
6350
6351 // IE completely kills leading whitespace when innerHTML is used
6352 if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
6353 div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
6354 }
6355
6356 elem = div.childNodes;
6357
6358 // Take out of fragment container (we need a fresh div each time)
6359 div.parentNode.removeChild( div );
6360 }
6361 }
6362
6363 if ( elem.nodeType ) {
6364 ret.push( elem );
6365 } else {
6366 jQuery.merge( ret, elem );
6367 }
6368 }
6369
6370 // Fix #11356: Clear elements from safeFragment
6371 if ( div ) {
6372 elem = div = safe = null;
6373 }
6374
6375 // Reset defaultChecked for any radios and checkboxes
6376 // about to be appended to the DOM in IE 6/7 (#8060)
6377 if ( !jQuery.support.appendChecked ) {
6378 for ( i = 0; (elem = ret[i]) != null; i++ ) {
6379 if ( jQuery.nodeName( elem, "input" ) ) {
6380 fixDefaultChecked( elem );
6381 } else if ( typeof elem.getElementsByTagName !== "undefined" ) {
6382 jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked );
6383 }
6384 }
6385 }
6386
6387 // Append elements to a provided document fragment
6388 if ( fragment ) {
6389 // Special handling of each script element
6390 handleScript = function( elem ) {
6391 // Check if we consider it executable
6392 if ( !elem.type || rscriptType.test( elem.type ) ) {
6393 // Detach the script and store it in the scripts array (if provided) or the fragment
6394 // Return truthy to indicate that it has been handled
6395 return scripts ?
6396 scripts.push( elem.parentNode ? elem.parentNode.removeChild( elem ) : elem ) :
6397 fragment.appendChild( elem );
6398 }
6399 };
6400
6401 for ( i = 0; (elem = ret[i]) != null; i++ ) {
6402 // Check if we're done after handling an executable script
6403 if ( !( jQuery.nodeName( elem, "script" ) && handleScript( elem ) ) ) {
6404 // Append to fragment and handle embedded scripts
6405 fragment.appendChild( elem );
6406 if ( typeof elem.getElementsByTagName !== "undefined" ) {
6407 // handleScript alters the DOM, so use jQuery.merge to ensure snapshot iteration
6408 jsTags = jQuery.grep( jQuery.merge( [], elem.getElementsByTagName("script") ), handleScript );
6409
6410 // Splice the scripts into ret after their former ancestor and advance our index beyond them
6411 ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) );
6412 i += jsTags.length;
6413 }
6414 }
6415 }
6416 }
6417
6418 return ret;
6419 },
6420
6421 cleanData: function( elems, /* internal */ acceptData ) {
6422 var data, id, elem, type,
6423 i = 0,
6424 internalKey = jQuery.expando,
6425 cache = jQuery.cache,
6426 deleteExpando = jQuery.support.deleteExpando,
6427 special = jQuery.event.special;
6428
6429 for ( ; (elem = elems[i]) != null; i++ ) {
6430
6431 if ( acceptData || jQuery.acceptData( elem ) ) {
6432
6433 id = elem[ internalKey ];
6434 data = id && cache[ id ];
6435
6436 if ( data ) {
6437 if ( data.events ) {
6438 for ( type in data.events ) {
6439 if ( special[ type ] ) {
6440 jQuery.event.remove( elem, type );
6441
6442 // This is a shortcut to avoid jQuery.event.remove's overhead
6443 } else {
6444 jQuery.removeEvent( elem, type, data.handle );
6445 }
6446 }
6447 }
6448
6449 // Remove cache only if it was not already removed by jQuery.event.remove
6450 if ( cache[ id ] ) {
6451
6452 delete cache[ id ];
6453
6454 // IE does not allow us to delete expando properties from nodes,
6455 // nor does it have a removeAttribute function on Document nodes;
6456 // we must handle all of these cases
6457 if ( deleteExpando ) {
6458 delete elem[ internalKey ];
6459
6460 } else if ( elem.removeAttribute ) {
6461 elem.removeAttribute( internalKey );
6462
6463 } else {
6464 elem[ internalKey ] = null;
6465 }
6466
6467 jQuery.deletedIds.push( id );
6468 }
6469 }
6470 }
6471 }
6472 }
6473 });
6474 // Limit scope pollution from any deprecated API
6475 (function() {
6476
6477 var matched, browser;
6478
6479 // Use of jQuery.browser is frowned upon.
6480 // More details: http://api.jquery.com/jQuery.browser
6481 // jQuery.uaMatch maintained for back-compat
6482 jQuery.uaMatch = function( ua ) {
6483 ua = ua.toLowerCase();
6484
6485 var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
6486 /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
6487 /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
6488 /(msie) ([\w.]+)/.exec( ua ) ||
6489 ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
6490 [];
6491
6492 return {
6493 browser: match[ 1 ] || "",
6494 version: match[ 2 ] || "0"
6495 };
6496 };
6497
6498 matched = jQuery.uaMatch( navigator.userAgent );
6499 browser = {};
6500
6501 if ( matched.browser ) {
6502 browser[ matched.browser ] = true;
6503 browser.version = matched.version;
6504 }
6505
6506 // Chrome is Webkit, but Webkit is also Safari.
6507 if ( browser.chrome ) {
6508 browser.webkit = true;
6509 } else if ( browser.webkit ) {
6510 browser.safari = true;
6511 }
6512
6513 jQuery.browser = browser;
6514
6515 jQuery.sub = function() {
6516 function jQuerySub( selector, context ) {
6517 return new jQuerySub.fn.init( selector, context );
6518 }
6519 jQuery.extend( true, jQuerySub, this );
6520 jQuerySub.superclass = this;
6521 jQuerySub.fn = jQuerySub.prototype = this();
6522 jQuerySub.fn.constructor = jQuerySub;
6523 jQuerySub.sub = this.sub;
6524 jQuerySub.fn.init = function init( selector, context ) {
6525 if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
6526 context = jQuerySub( context );
6527 }
6528
6529 return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
6530 };
6531 jQuerySub.fn.init.prototype = jQuerySub.fn;
6532 var rootjQuerySub = jQuerySub(document);
6533 return jQuerySub;
6534 };
6535
6536 })();
6537 var curCSS, iframe, iframeDoc,
6538 ralpha = /alpha\([^)]*\)/i,
6539 ropacity = /opacity=([^)]*)/,
6540 rposition = /^(top|right|bottom|left)$/,
6541 // swappable if display is none or starts with table except "table", "table-cell", or "table-caption"
6542 // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display
6543 rdisplayswap = /^(none|table(?!-c[ea]).+)/,
6544 rmargin = /^margin/,
6545 rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ),
6546 rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ),
6547 rrelNum = new RegExp( "^([-+])=(" + core_pnum + ")", "i" ),
6548 elemdisplay = { BODY: "block" },
6549
6550 cssShow = { position: "absolute", visibility: "hidden", display: "block" },
6551 cssNormalTransform = {
6552 letterSpacing: 0,
6553 fontWeight: 400
6554 },
6555
6556 cssExpand = [ "Top", "Right", "Bottom", "Left" ],
6557 cssPrefixes = [ "Webkit", "O", "Moz", "ms" ],
6558
6559 eventsToggle = jQuery.fn.toggle;
6560
6561 // return a css property mapped to a potentially vendor prefixed property
6562 function vendorPropName( style, name ) {
6563
6564 // shortcut for names that are not vendor prefixed
6565 if ( name in style ) {
6566 return name;
6567 }
6568
6569 // check for vendor prefixed names
6570 var capName = name.charAt(0).toUpperCase() + name.slice(1),
6571 origName = name,
6572 i = cssPrefixes.length;
6573
6574 while ( i-- ) {
6575 name = cssPrefixes[ i ] + capName;
6576 if ( name in style ) {
6577 return name;
6578 }
6579 }
6580
6581 return origName;
6582 }
6583
6584 function isHidden( elem, el ) {
6585 elem = el || elem;
6586 return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem );
6587 }
6588
6589 function showHide( elements, show ) {
6590 var elem, display,
6591 values = [],
6592 index = 0,
6593 length = elements.length;
6594
6595 for ( ; index < length; index++ ) {
6596 elem = elements[ index ];
6597 if ( !elem.style ) {
6598 continue;
6599 }
6600 values[ index ] = jQuery._data( elem, "olddisplay" );
6601 if ( show ) {
6602 // Reset the inline display of this element to learn if it is
6603 // being hidden by cascaded rules or not
6604 if ( !values[ index ] && elem.style.display === "none" ) {
6605 elem.style.display = "";
6606 }
6607
6608 // Set elements which have been overridden with display: none
6609 // in a stylesheet to whatever the default browser style is
6610 // for such an element
6611 if ( elem.style.display === "" && isHidden( elem ) ) {
6612 values[ index ] = jQuery._data( elem, "olddisplay", css_defaultDisplay(elem.nodeName) );
6613 }
6614 } else {
6615 display = curCSS( elem, "display" );
6616
6617 if ( !values[ index ] && display !== "none" ) {
6618 jQuery._data( elem, "olddisplay", display );
6619 }
6620 }
6621 }
6622
6623 // Set the display of most of the elements in a second loop
6624 // to avoid the constant reflow
6625 for ( index = 0; index < length; index++ ) {
6626 elem = elements[ index ];
6627 if ( !elem.style ) {
6628 continue;
6629 }
6630 if ( !show || elem.style.display === "none" || elem.style.display === "" ) {
6631 elem.style.display = show ? values[ index ] || "" : "none";
6632 }
6633 }
6634
6635 return elements;
6636 }
6637
6638 jQuery.fn.extend({
6639 css: function( name, value ) {
6640 return jQuery.access( this, function( elem, name, value ) {
6641 return value !== undefined ?
6642 jQuery.style( elem, name, value ) :
6643 jQuery.css( elem, name );
6644 }, name, value, arguments.length > 1 );
6645 },
6646 show: function() {
6647 return showHide( this, true );
6648 },
6649 hide: function() {
6650 return showHide( this );
6651 },
6652 toggle: function( state, fn2 ) {
6653 var bool = typeof state === "boolean";
6654
6655 if ( jQuery.isFunction( state ) && jQuery.isFunction( fn2 ) ) {
6656 return eventsToggle.apply( this, arguments );
6657 }
6658
6659 return this.each(function() {
6660 if ( bool ? state : isHidden( this ) ) {
6661 jQuery( this ).show();
6662 } else {
6663 jQuery( this ).hide();
6664 }
6665 });
6666 }
6667 });
6668
6669 jQuery.extend({
6670 // Add in style property hooks for overriding the default
6671 // behavior of getting and setting a style property
6672 cssHooks: {
6673 opacity: {
6674 get: function( elem, computed ) {
6675 if ( computed ) {
6676 // We should always get a number back from opacity
6677 var ret = curCSS( elem, "opacity" );
6678 return ret === "" ? "1" : ret;
6679
6680 }
6681 }
6682 }
6683 },
6684
6685 // Exclude the following css properties to add px
6686 cssNumber: {
6687 "fillOpacity": true,
6688 "fontWeight": true,
6689 "lineHeight": true,
6690 "opacity": true,
6691 "orphans": true,
6692 "widows": true,
6693 "zIndex": true,
6694 "zoom": true
6695 },
6696
6697 // Add in properties whose names you wish to fix before
6698 // setting or getting the value
6699 cssProps: {
6700 // normalize float css property
6701 "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat"
6702 },
6703
6704 // Get and set the style property on a DOM Node
6705 style: function( elem, name, value, extra ) {
6706 // Don't set styles on text and comment nodes
6707 if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) {
6708 return;
6709 }
6710
6711 // Make sure that we're working with the right name
6712 var ret, type, hooks,
6713 origName = jQuery.camelCase( name ),
6714 style = elem.style;
6715
6716 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( style, origName ) );
6717
6718 // gets hook for the prefixed version
6719 // followed by the unprefixed version
6720 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6721
6722 // Check if we're setting a value
6723 if ( value !== undefined ) {
6724 type = typeof value;
6725
6726 // convert relative number strings (+= or -=) to relative numbers. #7345
6727 if ( type === "string" && (ret = rrelNum.exec( value )) ) {
6728 value = ( ret[1] + 1 ) * ret[2] + parseFloat( jQuery.css( elem, name ) );
6729 // Fixes bug #9237
6730 type = "number";
6731 }
6732
6733 // Make sure that NaN and null values aren't set. See: #7116
6734 if ( value == null || type === "number" && isNaN( value ) ) {
6735 return;
6736 }
6737
6738 // If a number was passed in, add 'px' to the (except for certain CSS properties)
6739 if ( type === "number" && !jQuery.cssNumber[ origName ] ) {
6740 value += "px";
6741 }
6742
6743 // If a hook was provided, use that value, otherwise just set the specified value
6744 if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value, extra )) !== undefined ) {
6745 // Wrapped to prevent IE from throwing errors when 'invalid' values are provided
6746 // Fixes bug #5509
6747 try {
6748 style[ name ] = value;
6749 } catch(e) {}
6750 }
6751
6752 } else {
6753 // If a hook was provided get the non-computed value from there
6754 if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) {
6755 return ret;
6756 }
6757
6758 // Otherwise just get the value from the style object
6759 return style[ name ];
6760 }
6761 },
6762
6763 css: function( elem, name, numeric, extra ) {
6764 var val, num, hooks,
6765 origName = jQuery.camelCase( name );
6766
6767 // Make sure that we're working with the right name
6768 name = jQuery.cssProps[ origName ] || ( jQuery.cssProps[ origName ] = vendorPropName( elem.style, origName ) );
6769
6770 // gets hook for the prefixed version
6771 // followed by the unprefixed version
6772 hooks = jQuery.cssHooks[ name ] || jQuery.cssHooks[ origName ];
6773
6774 // If a hook was provided get the computed value from there
6775 if ( hooks && "get" in hooks ) {
6776 val = hooks.get( elem, true, extra );
6777 }
6778
6779 // Otherwise, if a way to get the computed value exists, use that
6780 if ( val === undefined ) {
6781 val = curCSS( elem, name );
6782 }
6783
6784 //convert "normal" to computed value
6785 if ( val === "normal" && name in cssNormalTransform ) {
6786 val = cssNormalTransform[ name ];
6787 }
6788
6789 // Return, converting to number if forced or a qualifier was provided and val looks numeric
6790 if ( numeric || extra !== undefined ) {
6791 num = parseFloat( val );
6792 return numeric || jQuery.isNumeric( num ) ? num || 0 : val;
6793 }
6794 return val;
6795 },
6796
6797 // A method for quickly swapping in/out CSS properties to get correct calculations
6798 swap: function( elem, options, callback ) {
6799 var ret, name,
6800 old = {};
6801
6802 // Remember the old values, and insert the new ones
6803 for ( name in options ) {
6804 old[ name ] = elem.style[ name ];
6805 elem.style[ name ] = options[ name ];
6806 }
6807
6808 ret = callback.call( elem );
6809
6810 // Revert the old values
6811 for ( name in options ) {
6812 elem.style[ name ] = old[ name ];
6813 }
6814
6815 return ret;
6816 }
6817 });
6818
6819 // NOTE: To any future maintainer, we've window.getComputedStyle
6820 // because jsdom on node.js will break without it.
6821 if ( window.getComputedStyle ) {
6822 curCSS = function( elem, name ) {
6823 var ret, width, minWidth, maxWidth,
6824 computed = window.getComputedStyle( elem, null ),
6825 style = elem.style;
6826
6827 if ( computed ) {
6828
6829 // getPropertyValue is only needed for .css('filter') in IE9, see #12537
6830 ret = computed.getPropertyValue( name ) || computed[ name ];
6831
6832 if ( ret === "" && !jQuery.contains( elem.ownerDocument, elem ) ) {
6833 ret = jQuery.style( elem, name );
6834 }
6835
6836 // A tribute to the "awesome hack by Dean Edwards"
6837 // Chrome < 17 and Safari 5.0 uses "computed value" instead of "used value" for margin-right
6838 // Safari 5.1.7 (at least) returns percentage for a larger set of values, but width seems to be reliably pixels
6839 // this is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values
6840 if ( rnumnonpx.test( ret ) && rmargin.test( name ) ) {
6841 width = style.width;
6842 minWidth = style.minWidth;
6843 maxWidth = style.maxWidth;
6844
6845 style.minWidth = style.maxWidth = style.width = ret;
6846 ret = computed.width;
6847
6848 style.width = width;
6849 style.minWidth = minWidth;
6850 style.maxWidth = maxWidth;
6851 }
6852 }
6853
6854 return ret;
6855 };
6856 } else if ( document.documentElement.currentStyle ) {
6857 curCSS = function( elem, name ) {
6858 var left, rsLeft,
6859 ret = elem.currentStyle && elem.currentStyle[ name ],
6860 style = elem.style;
6861
6862 // Avoid setting ret to empty string here
6863 // so we don't default to auto
6864 if ( ret == null && style && style[ name ] ) {
6865 ret = style[ name ];
6866 }
6867
6868 // From the awesome hack by Dean Edwards
6869 // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
6870
6871 // If we're not dealing with a regular pixel number
6872 // but a number that has a weird ending, we need to convert it to pixels
6873 // but not position css attributes, as those are proportional to the parent element instead
6874 // and we can't measure the parent instead because it might trigger a "stacking dolls" problem
6875 if ( rnumnonpx.test( ret ) && !rposition.test( name ) ) {
6876
6877 // Remember the original values
6878 left = style.left;
6879 rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
6880
6881 // Put in the new values to get a computed value out
6882 if ( rsLeft ) {
6883 elem.runtimeStyle.left = elem.currentStyle.left;
6884 }
6885 style.left = name === "fontSize" ? "1em" : ret;
6886 ret = style.pixelLeft + "px";
6887
6888 // Revert the changed values
6889 style.left = left;
6890 if ( rsLeft ) {
6891 elem.runtimeStyle.left = rsLeft;
6892 }
6893 }
6894
6895 return ret === "" ? "auto" : ret;
6896 };
6897 }
6898
6899 function setPositiveNumber( elem, value, subtract ) {
6900 var matches = rnumsplit.exec( value );
6901 return matches ?
6902 Math.max( 0, matches[ 1 ] - ( subtract || 0 ) ) + ( matches[ 2 ] || "px" ) :
6903 value;
6904 }
6905
6906 function augmentWidthOrHeight( elem, name, extra, isBorderBox ) {
6907 var i = extra === ( isBorderBox ? "border" : "content" ) ?
6908 // If we already have the right measurement, avoid augmentation
6909 4 :
6910 // Otherwise initialize for horizontal or vertical properties
6911 name === "width" ? 1 : 0,
6912
6913 val = 0;
6914
6915 for ( ; i < 4; i += 2 ) {
6916 // both box models exclude margin, so add it if we want it
6917 if ( extra === "margin" ) {
6918 // we use jQuery.css instead of curCSS here
6919 // because of the reliableMarginRight CSS hook!
6920 val += jQuery.css( elem, extra + cssExpand[ i ], true );
6921 }
6922
6923 // From this point on we use curCSS for maximum performance (relevant in animations)
6924 if ( isBorderBox ) {
6925 // border-box includes padding, so remove it if we want content
6926 if ( extra === "content" ) {
6927 val -= parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
6928 }
6929
6930 // at this point, extra isn't border nor margin, so remove border
6931 if ( extra !== "margin" ) {
6932 val -= parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
6933 }
6934 } else {
6935 // at this point, extra isn't content, so add padding
6936 val += parseFloat( curCSS( elem, "padding" + cssExpand[ i ] ) ) || 0;
6937
6938 // at this point, extra isn't content nor padding, so add border
6939 if ( extra !== "padding" ) {
6940 val += parseFloat( curCSS( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0;
6941 }
6942 }
6943 }
6944
6945 return val;
6946 }
6947
6948 function getWidthOrHeight( elem, name, extra ) {
6949
6950 // Start with offset property, which is equivalent to the border-box value
6951 var val = name === "width" ? elem.offsetWidth : elem.offsetHeight,
6952 valueIsBorderBox = true,
6953 isBorderBox = jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box";
6954
6955 // some non-html elements return undefined for offsetWidth, so check for null/undefined
6956 // svg - https://bugzilla.mozilla.org/show_bug.cgi?id=649285
6957 // MathML - https://bugzilla.mozilla.org/show_bug.cgi?id=491668
6958 if ( val <= 0 || val == null ) {
6959 // Fall back to computed then uncomputed css if necessary
6960 val = curCSS( elem, name );
6961 if ( val < 0 || val == null ) {
6962 val = elem.style[ name ];
6963 }
6964
6965 // Computed unit is not pixels. Stop here and return.
6966 if ( rnumnonpx.test(val) ) {
6967 return val;
6968 }
6969
6970 // we need the check for style in case a browser which returns unreliable values
6971 // for getComputedStyle silently falls back to the reliable elem.style
6972 valueIsBorderBox = isBorderBox && ( jQuery.support.boxSizingReliable || val === elem.style[ name ] );
6973
6974 // Normalize "", auto, and prepare for extra
6975 val = parseFloat( val ) || 0;
6976 }
6977
6978 // use the active box-sizing model to add/subtract irrelevant styles
6979 return ( val +
6980 augmentWidthOrHeight(
6981 elem,
6982 name,
6983 extra || ( isBorderBox ? "border" : "content" ),
6984 valueIsBorderBox
6985 )
6986 ) + "px";
6987 }
6988
6989
6990 // Try to determine the default display value of an element
6991 function css_defaultDisplay( nodeName ) {
6992 if ( elemdisplay[ nodeName ] ) {
6993 return elemdisplay[ nodeName ];
6994 }
6995
6996 var elem = jQuery( "<" + nodeName + ">" ).appendTo( document.body ),
6997 display = elem.css("display");
6998 elem.remove();
6999
7000 // If the simple way fails,
7001 // get element's real default display by attaching it to a temp iframe
7002 if ( display === "none" || display === "" ) {
7003 // Use the already-created iframe if possible
7004 iframe = document.body.appendChild(
7005 iframe || jQuery.extend( document.createElement("iframe"), {
7006 frameBorder: 0,
7007 width: 0,
7008 height: 0
7009 })
7010 );
7011
7012 // Create a cacheable copy of the iframe document on first call.
7013 // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML
7014 // document to it; WebKit & Firefox won't allow reusing the iframe document.
7015 if ( !iframeDoc || !iframe.createElement ) {
7016 iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document;
7017 iframeDoc.write("<!doctype html><html><body>");
7018 iframeDoc.close();
7019 }
7020
7021 elem = iframeDoc.body.appendChild( iframeDoc.createElement(nodeName) );
7022
7023 display = curCSS( elem, "display" );
7024 document.body.removeChild( iframe );
7025 }
7026
7027 // Store the correct default display
7028 elemdisplay[ nodeName ] = display;
7029
7030 return display;
7031 }
7032
7033 jQuery.each([ "height", "width" ], function( i, name ) {
7034 jQuery.cssHooks[ name ] = {
7035 get: function( elem, computed, extra ) {
7036 if ( computed ) {
7037 // certain elements can have dimension info if we invisibly show them
7038 // however, it must have a current display style that would benefit from this
7039 if ( elem.offsetWidth === 0 && rdisplayswap.test( curCSS( elem, "display" ) ) ) {
7040 return jQuery.swap( elem, cssShow, function() {
7041 return getWidthOrHeight( elem, name, extra );
7042 });
7043 } else {
7044 return getWidthOrHeight( elem, name, extra );
7045 }
7046 }
7047 },
7048
7049 set: function( elem, value, extra ) {
7050 return setPositiveNumber( elem, value, extra ?
7051 augmentWidthOrHeight(
7052 elem,
7053 name,
7054 extra,
7055 jQuery.support.boxSizing && jQuery.css( elem, "boxSizing" ) === "border-box"
7056 ) : 0
7057 );
7058 }
7059 };
7060 });
7061
7062 if ( !jQuery.support.opacity ) {
7063 jQuery.cssHooks.opacity = {
7064 get: function( elem, computed ) {
7065 // IE uses filters for opacity
7066 return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ?
7067 ( 0.01 * parseFloat( RegExp.$1 ) ) + "" :
7068 computed ? "1" : "";
7069 },
7070
7071 set: function( elem, value ) {
7072 var style = elem.style,
7073 currentStyle = elem.currentStyle,
7074 opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "",
7075 filter = currentStyle && currentStyle.filter || style.filter || "";
7076
7077 // IE has trouble with opacity if it does not have layout
7078 // Force it by setting the zoom level
7079 style.zoom = 1;
7080
7081 // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652
7082 if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" &&
7083 style.removeAttribute ) {
7084
7085 // Setting style.filter to null, "" & " " still leave "filter:" in the cssText
7086 // if "filter:" is present at all, clearType is disabled, we want to avoid this
7087 // style.removeAttribute is IE Only, but so apparently is this code path...
7088 style.removeAttribute( "filter" );
7089
7090 // if there there is no filter style applied in a css rule, we are done
7091 if ( currentStyle && !currentStyle.filter ) {
7092 return;
7093 }
7094 }
7095
7096 // otherwise, set new filter values
7097 style.filter = ralpha.test( filter ) ?
7098 filter.replace( ralpha, opacity ) :
7099 filter + " " + opacity;
7100 }
7101 };
7102 }
7103
7104 // These hooks cannot be added until DOM ready because the support test
7105 // for it is not run until after DOM ready
7106 jQuery(function() {
7107 if ( !jQuery.support.reliableMarginRight ) {
7108 jQuery.cssHooks.marginRight = {
7109 get: function( elem, computed ) {
7110 // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
7111 // Work around by temporarily setting element display to inline-block
7112 return jQuery.swap( elem, { "display": "inline-block" }, function() {
7113 if ( computed ) {
7114 return curCSS( elem, "marginRight" );
7115 }
7116 });
7117 }
7118 };
7119 }
7120
7121 // Webkit bug: https://bugs.webkit.org/show_bug.cgi?id=29084
7122 // getComputedStyle returns percent when specified for top/left/bottom/right
7123 // rather than make the css module depend on the offset module, we just check for it here
7124 if ( !jQuery.support.pixelPosition && jQuery.fn.position ) {
7125 jQuery.each( [ "top", "left" ], function( i, prop ) {
7126 jQuery.cssHooks[ prop ] = {
7127 get: function( elem, computed ) {
7128 if ( computed ) {
7129 var ret = curCSS( elem, prop );
7130 // if curCSS returns percentage, fallback to offset
7131 return rnumnonpx.test( ret ) ? jQuery( elem ).position()[ prop ] + "px" : ret;
7132 }
7133 }
7134 };
7135 });
7136 }
7137
7138 });
7139
7140 if ( jQuery.expr && jQuery.expr.filters ) {
7141 jQuery.expr.filters.hidden = function( elem ) {
7142 return ( elem.offsetWidth === 0 && elem.offsetHeight === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || curCSS( elem, "display" )) === "none");
7143 };
7144
7145 jQuery.expr.filters.visible = function( elem ) {
7146 return !jQuery.expr.filters.hidden( elem );
7147 };
7148 }
7149
7150 // These hooks are used by animate to expand properties
7151 jQuery.each({
7152 margin: "",
7153 padding: "",
7154 border: "Width"
7155 }, function( prefix, suffix ) {
7156 jQuery.cssHooks[ prefix + suffix ] = {
7157 expand: function( value ) {
7158 var i,
7159
7160 // assumes a single number if not a string
7161 parts = typeof value === "string" ? value.split(" ") : [ value ],
7162 expanded = {};
7163
7164 for ( i = 0; i < 4; i++ ) {
7165 expanded[ prefix + cssExpand[ i ] + suffix ] =
7166 parts[ i ] || parts[ i - 2 ] || parts[ 0 ];
7167 }
7168
7169 return expanded;
7170 }
7171 };
7172
7173 if ( !rmargin.test( prefix ) ) {
7174 jQuery.cssHooks[ prefix + suffix ].set = setPositiveNumber;
7175 }
7176 });
7177 var r20 = /%20/g,
7178 rbracket = /\[\]$/,
7179 rCRLF = /\r?\n/g,
7180 rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
7181 rselectTextarea = /^(?:select|textarea)/i;
7182
7183 jQuery.fn.extend({
7184 serialize: function() {
7185 return jQuery.param( this.serializeArray() );
7186 },
7187 serializeArray: function() {
7188 return this.map(function(){
7189 return this.elements ? jQuery.makeArray( this.elements ) : this;
7190 })
7191 .filter(function(){
7192 return this.name && !this.disabled &&
7193 ( this.checked || rselectTextarea.test( this.nodeName ) ||
7194 rinput.test( this.type ) );
7195 })
7196 .map(function( i, elem ){
7197 var val = jQuery( this ).val();
7198
7199 return val == null ?
7200 null :
7201 jQuery.isArray( val ) ?
7202 jQuery.map( val, function( val, i ){
7203 return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7204 }) :
7205 { name: elem.name, value: val.replace( rCRLF, "\r\n" ) };
7206 }).get();
7207 }
7208 });
7209
7210 //Serialize an array of form elements or a set of
7211 //key/values into a query string
7212 jQuery.param = function( a, traditional ) {
7213 var prefix,
7214 s = [],
7215 add = function( key, value ) {
7216 // If value is a function, invoke it and return its value
7217 value = jQuery.isFunction( value ) ? value() : ( value == null ? "" : value );
7218 s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value );
7219 };
7220
7221 // Set traditional to true for jQuery <= 1.3.2 behavior.
7222 if ( traditional === undefined ) {
7223 traditional = jQuery.ajaxSettings && jQuery.ajaxSettings.traditional;
7224 }
7225
7226 // If an array was passed in, assume that it is an array of form elements.
7227 if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) {
7228 // Serialize the form elements
7229 jQuery.each( a, function() {
7230 add( this.name, this.value );
7231 });
7232
7233 } else {
7234 // If traditional, encode the "old" way (the way 1.3.2 or older
7235 // did it), otherwise encode params recursively.
7236 for ( prefix in a ) {
7237 buildParams( prefix, a[ prefix ], traditional, add );
7238 }
7239 }
7240
7241 // Return the resulting serialization
7242 return s.join( "&" ).replace( r20, "+" );
7243 };
7244
7245 function buildParams( prefix, obj, traditional, add ) {
7246 var name;
7247
7248 if ( jQuery.isArray( obj ) ) {
7249 // Serialize array item.
7250 jQuery.each( obj, function( i, v ) {
7251 if ( traditional || rbracket.test( prefix ) ) {
7252 // Treat each array item as a scalar.
7253 add( prefix, v );
7254
7255 } else {
7256 // If array item is non-scalar (array or object), encode its
7257 // numeric index to resolve deserialization ambiguity issues.
7258 // Note that rack (as of 1.0.0) can't currently deserialize
7259 // nested arrays properly, and attempting to do so may cause
7260 // a server error. Possible fixes are to modify rack's
7261 // deserialization algorithm or to provide an option or flag
7262 // to force array serialization to be shallow.
7263 buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add );
7264 }
7265 });
7266
7267 } else if ( !traditional && jQuery.type( obj ) === "object" ) {
7268 // Serialize object item.
7269 for ( name in obj ) {
7270 buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add );
7271 }
7272
7273 } else {
7274 // Serialize scalar item.
7275 add( prefix, obj );
7276 }
7277 }
7278 var
7279 // Document location
7280 ajaxLocParts,
7281 ajaxLocation,
7282
7283 rhash = /#.*$/,
7284 rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL
7285 // #7653, #8125, #8152: local protocol detection
7286 rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,
7287 rnoContent = /^(?:GET|HEAD)$/,
7288 rprotocol = /^\/\//,
7289 rquery = /\?/,
7290 rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
7291 rts = /([?&])_=[^&]*/,
7292 rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,
7293
7294 // Keep a copy of the old load method
7295 _load = jQuery.fn.load,
7296
7297 /* Prefilters
7298 * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example)
7299 * 2) These are called:
7300 * - BEFORE asking for a transport
7301 * - AFTER param serialization (s.data is a string if s.processData is true)
7302 * 3) key is the dataType
7303 * 4) the catchall symbol "*" can be used
7304 * 5) execution will start with transport dataType and THEN continue down to "*" if needed
7305 */
7306 prefilters = {},
7307
7308 /* Transports bindings
7309 * 1) key is the dataType
7310 * 2) the catchall symbol "*" can be used
7311 * 3) selection will start with transport dataType and THEN go to "*" if needed
7312 */
7313 transports = {},
7314
7315 // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression
7316 allTypes = ["*/"] + ["*"];
7317
7318 // #8138, IE may throw an exception when accessing
7319 // a field from window.location if document.domain has been set
7320 try {
7321 ajaxLocation = location.href;
7322 } catch( e ) {
7323 // Use the href attribute of an A element
7324 // since IE will modify it given document.location
7325 ajaxLocation = document.createElement( "a" );
7326 ajaxLocation.href = "";
7327 ajaxLocation = ajaxLocation.href;
7328 }
7329
7330 // Segment location into parts
7331 ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || [];
7332
7333 // Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport
7334 function addToPrefiltersOrTransports( structure ) {
7335
7336 // dataTypeExpression is optional and defaults to "*"
7337 return function( dataTypeExpression, func ) {
7338
7339 if ( typeof dataTypeExpression !== "string" ) {
7340 func = dataTypeExpression;
7341 dataTypeExpression = "*";
7342 }
7343
7344 var dataType, list, placeBefore,
7345 dataTypes = dataTypeExpression.toLowerCase().split( core_rspace ),
7346 i = 0,
7347 length = dataTypes.length;
7348
7349 if ( jQuery.isFunction( func ) ) {
7350 // For each dataType in the dataTypeExpression
7351 for ( ; i < length; i++ ) {
7352 dataType = dataTypes[ i ];
7353 // We control if we're asked to add before
7354 // any existing element
7355 placeBefore = /^\+/.test( dataType );
7356 if ( placeBefore ) {
7357 dataType = dataType.substr( 1 ) || "*";
7358 }
7359 list = structure[ dataType ] = structure[ dataType ] || [];
7360 // then we add to the structure accordingly
7361 list[ placeBefore ? "unshift" : "push" ]( func );
7362 }
7363 }
7364 };
7365 }
7366
7367 // Base inspection function for prefilters and transports
7368 function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR,
7369 dataType /* internal */, inspected /* internal */ ) {
7370
7371 dataType = dataType || options.dataTypes[ 0 ];
7372 inspected = inspected || {};
7373
7374 inspected[ dataType ] = true;
7375
7376 var selection,
7377 list = structure[ dataType ],
7378 i = 0,
7379 length = list ? list.length : 0,
7380 executeOnly = ( structure === prefilters );
7381
7382 for ( ; i < length && ( executeOnly || !selection ); i++ ) {
7383 selection = list[ i ]( options, originalOptions, jqXHR );
7384 // If we got redirected to another dataType
7385 // we try there if executing only and not done already
7386 if ( typeof selection === "string" ) {
7387 if ( !executeOnly || inspected[ selection ] ) {
7388 selection = undefined;
7389 } else {
7390 options.dataTypes.unshift( selection );
7391 selection = inspectPrefiltersOrTransports(
7392 structure, options, originalOptions, jqXHR, selection, inspected );
7393 }
7394 }
7395 }
7396 // If we're only executing or nothing was selected
7397 // we try the catchall dataType if not done already
7398 if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) {
7399 selection = inspectPrefiltersOrTransports(
7400 structure, options, originalOptions, jqXHR, "*", inspected );
7401 }
7402 // unnecessary when only executing (prefilters)
7403 // but it'll be ignored by the caller in that case
7404 return selection;
7405 }
7406
7407 // A special extend for ajax options
7408 // that takes "flat" options (not to be deep extended)
7409 // Fixes #9887
7410 function ajaxExtend( target, src ) {
7411 var key, deep,
7412 flatOptions = jQuery.ajaxSettings.flatOptions || {};
7413 for ( key in src ) {
7414 if ( src[ key ] !== undefined ) {
7415 ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ];
7416 }
7417 }
7418 if ( deep ) {
7419 jQuery.extend( true, target, deep );
7420 }
7421 }
7422
7423 jQuery.fn.load = function( url, params, callback ) {
7424 if ( typeof url !== "string" && _load ) {
7425 return _load.apply( this, arguments );
7426 }
7427
7428 // Don't do a request if no elements are being requested
7429 if ( !this.length ) {
7430 return this;
7431 }
7432
7433 var selector, type, response,
7434 self = this,
7435 off = url.indexOf(" ");
7436
7437 if ( off >= 0 ) {
7438 selector = url.slice( off, url.length );
7439 url = url.slice( 0, off );
7440 }
7441
7442 // If it's a function
7443 if ( jQuery.isFunction( params ) ) {
7444
7445 // We assume that it's the callback
7446 callback = params;
7447 params = undefined;
7448
7449 // Otherwise, build a param string
7450 } else if ( params && typeof params === "object" ) {
7451 type = "POST";
7452 }
7453
7454 // Request the remote document
7455 jQuery.ajax({
7456 url: url,
7457
7458 // if "type" variable is undefined, then "GET" method will be used
7459 type: type,
7460 dataType: "html",
7461 data: params,
7462 complete: function( jqXHR, status ) {
7463 if ( callback ) {
7464 self.each( callback, response || [ jqXHR.responseText, status, jqXHR ] );
7465 }
7466 }
7467 }).done(function( responseText ) {
7468
7469 // Save response for use in complete callback
7470 response = arguments;
7471
7472 // See if a selector was specified
7473 self.html( selector ?
7474
7475 // Create a dummy div to hold the results
7476 jQuery("<div>")
7477
7478 // inject the contents of the document in, removing the scripts
7479 // to avoid any 'Permission Denied' errors in IE
7480 .append( responseText.replace( rscript, "" ) )
7481
7482 // Locate the specified elements
7483 .find( selector ) :
7484
7485 // If not, just inject the full result
7486 responseText );
7487
7488 });
7489
7490 return this;
7491 };
7492
7493 // Attach a bunch of functions for handling common AJAX events
7494 jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){
7495 jQuery.fn[ o ] = function( f ){
7496 return this.on( o, f );
7497 };
7498 });
7499
7500 jQuery.each( [ "get", "post" ], function( i, method ) {
7501 jQuery[ method ] = function( url, data, callback, type ) {
7502 // shift arguments if data argument was omitted
7503 if ( jQuery.isFunction( data ) ) {
7504 type = type || callback;
7505 callback = data;
7506 data = undefined;
7507 }
7508
7509 return jQuery.ajax({
7510 type: method,
7511 url: url,
7512 data: data,
7513 success: callback,
7514 dataType: type
7515 });
7516 };
7517 });
7518
7519 jQuery.extend({
7520
7521 getScript: function( url, callback ) {
7522 return jQuery.get( url, undefined, callback, "script" );
7523 },
7524
7525 getJSON: function( url, data, callback ) {
7526 return jQuery.get( url, data, callback, "json" );
7527 },
7528
7529 // Creates a full fledged settings object into target
7530 // with both ajaxSettings and settings fields.
7531 // If target is omitted, writes into ajaxSettings.
7532 ajaxSetup: function( target, settings ) {
7533 if ( settings ) {
7534 // Building a settings object
7535 ajaxExtend( target, jQuery.ajaxSettings );
7536 } else {
7537 // Extending ajaxSettings
7538 settings = target;
7539 target = jQuery.ajaxSettings;
7540 }
7541 ajaxExtend( target, settings );
7542 return target;
7543 },
7544
7545 ajaxSettings: {
7546 url: ajaxLocation,
7547 isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ),
7548 global: true,
7549 type: "GET",
7550 contentType: "application/x-www-form-urlencoded; charset=UTF-8",
7551 processData: true,
7552 async: true,
7553 /*
7554 timeout: 0,
7555 data: null,
7556 dataType: null,
7557 username: null,
7558 password: null,
7559 cache: null,
7560 throws: false,
7561 traditional: false,
7562 headers: {},
7563 */
7564
7565 accepts: {
7566 xml: "application/xml, text/xml",
7567 html: "text/html",
7568 text: "text/plain",
7569 json: "application/json, text/javascript",
7570 "*": allTypes
7571 },
7572
7573 contents: {
7574 xml: /xml/,
7575 html: /html/,
7576 json: /json/
7577 },
7578
7579 responseFields: {
7580 xml: "responseXML",
7581 text: "responseText"
7582 },
7583
7584 // List of data converters
7585 // 1) key format is "source_type destination_type" (a single space in-between)
7586 // 2) the catchall symbol "*" can be used for source_type
7587 converters: {
7588
7589 // Convert anything to text
7590 "* text": window.String,
7591
7592 // Text to html (true = no transformation)
7593 "text html": true,
7594
7595 // Evaluate text as a json expression
7596 "text json": jQuery.parseJSON,
7597
7598 // Parse text as xml
7599 "text xml": jQuery.parseXML
7600 },
7601
7602 // For options that shouldn't be deep extended:
7603 // you can add your own custom options here if
7604 // and when you create one that shouldn't be
7605 // deep extended (see ajaxExtend)
7606 flatOptions: {
7607 context: true,
7608 url: true
7609 }
7610 },
7611
7612 ajaxPrefilter: addToPrefiltersOrTransports( prefilters ),
7613 ajaxTransport: addToPrefiltersOrTransports( transports ),
7614
7615 // Main method
7616 ajax: function( url, options ) {
7617
7618 // If url is an object, simulate pre-1.5 signature
7619 if ( typeof url === "object" ) {
7620 options = url;
7621 url = undefined;
7622 }
7623
7624 // Force options to be an object
7625 options = options || {};
7626
7627 var // ifModified key
7628 ifModifiedKey,
7629 // Response headers
7630 responseHeadersString,
7631 responseHeaders,
7632 // transport
7633 transport,
7634 // timeout handle
7635 timeoutTimer,
7636 // Cross-domain detection vars
7637 parts,
7638 // To know if global events are to be dispatched
7639 fireGlobals,
7640 // Loop variable
7641 i,
7642 // Create the final options object
7643 s = jQuery.ajaxSetup( {}, options ),
7644 // Callbacks context
7645 callbackContext = s.context || s,
7646 // Context for global events
7647 // It's the callbackContext if one was provided in the options
7648 // and if it's a DOM node or a jQuery collection
7649 globalEventContext = callbackContext !== s &&
7650 ( callbackContext.nodeType || callbackContext instanceof jQuery ) ?
7651 jQuery( callbackContext ) : jQuery.event,
7652 // Deferreds
7653 deferred = jQuery.Deferred(),
7654 completeDeferred = jQuery.Callbacks( "once memory" ),
7655 // Status-dependent callbacks
7656 statusCode = s.statusCode || {},
7657 // Headers (they are sent all at once)
7658 requestHeaders = {},
7659 requestHeadersNames = {},
7660 // The jqXHR state
7661 state = 0,
7662 // Default abort message
7663 strAbort = "canceled",
7664 // Fake xhr
7665 jqXHR = {
7666
7667 readyState: 0,
7668
7669 // Caches the header
7670 setRequestHeader: function( name, value ) {
7671 if ( !state ) {
7672 var lname = name.toLowerCase();
7673 name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name;
7674 requestHeaders[ name ] = value;
7675 }
7676 return this;
7677 },
7678
7679 // Raw string
7680 getAllResponseHeaders: function() {
7681 return state === 2 ? responseHeadersString : null;
7682 },
7683
7684 // Builds headers hashtable if needed
7685 getResponseHeader: function( key ) {
7686 var match;
7687 if ( state === 2 ) {
7688 if ( !responseHeaders ) {
7689 responseHeaders = {};
7690 while( ( match = rheaders.exec( responseHeadersString ) ) ) {
7691 responseHeaders[ match[1].toLowerCase() ] = match[ 2 ];
7692 }
7693 }
7694 match = responseHeaders[ key.toLowerCase() ];
7695 }
7696 return match === undefined ? null : match;
7697 },
7698
7699 // Overrides response content-type header
7700 overrideMimeType: function( type ) {
7701 if ( !state ) {
7702 s.mimeType = type;
7703 }
7704 return this;
7705 },
7706
7707 // Cancel the request
7708 abort: function( statusText ) {
7709 statusText = statusText || strAbort;
7710 if ( transport ) {
7711 transport.abort( statusText );
7712 }
7713 done( 0, statusText );
7714 return this;
7715 }
7716 };
7717
7718 // Callback for when everything is done
7719 // It is defined here because jslint complains if it is declared
7720 // at the end of the function (which would be more logical and readable)
7721 function done( status, nativeStatusText, responses, headers ) {
7722 var isSuccess, success, error, response, modified,
7723 statusText = nativeStatusText;
7724
7725 // Called once
7726 if ( state === 2 ) {
7727 return;
7728 }
7729
7730 // State is "done" now
7731 state = 2;
7732
7733 // Clear timeout if it exists
7734 if ( timeoutTimer ) {
7735 clearTimeout( timeoutTimer );
7736 }
7737
7738 // Dereference transport for early garbage collection
7739 // (no matter how long the jqXHR object will be used)
7740 transport = undefined;
7741
7742 // Cache response headers
7743 responseHeadersString = headers || "";
7744
7745 // Set readyState
7746 jqXHR.readyState = status > 0 ? 4 : 0;
7747
7748 // Get response data
7749 if ( responses ) {
7750 response = ajaxHandleResponses( s, jqXHR, responses );
7751 }
7752
7753 // If successful, handle type chaining
7754 if ( status >= 200 && status < 300 || status === 304 ) {
7755
7756 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
7757 if ( s.ifModified ) {
7758
7759 modified = jqXHR.getResponseHeader("Last-Modified");
7760 if ( modified ) {
7761 jQuery.lastModified[ ifModifiedKey ] = modified;
7762 }
7763 modified = jqXHR.getResponseHeader("Etag");
7764 if ( modified ) {
7765 jQuery.etag[ ifModifiedKey ] = modified;
7766 }
7767 }
7768
7769 // If not modified
7770 if ( status === 304 ) {
7771
7772 statusText = "notmodified";
7773 isSuccess = true;
7774
7775 // If we have data
7776 } else {
7777
7778 isSuccess = ajaxConvert( s, response );
7779 statusText = isSuccess.state;
7780 success = isSuccess.data;
7781 error = isSuccess.error;
7782 isSuccess = !error;
7783 }
7784 } else {
7785 // We extract error from statusText
7786 // then normalize statusText and status for non-aborts
7787 error = statusText;
7788 if ( !statusText || status ) {
7789 statusText = "error";
7790 if ( status < 0 ) {
7791 status = 0;
7792 }
7793 }
7794 }
7795
7796 // Set data for the fake xhr object
7797 jqXHR.status = status;
7798 jqXHR.statusText = ( nativeStatusText || statusText ) + "";
7799
7800 // Success/Error
7801 if ( isSuccess ) {
7802 deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] );
7803 } else {
7804 deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] );
7805 }
7806
7807 // Status-dependent callbacks
7808 jqXHR.statusCode( statusCode );
7809 statusCode = undefined;
7810
7811 if ( fireGlobals ) {
7812 globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ),
7813 [ jqXHR, s, isSuccess ? success : error ] );
7814 }
7815
7816 // Complete
7817 completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] );
7818
7819 if ( fireGlobals ) {
7820 globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] );
7821 // Handle the global AJAX counter
7822 if ( !( --jQuery.active ) ) {
7823 jQuery.event.trigger( "ajaxStop" );
7824 }
7825 }
7826 }
7827
7828 // Attach deferreds
7829 deferred.promise( jqXHR );
7830 jqXHR.success = jqXHR.done;
7831 jqXHR.error = jqXHR.fail;
7832 jqXHR.complete = completeDeferred.add;
7833
7834 // Status-dependent callbacks
7835 jqXHR.statusCode = function( map ) {
7836 if ( map ) {
7837 var tmp;
7838 if ( state < 2 ) {
7839 for ( tmp in map ) {
7840 statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ];
7841 }
7842 } else {
7843 tmp = map[ jqXHR.status ];
7844 jqXHR.always( tmp );
7845 }
7846 }
7847 return this;
7848 };
7849
7850 // Remove hash character (#7531: and string promotion)
7851 // Add protocol if not provided (#5866: IE7 issue with protocol-less urls)
7852 // We also use the url parameter if available
7853 s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" );
7854
7855 // Extract dataTypes list
7856 s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( core_rspace );
7857
7858 // A cross-domain request is in order when we have a protocol:host:port mismatch
7859 if ( s.crossDomain == null ) {
7860 parts = rurl.exec( s.url.toLowerCase() );
7861 s.crossDomain = !!( parts &&
7862 ( parts[ 1 ] !== ajaxLocParts[ 1 ] || parts[ 2 ] !== ajaxLocParts[ 2 ] ||
7863 ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) !=
7864 ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) )
7865 );
7866 }
7867
7868 // Convert data if not already a string
7869 if ( s.data && s.processData && typeof s.data !== "string" ) {
7870 s.data = jQuery.param( s.data, s.traditional );
7871 }
7872
7873 // Apply prefilters
7874 inspectPrefiltersOrTransports( prefilters, s, options, jqXHR );
7875
7876 // If request was aborted inside a prefilter, stop there
7877 if ( state === 2 ) {
7878 return jqXHR;
7879 }
7880
7881 // We can fire global events as of now if asked to
7882 fireGlobals = s.global;
7883
7884 // Uppercase the type
7885 s.type = s.type.toUpperCase();
7886
7887 // Determine if request has content
7888 s.hasContent = !rnoContent.test( s.type );
7889
7890 // Watch for a new set of requests
7891 if ( fireGlobals && jQuery.active++ === 0 ) {
7892 jQuery.event.trigger( "ajaxStart" );
7893 }
7894
7895 // More options handling for requests with no content
7896 if ( !s.hasContent ) {
7897
7898 // If data is available, append data to url
7899 if ( s.data ) {
7900 s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
7901 // #9682: remove data so that it's not used in an eventual retry
7902 delete s.data;
7903 }
7904
7905 // Get ifModifiedKey before adding the anti-cache parameter
7906 ifModifiedKey = s.url;
7907
7908 // Add anti-cache in url if needed
7909 if ( s.cache === false ) {
7910
7911 var ts = jQuery.now(),
7912 // try replacing _= if it is there
7913 ret = s.url.replace( rts, "$1_=" + ts );
7914
7915 // if nothing was replaced, add timestamp to the end
7916 s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" );
7917 }
7918 }
7919
7920 // Set the correct header, if data is being sent
7921 if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
7922 jqXHR.setRequestHeader( "Content-Type", s.contentType );
7923 }
7924
7925 // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
7926 if ( s.ifModified ) {
7927 ifModifiedKey = ifModifiedKey || s.url;
7928 if ( jQuery.lastModified[ ifModifiedKey ] ) {
7929 jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] );
7930 }
7931 if ( jQuery.etag[ ifModifiedKey ] ) {
7932 jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] );
7933 }
7934 }
7935
7936 // Set the Accepts header for the server, depending on the dataType
7937 jqXHR.setRequestHeader(
7938 "Accept",
7939 s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ?
7940 s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) :
7941 s.accepts[ "*" ]
7942 );
7943
7944 // Check for headers option
7945 for ( i in s.headers ) {
7946 jqXHR.setRequestHeader( i, s.headers[ i ] );
7947 }
7948
7949 // Allow custom headers/mimetypes and early abort
7950 if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) {
7951 // Abort if not done already and return
7952 return jqXHR.abort();
7953
7954 }
7955
7956 // aborting is no longer a cancellation
7957 strAbort = "abort";
7958
7959 // Install callbacks on deferreds
7960 for ( i in { success: 1, error: 1, complete: 1 } ) {
7961 jqXHR[ i ]( s[ i ] );
7962 }
7963
7964 // Get transport
7965 transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR );
7966
7967 // If no transport, we auto-abort
7968 if ( !transport ) {
7969 done( -1, "No Transport" );
7970 } else {
7971 jqXHR.readyState = 1;
7972 // Send global event
7973 if ( fireGlobals ) {
7974 globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] );
7975 }
7976 // Timeout
7977 if ( s.async && s.timeout > 0 ) {
7978 timeoutTimer = setTimeout( function(){
7979 jqXHR.abort( "timeout" );
7980 }, s.timeout );
7981 }
7982
7983 try {
7984 state = 1;
7985 transport.send( requestHeaders, done );
7986 } catch (e) {
7987 // Propagate exception as error if not done
7988 if ( state < 2 ) {
7989 done( -1, e );
7990 // Simply rethrow otherwise
7991 } else {
7992 throw e;
7993 }
7994 }
7995 }
7996
7997 return jqXHR;
7998 },
7999
8000 // Counter for holding the number of active queries
8001 active: 0,
8002
8003 // Last-Modified header cache for next request
8004 lastModified: {},
8005 etag: {}
8006
8007 });
8008
8009 /* Handles responses to an ajax request:
8010 * - sets all responseXXX fields accordingly
8011 * - finds the right dataType (mediates between content-type and expected dataType)
8012 * - returns the corresponding response
8013 */
8014 function ajaxHandleResponses( s, jqXHR, responses ) {
8015
8016 var ct, type, finalDataType, firstDataType,
8017 contents = s.contents,
8018 dataTypes = s.dataTypes,
8019 responseFields = s.responseFields;
8020
8021 // Fill responseXXX fields
8022 for ( type in responseFields ) {
8023 if ( type in responses ) {
8024 jqXHR[ responseFields[type] ] = responses[ type ];
8025 }
8026 }
8027
8028 // Remove auto dataType and get content-type in the process
8029 while( dataTypes[ 0 ] === "*" ) {
8030 dataTypes.shift();
8031 if ( ct === undefined ) {
8032 ct = s.mimeType || jqXHR.getResponseHeader( "content-type" );
8033 }
8034 }
8035
8036 // Check if we're dealing with a known content-type
8037 if ( ct ) {
8038 for ( type in contents ) {
8039 if ( contents[ type ] && contents[ type ].test( ct ) ) {
8040 dataTypes.unshift( type );
8041 break;
8042 }
8043 }
8044 }
8045
8046 // Check to see if we have a response for the expected dataType
8047 if ( dataTypes[ 0 ] in responses ) {
8048 finalDataType = dataTypes[ 0 ];
8049 } else {
8050 // Try convertible dataTypes
8051 for ( type in responses ) {
8052 if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) {
8053 finalDataType = type;
8054 break;
8055 }
8056 if ( !firstDataType ) {
8057 firstDataType = type;
8058 }
8059 }
8060 // Or just use first one
8061 finalDataType = finalDataType || firstDataType;
8062 }
8063
8064 // If we found a dataType
8065 // We add the dataType to the list if needed
8066 // and return the corresponding response
8067 if ( finalDataType ) {
8068 if ( finalDataType !== dataTypes[ 0 ] ) {
8069 dataTypes.unshift( finalDataType );
8070 }
8071 return responses[ finalDataType ];
8072 }
8073 }
8074
8075 // Chain conversions given the request and the original response
8076 function ajaxConvert( s, response ) {
8077
8078 var conv, conv2, current, tmp,
8079 // Work with a copy of dataTypes in case we need to modify it for conversion
8080 dataTypes = s.dataTypes.slice(),
8081 prev = dataTypes[ 0 ],
8082 converters = {},
8083 i = 0;
8084
8085 // Apply the dataFilter if provided
8086 if ( s.dataFilter ) {
8087 response = s.dataFilter( response, s.dataType );
8088 }
8089
8090 // Create converters map with lowercased keys
8091 if ( dataTypes[ 1 ] ) {
8092 for ( conv in s.converters ) {
8093 converters[ conv.toLowerCase() ] = s.converters[ conv ];
8094 }
8095 }
8096
8097 // Convert to each sequential dataType, tolerating list modification
8098 for ( ; (current = dataTypes[++i]); ) {
8099
8100 // There's only work to do if current dataType is non-auto
8101 if ( current !== "*" ) {
8102
8103 // Convert response if prev dataType is non-auto and differs from current
8104 if ( prev !== "*" && prev !== current ) {
8105
8106 // Seek a direct converter
8107 conv = converters[ prev + " " + current ] || converters[ "* " + current ];
8108
8109 // If none found, seek a pair
8110 if ( !conv ) {
8111 for ( conv2 in converters ) {
8112
8113 // If conv2 outputs current
8114 tmp = conv2.split(" ");
8115 if ( tmp[ 1 ] === current ) {
8116
8117 // If prev can be converted to accepted input
8118 conv = converters[ prev + " " + tmp[ 0 ] ] ||
8119 converters[ "* " + tmp[ 0 ] ];
8120 if ( conv ) {
8121 // Condense equivalence converters
8122 if ( conv === true ) {
8123 conv = converters[ conv2 ];
8124
8125 // Otherwise, insert the intermediate dataType
8126 } else if ( converters[ conv2 ] !== true ) {
8127 current = tmp[ 0 ];
8128 dataTypes.splice( i--, 0, current );
8129 }
8130
8131 break;
8132 }
8133 }
8134 }
8135 }
8136
8137 // Apply converter (if not an equivalence)
8138 if ( conv !== true ) {
8139
8140 // Unless errors are allowed to bubble, catch and return them
8141 if ( conv && s["throws"] ) {
8142 response = conv( response );
8143 } else {
8144 try {
8145 response = conv( response );
8146 } catch ( e ) {
8147 return { state: "parsererror", error: conv ? e : "No conversion from " + prev + " to " + current };
8148 }
8149 }
8150 }
8151 }
8152
8153 // Update prev for next iteration
8154 prev = current;
8155 }
8156 }
8157
8158 return { state: "success", data: response };
8159 }
8160 var oldCallbacks = [],
8161 rquestion = /\?/,
8162 rjsonp = /(=)\?(?=&|$)|\?\?/,
8163 nonce = jQuery.now();
8164
8165 // Default jsonp settings
8166 jQuery.ajaxSetup({
8167 jsonp: "callback",
8168 jsonpCallback: function() {
8169 var callback = oldCallbacks.pop() || ( jQuery.expando + "_" + ( nonce++ ) );
8170 this[ callback ] = true;
8171 return callback;
8172 }
8173 });
8174
8175 // Detect, normalize options and install callbacks for jsonp requests
8176 jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) {
8177
8178 var callbackName, overwritten, responseContainer,
8179 data = s.data,
8180 url = s.url,
8181 hasCallback = s.jsonp !== false,
8182 replaceInUrl = hasCallback && rjsonp.test( url ),
8183 replaceInData = hasCallback && !replaceInUrl && typeof data === "string" &&
8184 !( s.contentType || "" ).indexOf("application/x-www-form-urlencoded") &&
8185 rjsonp.test( data );
8186
8187 // Handle iff the expected data type is "jsonp" or we have a parameter to set
8188 if ( s.dataTypes[ 0 ] === "jsonp" || replaceInUrl || replaceInData ) {
8189
8190 // Get callback name, remembering preexisting value associated with it
8191 callbackName = s.jsonpCallback = jQuery.isFunction( s.jsonpCallback ) ?
8192 s.jsonpCallback() :
8193 s.jsonpCallback;
8194 overwritten = window[ callbackName ];
8195
8196 // Insert callback into url or form data
8197 if ( replaceInUrl ) {
8198 s.url = url.replace( rjsonp, "$1" + callbackName );
8199 } else if ( replaceInData ) {
8200 s.data = data.replace( rjsonp, "$1" + callbackName );
8201 } else if ( hasCallback ) {
8202 s.url += ( rquestion.test( url ) ? "&" : "?" ) + s.jsonp + "=" + callbackName;
8203 }
8204
8205 // Use data converter to retrieve json after script execution
8206 s.converters["script json"] = function() {
8207 if ( !responseContainer ) {
8208 jQuery.error( callbackName + " was not called" );
8209 }
8210 return responseContainer[ 0 ];
8211 };
8212
8213 // force json dataType
8214 s.dataTypes[ 0 ] = "json";
8215
8216 // Install callback
8217 window[ callbackName ] = function() {
8218 responseContainer = arguments;
8219 };
8220
8221 // Clean-up function (fires after converters)
8222 jqXHR.always(function() {
8223 // Restore preexisting value
8224 window[ callbackName ] = overwritten;
8225
8226 // Save back as free
8227 if ( s[ callbackName ] ) {
8228 // make sure that re-using the options doesn't screw things around
8229 s.jsonpCallback = originalSettings.jsonpCallback;
8230
8231 // save the callback name for future use
8232 oldCallbacks.push( callbackName );
8233 }
8234
8235 // Call if it was a function and we have a response
8236 if ( responseContainer && jQuery.isFunction( overwritten ) ) {
8237 overwritten( responseContainer[ 0 ] );
8238 }
8239
8240 responseContainer = overwritten = undefined;
8241 });
8242
8243 // Delegate to script
8244 return "script";
8245 }
8246 });
8247 // Install script dataType
8248 jQuery.ajaxSetup({
8249 accepts: {
8250 script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"
8251 },
8252 contents: {
8253 script: /javascript|ecmascript/
8254 },
8255 converters: {
8256 "text script": function( text ) {
8257 jQuery.globalEval( text );
8258 return text;
8259 }
8260 }
8261 });
8262
8263 // Handle cache's special case and global
8264 jQuery.ajaxPrefilter( "script", function( s ) {
8265 if ( s.cache === undefined ) {
8266 s.cache = false;
8267 }
8268 if ( s.crossDomain ) {
8269 s.type = "GET";
8270 s.global = false;
8271 }
8272 });
8273
8274 // Bind script tag hack transport
8275 jQuery.ajaxTransport( "script", function(s) {
8276
8277 // This transport only deals with cross domain requests
8278 if ( s.crossDomain ) {
8279
8280 var script,
8281 head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement;
8282
8283 return {
8284
8285 send: function( _, callback ) {
8286
8287 script = document.createElement( "script" );
8288
8289 script.async = "async";
8290
8291 if ( s.scriptCharset ) {
8292 script.charset = s.scriptCharset;
8293 }
8294
8295 script.src = s.url;
8296
8297 // Attach handlers for all browsers
8298 script.onload = script.onreadystatechange = function( _, isAbort ) {
8299
8300 if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) {
8301
8302 // Handle memory leak in IE
8303 script.onload = script.onreadystatechange = null;
8304
8305 // Remove the script
8306 if ( head && script.parentNode ) {
8307 head.removeChild( script );
8308 }
8309
8310 // Dereference the script
8311 script = undefined;
8312
8313 // Callback if not abort
8314 if ( !isAbort ) {
8315 callback( 200, "success" );
8316 }
8317 }
8318 };
8319 // Use insertBefore instead of appendChild to circumvent an IE6 bug.
8320 // This arises when a base node is used (#2709 and #4378).
8321 head.insertBefore( script, head.firstChild );
8322 },
8323
8324 abort: function() {
8325 if ( script ) {
8326 script.onload( 0, 1 );
8327 }
8328 }
8329 };
8330 }
8331 });
8332 var xhrCallbacks,
8333 // #5280: Internet Explorer will keep connections alive if we don't abort on unload
8334 xhrOnUnloadAbort = window.ActiveXObject ? function() {
8335 // Abort all pending requests
8336 for ( var key in xhrCallbacks ) {
8337 xhrCallbacks[ key ]( 0, 1 );
8338 }
8339 } : false,
8340 xhrId = 0;
8341
8342 // Functions to create xhrs
8343 function createStandardXHR() {
8344 try {
8345 return new window.XMLHttpRequest();
8346 } catch( e ) {}
8347 }
8348
8349 function createActiveXHR() {
8350 try {
8351 return new window.ActiveXObject( "Microsoft.XMLHTTP" );
8352 } catch( e ) {}
8353 }
8354
8355 // Create the request object
8356 // (This is still attached to ajaxSettings for backward compatibility)
8357 jQuery.ajaxSettings.xhr = window.ActiveXObject ?
8358 /* Microsoft failed to properly
8359 * implement the XMLHttpRequest in IE7 (can't request local files),
8360 * so we use the ActiveXObject when it is available
8361 * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
8362 * we need a fallback.
8363 */
8364 function() {
8365 return !this.isLocal && createStandardXHR() || createActiveXHR();
8366 } :
8367 // For all other browsers, use the standard XMLHttpRequest object
8368 createStandardXHR;
8369
8370 // Determine support properties
8371 (function( xhr ) {
8372 jQuery.extend( jQuery.support, {
8373 ajax: !!xhr,
8374 cors: !!xhr && ( "withCredentials" in xhr )
8375 });
8376 })( jQuery.ajaxSettings.xhr() );
8377
8378 // Create transport if the browser can provide an xhr
8379 if ( jQuery.support.ajax ) {
8380
8381 jQuery.ajaxTransport(function( s ) {
8382 // Cross domain only allowed if supported through XMLHttpRequest
8383 if ( !s.crossDomain || jQuery.support.cors ) {
8384
8385 var callback;
8386
8387 return {
8388 send: function( headers, complete ) {
8389
8390 // Get a new xhr
8391 var handle, i,
8392 xhr = s.xhr();
8393
8394 // Open the socket
8395 // Passing null username, generates a login popup on Opera (#2865)
8396 if ( s.username ) {
8397 xhr.open( s.type, s.url, s.async, s.username, s.password );
8398 } else {
8399 xhr.open( s.type, s.url, s.async );
8400 }
8401
8402 // Apply custom fields if provided
8403 if ( s.xhrFields ) {
8404 for ( i in s.xhrFields ) {
8405 xhr[ i ] = s.xhrFields[ i ];
8406 }
8407 }
8408
8409 // Override mime type if needed
8410 if ( s.mimeType && xhr.overrideMimeType ) {
8411 xhr.overrideMimeType( s.mimeType );
8412 }
8413
8414 // X-Requested-With header
8415 // For cross-domain requests, seeing as conditions for a preflight are
8416 // akin to a jigsaw puzzle, we simply never set it to be sure.
8417 // (it can always be set on a per-request basis or even using ajaxSetup)
8418 // For same-domain requests, won't change header if already provided.
8419 if ( !s.crossDomain && !headers["X-Requested-With"] ) {
8420 headers[ "X-Requested-With" ] = "XMLHttpRequest";
8421 }
8422
8423 // Need an extra try/catch for cross domain requests in Firefox 3
8424 try {
8425 for ( i in headers ) {
8426 xhr.setRequestHeader( i, headers[ i ] );
8427 }
8428 } catch( _ ) {}
8429
8430 // Do send the request
8431 // This may raise an exception which is actually
8432 // handled in jQuery.ajax (so no try/catch here)
8433 xhr.send( ( s.hasContent && s.data ) || null );
8434
8435 // Listener
8436 callback = function( _, isAbort ) {
8437
8438 var status,
8439 statusText,
8440 responseHeaders,
8441 responses,
8442 xml;
8443
8444 // Firefox throws exceptions when accessing properties
8445 // of an xhr when a network error occurred
8446 // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE)
8447 try {
8448
8449 // Was never called and is aborted or complete
8450 if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
8451
8452 // Only called once
8453 callback = undefined;
8454
8455 // Do not keep as active anymore
8456 if ( handle ) {
8457 xhr.onreadystatechange = jQuery.noop;
8458 if ( xhrOnUnloadAbort ) {
8459 delete xhrCallbacks[ handle ];
8460 }
8461 }
8462
8463 // If it's an abort
8464 if ( isAbort ) {
8465 // Abort it manually if needed
8466 if ( xhr.readyState !== 4 ) {
8467 xhr.abort();
8468 }
8469 } else {
8470 status = xhr.status;
8471 responseHeaders = xhr.getAllResponseHeaders();
8472 responses = {};
8473 xml = xhr.responseXML;
8474
8475 // Construct response list
8476 if ( xml && xml.documentElement /* #4958 */ ) {
8477 responses.xml = xml;
8478 }
8479
8480 // When requesting binary data, IE6-9 will throw an exception
8481 // on any attempt to access responseText (#11426)
8482 try {
8483 responses.text = xhr.responseText;
8484 } catch( e ) {
8485 }
8486
8487 // Firefox throws an exception when accessing
8488 // statusText for faulty cross-domain requests
8489 try {
8490 statusText = xhr.statusText;
8491 } catch( e ) {
8492 // We normalize with Webkit giving an empty statusText
8493 statusText = "";
8494 }
8495
8496 // Filter status for non standard behaviors
8497
8498 // If the request is local and we have data: assume a success
8499 // (success with no data won't get notified, that's the best we
8500 // can do given current implementations)
8501 if ( !status && s.isLocal && !s.crossDomain ) {
8502 status = responses.text ? 200 : 404;
8503 // IE - #1450: sometimes returns 1223 when it should be 204
8504 } else if ( status === 1223 ) {
8505 status = 204;
8506 }
8507 }
8508 }
8509 } catch( firefoxAccessException ) {
8510 if ( !isAbort ) {
8511 complete( -1, firefoxAccessException );
8512 }
8513 }
8514
8515 // Call complete if needed
8516 if ( responses ) {
8517 complete( status, statusText, responses, responseHeaders );
8518 }
8519 };
8520
8521 if ( !s.async ) {
8522 // if we're in sync mode we fire the callback
8523 callback();
8524 } else if ( xhr.readyState === 4 ) {
8525 // (IE6 & IE7) if it's in cache and has been
8526 // retrieved directly we need to fire the callback
8527 setTimeout( callback, 0 );
8528 } else {
8529 handle = ++xhrId;
8530 if ( xhrOnUnloadAbort ) {
8531 // Create the active xhrs callbacks list if needed
8532 // and attach the unload handler
8533 if ( !xhrCallbacks ) {
8534 xhrCallbacks = {};
8535 jQuery( window ).unload( xhrOnUnloadAbort );
8536 }
8537 // Add to list of active xhrs callbacks
8538 xhrCallbacks[ handle ] = callback;
8539 }
8540 xhr.onreadystatechange = callback;
8541 }
8542 },
8543
8544 abort: function() {
8545 if ( callback ) {
8546 callback(0,1);
8547 }
8548 }
8549 };
8550 }
8551 });
8552 }
8553 var fxNow, timerId,
8554 rfxtypes = /^(?:toggle|show|hide)$/,
8555 rfxnum = new RegExp( "^(?:([-+])=|)(" + core_pnum + ")([a-z%]*)$", "i" ),
8556 rrun = /queueHooks$/,
8557 animationPrefilters = [ defaultPrefilter ],
8558 tweeners = {
8559 "*": [function( prop, value ) {
8560 var end, unit,
8561 tween = this.createTween( prop, value ),
8562 parts = rfxnum.exec( value ),
8563 target = tween.cur(),
8564 start = +target || 0,
8565 scale = 1,
8566 maxIterations = 20;
8567
8568 if ( parts ) {
8569 end = +parts[2];
8570 unit = parts[3] || ( jQuery.cssNumber[ prop ] ? "" : "px" );
8571
8572 // We need to compute starting value
8573 if ( unit !== "px" && start ) {
8574 // Iteratively approximate from a nonzero starting point
8575 // Prefer the current property, because this process will be trivial if it uses the same units
8576 // Fallback to end or a simple constant
8577 start = jQuery.css( tween.elem, prop, true ) || end || 1;
8578
8579 do {
8580 // If previous iteration zeroed out, double until we get *something*
8581 // Use a string for doubling factor so we don't accidentally see scale as unchanged below
8582 scale = scale || ".5";
8583
8584 // Adjust and apply
8585 start = start / scale;
8586 jQuery.style( tween.elem, prop, start + unit );
8587
8588 // Update scale, tolerating zero or NaN from tween.cur()
8589 // And breaking the loop if scale is unchanged or perfect, or if we've just had enough
8590 } while ( scale !== (scale = tween.cur() / target) && scale !== 1 && --maxIterations );
8591 }
8592
8593 tween.unit = unit;
8594 tween.start = start;
8595 // If a +=/-= token was provided, we're doing a relative animation
8596 tween.end = parts[1] ? start + ( parts[1] + 1 ) * end : end;
8597 }
8598 return tween;
8599 }]
8600 };
8601
8602 // Animations created synchronously will run synchronously
8603 function createFxNow() {
8604 setTimeout(function() {
8605 fxNow = undefined;
8606 }, 0 );
8607 return ( fxNow = jQuery.now() );
8608 }
8609
8610 function createTweens( animation, props ) {
8611 jQuery.each( props, function( prop, value ) {
8612 var collection = ( tweeners[ prop ] || [] ).concat( tweeners[ "*" ] ),
8613 index = 0,
8614 length = collection.length;
8615 for ( ; index < length; index++ ) {
8616 if ( collection[ index ].call( animation, prop, value ) ) {
8617
8618 // we're done with this property
8619 return;
8620 }
8621 }
8622 });
8623 }
8624
8625 function Animation( elem, properties, options ) {
8626 var result,
8627 index = 0,
8628 tweenerIndex = 0,
8629 length = animationPrefilters.length,
8630 deferred = jQuery.Deferred().always( function() {
8631 // don't match elem in the :animated selector
8632 delete tick.elem;
8633 }),
8634 tick = function() {
8635 var currentTime = fxNow || createFxNow(),
8636 remaining = Math.max( 0, animation.startTime + animation.duration - currentTime ),
8637 // archaic crash bug won't allow us to use 1 - ( 0.5 || 0 ) (#12497)
8638 temp = remaining / animation.duration || 0,
8639 percent = 1 - temp,
8640 index = 0,
8641 length = animation.tweens.length;
8642
8643 for ( ; index < length ; index++ ) {
8644 animation.tweens[ index ].run( percent );
8645 }
8646
8647 deferred.notifyWith( elem, [ animation, percent, remaining ]);
8648
8649 if ( percent < 1 && length ) {
8650 return remaining;
8651 } else {
8652 deferred.resolveWith( elem, [ animation ] );
8653 return false;
8654 }
8655 },
8656 animation = deferred.promise({
8657 elem: elem,
8658 props: jQuery.extend( {}, properties ),
8659 opts: jQuery.extend( true, { specialEasing: {} }, options ),
8660 originalProperties: properties,
8661 originalOptions: options,
8662 startTime: fxNow || createFxNow(),
8663 duration: options.duration,
8664 tweens: [],
8665 createTween: function( prop, end, easing ) {
8666 var tween = jQuery.Tween( elem, animation.opts, prop, end,
8667 animation.opts.specialEasing[ prop ] || animation.opts.easing );
8668 animation.tweens.push( tween );
8669 return tween;
8670 },
8671 stop: function( gotoEnd ) {
8672 var index = 0,
8673 // if we are going to the end, we want to run all the tweens
8674 // otherwise we skip this part
8675 length = gotoEnd ? animation.tweens.length : 0;
8676
8677 for ( ; index < length ; index++ ) {
8678 animation.tweens[ index ].run( 1 );
8679 }
8680
8681 // resolve when we played the last frame
8682 // otherwise, reject
8683 if ( gotoEnd ) {
8684 deferred.resolveWith( elem, [ animation, gotoEnd ] );
8685 } else {
8686 deferred.rejectWith( elem, [ animation, gotoEnd ] );
8687 }
8688 return this;
8689 }
8690 }),
8691 props = animation.props;
8692
8693 propFilter( props, animation.opts.specialEasing );
8694
8695 for ( ; index < length ; index++ ) {
8696 result = animationPrefilters[ index ].call( animation, elem, props, animation.opts );
8697 if ( result ) {
8698 return result;
8699 }
8700 }
8701
8702 createTweens( animation, props );
8703
8704 if ( jQuery.isFunction( animation.opts.start ) ) {
8705 animation.opts.start.call( elem, animation );
8706 }
8707
8708 jQuery.fx.timer(
8709 jQuery.extend( tick, {
8710 anim: animation,
8711 queue: animation.opts.queue,
8712 elem: elem
8713 })
8714 );
8715
8716 // attach callbacks from options
8717 return animation.progress( animation.opts.progress )
8718 .done( animation.opts.done, animation.opts.complete )
8719 .fail( animation.opts.fail )
8720 .always( animation.opts.always );
8721 }
8722
8723 function propFilter( props, specialEasing ) {
8724 var index, name, easing, value, hooks;
8725
8726 // camelCase, specialEasing and expand cssHook pass
8727 for ( index in props ) {
8728 name = jQuery.camelCase( index );
8729 easing = specialEasing[ name ];
8730 value = props[ index ];
8731 if ( jQuery.isArray( value ) ) {
8732 easing = value[ 1 ];
8733 value = props[ index ] = value[ 0 ];
8734 }
8735
8736 if ( index !== name ) {
8737 props[ name ] = value;
8738 delete props[ index ];
8739 }
8740
8741 hooks = jQuery.cssHooks[ name ];
8742 if ( hooks && "expand" in hooks ) {
8743 value = hooks.expand( value );
8744 delete props[ name ];
8745
8746 // not quite $.extend, this wont overwrite keys already present.
8747 // also - reusing 'index' from above because we have the correct "name"
8748 for ( index in value ) {
8749 if ( !( index in props ) ) {
8750 props[ index ] = value[ index ];
8751 specialEasing[ index ] = easing;
8752 }
8753 }
8754 } else {
8755 specialEasing[ name ] = easing;
8756 }
8757 }
8758 }
8759
8760 jQuery.Animation = jQuery.extend( Animation, {
8761
8762 tweener: function( props, callback ) {
8763 if ( jQuery.isFunction( props ) ) {
8764 callback = props;
8765 props = [ "*" ];
8766 } else {
8767 props = props.split(" ");
8768 }
8769
8770 var prop,
8771 index = 0,
8772 length = props.length;
8773
8774 for ( ; index < length ; index++ ) {
8775 prop = props[ index ];
8776 tweeners[ prop ] = tweeners[ prop ] || [];
8777 tweeners[ prop ].unshift( callback );
8778 }
8779 },
8780
8781 prefilter: function( callback, prepend ) {
8782 if ( prepend ) {
8783 animationPrefilters.unshift( callback );
8784 } else {
8785 animationPrefilters.push( callback );
8786 }
8787 }
8788 });
8789
8790 function defaultPrefilter( elem, props, opts ) {
8791 var index, prop, value, length, dataShow, toggle, tween, hooks, oldfire,
8792 anim = this,
8793 style = elem.style,
8794 orig = {},
8795 handled = [],
8796 hidden = elem.nodeType && isHidden( elem );
8797
8798 // handle queue: false promises
8799 if ( !opts.queue ) {
8800 hooks = jQuery._queueHooks( elem, "fx" );
8801 if ( hooks.unqueued == null ) {
8802 hooks.unqueued = 0;
8803 oldfire = hooks.empty.fire;
8804 hooks.empty.fire = function() {
8805 if ( !hooks.unqueued ) {
8806 oldfire();
8807 }
8808 };
8809 }
8810 hooks.unqueued++;
8811
8812 anim.always(function() {
8813 // doing this makes sure that the complete handler will be called
8814 // before this completes
8815 anim.always(function() {
8816 hooks.unqueued--;
8817 if ( !jQuery.queue( elem, "fx" ).length ) {
8818 hooks.empty.fire();
8819 }
8820 });
8821 });
8822 }
8823
8824 // height/width overflow pass
8825 if ( elem.nodeType === 1 && ( "height" in props || "width" in props ) ) {
8826 // Make sure that nothing sneaks out
8827 // Record all 3 overflow attributes because IE does not
8828 // change the overflow attribute when overflowX and
8829 // overflowY are set to the same value
8830 opts.overflow = [ style.overflow, style.overflowX, style.overflowY ];
8831
8832 // Set display property to inline-block for height/width
8833 // animations on inline elements that are having width/height animated
8834 if ( jQuery.css( elem, "display" ) === "inline" &&
8835 jQuery.css( elem, "float" ) === "none" ) {
8836
8837 // inline-level elements accept inline-block;
8838 // block-level elements need to be inline with layout
8839 if ( !jQuery.support.inlineBlockNeedsLayout || css_defaultDisplay( elem.nodeName ) === "inline" ) {
8840 style.display = "inline-block";
8841
8842 } else {
8843 style.zoom = 1;
8844 }
8845 }
8846 }
8847
8848 if ( opts.overflow ) {
8849 style.overflow = "hidden";
8850 if ( !jQuery.support.shrinkWrapBlocks ) {
8851 anim.done(function() {
8852 style.overflow = opts.overflow[ 0 ];
8853 style.overflowX = opts.overflow[ 1 ];
8854 style.overflowY = opts.overflow[ 2 ];
8855 });
8856 }
8857 }
8858
8859
8860 // show/hide pass
8861 for ( index in props ) {
8862 value = props[ index ];
8863 if ( rfxtypes.exec( value ) ) {
8864 delete props[ index ];
8865 toggle = toggle || value === "toggle";
8866 if ( value === ( hidden ? "hide" : "show" ) ) {
8867 continue;
8868 }
8869 handled.push( index );
8870 }
8871 }
8872
8873 length = handled.length;
8874 if ( length ) {
8875 dataShow = jQuery._data( elem, "fxshow" ) || jQuery._data( elem, "fxshow", {} );
8876 if ( "hidden" in dataShow ) {
8877 hidden = dataShow.hidden;
8878 }
8879
8880 // store state if its toggle - enables .stop().toggle() to "reverse"
8881 if ( toggle ) {
8882 dataShow.hidden = !hidden;
8883 }
8884 if ( hidden ) {
8885 jQuery( elem ).show();
8886 } else {
8887 anim.done(function() {
8888 jQuery( elem ).hide();
8889 });
8890 }
8891 anim.done(function() {
8892 var prop;
8893 jQuery.removeData( elem, "fxshow", true );
8894 for ( prop in orig ) {
8895 jQuery.style( elem, prop, orig[ prop ] );
8896 }
8897 });
8898 for ( index = 0 ; index < length ; index++ ) {
8899 prop = handled[ index ];
8900 tween = anim.createTween( prop, hidden ? dataShow[ prop ] : 0 );
8901 orig[ prop ] = dataShow[ prop ] || jQuery.style( elem, prop );
8902
8903 if ( !( prop in dataShow ) ) {
8904 dataShow[ prop ] = tween.start;
8905 if ( hidden ) {
8906 tween.end = tween.start;
8907 tween.start = prop === "width" || prop === "height" ? 1 : 0;
8908 }
8909 }
8910 }
8911 }
8912 }
8913
8914 function Tween( elem, options, prop, end, easing ) {
8915 return new Tween.prototype.init( elem, options, prop, end, easing );
8916 }
8917 jQuery.Tween = Tween;
8918
8919 Tween.prototype = {
8920 constructor: Tween,
8921 init: function( elem, options, prop, end, easing, unit ) {
8922 this.elem = elem;
8923 this.prop = prop;
8924 this.easing = easing || "swing";
8925 this.options = options;
8926 this.start = this.now = this.cur();
8927 this.end = end;
8928 this.unit = unit || ( jQuery.cssNumber[ prop ] ? "" : "px" );
8929 },
8930 cur: function() {
8931 var hooks = Tween.propHooks[ this.prop ];
8932
8933 return hooks && hooks.get ?
8934 hooks.get( this ) :
8935 Tween.propHooks._default.get( this );
8936 },
8937 run: function( percent ) {
8938 var eased,
8939 hooks = Tween.propHooks[ this.prop ];
8940
8941 if ( this.options.duration ) {
8942 this.pos = eased = jQuery.easing[ this.easing ](
8943 percent, this.options.duration * percent, 0, 1, this.options.duration
8944 );
8945 } else {
8946 this.pos = eased = percent;
8947 }
8948 this.now = ( this.end - this.start ) * eased + this.start;
8949
8950 if ( this.options.step ) {
8951 this.options.step.call( this.elem, this.now, this );
8952 }
8953
8954 if ( hooks && hooks.set ) {
8955 hooks.set( this );
8956 } else {
8957 Tween.propHooks._default.set( this );
8958 }
8959 return this;
8960 }
8961 };
8962
8963 Tween.prototype.init.prototype = Tween.prototype;
8964
8965 Tween.propHooks = {
8966 _default: {
8967 get: function( tween ) {
8968 var result;
8969
8970 if ( tween.elem[ tween.prop ] != null &&
8971 (!tween.elem.style || tween.elem.style[ tween.prop ] == null) ) {
8972 return tween.elem[ tween.prop ];
8973 }
8974
8975 // passing any value as a 4th parameter to .css will automatically
8976 // attempt a parseFloat and fallback to a string if the parse fails
8977 // so, simple values such as "10px" are parsed to Float.
8978 // complex values such as "rotate(1rad)" are returned as is.
8979 result = jQuery.css( tween.elem, tween.prop, false, "" );
8980 // Empty strings, null, undefined and "auto" are converted to 0.
8981 return !result || result === "auto" ? 0 : result;
8982 },
8983 set: function( tween ) {
8984 // use step hook for back compat - use cssHook if its there - use .style if its
8985 // available and use plain properties where available
8986 if ( jQuery.fx.step[ tween.prop ] ) {
8987 jQuery.fx.step[ tween.prop ]( tween );
8988 } else if ( tween.elem.style && ( tween.elem.style[ jQuery.cssProps[ tween.prop ] ] != null || jQuery.cssHooks[ tween.prop ] ) ) {
8989 jQuery.style( tween.elem, tween.prop, tween.now + tween.unit );
8990 } else {
8991 tween.elem[ tween.prop ] = tween.now;
8992 }
8993 }
8994 }
8995 };
8996
8997 // Remove in 2.0 - this supports IE8's panic based approach
8998 // to setting things on disconnected nodes
8999
9000 Tween.propHooks.scrollTop = Tween.propHooks.scrollLeft = {
9001 set: function( tween ) {
9002 if ( tween.elem.nodeType && tween.elem.parentNode ) {
9003 tween.elem[ tween.prop ] = tween.now;
9004 }
9005 }
9006 };
9007
9008 jQuery.each([ "toggle", "show", "hide" ], function( i, name ) {
9009 var cssFn = jQuery.fn[ name ];
9010 jQuery.fn[ name ] = function( speed, easing, callback ) {
9011 return speed == null || typeof speed === "boolean" ||
9012 // special check for .toggle( handler, handler, ... )
9013 ( !i && jQuery.isFunction( speed ) && jQuery.isFunction( easing ) ) ?
9014 cssFn.apply( this, arguments ) :
9015 this.animate( genFx( name, true ), speed, easing, callback );
9016 };
9017 });
9018
9019 jQuery.fn.extend({
9020 fadeTo: function( speed, to, easing, callback ) {
9021
9022 // show any hidden elements after setting opacity to 0
9023 return this.filter( isHidden ).css( "opacity", 0 ).show()
9024
9025 // animate to the value specified
9026 .end().animate({ opacity: to }, speed, easing, callback );
9027 },
9028 animate: function( prop, speed, easing, callback ) {
9029 var empty = jQuery.isEmptyObject( prop ),
9030 optall = jQuery.speed( speed, easing, callback ),
9031 doAnimation = function() {
9032 // Operate on a copy of prop so per-property easing won't be lost
9033 var anim = Animation( this, jQuery.extend( {}, prop ), optall );
9034
9035 // Empty animations resolve immediately
9036 if ( empty ) {
9037 anim.stop( true );
9038 }
9039 };
9040
9041 return empty || optall.queue === false ?
9042 this.each( doAnimation ) :
9043 this.queue( optall.queue, doAnimation );
9044 },
9045 stop: function( type, clearQueue, gotoEnd ) {
9046 var stopQueue = function( hooks ) {
9047 var stop = hooks.stop;
9048 delete hooks.stop;
9049 stop( gotoEnd );
9050 };
9051
9052 if ( typeof type !== "string" ) {
9053 gotoEnd = clearQueue;
9054 clearQueue = type;
9055 type = undefined;
9056 }
9057 if ( clearQueue && type !== false ) {
9058 this.queue( type || "fx", [] );
9059 }
9060
9061 return this.each(function() {
9062 var dequeue = true,
9063 index = type != null && type + "queueHooks",
9064 timers = jQuery.timers,
9065 data = jQuery._data( this );
9066
9067 if ( index ) {
9068 if ( data[ index ] && data[ index ].stop ) {
9069 stopQueue( data[ index ] );
9070 }
9071 } else {
9072 for ( index in data ) {
9073 if ( data[ index ] && data[ index ].stop && rrun.test( index ) ) {
9074 stopQueue( data[ index ] );
9075 }
9076 }
9077 }
9078
9079 for ( index = timers.length; index--; ) {
9080 if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) {
9081 timers[ index ].anim.stop( gotoEnd );
9082 dequeue = false;
9083 timers.splice( index, 1 );
9084 }
9085 }
9086
9087 // start the next in the queue if the last step wasn't forced
9088 // timers currently will call their complete callbacks, which will dequeue
9089 // but only if they were gotoEnd
9090 if ( dequeue || !gotoEnd ) {
9091 jQuery.dequeue( this, type );
9092 }
9093 });
9094 }
9095 });
9096
9097 // Generate parameters to create a standard animation
9098 function genFx( type, includeWidth ) {
9099 var which,
9100 attrs = { height: type },
9101 i = 0;
9102
9103 // if we include width, step value is 1 to do all cssExpand values,
9104 // if we don't include width, step value is 2 to skip over Left and Right
9105 includeWidth = includeWidth? 1 : 0;
9106 for( ; i < 4 ; i += 2 - includeWidth ) {
9107 which = cssExpand[ i ];
9108 attrs[ "margin" + which ] = attrs[ "padding" + which ] = type;
9109 }
9110
9111 if ( includeWidth ) {
9112 attrs.opacity = attrs.width = type;
9113 }
9114
9115 return attrs;
9116 }
9117
9118 // Generate shortcuts for custom animations
9119 jQuery.each({
9120 slideDown: genFx("show"),
9121 slideUp: genFx("hide"),
9122 slideToggle: genFx("toggle"),
9123 fadeIn: { opacity: "show" },
9124 fadeOut: { opacity: "hide" },
9125 fadeToggle: { opacity: "toggle" }
9126 }, function( name, props ) {
9127 jQuery.fn[ name ] = function( speed, easing, callback ) {
9128 return this.animate( props, speed, easing, callback );
9129 };
9130 });
9131
9132 jQuery.speed = function( speed, easing, fn ) {
9133 var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : {
9134 complete: fn || !fn && easing ||
9135 jQuery.isFunction( speed ) && speed,
9136 duration: speed,
9137 easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing
9138 };
9139
9140 opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
9141 opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default;
9142
9143 // normalize opt.queue - true/undefined/null -> "fx"
9144 if ( opt.queue == null || opt.queue === true ) {
9145 opt.queue = "fx";
9146 }
9147
9148 // Queueing
9149 opt.old = opt.complete;
9150
9151 opt.complete = function() {
9152 if ( jQuery.isFunction( opt.old ) ) {
9153 opt.old.call( this );
9154 }
9155
9156 if ( opt.queue ) {
9157 jQuery.dequeue( this, opt.queue );
9158 }
9159 };
9160
9161 return opt;
9162 };
9163
9164 jQuery.easing = {
9165 linear: function( p ) {
9166 return p;
9167 },
9168 swing: function( p ) {
9169 return 0.5 - Math.cos( p*Math.PI ) / 2;
9170 }
9171 };
9172
9173 jQuery.timers = [];
9174 jQuery.fx = Tween.prototype.init;
9175 jQuery.fx.tick = function() {
9176 var timer,
9177 timers = jQuery.timers,
9178 i = 0;
9179
9180 fxNow = jQuery.now();
9181
9182 for ( ; i < timers.length; i++ ) {
9183 timer = timers[ i ];
9184 // Checks the timer has not already been removed
9185 if ( !timer() && timers[ i ] === timer ) {
9186 timers.splice( i--, 1 );
9187 }
9188 }
9189
9190 if ( !timers.length ) {
9191 jQuery.fx.stop();
9192 }
9193 fxNow = undefined;
9194 };
9195
9196 jQuery.fx.timer = function( timer ) {
9197 if ( timer() && jQuery.timers.push( timer ) && !timerId ) {
9198 timerId = setInterval( jQuery.fx.tick, jQuery.fx.interval );
9199 }
9200 };
9201
9202 jQuery.fx.interval = 13;
9203
9204 jQuery.fx.stop = function() {
9205 clearInterval( timerId );
9206 timerId = null;
9207 };
9208
9209 jQuery.fx.speeds = {
9210 slow: 600,
9211 fast: 200,
9212 // Default speed
9213 _default: 400
9214 };
9215
9216 // Back Compat <1.8 extension point
9217 jQuery.fx.step = {};
9218
9219 if ( jQuery.expr && jQuery.expr.filters ) {
9220 jQuery.expr.filters.animated = function( elem ) {
9221 return jQuery.grep(jQuery.timers, function( fn ) {
9222 return elem === fn.elem;
9223 }).length;
9224 };
9225 }
9226 var rroot = /^(?:body|html)$/i;
9227
9228 jQuery.fn.offset = function( options ) {
9229 if ( arguments.length ) {
9230 return options === undefined ?
9231 this :
9232 this.each(function( i ) {
9233 jQuery.offset.setOffset( this, options, i );
9234 });
9235 }
9236
9237 var docElem, body, win, clientTop, clientLeft, scrollTop, scrollLeft,
9238 box = { top: 0, left: 0 },
9239 elem = this[ 0 ],
9240 doc = elem && elem.ownerDocument;
9241
9242 if ( !doc ) {
9243 return;
9244 }
9245
9246 if ( (body = doc.body) === elem ) {
9247 return jQuery.offset.bodyOffset( elem );
9248 }
9249
9250 docElem = doc.documentElement;
9251
9252 // Make sure it's not a disconnected DOM node
9253 if ( !jQuery.contains( docElem, elem ) ) {
9254 return box;
9255 }
9256
9257 // If we don't have gBCR, just use 0,0 rather than error
9258 // BlackBerry 5, iOS 3 (original iPhone)
9259 if ( typeof elem.getBoundingClientRect !== "undefined" ) {
9260 box = elem.getBoundingClientRect();
9261 }
9262 win = getWindow( doc );
9263 clientTop = docElem.clientTop || body.clientTop || 0;
9264 clientLeft = docElem.clientLeft || body.clientLeft || 0;
9265 scrollTop = win.pageYOffset || docElem.scrollTop;
9266 scrollLeft = win.pageXOffset || docElem.scrollLeft;
9267 return {
9268 top: box.top + scrollTop - clientTop,
9269 left: box.left + scrollLeft - clientLeft
9270 };
9271 };
9272
9273 jQuery.offset = {
9274
9275 bodyOffset: function( body ) {
9276 var top = body.offsetTop,
9277 left = body.offsetLeft;
9278
9279 if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) {
9280 top += parseFloat( jQuery.css(body, "marginTop") ) || 0;
9281 left += parseFloat( jQuery.css(body, "marginLeft") ) || 0;
9282 }
9283
9284 return { top: top, left: left };
9285 },
9286
9287 setOffset: function( elem, options, i ) {
9288 var position = jQuery.css( elem, "position" );
9289
9290 // set position first, in-case top/left are set even on static elem
9291 if ( position === "static" ) {
9292 elem.style.position = "relative";
9293 }
9294
9295 var curElem = jQuery( elem ),
9296 curOffset = curElem.offset(),
9297 curCSSTop = jQuery.css( elem, "top" ),
9298 curCSSLeft = jQuery.css( elem, "left" ),
9299 calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1,
9300 props = {}, curPosition = {}, curTop, curLeft;
9301
9302 // need to be able to calculate position if either top or left is auto and position is either absolute or fixed
9303 if ( calculatePosition ) {
9304 curPosition = curElem.position();
9305 curTop = curPosition.top;
9306 curLeft = curPosition.left;
9307 } else {
9308 curTop = parseFloat( curCSSTop ) || 0;
9309 curLeft = parseFloat( curCSSLeft ) || 0;
9310 }
9311
9312 if ( jQuery.isFunction( options ) ) {
9313 options = options.call( elem, i, curOffset );
9314 }
9315
9316 if ( options.top != null ) {
9317 props.top = ( options.top - curOffset.top ) + curTop;
9318 }
9319 if ( options.left != null ) {
9320 props.left = ( options.left - curOffset.left ) + curLeft;
9321 }
9322
9323 if ( "using" in options ) {
9324 options.using.call( elem, props );
9325 } else {
9326 curElem.css( props );
9327 }
9328 }
9329 };
9330
9331
9332 jQuery.fn.extend({
9333
9334 position: function() {
9335 if ( !this[0] ) {
9336 return;
9337 }
9338
9339 var elem = this[0],
9340
9341 // Get *real* offsetParent
9342 offsetParent = this.offsetParent(),
9343
9344 // Get correct offsets
9345 offset = this.offset(),
9346 parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
9347
9348 // Subtract element margins
9349 // note: when an element has margin: auto the offsetLeft and marginLeft
9350 // are the same in Safari causing offset.left to incorrectly be 0
9351 offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0;
9352 offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0;
9353
9354 // Add offsetParent borders
9355 parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0;
9356 parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0;
9357
9358 // Subtract the two offsets
9359 return {
9360 top: offset.top - parentOffset.top,
9361 left: offset.left - parentOffset.left
9362 };
9363 },
9364
9365 offsetParent: function() {
9366 return this.map(function() {
9367 var offsetParent = this.offsetParent || document.body;
9368 while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
9369 offsetParent = offsetParent.offsetParent;
9370 }
9371 return offsetParent || document.body;
9372 });
9373 }
9374 });
9375
9376
9377 // Create scrollLeft and scrollTop methods
9378 jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) {
9379 var top = /Y/.test( prop );
9380
9381 jQuery.fn[ method ] = function( val ) {
9382 return jQuery.access( this, function( elem, method, val ) {
9383 var win = getWindow( elem );
9384
9385 if ( val === undefined ) {
9386 return win ? (prop in win) ? win[ prop ] :
9387 win.document.documentElement[ method ] :
9388 elem[ method ];
9389 }
9390
9391 if ( win ) {
9392 win.scrollTo(
9393 !top ? val : jQuery( win ).scrollLeft(),
9394 top ? val : jQuery( win ).scrollTop()
9395 );
9396
9397 } else {
9398 elem[ method ] = val;
9399 }
9400 }, method, val, arguments.length, null );
9401 };
9402 });
9403
9404 function getWindow( elem ) {
9405 return jQuery.isWindow( elem ) ?
9406 elem :
9407 elem.nodeType === 9 ?
9408 elem.defaultView || elem.parentWindow :
9409 false;
9410 }
9411 // Create innerHeight, innerWidth, height, width, outerHeight and outerWidth methods
9412 jQuery.each( { Height: "height", Width: "width" }, function( name, type ) {
9413 jQuery.each( { padding: "inner" + name, content: type, "": "outer" + name }, function( defaultExtra, funcName ) {
9414 // margin is only for outerHeight, outerWidth
9415 jQuery.fn[ funcName ] = function( margin, value ) {
9416 var chainable = arguments.length && ( defaultExtra || typeof margin !== "boolean" ),
9417 extra = defaultExtra || ( margin === true || value === true ? "margin" : "border" );
9418
9419 return jQuery.access( this, function( elem, type, value ) {
9420 var doc;
9421
9422 if ( jQuery.isWindow( elem ) ) {
9423 // As of 5/8/2012 this will yield incorrect results for Mobile Safari, but there
9424 // isn't a whole lot we can do. See pull request at this URL for discussion:
9425 // https://github.com/jquery/jquery/pull/764
9426 return elem.document.documentElement[ "client" + name ];
9427 }
9428
9429 // Get document width or height
9430 if ( elem.nodeType === 9 ) {
9431 doc = elem.documentElement;
9432
9433 // Either scroll[Width/Height] or offset[Width/Height] or client[Width/Height], whichever is greatest
9434 // unfortunately, this causes bug #3838 in IE6/8 only, but there is currently no good, small way to fix it.
9435 return Math.max(
9436 elem.body[ "scroll" + name ], doc[ "scroll" + name ],
9437 elem.body[ "offset" + name ], doc[ "offset" + name ],
9438 doc[ "client" + name ]
9439 );
9440 }
9441
9442 return value === undefined ?
9443 // Get width or height on the element, requesting but not forcing parseFloat
9444 jQuery.css( elem, type, value, extra ) :
9445
9446 // Set width or height on the element
9447 jQuery.style( elem, type, value, extra );
9448 }, type, chainable ? margin : undefined, chainable, null );
9449 };
9450 });
9451 });
9452 // Expose jQuery to the global object
9453 window.jQuery = window.$ = jQuery;
9454
9455 // Expose jQuery as an AMD module, but only for AMD loaders that
9456 // understand the issues with loading multiple versions of jQuery
9457 // in a page that all might call define(). The loader will indicate
9458 // they have special allowances for multiple jQuery versions by
9459 // specifying define.amd.jQuery = true. Register as a named module,
9460 // since jQuery can be concatenated with other files that may use define,
9461 // but not use a proper concatenation script that understands anonymous
9462 // AMD modules. A named AMD is safest and most robust way to register.
9463 // Lowercase jquery is used because AMD module names are derived from
9464 // file names, and jQuery is normally delivered in a lowercase file name.
9465 // Do this after creating the global so that if an AMD module wants to call
9466 // noConflict to hide this version of jQuery, it will work.
9467 if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
9468 define( "jquery", [], function () { return jQuery; } );
9469 }
9470
9471 })( window );
4141 controller: 'dashboardCtrl',
4242 title: 'Dashboard | '
4343 }).
44 when('/dashboard', {
45 templateUrl: 'scripts/partials/workspaces.html',
44 when('/dashboard/ws', {
45 templateUrl: 'scripts/commons/partials/workspaces.html',
4646 controller: 'workspacesCtrl',
4747 title: 'Dashboard | '
4848 }).
49 when('/dashboard', {
50 templateUrl: 'scripts/commons/partials/workspaces.html',
51 controller: 'workspacesCtrl',
52 title: 'Dashboard | '
53 }).
54 when('/hosts/ws/:wsId', {
55 templateUrl: 'scripts/hosts/partials/list.html',
56 controller: 'hostsCtrl',
57 title: 'Hosts | '
58 }).
59 when('/hosts/ws', {
60 templateUrl: 'scripts/commons/partials/workspaces.html',
61 controller: 'workspacesCtrl',
62 title: 'Hosts | '
63 }).
64 when('/hosts', {
65 templateUrl: 'scripts/commons/partials/workspaces.html',
66 controller: 'workspacesCtrl',
67 title: 'Hosts | '
68 }).
69 when('/host/ws/:wsId/hid/:hidId', {
70 templateUrl: 'scripts/services/partials/list.html',
71 controller: 'hostCtrl',
72 title: 'Services | '
73 }).
74 when('/status/ws/:wsId/search/:search', {
75 templateUrl: 'scripts/statusReport/partials/statusReport.html',
76 controller: 'statusReportCtrl',
77 title: 'Status Report | '
78 }).
79 when('/status/ws/:wsId/search', {
80 templateUrl: 'scripts/statusReport/partials/statusReport.html',
81 controller: 'statusReportCtrl',
82 title: 'Status Report | '
83 }).
4984 when('/status/ws/:wsId', {
50 templateUrl: 'scripts/partials/status_report.html',
85 templateUrl: 'scripts/statusReport/partials/statusReport.html',
5186 controller: 'statusReportCtrl',
87 title: 'Status Report | '
88 }).
89 when('/status/ws', {
90 templateUrl: 'scripts/commons/partials/workspaces.html',
91 controller: 'workspacesCtrl',
92 title: 'Status Report | '
93 }).
94 when('/status', {
95 templateUrl: 'scripts/commons/partials/workspaces.html',
96 controller: 'workspacesCtrl',
5297 title: 'Status Report | '
5398 }).
5499 when('/workspaces', {
56101 controller: 'workspacesCtrl',
57102 title: 'Workspaces | '
58103 }).
59 when('/status', {
60 templateUrl: 'scripts/partials/workspaces.html',
61 controller: 'workspacesCtrl',
62 title: 'Status Report | '
63 }).
64104 otherwise({
65 templateUrl: 'scripts/partials/home.html',
105 templateUrl: 'scripts/commons/partials/home.html',
66106 controller: 'statusReportCtrl'
67107 });
68108 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('commonsModalDelete', ['$scope', '$modalInstance', 'msg', function($scope, $modalInstance, msg) {
6 $scope.msg = msg;
7
8 $scope.ok = function() {
9 $modalInstance.close();
10 };
11
12 $scope.cancel = function() {
13 $modalInstance.dismiss('cancel');
14 };
15 }]);
16
17 angular.module('faradayApp')
18 .controller('commonsModalKoCtrl', ['$scope', '$modalInstance', 'msg', function($scope, $modalInstance, msg) {
19 $scope.msg = msg;
20
21 $scope.ok = function() {
22 $modalInstance.close();
23 };
24 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 /**
5 * @see http://docs.angularjs.org/guide/concepts
6 * @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController
7 * @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166
8 */
9
10 angular.module('faradayApp')
11 .directive('contenteditable', ['$timeout', function($timeout) { return {
12 restrict: 'A',
13 require: '?ngModel',
14 link: function(scope, element, attrs, ngModel) {
15 // don't do anything unless this is actually bound to a model
16 if (!ngModel) {
17 return
18 }
19
20 // options
21 var opts = {}
22 angular.forEach([
23 'stripBr',
24 'noLineBreaks',
25 'selectNonEditable',
26 'moveCaretToEndOnChange',
27 ], function(opt) {
28 var o = attrs[opt]
29 opts[opt] = o && o !== 'false'
30 })
31
32 // view -> model
33 element.bind('input', function(e) {
34 scope.$apply(function() {
35 var html, html2, rerender
36 html = element.html()
37 rerender = false
38 if (opts.stripBr) {
39 html = html.replace(/<br>$/, '')
40 }
41 if (opts.noLineBreaks) {
42 html2 = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '')
43 if (html2 !== html) {
44 rerender = true
45 html = html2
46 }
47 }
48 ngModel.$setViewValue(html)
49 if (rerender) {
50 ngModel.$render()
51 }
52 if (html === '') {
53 // the cursor disappears if the contents is empty
54 // so we need to refocus
55 $timeout(function(){
56 element[0].blur()
57 element[0].focus()
58 })
59 }
60 })
61 })
62
63 // model -> view
64 var oldRender = ngModel.$render
65 ngModel.$render = function() {
66 var el, el2, range, sel
67 if (!!oldRender) {
68 oldRender()
69 }
70 element.html(ngModel.$viewValue || '')
71 if (opts.moveCaretToEndOnChange) {
72 el = element[0]
73 range = document.createRange()
74 sel = window.getSelection()
75 if (el.childNodes.length > 0) {
76 el2 = el.childNodes[el.childNodes.length - 1]
77 range.setStartAfter(el2)
78 } else {
79 range.setStartAfter(el)
80 }
81 range.collapse(true)
82 sel.removeAllRanges()
83 sel.addRange(range)
84 }
85 }
86 if (opts.selectNonEditable) {
87 element.bind('click', function(e) {
88 var range, sel, target
89 target = e.toElement
90 if (target !== this && angular.element(target).attr('contenteditable') === 'false') {
91 range = document.createRange()
92 sel = window.getSelection()
93 range.setStartBefore(target)
94 range.setEndAfter(target)
95 sel.removeAllRanges()
96 sel.addRange(range)
97 }
98 })
99 }
100 }
101 }}]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('filter', []).filter('startFrom', function() {
5 return function(input, start) {
6 start = +start; //parse to int
7 return input.slice(start);
8 }
9 });
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5 <div class="right-main"><div id="reports-main" class="fila clearfix">
6 <h2 class="ws-label">
7 <span id="ws-name" class="label label-default"
8 title="Current workspace">Welcome to Faraday!</span><!-- WS name -->
9 </h2><!-- .ws-label -->
10 <div class="reports">
11 <div class="reports">
12 <div class="ws-list home-list community clearfix">
13 <a href="#/dashboard" class="ws-link item animated flipInX">
14 <img src="images/ico-dashboard.svg" />
15 <span class="ws-name">Dashboard</span>
16 <small>
17 Gain insight into your project.<br/>
18 <strong>Visualise the progress</strong>
19 </small>
20 </a>
21 <a href="#/status" class="ws-link item animated flipInX">
22 <img src="images/ico-status.svg" />
23 <span class="ws-name">Status Report</span>
24 <small>
25 All the vulnerabilities in one place.<br/>
26 <strong>Manage findings</strong>
27 </small>
28 </a>
29 <a href="#/workspaces" class="ws-link item animated flipInX">
30 <img src="images/ico-workspaces.svg" />
31 <span class="ws-name">Workspaces</span>
32 <small>
33 Create and edit projects.<br/>
34 <strong>Manage your projects</strong>
35 </small>
36 </a>
37 </div><!-- .ws-list -->
38 </div><!-- .reports -->
39 </div><!-- #reports-main --></div><!-- .right-main -->
40 </section>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Bulk deletion</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{msg}}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
12 <button class="btn btn-success" ng-click="ok()">OK</button>
13 </div>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Oops!</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{ msg }}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-success" ng-click="ok()">OK</button>
12 </div>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <section id="main" class="seccion clearfix">
4 <div class="right-main"><div id="reports-main" class="fila clearfix">
5 <div class="jumbotron">
6 <h1><b>Welcome!</b></h1>
7 <p>These are your available workspaces</p>
8 </div><!-- .jumbotron -->
9 <div class="reports normal-size">
10 <table class="status-report table-striped table-responsive table-hover">
11 <thead>
12 <tr>
13 <th>Name</th>
14 <th>Vulns</th>
15 <th>Hosts</th>
16 <th>Services</th>
17 </tr>
18 </thead>
19 <tbody>
20 <tr ng-repeat="ws in wss">
21 <td><a href="#/{{hash}}/ws/{{ws}}"><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'">{{ws}}</span></a></td>
22 <td ng-bind="objects[ws]['total vulns']"></td>
23 <td ng-bind="objects[ws]['hosts']"></td>
24 <td ng-bind="objects[ws]['services']"></td>
25 </tr>
26 </tbody>
27 </table>
28 </div><!-- .reports -->
29 </div><!-- #reports-main --></div><!-- .right-main -->
30 </section>
3333 };
3434
3535 commonsFact.arrayToObject = function(array){
36 var refArray = [];
36 var arrayOfObjects = [];
3737 if (array != undefined){
3838 array.forEach(function(r){
39 refArray.push({ref:r});
39 arrayOfObjects.push({key:r});
4040 });
4141 }
42 return refArray;
42 return arrayOfObjects;
4343 }
4444
4545 commonsFact.objectToArray = function(object){
4646 var res = {};
47 var arrayReferences = [];
47 var array = [];
4848 object.forEach(function(r){
49 arrayReferences.push(r.ref);
49 array.push(r.key);
5050 });
51 arrayReferences = arrayReferences.filter(Boolean);
52 return arrayReferences;
51 array = array.filter(Boolean);
52 return array;
5353 }
5454
5555 commonsFact.htmlentities = function(string, quote_style, charset, double_encode) {
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .directive('d3HorizontalStackedBar', ['d3Service', '$window', '$compile',
6 function(d3Service, $window, $compile) {
7 return {
8 restrict: 'EA',
9 scope: {
10 data: '=data'
11 },
12 link: function(scope, ele, attrs) {
13 d3Service.d3().then(function(d3) {
14 // update scope when changes occur
15 scope.$watch('data', function(newData) {
16 if(newData != undefined) scope.render(newData);
17 }, true);
18
19 // redraw graphic when resize occurs
20 angular.element($window).on('resize', function(e) {
21 if(scope.data != undefined) scope.render(scope.data);
22 });
23
24 }); // d3Service.d3()
25
26 scope.render = function(data) {
27 d3.select('.stackedbars').selectAll('svg').remove();
28
29 var margins = {
30 top: 12,
31 left: 24,
32 right: 12,
33 bottom: 12
34 };
35
36 // get parent width to calculate graphic width
37 var pwidth = ele.parent().width(),
38 width = pwidth * 0.9,
39 height = 80 - margins.top - margins.bottom;
40
41 var dataset = data.map(function(d) {
42 return [{
43 k: d.key,
44 c: d.color,
45 y: d.value,
46 x: 0
47 }];
48 });
49
50 stack = d3.layout.stack();
51 stack(dataset);
52
53 var dataset = dataset.map(function (group) {
54 return group.map(function (d) {
55 // Invert the x and y values, and y0 becomes x0
56 return {
57 k: d.k,
58 c: d.c,
59 x: d.y,
60 y: d.x,
61 x0: d.y0
62 };
63 });
64 }),
65 svg = d3.select(".stackedbars")
66 .append('svg')
67 .attr('width', width + margins.left + margins.right)
68 .attr('height', height + margins.top + margins.bottom)
69 .append('g')
70 .attr('transform', 'translate(' + margins.left + ',' + margins.top + ')'),
71 xMax = d3.max(dataset, function (group) {
72 return d3.max(group, function (d) {
73 return d.x + d.x0;
74 });
75 }),
76 xScale = d3.scale.linear()
77 .domain([0, xMax])
78 .range([0, width]),
79 months = dataset[0].map(function (d) {
80 return d.y;
81 }),
82 yScale = d3.scale.ordinal()
83 .domain(months)
84 .rangeRoundBands([0, height], .1),
85 xAxis = d3.svg.axis()
86 .scale(xScale)
87 .orient('bottom'),
88 yAxis = d3.svg.axis()
89 .scale(yScale)
90 .orient('left'),
91 groups = svg.selectAll('g')
92 .data(dataset)
93 .enter()
94 .append('g')
95 .style('fill', function (d, i) {
96 return d[0].c;
97 }),
98 rects = groups.selectAll('rect')
99 .data(function (d) {
100 return d;
101 })
102 .enter()
103 .append('rect')
104 .attr('x', function (d) {
105 return xScale(d.x0);
106 })
107 .attr('y', function (d, i) {
108 return yScale(d.y);
109 })
110 .attr('height', function (d) {
111 return yScale.rangeBand();
112 })
113 .attr('width', function (d) {
114 return xScale(d.x);
115 })
116 .attr('tooltip-append-to-body', true)
117 .attr('tooltip', function(d) {
118 return d.k + " sums $" + d.x;
119 });
120
121 ele.removeAttr("d3-horizontal-stacked-bar");
122 $compile(ele)(scope);
123 }; // scope.render definition
124 }
125 };
126 }]);
33
44 angular.module('faradayApp')
55 .controller('dashboardCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', 'statusReportFact',
7 function($scope, $filter, $route, $routeParams, statusReportFact) {
8 //current workspace
9 $scope.workspace = $routeParams.wsId;
10 $scope.workspaces = [];
6 ['$scope', '$filter', '$route', '$routeParams', '$location', 'statusReportFact',
7 function($scope, $filter, $route, $routeParams, $location, statusReportFact) {
8 init = function() {
9 //current workspace
10 $scope.workspace = $routeParams.wsId;
11 $scope.workspaces = [];
1112
12 statusReportFact.getWorkspaces().then(function(wss) {
13 $scope.workspaces = wss;
14 });
13 statusReportFact.getWorkspaces().then(function(wss) {
14 $scope.workspaces = wss;
15 });
16 };
17
18 $scope.navigate = function(route) {
19 $location.path(route);
20 };
21
22 init();
1523 }]);
8282 if($scope.treemapData.children){
8383 var modal = $modal.open({
8484 templateUrl: 'scripts/dashboard/partials/modal-treemap.html',
85 controller: 'summarizedCtrlBarModal',
85 controller: 'treemapModalCtrl',
8686 size: 'lg',
8787 resolve: {
8888 workspace: function(){
8989 return $scope.workspace;
9090 }
9191 }
92 });
93
94 modal.result.then(function(data) {
95 $scope.insert(data);
9692 });
9793 }
9894 };
10096 }]);
10197
10298 angular.module('faradayApp')
103 .controller('summarizedCtrlBarModal',
99 .controller('treemapModalCtrl',
104100 ['$scope', '$modalInstance', 'dashboardSrv', 'workspace',
105101 function($scope, $modalInstance, dashboardSrv, workspace){
106102
33
44 angular.module('faradayApp')
55 .controller('summarizedCtrl',
6 ['$scope', '$route', '$routeParams', '$modal', 'dashboardSrv',
7 function($scope, $route, $routeParams, $modal, dashboardSrv) {
6 ['$scope', '$route', '$routeParams', '$modal', 'dashboardSrv', 'statusReportFact',
7 function($scope, $route, $routeParams, $modal, dashboardSrv, statusReportFact) {
88 //current workspace
99 var workspace = $routeParams.wsId;
1010 $scope.servicesCount = [];
5555 $scope.hostSortReverse = !$scope.hostSortReverse;
5656 }
5757
58 // vuln table sorting
59 $scope.vulnSortField = 'date';
60 $scope.vulnSortReverse = true;
61 // toggles sort field and order
62 $scope.vulnToggleSort = function(field) {
63 $scope.vulnToggleSortField(field);
64 $scope.vulnToggleReverse();
65 };
66
67 // toggles column sort field
68 $scope.vulnToggleSortField = function(field) {
69 $scope.vulnSortField = field;
70 };
71
72 // toggle column sort order
73 $scope.vulnToggleReverse = function() {
74 $scope.vulnSortReverse = !$scope.vulnSortReverse;
75 };
76
5877 if (workspace != undefined){
5978 $scope.workspace = workspace;
6079 dashboardSrv.getServicesCount(workspace).then(function(res){
102121 accumulate(tmp, "high", tvuln.value);
103122 } else if (tvuln.key == 5 || tvuln.key == "critical") {
104123 accumulate(tmp, "critical", tvuln.value);
124 } else if (tvuln.key == 6 || tvuln.key == "unclassified") {
125 accumulate(tmp, "unclassified", tvuln.value);
105126 }
106127 });
107128 $scope.vulnsCount = tmp;
148169 });
149170 });
150171 });
172 $scope.vulns = statusReportFact.getVulns(workspace);
173 $scope.vulns.sort(function(a,b){
174 return b.meta.create_time - a.meta.create_time;
175 });
176 $scope.vulns = $scope.vulns.splice(0,5);
151177 }
152178
153179 $scope.numberOfPages = function() {
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('vulnsbypriceCtrl',
6 ['$scope', '$rootScope', '$route', '$routeParams', 'dashboardSrv',
7 function($scope, $rootScope, $route, $routeParams, dashboardSrv) {
8 init = function() {
9 //current workspace
10 $scope.workspace = $routeParams.wsId;
11
12 $scope.prices = {
13 "critical": "5000",
14 "high": "3000",
15 "med": "1000",
16 "low": "500",
17 "info": "0",
18 "unclassified": "0"
19 };
20
21 dashboardSrv.getVulnerabilities($scope.workspace).then(function(res) {
22 $scope.vulns = res;
23 $scope.generateData(res, $scope.prices);
24 });
25
26 $scope.$watch('prices', function(ps) {
27 if($scope.vulns != undefined) $scope.generateData($scope.vulns, ps);
28 }, true);
29 };
30
31 $scope.generateData = function(vulns, prices) {
32 $scope.data = $scope.generatePrices(vulns, prices);
33 $scope.total = $scope.generateTotal($scope.data);
34 };
35
36 $scope.generateTotal = function(data) {
37 var total = 0;
38
39 for(var d in data) {
40 if(data.hasOwnProperty(d)) {
41 total += parseInt(data[d]['value']);
42 }
43 }
44
45 return total;
46 };
47
48 $scope.generatePrices = function(vulns, prices) {
49 var data = [
50 {
51 color: '#932ebe',
52 value: 0,
53 key: 'critical'
54 }, {
55 color: '#DF3936',
56 value: 0,
57 key: 'high'
58 }, {
59 color: '#DFBF35',
60 value: 0,
61 key: 'med'
62 }, {
63 color: '#A1CE31',
64 value: 0,
65 key: 'low'
66 }, {
67 color: '#2e97bd',
68 value: 0,
69 key: 'info'
70 }, {
71 color: '#999999',
72 value: 0,
73 key: 'unclassified'
74 }
75 ];
76
77 vulns.forEach(function(vuln) {
78 var sev = vuln.value.severity;
79
80 if(sev == 0 || sev == "unclassified") {
81 dashboardSrv.accumulate(data, "unclassified", parseInt(prices[sev]));
82 } else if(sev == 1 || sev == "info") {
83 dashboardSrv.accumulate(data, "info", parseInt(prices[sev]));
84 } else if(sev == 2 || sev == "low") {
85 dashboardSrv.accumulate(data, "low", parseInt(prices[sev]));
86 } else if(sev == 3 || sev == "med") {
87 dashboardSrv.accumulate(data, "med", parseInt(prices[sev]));
88 } else if(sev == 4 || sev == "high") {
89 dashboardSrv.accumulate(data, "high", parseInt(prices[sev]));
90 } else if(sev == 5 || sev == "critical") {
91 dashboardSrv.accumulate(data, "critical", parseInt(prices[sev]));
92 }
93 });
94
95 return data;
96 };
97
98 init();
99 }]);
0 <div id='list'>
1 <div class='col-lg-6'>
2 <article class='panel panel-default'>
3 <header>
4 <h2>Commands History
5 <span class="glyphicon glyphicon-info-sign" tooltip="Shows current WS' executed commands"></span>
6 </h2>
7 </header>
8 <div ng-if="commands.length == 0" class="alert alert-info alert-dismissible">
9 <button type="button" class="close" data-dismiss="alert">
10 <span aria-hidden="true">&times;</span>
11 <span class="sr-only">Close</span>
12 </button>
13 <p>No commands found yet.</p>
14 </div>
15 <table id="commands" ng-if="commands.length > 0" class="tablesorter table table-striped">
16 <thead>
17 <tr>
18 <th><a href="" ng-click="cmdToggleSort('user')">By</a></th>
19 <th><a href="" ng-click="cmdToggleSort('command')">Command</a></th>
20 <th><a href="" ng-click="cmdToggleSort('date')">Start Date</a></th>
21 <th><a href="" ng-click="cmdToggleSort('duration')">Duration</a></th>
22 </tr>
23 </thead>
24 <tbody>
25 <tr ng-repeat="cmd in commands | orderObjectBy:cmdSortField:cmdSortReverse">
26 <td><p tooltip="{{cmd.ip}}">{{cmd.user}}@{{cmd.hostname}}</p></td>
27 <td>{{cmd.command}}</td>
28 <td>{{cmd.date | date:"MM/dd/yyyy 'at' h:mma"}}</td>
29 <td ng-bind="cmd.duration || 'undefined'"></td>
30 </tr>
31 </tbody>
32 </table>
33 </article>
34 </div>
35 </div>
0 <div id='compound'>
1 <div class='col-lg-6'>
2 <article class='panel panel-default'>
3 <header>
4 <h2><a href="#/hosts/ws/{{workspace}}">Hosts</a>
5 <span class="glyphicon glyphicon-info-sign" tooltip="All hosts, each one showing its service count and operating system. By clicking on a host IP you can access a list with all of its services"></span>
6 </h2>
7 </header>
8 <div ng-if="hosts.length == 0" class="alert alert-info alert-dismissible">
9 <button type="button" class="close" data-dismiss="alert">
10 <span aria-hidden="true">&times;</span>
11 <span class="sr-only">Close</span>
12 </button>
13 <p>No hosts found yet.</p>
14 </div>
15 <table id="hosts" ng-if="hosts.length > 0" class="tablesorter table table-striped">
16 <thead>
17 <tr>
18 <th><a href="" ng-click="hostToggleSort('name')">Host</a></th>
19 <th><a href="" ng-click="hostToggleSort('servicesCount')">Services</a></th>
20 <th><a href="" ng-click="hostToggleSort('os')">OS</a></th>
21 </tr>
22 </thead>
23 <tbody>
24 <tr ng-repeat="host in hosts | orderBy:hostSortField:hostSortReverse |
25 startFrom:currentPage*pageSize | limitTo:pageSize">
26 <td><a href="" class="host" ng-click="showServices(host.id)">{{host.name}}</a></td>
27 <td>{{host.servicesCount}}</td>
28 <td>
29 <img ng-if="host.icon != undefined" ng-src="../././reports/images/{{host.icon}}.png" tooltip="{{host.os}}"/>
30 <span ng-if="host.icon == undefined" class="glyphicon glyphicon-question-sign" tooltip="{{host.os}}"></span>
31 </td>
32 </tr>
33 </tbody>
34 </table>
35 <div class="showPagination" ng-show="showPagination">
36 <div class="form-group">
37 <ul class="pagination">
38 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
39 <li><a>{{currentPage}}/{{numberOfPages()}}</a></li>
40 <li><a ng-hide="currentPage >= numberOfPages()" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
41 </ul>
42 <form name="goToPage" id="goToPageStatus">
43 <input type="number" min="0" max="{{numberOfPages()}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
44 <button class="btn btn-default" ng-click="go()">GO</button>
45 <input id="vuln-per-page" type="number" min="0" class="form-control vuln_per_page" ng-model="pagination" placeholder="Numbre page" />
46 </form>
47 </div>
48 </div>
49 </article>
50 </div>
51 </div>
2323 </div><!-- .reports -->
2424 <div ng-controller="graphicsBarCtrl" ng-include="'scripts/dashboard/partials/graphics-bar.html'"></div>
2525 <div ng-controller="summarizedCtrl" ng-include="'scripts/dashboard/partials/summarized.html'"></div>
26
26 <div ng-controller="summarizedCtrl" ng-include="'scripts/dashboard/partials/vulns-by-severity.html'"></div>
27 <div ng-controller="vulnsbypriceCtrl" ng-include="'scripts/dashboard/partials/vulns-by-price.html'"></div>
28 <div ng-controller="summarizedCtrl" ng-include="'scripts/dashboard/partials/last-vulns.html'"></div>
29 <div ng-controller="summarizedCtrl" ng-include="'scripts/dashboard/partials/compound.html'"></div>
30 <div ng-controller="summarizedCtrl" ng-include="'scripts/dashboard/partials/commands-list.html'"></div>
2731 </div><!-- #reports-main -->
2832 </div><!-- .right-main -->
29 </section>
33 </section>
0 <div class='col-md-6 left-big-box'>
1 <article class='panel panel-default'>
2 <header>
3 <h2>Last Vulnerabilities
4 <span class="glyphicon glyphicon-info-sign" tooltip="Last vulnerabilities added"></span>
5 </h2>
6 </header>
7 <div ng-if="vulns.length == 0" class="alert alert-info alert-dismissible">
8 <button type="button" class="close" data-dismiss="alert">
9 <span aria-hidden="true">&times;</span>
10 <span class="sr-only">Close</span>
11 </button>
12 <p>No vulnerabilities found yet.</p>
13 </div>
14 <table ng-if="vulns.length > 0" class="tablesorter table table-striped last-vuln">
15 <thead>
16 <tr>
17 <th><a href="" ng-click="vulnToggleSort('date')">Date</a></th>
18 <th><a href="" ng-click="vulnToggleSort('target')">Target</a></th>
19 <th><a href="" ng-click="vulnToggleSort('severity')">Severity</a></th>
20 <th><a href="" ng-click="vulnToggleSort('name')">Name</a></th>
21 <th><a href="" ng-click="vulnToggleSort('web')">Web</a></th>
22 <th><a href="" ng-click="vulnToggleSort('easeofresolution')">Ease of resolution</a></th>
23 </tr>
24 </thead>
25 <tbody>
26 <tr ng-repeat="vuln in vulns | orderObjectBy:vulnSortField:vulnSortReverse">
27 <td>{{vuln.date | date:"MM/dd/yyyy 'at' h:mma"}}</td>
28 <td>{{vuln.target}}</td>
29 <td>{{vuln.severity}}</td>
30 <td>{{vuln.name}}</td>
31 <td>
32 <span class="glyphicon glyphicon-ok" ng-show="vuln.web"></span>
33 <span class="glyphicon glyphicon-remove" ng-show="!vuln.web"></span>
34 </td>
35 <td>{{vuln.easeofresolution}}</td>
36 </tr>
37 </tbody>
38 </table>
39 </article>
40 </div>
6363 </article>
6464 </div>
6565 </div>
66 <div id='compound'>
67 <div class='col-lg-6'>
68 <article class='panel panel-default'>
69 <header>
70 <h2>Hosts
71 <span class="glyphicon glyphicon-info-sign" tooltip="All hosts, each one showing its service count and operating system. By clicking on a host IP you can access a list with all of its services"></span>
72 </h2>
73 </header>
74 <div ng-if="hosts.length == 0" class="alert alert-info alert-dismissible">
75 <button type="button" class="close" data-dismiss="alert">
76 <span aria-hidden="true">&times;</span>
77 <span class="sr-only">Close</span>
78 </button>
79 <p>No hosts found yet.</p>
80 </div>
81 <table id="hosts" ng-if="hosts.length > 0" class="tablesorter table table-striped">
82 <thead>
83 <tr>
84 <th><a href="" ng-click="hostToggleSort('name')">Host</a></th>
85 <th><a href="" ng-click="hostToggleSort('servicesCount')">Services</a></th>
86 <th><a href="" ng-click="hostToggleSort('os')">OS</a></th>
87 </tr>
88 </thead>
89 <tbody>
90 <tr ng-repeat="host in hosts | orderBy:hostSortField:hostSortReverse |
91 startFrom:currentPage*pageSize | limitTo:pageSize">
92 <td><a href="" class="host" ng-click="showServices(host.id)">{{host.name}}</a></td>
93 <td>{{host.servicesCount}}</td>
94 <td>
95 <img ng-if="host.icon != undefined" ng-src="../././reports/images/{{host.icon}}.png" tooltip="{{host.os}}"/>
96 <span ng-if="host.icon == undefined" class="glyphicon glyphicon-question-sign" tooltip="{{host.os}}"></span>
97 </td>
98 </tr>
99 </tbody>
100 </table>
101 <div class="showPagination" ng-show="showPagination">
102 <div class="form-group">
103 <ul class="pagination">
104 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
105 <li><a>{{currentPage}}/{{numberOfPages()}}</a></li>
106 <li><a ng-hide="currentPage >= numberOfPages()" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
107 </ul>
108 <form name="goToPage" id="goToPageStatus">
109 <input type="number" min="0" max="{{numberOfPages()}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
110 <button class="btn btn-default" ng-click="go()">GO</button>
111 <input id="vuln-per-page" type="number" min="0" class="form-control vuln_per_page" ng-model="pagination" placeholder="Numbre page" />
112 </form>
113 </div>
114 </div>
115 </article>
116 </div>
117 </div>
118 <div id='vulns'>
119 <div class='col-lg-6'>
120 <article class='panel panel-default'>
121 <header>
122 <h2>
123 <a href="../././reports/index.html#/status/ws/{{workspace}}" class="status-report">Vulnerabilities</a>
124 <span class="glyphicon glyphicon-info-sign" tooltip="Vulnerabilities count arranged by severity"></span>
125 </h2>
126 </header>
127 <div ng-if="vulnsCount.length == 0" class="alert alert-info alert-dismissible">
128 <button type="button" class="close" data-dismiss="alert">
129 <span aria-hidden="true">&times;</span>
130 <span class="sr-only">Close</span>
131 </button>
132 <p>No vulnerabilities found yet.</p>
133 </div>
134 <div class='main box'>
135 <div ng-repeat="vuln in vulnsCount" class="columna unsexto">
136 <article class="dato2 fondo-{{vuln.key}}">
137 <section>
138 <div class="nro texto-blanco">{{vuln.value}}</div>
139 <div class="txt texto-blanco">{{vuln.key}}</div>
140 </section>
141 </article>
142 </div>
143 </div>
144 </article>
145 </div>
146 </div>
147 <div id='list'>
148 <div class='col-lg-6'>
149 <article class='panel panel-default'>
150 <header>
151 <h2>Commands History
152 <span class="glyphicon glyphicon-info-sign" tooltip="Shows current WS' executed commands"></span>
153 </h2>
154 </header>
155 <div ng-if="commands.length == 0" class="alert alert-info alert-dismissible">
156 <button type="button" class="close" data-dismiss="alert">
157 <span aria-hidden="true">&times;</span>
158 <span class="sr-only">Close</span>
159 </button>
160 <p>No commands found yet.</p>
161 </div>
162 <table id="commands" ng-if="commands.length > 0" class="tablesorter table table-striped">
163 <thead>
164 <tr>
165 <th><a href="" ng-click="cmdToggleSort('user')">By</a></th>
166 <th><a href="" ng-click="cmdToggleSort('command')">Command</a></th>
167 <th><a href="" ng-click="cmdToggleSort('date')">Start Date</a></th>
168 <th><a href="" ng-click="cmdToggleSort('duration')">Duration</a></th>
169 </tr>
170 </thead>
171 <tbody>
172 <tr ng-repeat="cmd in commands | orderObjectBy:cmdSortField:cmdSortReverse">
173 <td><p tooltip="{{cmd.ip}}">{{cmd.user}}@{{cmd.hostname}}</p></td>
174 <td>{{cmd.command}}</td>
175 <td>{{cmd.date | date:"MM/dd/yyyy 'at' h:mma"}}</td>
176 <td ng-bind="cmd.duration || 'undefined'"></td>
177 </tr>
178 </tbody>
179 </table>
180 </article>
181 </div>
182 </div>
0 <div id="vulns-by-price">
1 <div class="col-lg-6">
2 <article class="panel panel-default">
3 <header>
4 <h2>
5 Workspace's worth
6 <span class="glyphicon glyphicon-info-sign" tooltip="Total net worth of Workspace, according to current vulnerabilities' prices"></span>
7 </h2>
8 </header>
9 <div class="main">
10 <div class="center-lg-6"><h4><i class="fa fa-money fa-2x"></i> {{total | currency}} total</h4></div>
11 <div d3-horizontal-stacked-bar data="data" class="stackedbars"></div>
12 <div id="vulns-by-price-reference" class="center-lg-6">
13 <ul class="label-list">
14 <li ng-repeat="(severity, price) in prices" tooltip="Click on number to edit price"><span class="label vuln fondo-{{severity}}">{{severity}} $<span contenteditable="true" ng-model="prices[severity]"></span></span></li>
15 </ul>
16 </div><!-- #vulns-by-price-reference .center-lg-6 -->
17 </div>
18 </article>
19 </div><!-- .col-lg-6 -->
20 </div><!-- #vulns-by-price -->
0 <div id='vulns'>
1 <div class='col-lg-6'>
2 <article class='panel panel-default'>
3 <header>
4 <h2>
5 <a href="../././reports/index.html#/status/ws/{{workspace}}" class="status-report">Vulnerabilities</a>
6 <span class="glyphicon glyphicon-info-sign" tooltip="Vulnerabilities count arranged by severity"></span>
7 </h2>
8 </header>
9 <div ng-if="vulnsCount.length == 0" class="alert alert-info alert-dismissible">
10 <button type="button" class="close" data-dismiss="alert">
11 <span aria-hidden="true">&times;</span>
12 <span class="sr-only">Close</span>
13 </button>
14 <p>No vulnerabilities found yet.</p>
15 </div>
16 <div class='main box'>
17 <div ng-repeat="vuln in vulnsCount" class="columna unsexto cursor">
18 <article class="dato2 fondo-{{vuln.key}}" ng-click="navigate('/status/ws/'+workspace+'/search/severity='+vuln.key)">
19 <section>
20 <div class="nro texto-blanco">{{vuln.value}}</div>
21 <div class="txt texto-blanco">{{vuln.key}}</div>
22 </section>
23 </article>
24 </div>
25 </div>
26 </article>
27 </div>
28 </div>
2828
2929 dashboardSrv.getServicesCount = function(ws) {
3030 var url = BASEURL + "/" + ws + "/_design/hosts/_view/byservices?group=true";
31 return dashboardSrv._getView(url);
32 };
33
34 dashboardSrv.getVulnerabilities = function(ws) {
35 var url = BASEURL + "/" + ws + "/_design/vulns/_view/all";
3136 return dashboardSrv._getView(url);
3237 };
3338
133138 return deferred.promise;
134139 };
135140
136 dashboardSrv.getName = function(ws, id){
141 dashboardSrv.getName = function(ws, id) {
137142 var deferred = $q.defer();
138143 url = BASEURL + "/" + ws + "/" + id;
139144
145150 });
146151
147152 return deferred.promise;
148 }
153 };
154
155
156 dashboardSrv.accumulate = function(_array, key, value) {
157 _array.forEach(function(obj){
158 if(obj.key == key) {
159 obj.value += value;
160 }
161 });
162 };
149163
150164 return dashboardSrv;
151 }]);
165 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('hostCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'statusReportFact', 'dashboardSrv', 'servicesManager',
7 function($scope, $filter, $route, $routeParams, $modal, hostsManager, statusReportFact, dashboardSrv, servicesManager) {
8
9
10 init = function() {
11 $scope.selectall = false;
12 // current workspace
13 $scope.workspace = $routeParams.wsId;
14 //ID of current host
15 var hostId = $routeParams.hidId;
16 // load all workspaces
17 statusReportFact.getWorkspaces().then(function(wss) {
18 $scope.workspaces = wss;
19 });
20 // current host
21 hostsManager.getHost(hostId, $scope.workspace).then(function(host){
22 $scope.host = host;
23 });
24 // services by host
25 $scope.services = [];
26 dashboardSrv.getServicesByHost($scope.workspace, hostId).then(function(services){
27 services.forEach(function(service){
28 servicesManager.getService(service.id, $scope.workspace, true).then(function(s){
29 $scope.services.push(s);
30 });
31 });
32 });
33 };
34
35 $scope.new = function() {
36 var modal = $modal.open({
37 templateUrl: 'scripts/services/partials/modalNew.html',
38 controller: 'serviceModalNew',
39 size: 'lg',
40 resolve: {
41 host: function() {
42 return $scope.host;
43 }
44 }
45 });
46
47 modal.result.then(function(data) {
48 $scope.insert(data);
49 });
50 };
51
52 $scope.insert = function(service) {
53 servicesManager.createService(service, $scope.workspace).then(function(service) {
54 $scope.services.push(service);
55 }, function(message) {
56 $modal.open(config = {
57 templateUrl: 'scripts/commons/partials/modalKO.html',
58 controller: 'commonsModalKoCtrl',
59 size: 'sm',
60 resolve: {
61 msg: function() {
62 return message;
63 }
64 }
65 });
66 });
67 };
68
69 $scope.update = function(services, data) {
70 services.forEach(function(service){
71 delete service.selected;
72 servicesManager.updateService(service, data, $scope.workspace).then(function(s) {
73 }, function(message){
74 console.log(message);
75 });
76 });
77 };
78
79 $scope.edit = function() {
80 var selected_service = [];
81
82 $scope.services.forEach(function(service) {
83 if(service.selected) {
84 // if more than one service was selected,
85 // we only use the last one, for now
86 selected_service.push(service);
87 }
88 });
89
90 if(selected_service.length > 0) {
91 var modal = $modal.open({
92 templateUrl: 'scripts/services/partials/modalEdit.html',
93 controller: 'serviceModalEdit',
94 size: 'lg',
95 resolve: {
96 service: function(){
97 return selected_service;
98 },
99 services: function() {
100 return $scope.services;
101 }
102 }
103 });
104
105 modal.result.then(function(data) {
106 $scope.update(selected_service, data);
107 });
108 } else {
109 $modal.open(config = {
110 templateUrl: 'scripts/commons/partials/modalKO.html',
111 controller: 'commonsModalKoCtrl',
112 size: 'sm',
113 resolve: {
114 msg: function() {
115 return 'No hosts were selected to edit';
116 }
117 }
118 });
119 }
120 };
121
122 $scope.delete = function() {
123 var selected = [];
124 $scope.services.forEach(function(service){
125 if(service.selected){
126 selected.push(service._id);
127 }
128 });
129
130 if(selected.length == 0) {
131 $modal.open(config = {
132 templateUrl: 'scripts/commons/partials/modalKO.html',
133 controller: 'commonsModalKoCtrl',
134 size: 'sm',
135 resolve: {
136 msg: function() {
137 return 'No hosts were selected to delete';
138 }
139 }
140 })
141 } else {
142 var message = "A host will be deleted";
143 if(selected.length > 1) {
144 message = selected.length + " hosts will be deleted";
145 }
146 message = message.concat(" along with all of its children. This operation cannot be undone. Are you sure you want to proceed?");
147 $modal.open(config = {
148 templateUrl: 'scripts/commons/partials/modalDelete.html',
149 controller: 'commonsModalDelete',
150 size: 'lg',
151 resolve: {
152 msg: function() {
153 return message;
154 }
155 }
156 }).result.then(function() {
157 $scope.remove(selected);
158 }, function() {
159 //dismised, do nothing
160 });
161 }
162 };
163
164 $scope.remove = function(ids) {
165 ids.forEach(function(id) {
166 servicesManager.deleteServices(id, $scope.workspace).then(function() {
167 var index = -1;
168 for(var i=0; i < $scope.services.length; i++) {
169 if($scope.services[i]._id === id) {
170 index = i;
171 break;
172 }
173 }
174 $scope.services.splice(index, 1);
175 }, function(message) {
176 console.log(message);
177 });
178 });
179 };
180
181
182 $scope.checkAll = function() {
183 $scope.selectall = !$scope.selectall;
184
185 angular.forEach($filter('filter')($scope.hosts, $scope.query), function(host) {
186 host.selected = $scope.selectall;
187 });
188 };
189
190 // toggles sort field and order
191 $scope.toggleSort = function(field) {
192 $scope.toggleSortField(field);
193 $scope.toggleReverse();
194 };
195
196 // toggles column sort field
197 $scope.toggleSortField = function(field) {
198 $scope.sortField = field;
199 };
200
201 // toggle column sort order
202 $scope.toggleReverse = function() {
203 $scope.reverse = !$scope.reverse;
204 }
205
206 init();
207 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('hostsCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', '$modal', 'hostsManager', 'statusReportFact',
7 function($scope, $filter, $route, $routeParams, $modal, hostsManager, statusReportFact) {
8
9 init = function() {
10 $scope.selectall = false;
11 // hosts list
12 $scope.hosts = [];
13 // current workspace
14 $scope.workspace = $routeParams.wsId;
15 // load all workspaces
16 statusReportFact.getWorkspaces().then(function(wss) {
17 $scope.workspaces = wss;
18 });
19
20 hostsManager.getHosts($scope.workspace).then(function(hosts) {
21 $scope.hosts = hosts;
22 $scope.loadIcons();
23 });
24 };
25
26 $scope.loadIcons = function() {
27 $scope.hosts.forEach(function(host) {
28 // load icons into object for HTML
29 // maybe this part should be directly in the view somehow
30 // or, even better, in a CSS file
31 oss = ["windows", "cisco", "router", "osx", "apple","linux", "unix"];
32 oss.forEach(function(os){
33 if(host.os.toLowerCase().indexOf(os) != -1) {
34 host.icon = os;
35 if(os == "unix") {
36 host.icon = "linux";
37 } else if(os == "apple") {
38 host.icon = "osx";
39 }
40 }
41 });
42 });
43 };
44
45 $scope.remove = function(ids) {
46 ids.forEach(function(id) {
47 hostsManager.deleteHost(id, $scope.workspace).then(function() {
48 var index = -1;
49 for(var i=0; i < $scope.hosts.length; i++) {
50 if($scope.hosts[i]._id === id) {
51 index = i;
52 break;
53 }
54 }
55 $scope.hosts.splice(index, 1);
56 }, function(message) {
57 console.log(message);
58 });
59 });
60 };
61
62 $scope.delete = function() {
63 var selected = [];
64
65 for(var i=0; i < $scope.hosts.length; i++) {
66 var host = $scope.hosts[i];
67 if(host.selected) {
68 selected.push(host._id);
69 }
70 };
71
72 if(selected.length == 0) {
73 $modal.open(config = {
74 templateUrl: 'scripts/commons/partials/modalKO.html',
75 controller: 'commonsModalKoCtrl',
76 size: 'sm',
77 resolve: {
78 msg: function() {
79 return 'No hosts were selected to delete';
80 }
81 }
82 })
83 } else {
84 var message = "A host will be deleted";
85 if(selected.length > 1) {
86 message = selected.length + " hosts will be deleted";
87 }
88 message = message.concat(" along with all of its children. This operation cannot be undone. Are you sure you want to proceed?");
89 $modal.open(config = {
90 templateUrl: 'scripts/commons/partials/modalDelete.html',
91 controller: 'commonsModalDelete',
92 size: 'lg',
93 resolve: {
94 msg: function() {
95 return message;
96 }
97 }
98 }).result.then(function() {
99 $scope.remove(selected);
100 }, function() {
101 //dismised, do nothing
102 });
103 }
104 };
105
106 $scope.insert = function(hostdata, interfaceData) {
107 var interfaceData = $scope.createInterface(hostdata, interfaceData);
108 hostsManager.createHost(hostdata, interfaceData, $scope.workspace).then(function(host) {
109 $scope.hosts.push(host);
110 $scope.loadIcons();
111 }, function(message) {
112 $modal.open(config = {
113 templateUrl: 'scripts/commons/partials/modalKO.html',
114 controller: 'commonsModalKoCtrl',
115 size: 'sm',
116 resolve: {
117 msg: function() {
118 return message;
119 }
120 }
121 });
122 });
123 }
124
125 $scope.new = function() {
126 var modal = $modal.open({
127 templateUrl: 'scripts/hosts/partials/modalNew.html',
128 controller: 'hostsModalNew',
129 size: 'lg',
130 resolve: {}
131 });
132
133 modal.result.then(function(data) {
134 hostdata = data[0];
135 interfaceData = data[1];
136 $scope.insert(hostdata, interfaceData);
137 });
138 };
139
140 $scope.update = function(host, hostdata, interfaceData) {
141 delete host.selected;
142 hostsManager.updateHost(host, hostdata, interfaceData, $scope.workspace).then(function() {
143 // load icons in case an operating system changed
144 $scope.loadIcons();
145 }, function(message){
146 console.log(message);
147 });
148 }
149
150 $scope.edit = function() {
151 var selected_host = null;
152
153 $scope.hosts.forEach(function(host) {
154 if(host.selected) {
155 // if more than one host was selected,
156 // we only use the last one, for now
157 selected_host = host;
158 }
159 });
160
161 if(selected_host) {
162 var modal = $modal.open({
163 templateUrl: 'scripts/hosts/partials/modalEdit.html',
164 controller: 'hostsModalEdit',
165 size: 'lg',
166 resolve: {
167 host: function(){
168 return selected_host;
169 }
170 }
171 });
172
173 modal.result.then(function(data) {
174 hostdata = data[0];
175 interfaceData = data[1];
176 $scope.update(selected_host, hostdata, interfaceData);
177 });
178 } else {
179 $modal.open(config = {
180 templateUrl: 'scripts/commons/partials/modalKO.html',
181 controller: 'commonsModalKoCtrl',
182 size: 'sm',
183 resolve: {
184 msg: function() {
185 return 'No hosts were selected to edit';
186 }
187 }
188 });
189 }
190 };
191
192 $scope.createInterface = function (hostData, interfaceData){
193 if(typeof(hostData.ipv4) == "undefined") hostData.ipv4 = "";
194 if(typeof(hostData.ipv6) == "undefined") hostData.ipv6 = "";
195 var interfaceData = {
196 "_id": CryptoJS.SHA1(hostData.name).toString() + "." + CryptoJS.SHA1("" + "._." + interfaceData.ipv4 + "._." + interfaceData.ipv6).toString(),
197 "description": "",
198 "hostnames": interfaceData.hostnames,
199 "ipv4": {
200 "mask": "0.0.0.0",
201 "gateway": "0.0.0.0",
202 "DNS": [],
203 "address": interfaceData.ipv4
204 },
205 "ipv6": {
206 "prefix": "00",
207 "gateway": "0000.0000.0000.0000",
208 "DNS": [],
209 "address": interfaceData.ipv6
210 },
211 "mac": interfaceData.mac,
212 "metadata": {
213 "update_time": new Date().getTime(),
214 "update_user": "",
215 "update_action": 0,
216 "creator": "",
217 "create_time": new Date().getTime(),
218 "update_controller_action": "",
219 "owner": "",
220
221 },
222 "name": hostData.name,
223 "network_segment": "",
224 "owned": false,
225 "owner": "",
226 "parent": CryptoJS.SHA1(hostData.name).toString(),
227 "ports": {
228 "filtered": 0,
229 "opened": 0,
230 "closed": 0
231 },
232 "type": "Interface"
233 };
234 return interfaceData;
235 };
236
237 $scope.checkAll = function() {
238 $scope.selectall = !$scope.selectall;
239
240 angular.forEach($filter('filter')($scope.hosts, $scope.query), function(host) {
241 host.selected = $scope.selectall;
242 });
243 };
244
245 // toggles sort field and order
246 $scope.toggleSort = function(field) {
247 $scope.toggleSortField(field);
248 $scope.toggleReverse();
249 };
250
251 // toggles column sort field
252 $scope.toggleSortField = function(field) {
253 $scope.sortField = field;
254 };
255
256 // toggle column sort order
257 $scope.toggleReverse = function() {
258 $scope.reverse = !$scope.reverse;
259 }
260
261 init();
262 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('hostsModalEdit',
6 ['$scope', '$modalInstance', '$routeParams', 'hostsManager', 'host', 'commonsFact',
7 function($scope, $modalInstance, $routeParams, hostsManager, host, commons) {
8
9 var ws = $routeParams.wsId;
10 $scope.hostdata = {};
11 hostsManager.getInterfaces(ws, host._id).then(function(resp){
12 $scope.interface = resp[0].value;
13 $scope.interface.hostnames = commons.arrayToObject($scope.interface.hostnames);
14 });
15
16 $scope.host = {
17 "_id": host._id,
18 "_rev": host._rev,
19 "description": host.description,
20 "default_gateway": "None",
21 "metadata": host.metadata,
22 "name": host.name,
23 "os": host.os,
24 "owner": "",
25 "owned": host.owned,
26 "parent": host.parent,
27 "type": host.type
28 };
29
30 $scope.ok = function() {
31 var date = new Date(),
32 timestamp = date.getTime()/1000.0;
33 $scope.interface.hostnames = commons.objectToArray($scope.interface.hostnames.filter(Boolean));
34
35 $scope.hostdata = $scope.host;
36 $scope.hostdata.metadata['update_time'] = timestamp;
37 $scope.hostdata.metadata['update_user'] = "UI Web";
38
39 $modalInstance.close([$scope.hostdata, $scope.interface]);
40 };
41
42 $scope.cancel = function() {
43 $modalInstance.dismiss('cancel');
44 };
45
46 $scope.newHostnames = function($event){
47 $scope.interface.hostnames.push({key:''});
48 $event.preventDefault();
49 }
50
51 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('hostsModalNew',
6 ['$scope', '$modalInstance', 'hostsManager',
7 function($scope, $modalInstance, hostsManager) {
8
9 $scope.hostdata = {
10 "name": "",
11 "description": "",
12 "default_gateway": "None",
13 "os": "",
14 "owned": false,
15 "owner": "",
16 };
17
18 $scope.interfaceData = {
19 "hostnames": [{key: ''}],
20 "ipv6": "0000:0000:0000:0000:0000:0000:0000:0000",
21 "ipv4": "0.0.0.0",
22 "mac": "00:00:00:00:00:00",
23 "interfaceOwner": "",
24 "interfaceOwned": false
25 };
26
27 $scope.ok = function() {
28 var hostnames = [];
29 var date = new Date(),
30 timestamp = date.getTime()/1000.0;
31
32 if($scope.hostdata.parent == undefined) $scope.hostdata.parent = null;
33
34 $scope.interfaceData.hostnames.forEach(function(hname){
35 hostnames.push(hname.hostname);
36 });
37
38 $scope.interfaceData.hostnames = hostnames.filter(Boolean);
39 $scope.hostdata.interfaceName = $scope.hostdata.name;
40 $scope.hostdata.metadata = {
41 "update_time": timestamp,
42 "update_user": "",
43 "update_action": 0,
44 "creator": "",
45 "create_time": timestamp,
46 "update_controller_action": "UI Web New",
47 "owner": ""
48 };
49 $modalInstance.close([$scope.hostdata,$scope.interfaceData]);
50 };
51
52 $scope.cancel = function() {
53 $modalInstance.dismiss('cancel');
54 };
55
56 $scope.newHostnames = function($event){
57 $scope.interfaceData.hostnames.push({key:''});
58 $event.preventDefault();
59 }
60
61 }]);
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5
6 <div class="right-main"><div id="reports-main" class="fila clearfix">
7 <h2 class="ws-label">
8 <span id="ws-name" class="label label-default"
9 title="Hosts">Hosts for {{workspace}} ({{hosts.length}})</span><!-- WS name -->
10 <div id="ws-control" class="btn-group">
11 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()">
12 <span class="glyphicon glyphicon-refresh"></span>
13 </button>
14 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
15 Change workspace <span class="caret"></span>
16 </button>
17 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu">
18 <li ng-repeat="ws in workspaces"><a href="#/hosts/ws/{{ws}}" class="ws" >{{ws}}</a></li>
19 </ul><!-- WS navigation -->
20 </div><!-- #ws-control -->
21 <button id="delete" type="button" class="btn btn-default" title="Delete selected hosts" ng-click="delete()">
22 <span class="glyphicon glyphicon-trash"></span>
23 Delete
24 </button>
25 <button id="merge" type="button" class="btn btn-default" title="Edit selected hosts" ng-click="edit()">
26 <span class="glyphicon glyphicon-pencil"></span>
27 Edit
28 </button>
29 <button id="merge" type="button" class="btn btn-success" title="New host" ng-click="new()">
30 <span class="glyphicon glyphicon-plus-sign"></span>
31 New
32 </button>
33 </h2><!-- .ws-label -->
34 <div class="reports col-md-9 col-sm-9 col-xs-12">
35 <table class="status-report hosts-list table table-responsive">
36 <thead>
37 <tr>
38 <th><input type="checkbox" ng-model="selectall" ng-click="checkAll()"/></th>
39 <th>
40 <a href="">Services</a>
41 </th>
42 <th>
43 <a href="" ng-click="toggleSort('name')">Name</a>
44 </th>
45 <th>
46 <a href="" ng-click="toggleSort('description')">Description</a>
47 </th>
48 <th>
49 <a href="" ng-click="toggleSort('os')">OS</a>
50 </th>
51 <th>
52 <a href="" ng-click="toggleSort('owned')">Owned</a>
53 </th>
54 </tr>
55 </thead>
56 <tbody>
57 <tr ng-repeat="host in hosts | filter:query | orderBy:sortField:reverse"
58 selection-model selection-model-type="checkbox"
59 selection-model-mode="multiple-additive"
60 selection-model-selected-class="multi-selected">
61 <td><input type="checkbox" name="{{host._id}}"/></td>
62 <td><a href="#/host/ws/{{workspace}}/hid/{{host._id}}">View</a></td>
63 <td ng-bind="host.name"></td>
64 <td ng-bind="host.description || '-'"></td>
65 <td>
66 <img ng-if="host.icon != undefined" ng-src="../././reports/images/{{host.icon}}.png" tooltip="{{host.os}}"/>
67 <span ng-if="host.icon == undefined" class="glyphicon glyphicon-question-sign" tooltip="{{host.os}}"></span>
68 </td>
69 <td ng-bind="host.owned"></td>
70 </tr>
71 </tbody>
72 </table><!-- #hosts -->
73 </div><!-- .reports -->
74 </div><!-- #reports-main --></div><!-- .right-main -->
75 </section><!-- #main -->
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">Edit host</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <label class="sr-only" for="name">Name</label>
17 <input type="text" class="form-control" id="name" placeholder="Name" ng-model="host.name" required/>
18 <span class="help-block normal-size">
19 Example: 192.168.0.1
20 </span>
21 </div>
22 </div><!-- .form-group -->
23 <div class="form-group">
24 <div class="col-md-12">
25 <label class="sr-only" for="description">Description</label>
26 <textarea class="form-control" id="description" placeholder="Description" ng-model="host.description"></textarea>
27 </div>
28 </div><!-- .form-group -->
29 <div class="form-group">
30 <div class="col-md-12">
31 <label class="sr-only" for="os">Operating System</label>
32 <input type="text" class="form-control" id="os" placeholder="Operating System" ng-model="host.os"/>
33 <span class="help-block normal-size">
34 Example: Linux 3.18.6-1-ARCH
35 </span>
36 </div>
37 </div><!-- .form-group -->
38 <div class="form-group">
39 <div class="col-md-12">
40 <div class="checkbox">
41 <label>
42 <input type="checkbox" id="owned" ng-model="host.owned"/>
43 <span class="normal-size">Owned</span>
44 </label>
45 </div><!-- .checkbox -->
46 </div>
47 </div><!-- .form-group -->
48 <div class="form-group">
49 <div class="col-md-12">
50 <h5>Hostnames</h5>
51 <span class="input-group-addon button-radius" ng-click="newHostnames($event)">Add Hostname</span>
52 </div>
53 <div class="col-md-12 input-margin" ng-repeat="hostname in interface.hostnames">
54 <div class="input-group margin-bottom-sm col-md-12">
55 <label class="sr-only" for="hostsnames">Hostname</label>
56 <input type="text" class="form-control" id="hostsnames" placeholder="Hostname" ng-model="hostname.key"/>
57 <span class="input-group-addon" ng-click="interface.hostnames.splice($index, 1)" ng-hide="interface.hostnames.length == 1"><i class="fa fa-minus-circle"></i></span>
58 </div>
59 </div>
60 </div><!-- .form-group -->
61 <div class="form-group">
62 <div class="col-md-6">
63 <label class="sr-only" for="ipv4">IP v4</label>
64 <input type="text" class="form-control" id="ipv4" placeholder="IP v4" ng-model="interface.ipv4.address"/>
65 </div>
66 <div class="col-md-6">
67 <label class="sr-only" for="ipv6">IP v6</label>
68 <input type="text" class="form-control" id="ipv6" placeholder="IP v6" ng-model="interface.ipv6.address"/>
69 </div>
70 </div><!-- .form-group -->
71 <div class="form-group">
72 <div class="col-md-12">
73 <label class="sr-only" for="mac">MAC</label>
74 <input type="text" class="form-control" id="mac" placeholder="MAC" ng-model="interface.mac"/>
75 </div>
76 </div><!-- .form-group -->
77 </div>
78 </div><!-- .modal-body -->
79 <div class="modal-footer">
80 <div class="modal-button">
81 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
82 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
83 </div>
84 </div>
85 </form>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">New host</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <label class="sr-only" for="name">Name</label>
17 <input type="text" class="form-control" id="name" placeholder="Name" ng-model="hostdata.name" required/>
18 <span class="help-block normal-size">
19 Example: 192.168.0.1
20 </span>
21 </div>
22 </div><!-- .form-group -->
23 <div class="form-group">
24 <div class="col-md-12">
25 <label class="sr-only" for="description">Description</label>
26 <textarea class="form-control" id="description" placeholder="Description" ng-model="hostdata.description"></textarea>
27 </div>
28 </div><!-- .form-group -->
29 <div class="form-group">
30 <div class="col-md-12">
31 <label class="sr-only" for="os">Operating System</label>
32 <input type="text" class="form-control" id="os" placeholder="Operating System" ng-model="hostdata.os"/>
33 <span class="help-block normal-size">
34 Example: Linux 3.18.6-1-ARCH
35 </span>
36 </div>
37 </div><!-- .form-group -->
38 <div class="form-group">
39 <div class="col-md-12">
40 <div class="checkbox">
41 <label>
42 <input type="checkbox" id="owned" ng-model="hostdata.owned"/>
43 <span class="normal-size">Owned</span>
44 </label>
45 </div><!-- .checkbox -->
46 </div>
47 </div><!-- .form-group -->
48 <div class="form-group">
49 <div class="col-md-12">
50 <h5>Hostnames</h5>
51 <span class="input-group-addon button-radius" ng-click="newHostnames($event)">Add Hostname</span>
52 </div>
53 <div class="col-md-12 input-margin" ng-repeat="hostname in interfaceData.hostnames">
54 <div class="input-group margin-bottom-sm col-md-12">
55 <label class="sr-only" for="hostsnames">Hostname</label>
56 <input type="text" class="form-control" id="hostsnames" placeholder="Hostname" ng-model="hostname.hostname"/>
57 <span class="input-group-addon" ng-click="interfaceData.hostnames.splice($index, 1)" ng-hide="interfaceData.hostnames.length == 1"><i class="fa fa-minus-circle"></i></span>
58 </div>
59 </div>
60 </div><!-- .form-group -->
61 <div class="form-group">
62 <div class="col-md-6">
63 <label class="sr-only" for="ipv4">IP v4</label>
64 <input type="text" class="form-control" id="ipv4" placeholder="IP v4" ng-model="interfaceData.ipv4" ng-required="!interfaceData.ipv6"/>
65 </div>
66 <div class="col-md-6">
67 <label class="sr-only" for="ipv6">IP v6</label>
68 <input type="text" class="form-control" id="ipv6" placeholder="IP v6" ng-model="interfaceData.ipv6" ng-required="!interfaceData.ipv4"/>
69 </div>
70 </div><!-- .form-group -->
71 <div class="form-group">
72 <div class="col-md-12">
73 <label class="sr-only" for="mac">MAC</label>
74 <input type="text" class="form-control" id="mac" placeholder="MAC" ng-model="interfaceData.mac"/>
75 </div>
76 </div><!-- .form-group -->
77 </div>
78 </div><!-- .modal-body -->
79 <div class="modal-footer">
80 <div class="modal-button">
81 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
82 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
83 </div>
84 </div>
85 </form>
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .factory('Host', ['BASEURL', '$http', function(BASEURL, $http) {
6 Host = function(data){
7 if(data) {
8 this.set(data);
9 }
10 };
11
12 Host.prototype = {
13 // TODO: instead of using angular.extend, we should check
14 // the attributes we're assigning to the host
15 set: function(data) {
16 // if there's no ID, we need to generate it based on the host name
17 if(data._id === undefined){
18 data['_id'] = CryptoJS.SHA1(data.name).toString();
19 }
20 data.type = "Host";
21 angular.extend(this, data);
22 },
23 delete: function(ws) {
24 var self = this,
25 bulk = {docs:[]};
26 return $http.get(BASEURL + ws + '/_all_docs?startkey="' + self._id + '"&endkey="' + self._id + '.z"').then(function(all) {
27 all.data.rows.forEach(function(row) {
28 bulk.docs.push({
29 "_id": row.id,
30 "_rev": row.value.rev,
31 "_deleted": true
32 });
33 });
34
35 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk));
36 });
37 },
38 update: function(data, interfaceData, ws) {
39 var self = this;
40 bulk = {docs:[data,interfaceData]};
41 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk)).success(function(data){
42 if(data.id == self._id){
43 self._rev = data.rev;
44 } else {
45 interfaceData._rev = data.rev;
46 }
47 });
48 },
49 save: function(ws, interfaceData) {
50 var self = this;
51 bulk = {docs:[self,interfaceData]};
52 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk)).success(function(data){
53 if(data.id == self._id){
54 self._rev = data.rev;
55 } else {
56 interfaceData._rev = data.rev;
57 }
58 });
59 }
60 }
61
62 return Host;
63 }]);
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('hostsFact', ['BASEURL', function(BASEURL) {
6 var hostsFact = {};
5 .factory('hostsManager', ['BASEURL', '$http', '$q', 'Host', function(BASEURL, $http, $q, Host) {
6 var hostsManager = {};
77
8 hostsFact.get = function(ws) {
8 hostsManager._objects = {};
9 hostsManager._get = function(id, data) {
10 var host = this._objects[id];
11
12 if(host) {
13 host.set(data);
14 } else {
15 host = new Host(data);
16 this._objects[id] = host;
17 }
18
19 return host;
20 }
21
22 hostsManager._search = function(id) {
23 return this._objects[id];
24 }
25
26 hostsManager._load = function(id, ws, deferred) {
27 var self = this;
28 $http.get(BASEURL + '/' + ws + '/' + id)
29 .success(function(data){
30 var host = self._get(data._id, data);
31 deferred.resolve(host);
32 })
33 .error(function(){
34 deferred.reject();
35 });
36 }
37
38 hostsManager.getHost = function(id, ws, force_reload) {
39 var deferred = $q.defer();
40 var host = this._search(id);
41 force_reload = force_reload || false;
42 if((host) && (!force_reload)) {
43 deferred.resolve(host);
44 } else {
45 this._load(id, ws, deferred);
46 }
47 return deferred.promise;
48 }
49
50 // async method - this is the one to use from now on!
51 hostsManager.getHosts = function(ws) {
52 var deferred = $q.defer();
53 var self = this;
54 $http.get(BASEURL + '/' + ws + '/_design/hosts/_view/hosts')
55 .success(function(hostsArray){
56 var hosts = [];
57 hostsArray.rows.forEach(function(hostData){
58 var host = self._get(hostData.value._id, hostData.value);
59 hosts.push(host);
60 });
61 deferred.resolve(hosts);
62 })
63 .error(function(){
64 deferred.reject();
65 })
66 return deferred.promise;
67 }
68
69 // sync method - still used in statusReportFact
70 hostsManager.get = function(ws) {
971 hosts_url = BASEURL + ws + "/_design/hosts/_view/hosts";
1072 var hosts = [];
1173 //gets hosts json from couch
1274 $.getJSON(hosts_url, function(data) {
1375 $.each(data.rows, function(n, obj) {
14 hosts[obj.id] = {"name": obj.value.name, "os": obj.value.os, "owned": obj.value.owned};
76 hosts[obj.id] = {
77 "categories": obj.value.categories,
78 "default_gateway": obj.value.default_gateway,
79 "description": obj.value.description,
80 "metadata": obj.value.metadata,
81 "name": obj.value.name,
82 "os": obj.value.os,
83 "owned": obj.value.owned,
84 "owner": obj.value.owner
85 };
1586 });
1687 });
1788 return hosts;
1889 }
19 return hostsFact;
90
91 hostsManager.deleteHost = function(id, ws) {
92 var deferred = $q.defer();
93 var self = this;
94 this.getHost(id, ws).then(function(host) {
95 host.delete(ws).then(function() {
96 delete self._objects[id];
97 deferred.resolve();
98 }, function(){
99 // host couldn't be deleted
100 deferred.reject("Error deleting host");
101 });
102 }, function(){
103 // host doesn't exist
104 deferred.reject("Host doesn't exist");
105 });
106 return deferred.promise;
107 }
108
109 hostsManager.createHost = function(hostData, interfaceData, ws) {
110 var deferred = $q.defer();
111 var self = this;
112
113 this.getHosts(ws).then(function(hosts) {
114 var host = new Host(hostData);
115 self.getHost(host._id, ws).then(function() {
116 deferred.reject("Host already exists");
117 }, function() {
118 // host doesn't exist, good to go
119 host.save(ws, interfaceData).then(function(){
120 host = self.getHost(host._id, ws);
121 deferred.resolve(host);
122 }, function(){
123 // host couldn't be saved
124 deferred.reject("Error: host couldn't be saved");
125 })
126 });
127 });
128
129 return deferred.promise;
130 }
131
132 hostsManager.updateHost = function(host, hostData, interfaceData, ws) {
133 var deferred = $q.defer();
134 var self = this;
135 this.getHost(host._id, ws).then(function(resp) {
136 resp.update(hostData, interfaceData, ws).then(function() {
137 // we need to reload the host in order
138 // to update _rev
139 host = self._load(host._id, ws, deferred);
140 deferred.resolve(host);
141 })
142 }, function(){
143 // host doesn't exist
144 deferred.reject("Host doesn't exist");
145 });
146 return deferred.promise;
147 }
148
149 hostsManager.getInterfaces = function(ws, hostId){
150 var deferred = $q.defer();
151 var self = this;
152 if(hostId) {
153 var url = BASEURL + '/' + ws + '/_design/interfaces/_view/interfaces?key=\"' + hostId + '\"';
154 }else{
155 var url = BASEURL + '/' + ws + '/_design/interfaces/_view/interfaces';
156 }
157 $http.get(url)
158 .success(function(interfaceArray){
159 var interfaces = interfaceArray.rows;
160 deferred.resolve(interfaces);
161 })
162 .error(function(){
163 deferred.reject();
164 })
165 return deferred.promise;
166 }
167
168 return hostsManager;
20169 }]);
+0
-21
views/reports/_attachments/scripts/modal/controllers/modalDelete.js less more
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalDeleteCtrl', ['$scope', '$modalInstance', 'amount', function($scope, $modalInstance, amount) {
6 if(amount == 1) {
7 $scope.message = "A vulnerability will be deleted.";
8 } else {
9 $scope.message = amount + " vulnerabilities will be deleted.";
10 }
11 $scope.message += " This action cannot be undone. Are you sure you want to proceed?";
12
13 $scope.ok = function() {
14 $modalInstance.close();
15 };
16
17 $scope.cancel = function() {
18 $modalInstance.dismiss('cancel');
19 };
20 }]);
+0
-214
views/reports/_attachments/scripts/modal/controllers/modalEdit.js less more
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalEditCtrl', ['$scope', '$modalInstance', 'EASEOFRESOLUTION', 'commonsFact', 'severities', 'vulns',
6 function($scope, $modalInstance, EASEOFRESOLUTION, commons, severities, vulns) {
7 $scope.easeofresolution = EASEOFRESOLUTION;
8 $scope.evidence = {};
9 $scope.icons = {};
10 $scope.severities = severities;
11 $scope.vulns = vulns;
12 $scope.web = false;
13 $scope.mixed = 0x00;
14 $scope.vulnc = 0;
15 $scope.vulnid = 0;
16 $scope.file_name_error = false;
17 $scope.p_impact = {
18 "accountability": false,
19 "availability": false,
20 "confidentiality": false,
21 "integrity": false
22 };
23 $scope.impact = {
24 "accountability": false,
25 "availability": false,
26 "confidentiality": false,
27 "integrity": false
28 };
29 var vuln_mask = {"VulnerabilityWeb": 0x01, "Vulnerability": 0x10};
30
31 $scope.pickVuln = function(v) {
32 $scope.p_name = v.name;
33 $scope.p_desc = v.desc;
34 $scope.p_data = v.data;
35 $scope.severitySelection = v.severity;
36 $scope.easeOfResolutionSelection = v.easeofresolution;
37 $scope.p_method = v.method;
38 $scope.p_pname = v.pname;
39 $scope.p_params = v.params;
40 $scope.p_path = v.path;
41 $scope.p_query = v.query;
42 $scope.p_website = v.website;
43 $scope.p_refs = v.refs;
44 $scope.p_request = v.request;
45 $scope.p_response = v.response;
46 $scope.p_resolution = v.resolution;
47
48 $scope.name = $scope.p_name;
49 $scope.data = $scope.p_data;
50 $scope.desc = $scope.p_desc;
51 $scope.method = $scope.p_method;
52 $scope.params = $scope.p_params;
53 $scope.path = $scope.p_path;
54 $scope.pname = $scope.p_pname;
55 $scope.query = $scope.p_query;
56 $scope.refs = $scope.p_refs;
57 $scope.request = $scope.p_request;
58 $scope.response = $scope.p_response;
59 $scope.resolution = $scope.p_resolution;
60 $scope.website = $scope.p_website;
61
62 for(var key in v.impact) {
63 $scope.impact[key] = v.impact[key];
64 $scope.p_impact[key] = v.impact[key];
65 }
66
67 };
68
69 $scope.toggleImpact = function(key) {
70 $scope.impact[key] = !$scope.impact[key];
71 };
72
73 $scope.call = function(){
74 $scope.refs = commons.arrayToObject($scope.refs);
75 };
76 vulnid_count=0
77 $scope.vulns.forEach(function(v) {
78 if(v.selected) {
79 if(typeof(v.attachments) != undefined && v.attachments != undefined) {
80 v.attachments.forEach(function(name) {
81 $scope.evidence[name] = {"name": name};
82 });
83 $scope.icons = commons.loadIcons($scope.evidence);
84 }
85 $scope.mixed = $scope.mixed | vuln_mask[v.type];
86 $scope.vulnc++;
87 $scope.vulnid = vulnid_count;
88 if (v.type === "VulnerabilityWeb") {
89 $scope.web = true;
90 //web
91 }
92
93 }
94 vulnid_count++;
95
96
97 });
98
99 if ($scope.vulnc == 1) {
100 $scope.pickVuln($scope.vulns[$scope.vulnid]);
101 }
102
103 $scope.unit = $scope.vulnc == 1;
104
105 if ($scope.vulnc > 1) {
106 $scope.p_name = "";
107 $scope.p_desc = "";
108 $scope.p_data = "";
109 $scope.p_method = "";
110 $scope.p_pname = "";
111 $scope.p_params = "";
112 $scope.p_path = "";
113 $scope.p_query = "";
114 $scope.p_website = "";
115 $scope.p_refs = "";
116 $scope.p_request = "";
117 $scope.p_response = "";
118 $scope.p_resolution = "";
119 }
120
121 if($scope.mixed == 0x11) {
122 $scope.mixed = true;
123 } else {
124 $scope.mixed = false;
125 }
126
127 $scope.isChecked = function(i) {
128 return i.selected;
129 };
130
131 $scope.ok = function() {
132 var res = {},
133 evidence = [];
134
135 for(var key in $scope.impact) {
136 $scope.impact[key] = Boolean($scope.impact[key]);
137 }
138
139 for(var key in $scope.evidence) {
140 if(Object.keys($scope.evidence[key]).length == 1) {
141 evidence.push(key);
142 } else {
143 evidence.push($scope.evidence[key]);
144 }
145 }
146
147 $scope.refs = commons.objectToArray($scope.refs);
148
149 if($scope.web) {
150 res = {
151 "data": $scope.data,
152 "desc": $scope.desc,
153 "easeofresolution": $scope.easeOfResolutionSelection,
154 "evidence": $scope.evidence,
155 "impact": $scope.impact,
156 "method": $scope.method,
157 "name": $scope.name,
158 "params": $scope.params,
159 "path": $scope.path,
160 "pname": $scope.pname,
161 "query": $scope.query,
162 "refs": $scope.refs,
163 "request": $scope.request,
164 "response": $scope.response,
165 "resolution": $scope.resolution,
166 "severity": $scope.severitySelection,
167 "vulns": $scope.vulns,
168 "website": $scope.website
169 };
170 } else {
171 res = {
172 "data": $scope.data,
173 "desc": $scope.desc,
174 "easeofresolution": $scope.easeOfResolutionSelection,
175 "evidence": $scope.evidence,
176 "impact": $scope.impact,
177 "name": $scope.name,
178 "refs": $scope.refs,
179 "resolution": $scope.resolution,
180 "severity": $scope.severitySelection,
181 "vulns": $scope.vulns
182 };
183 }
184
185 $modalInstance.close(res);
186 };
187
188 $scope.cancel = function() {
189 $modalInstance.dismiss('cancel');
190 };
191
192 $scope.refs = commons.arrayToObject($scope.refs);
193
194 $scope.newReference = function($event){
195 $scope.refs.push({ref:''});
196 };
197
198 $scope.selectedFiles = function(files, e) {
199 files.forEach(function(file) {
200 if(file.name.charAt(0) != "_") {
201 if(!$scope.evidence.hasOwnProperty(file)) $scope.evidence[file.name] = file;
202 } else {
203 $scope.file_name_error = true;
204 }
205 });
206 $scope.icons = commons.loadIcons($scope.evidence);
207 }
208
209 $scope.removeEvidence = function(name) {
210 delete $scope.evidence[name];
211 delete $scope.icons[name];
212 }
213 }]);
+0
-12
views/reports/_attachments/scripts/modal/controllers/modalKO.js less more
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalKoCtrl', ['$scope', '$modalInstance', 'msg', function($scope, $modalInstance, msg) {
6 $scope.msg = msg;
7
8 $scope.ok = function() {
9 $modalInstance.close();
10 };
11 }]);
+0
-204
views/reports/_attachments/scripts/modal/controllers/modalNew.js less more
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalNewCtrl',
6 ['$scope', '$modalInstance', '$filter', '$upload', 'EASEOFRESOLUTION', 'targetFact', 'commonsFact', 'severities', 'workspace',
7 function($scope, $modalInstance, $filter, $upload, EASEOFRESOLUTION, targetFact, commons, severities, workspace) {
8
9 $scope.typeOptions = [
10 {name:'Vulnerability', value:'Vulnerability'},
11 {name:'VulnerabilityWeb',value:'VulnerabilityWeb'}
12 ];
13
14 $scope.easeofresolution = EASEOFRESOLUTION;
15 $scope.vuln_type = $scope.typeOptions[0].value;
16 $scope.severities = severities;
17 $scope.workspace = workspace;
18 $scope.target_selected = null;
19 $scope.not_target_selected = false;
20 $scope.incompatible_vulnWeb = false;
21 $scope.refs = [{ref:''}];
22 $scope.evidence = {};
23 $scope.icons = {};
24 $scope.showPagination = 1;
25 $scope.currentPage = 0;
26 $scope.pageSize = 5;
27 $scope.pagination = 10;
28 $scope.file_name_error = false;
29 $scope.impact = {
30 "accountability": false,
31 "availability": false,
32 "confidentiality": false,
33 "integrity": false
34 };
35
36 var name_selected,
37 host_selected,
38 d = {},
39 hosts = targetFact.getTarget($scope.workspace, true);
40
41 hosts.forEach(function(h) {
42 h.services = [];
43 d[h._id] = h;
44 });
45
46 var services = targetFact.getTarget($scope.workspace, false);
47
48 for(var i = 0; i < services.length; i++){
49 var host = [];
50 services[i].selected = false;
51 host = d[services[i].hid];
52 host.services.push(services[i]);
53 }
54
55 $scope.hosts_with_services = hosts;
56
57 $scope.numberOfPages = function() {
58 var filteredData = $filter('filter')($scope.hosts_with_services,$scope.search_notes);
59 if (filteredData.length <= 10){
60 $scope.showPagination = 0;
61 } else {
62 $scope.showPagination = 1;
63 };
64
65 return Math.ceil(filteredData.length/$scope.pagination);
66 };
67
68 $scope.selectedFiles = function(files, e) {
69 files.forEach(function(file) {
70 if(file.name.charAt(0) != "_") {
71 if(!$scope.evidence.hasOwnProperty(file)) $scope.evidence[file.name] = file;
72 } else {
73 $scope.file_name_error = true;
74 }
75 });
76 $scope.icons = commons.loadIcons($scope.evidence);
77 };
78
79 $scope.removeEvidence = function(name) {
80 delete $scope.evidence[name];
81 delete $scope.icons[name];
82 };
83
84 $scope.toggleImpact = function(key) {
85 $scope.impact[key] = !$scope.impact[key];
86 };
87
88 $scope.ok = function() {
89 if($scope.vuln_type == "VulnerabilityWeb" && host_selected == true){
90 $scope.incompatible_vulnWeb = true;
91 } else {
92 var res = {},
93 id = $scope.target_selected._id + "." + CryptoJS.SHA1($scope.name + "." + $scope.desc).toString(),
94 sha = CryptoJS.SHA1($scope.name + "." + $scope.desc).toString(),
95 myDate = new Date(),
96 myEpoch = myDate.getTime()/1000.0,
97 extra_vulns_prop = {},
98 arrayReferences = [];
99
100 for(var key in $scope.impact) {
101 $scope.impact[key] = Boolean($scope.impact[key]);
102 }
103
104 $scope.refs.forEach(function(r){
105 arrayReferences.push(r.ref);
106 });
107
108 arrayReferences.filter(Boolean);
109
110 var res = {
111 "id": id,
112 "data": $scope.data,
113 "date": myEpoch,
114 "desc": $scope.desc,
115 "easeofresolution": $scope.easeOfResolutionSelection,
116 "evidence": $scope.evidence,
117 "impact": $scope.impact,
118 "meta": {
119 'create_time': myEpoch,
120 "update_time": myEpoch,
121 "update_user": 'UI Web',
122 'update_action': 0,
123 'creator': 'UI Web',
124 'create_time': myEpoch,
125 'update_controller_action': 'UI Web New',
126 'owner': 'anonymous'
127 },
128 "name": $scope.name,
129 "oid": sha,
130 "owned": false,
131 "owner": "",
132 "couch_parent": $scope.target_selected._id,
133 "refs": arrayReferences,
134 "resolution": $scope.resolution,
135 "status": $scope.vuln_type,
136 "severity": $scope.severitySelection,
137 "target": name_selected,
138 "type": $scope.vuln_type
139 }
140
141 if($scope.vuln_type == "VulnerabilityWeb") {
142 extra_vulns_prop = {
143 "path": $scope.path,
144 "pname": $scope.pname,
145 "query": $scope.query,
146 "request": $scope.request,
147 "resolution": $scope.resolution,
148 "response": $scope.response,
149 "web": true,
150 "website": $scope.website
151 };
152 } else {
153 extra_vulns_prop = {
154 "web": false
155 };
156 }
157
158 for(var key in extra_vulns_prop) {
159 res[key] = extra_vulns_prop[key];
160 }
161
162 $modalInstance.close(res);
163 }
164 };
165
166 $scope.cancel = function() {
167 $modalInstance.dismiss('cancel');
168 };
169
170 $scope.$parent.isopen = ($scope.$parent.default === $scope.item);
171
172 $scope.$watch('isopen', function (newvalue, oldvalue, $scope) {
173 $scope.$parent.isopen = newvalue;
174 });
175
176 $scope.selected = function(i,j){
177 if($scope.target_selected){
178 $scope.target_selected.selected = false;
179 }
180 if(j != null){
181 host_selected = false;
182 $scope.target_selected = j;
183 name_selected = i.name;
184 }else{
185 host_selected = true;
186 $scope.target_selected = i;
187 name_selected = i.name;
188 }
189 $scope.target_selected.selected = true;
190 $scope.not_target_selected = true;
191 }
192
193 $scope.go = function(){
194 if($scope.go_page < $scope.numberOfPages()+2 && $scope.go_page > -1){
195 $scope.currentPage = $scope.go_page;
196 }
197 }
198
199 $scope.newReference = function($event){
200 $scope.refs.push({ref:''});
201 $event.preventDefault();
202 }
203 }]);
+0
-10
views/reports/_attachments/scripts/modal/filter/Filter.js less more
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('filter', []).filter('startFrom', function() {
5 return function(input, start) {
6 start = +start; //parse to int
7 return input.slice(start);
8 }
9 });
44 <nav>
55 <ul>
66 <li>
7 <a href="#/dashboard/ws/{{workspace}}" class="ws-dashboard" style="color: #ffffff !important" title="WS Dashboard">
7 <a href="#/dashboard/ws/{{workspace}}" class="ws-dashboard" style="color: #ffffff !important" tooltip="Dashboard" tooltip-placement="right">
88 <img src="images/ico-dashboard-menu.svg" alt="Dashboard"/>
99 </a>
1010 </li>
1111 <li>
12 <a href="#/status/ws/{{workspace}}" class="status-report" style="color: #ffffff !important" title="Status Report">
12 <a href="#/status/ws/{{workspace}}" class="status-report" style="color: #ffffff !important" tooltip="Status Report" tooltip-placement="right">
1313 <img src="images/ico-status-menu.svg" alt="Status Report"/>
1414 </a>
1515 </li>
1616 <li>
17 <a href="#/workspaces" class="workspaces" style="color: #ffffff !important" title="Workspaces">
17 <a href="#/workspaces" class="workspaces" style="color: #ffffff !important" tooltip="Workspaces" tooltip-placement="right">
1818 <img src="images/ico-workspaces-menu.svg" alt="Workspaces"/>
1919 </a>
2020 </li>
+0
-41
views/reports/_attachments/scripts/partials/home.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5 <div class="right-main"><div id="reports-main" class="fila clearfix">
6 <h2 class="ws-label">
7 <span id="ws-name" class="label label-default"
8 title="Current workspace">Welcome to Faraday!</span><!-- WS name -->
9 </h2><!-- .ws-label -->
10 <div class="reports">
11 <div class="reports">
12 <div class="ws-list home-list community clearfix">
13 <a href="#/dashboard" class="ws-link item animated flipInX">
14 <img src="images/ico-dashboard.svg" />
15 <span class="ws-name">Dashboard</span>
16 <small>
17 Gain insight into your project.<br/>
18 <strong>Visualise the progress</strong>
19 </small>
20 </a>
21 <a href="#/status" class="ws-link item animated flipInX">
22 <img src="images/ico-status.svg" />
23 <span class="ws-name">Status Report</span>
24 <small>
25 All the vulnerabilities in one place.<br/>
26 <strong>Manage findings</strong>
27 </small>
28 </a>
29 <a href="#/workspaces" class="ws-link item animated flipInX">
30 <img src="images/ico-workspaces.svg" />
31 <span class="ws-name">Workspaces</span>
32 <small>
33 Create and edit projects.<br/>
34 <strong>Manage your projects</strong>
35 </small>
36 </a>
37 </div><!-- .ws-list -->
38 </div><!-- .reports -->
39 </div><!-- #reports-main --></div><!-- .right-main -->
40 </section>
+0
-14
views/reports/_attachments/scripts/partials/modal-delete.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Bulk deletion</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{message}}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
12 <button class="btn btn-success" ng-click="ok()">OK</button>
13 </div>
+0
-191
views/reports/_attachments/scripts/partials/modal-edit.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="formEdit" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
8 <button class="btn btn-success" ng-click="ok()" ng-disabled="formEdit.$invalid">OK</button>
9 </div>
10 <h3 class="modal-title">Bulk edit</h3>
11 </div>
12 <div class="modal-body">
13 <div ng-if="mixed">
14 <div class="alert alert-danger alert-dismissible" role="alert">
15 <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
16 <h5><strong>Hey!</strong> You have selected both vulnerabilities and web vulnerabilities. Please keep in mind that fields in red are specific for web vulnerabilities and these will not be added to regular vulnerabilities.</h5>
17 </div>
18 </div><!-- ng-if -->
19 <div ng-if="!web">
20 <h5><small>
21 Please choose a severity, name, description and data
22 </small></h5>
23 </div><!-- ng-if -->
24 <div class="form-horizontal">
25 <div class="form-group">
26 <div class="col-md-6">
27 <h5>Severity</h5>
28 <select class="form-control" ng-model="severitySelection" ng-options="s as s for s in severities">
29 </select>
30 </div>
31 <div class="col-md-6">
32 <h5>Ease of Resolution</h5>
33 <select class="form-control" ng-model="easeOfResolutionSelection" ng-options="e as e for e in easeofresolution">
34 <option value=""></option>
35 </select>
36 </div>
37 </div><!-- .form-group -->
38 <div class="form-group">
39 <div class="col-md-12">
40 <label class="sr-only" for="vuln-name">Vuln name</label>
41 <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="name"/>
42 </div>
43 </div><!-- .form-group -->
44 <div class="form-group">
45 <div class="col-md-12">
46 <label class="sr-only" for="vuln-desc">Vuln description</label>
47 <textarea class="form-control" id="vuln-desc" placeholder="Description" value="{{p_desc}}" ng-model="desc"></textarea>
48 </div>
49 </div><!-- .form-group -->
50 <div class="form-group">
51 <div class="col-md-12">
52 <label class="sr-only" for="vuln-data">Vuln data</label>
53 <textarea class="form-control" id="vuln-data" placeholder="Data" value="{{p_data}}" ng-model="data"></textarea>
54 </div>
55 </div><!-- .form-group -->
56 <div class="form-group">
57 <div class="col-md-12">
58 <h4>References</h4>
59 <span class="input-group-addon add-reference" ng-click="newReference($event)">Add Reference</span>
60 </div>
61 <div class="col-md-12 reference" ng-repeat="reference in refs">
62 <div class="input-group margin-bottom-sm">
63 <label class="sr-only" for="vuln-refs">References</label>
64 <input type="text" class="form-control" id="vuln-refs" placeholder="Reference" ng-model="reference.ref"/>
65 <span class="input-group-addon" ng-click="refs.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
66 </div>
67 </div>
68 </div><!-- .form-group -->
69 <div class="form-group">
70 <div class="col-md-12">
71 <label class="sr-only" for="vuln-resolution">Vuln Resolution</label>
72 <textarea class="form-control" id="vuln-resolution" placeholder="Resolution" ng-model="resolution"></textarea>
73 </div>
74 </div><!-- .form-group -->
75 <div ng-if="web">
76 <div class="form-group">
77 <div class="col-md-4 has-error">
78 <label class="sr-only control-label" for="vuln-method">Method</label>
79 <input type="text" class="form-control input-error" id="vuln-method" value="{{p_method}}" placeholder="Method" ng-model="$parent.method"/>
80 </div>
81 <div class="col-md-3 has-error">
82 <label class="sr-only control-label" for="vuln-pname">Param Name</label>
83 <input type="text" class="form-control input-error" id="vuln-pname" value="{{p_pname}}" placeholder="Param name" ng-model="$parent.pname"/>
84 </div>
85 <div class="col-md-5 has-error">
86 <label class="sr-only control-label" for="vuln-params">Params</label>
87 <input type="text" class="form-control input-error" id="vuln-params" value="{{p_params}}" placeholder="Params" ng-model="$parent.params"/>
88 </div>
89 </div><!-- .form-group -->
90 <div class="form-group">
91 <div class="col-md-4 has-error">
92 <label class="sr-only control-label" for="vuln-path">Path</label>
93 <input type="text" class="form-control input-error" id="vuln-path" value="{{p_path}}" placeholder="Path" ng-model="$parent.path"/>
94 </div>
95 <div class="col-md-4 has-error">
96 <label class="sr-only control-label" for="vuln-query">Query</label>
97 <input type="text" class="form-control input-error" id="vuln-query" value="{{p_query}}"placeholder="Query" ng-model="$parent.query"/>
98 </div>
99 <div class="col-md-4 has-error">
100 <label class="sr-only control-label" for="vuln-website">Website</label>
101 <input type="text" class="form-control input-error" id="vuln-website" value="{{p_website}}" placeholder="Website" ng-model="$parent.website"/>
102 </div>
103 </div><!-- .form-group -->
104 <div class="form-group">
105 <div class="col-md-12 has-error">
106 <label class="sr-only control-label" for="vuln-request">Request</label>
107 <textarea class="form-control input-error" id="vuln-request" value="{{p_request}}" placeholder="Request" ng-model="$parent.request"></textarea>
108 </div>
109 </div><!-- .form-group -->
110 <div class="form-group">
111 <div class="col-md-12 has-error">
112 <label class="sr-only control-label" for="vuln-response">Response</label>
113 <textarea class="form-control input-error" id="vuln-response" value="{{p_response}}" placeholder="Response" ng-model="$parent.response"></textarea>
114 </div>
115 </div><!-- .form-group -->
116 </div><!-- ng-if -->
117 </div><!-- .form-horizontal -->
118
119 <h4>Impact</h4>
120 <div ng-repeat="(key, value) in impact" class="normal-size" style="cursor: pointer;">
121 <h4><span ng-class="{'label label-default': !value, 'label label-success': value}" ng-click="toggleImpact(key)">{{key}}</span></h4>
122 </div><!-- .normal-size -->
123
124 <div ng-if="vulnc == 1">
125 <h4>Evidence</h4>
126 <form>
127 <div class="alert alert-danger normal-size" role="alert" ng-if="file_name_error">
128 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
129 <span class="sr-only">Error:</span>
130 Cannot upload evidence starting with underscore, please choose a different name for the file.
131 </div>
132 <div class="form-group normal-size">
133 <input type="file" id="evidence" ng-file-select ng-multiple="true" resetOnClick="false" ng-file-change="selectedFiles($files, $event)"/>
134 <p class="help-block">Multiple files are allowed.</p>
135 </div><!-- .form-group -->
136 </form>
137 <div id="evidenceFiles" class="normal-size" ng-if="evidence">
138 <ul>
139 <li ng-repeat="e in evidence | orderObjectBy:'name':true | orderBy:'name'">
140 <div class="btn-group">
141 <button type="button" class="btn btn-default">
142 <span class="fa {{icons[e.name]}}" title="Evidence {{e.name}}"></span> {{e.name}}
143 </button><!-- ng-repeat -->
144 <button type="button" class="btn btn-danger" ng-click="removeEvidence(e.name)">
145 <span class="glyphicon glyphicon-trash"></span>
146 </button>
147 </div>
148 </li>
149 </ul>
150 </div><!-- #evidenceFiles -->
151 </div><!-- ng-if -->
152
153 <h5><small>
154 Vulnerabilities to update
155 </small></h5>
156 <table class="csv-export status-report">
157 <thead>
158 <tr>
159 <th><a href="" ng-click="sortField = 'date'; reverse = !reverse">Date</a></th>
160 <th><a href="" ng-click="sortField = 'web'; reverse = !reverse">Web</a></th>
161 <th><a href="" ng-click="sortField = 'status'; reverse = !reverse">Status</a></th>
162 <th><a href="" ng-click="sortField = 'severity'; reverse = !reverse">Severity</a></th>
163 <th><a href="" ng-click="sortField = 'name'; reverse = !reverse">Name</a></th>
164 <th><a href="" ng-click="sortField = 'target'; reverse = !reverse">Target</a></th>
165 <th><a href="" ng-click="sortField = 'desc'; reverse = !reverse">Desc</a></th>
166 <th><a href="">Copy</a></th>
167 </tr>
168 </thead>
169 <tbody>
170 <tr ng-repeat="v in vulns | filter:isChecked | orderBy:sortField:reverse">
171 <td>{{v.date | date:'MM/dd/yyyy'}}</td>
172 <td>
173 <span class="glyphicon glyphicon-ok" ng-show="v.web"></span>
174 <span class="glyphicon glyphicon-remove" ng-show="!v.web"></span>
175 </td>
176 <td>Vulnerable</td>
177 <td>{{v.severity}}</td>
178 <td>{{v.name}}</td>
179 <td>{{v.target}}</td>
180 <td text-collapse text-collapse-max-length="50" text-collapse-text="{{v.desc}}"></td>
181 <td><i class="fa fa-copy copy-icon fa-lg" ng-click="pickVuln(v);call();"></i></td>
182 </tr>
183 </tbody>
184 </table><!-- #hosts -->
185 </div><!-- .modal-body -->
186 <div class="modal-footer">
187 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
188 <button class="btn btn-success" ng-click="ok()" ng-disabled="formEdit.$invalid">OK</button>
189 </div>
190 </form>
+0
-13
views/reports/_attachments/scripts/partials/modal-ko.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Oops!</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{ msg }}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-success" ng-click="ok()">OK</button>
12 </div>
+0
-199
views/reports/_attachments/scripts/partials/modal-new.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
8 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
9 </div>
10 <h3 class="modal-title">Vulnerability creation</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <input type="text" ng-model="search_notes" class="form-control input-sm" placeholder="Search" ng-change="currentPage = 0">
17 <accordion close-others="true">
18 <accordion-group is-open="isopen" ng-repeat="host in hosts_with_services | filter:search_notes | startFrom:currentPage*pageSize | limitTo:pageSize">
19 <accordion-heading>
20 <a ng-click="selected(host,null)" ng-class="{'multi-selected': host.selected == true}">{{host.name}}</a>
21 <i class="pull-right glyphicon"
22 ng-class="{'glyphicon glyphicon-minus-sign': isopen, 'glyphicon glyphicon-plus-sign': !isopen}"></i>
23 </accordion-heading>
24 <div class="panel-body" ng-repeat="service in host.services">
25 <a ng-model="service" ng-click="selected(host,service)" ng-class="{'multi-selected': service.selected == true}">{{service.name}}</a>
26 </div>
27 </accordion-group>
28 </accordion>
29 <div class="showPagination" ng-show="showPagination">
30 <div class="form-group">
31 <ul class="pagination">
32 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
33 <li><a>{{currentPage}}/{{numberOfPages()+1}}</a></li>
34 <li><a ng-hide="currentPage >= numberOfPages()+1" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
35 </ul>
36 <form name="goToPage">
37 <div class="col-md-2">
38 <input type="number" min="0" max="{{numberOfPages()+1}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
39 </div>
40 <button class="btn btn-danger" ng-click="go()">GO</button>
41 </form>
42 </div>
43 </div>
44 </div>
45 </div>
46 </div>
47
48 <div class="alert alert-danger target_not_selected" role="alert" ng-hide="not_target_selected">
49 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
50 <span class="sr-only">Error:</span>
51 There is no target selected
52 </div>
53 <div class="alert alert-danger target_not_selected" role="alert" ng-show="incompatible_vulnWeb">
54 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
55 <span class="sr-only">Error:</span>
56 Vulnerability incompatible with host target, please select a service
57 </div>
58
59 <div class="form-horizontal">
60 <div class="form-group">
61 <div class="col-md-4">
62 <h5>Type</h5>
63 <select class="form-control" ng-model="vuln_type" ng-options="option.value as option.name for option in typeOptions">
64 </select>
65 </div>
66 <div class="col-md-4">
67 <h5>Severity</h5>
68 <select class="form-control" ng-model="severitySelection" ng-options="s as s for s in severities" required>
69 </select>
70 </div>
71 <div class="col-md-4">
72 <h5>Ease of Resolution</h5>
73 <select class="form-control" ng-model="easeOfResolutionSelection" ng-options="e as e for e in easeofresolution">
74 <option value=""></option>
75 </select>
76 </div>
77 </div><!-- .form-group -->
78 <div class="form-group">
79 <div class="col-md-12">
80 <label class="sr-only" for="vuln-name">Vuln name</label>
81 <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="name" required/>
82 </div>
83 </div><!-- .form-group -->
84 <div class="form-group">
85 <div class="col-md-12">
86 <label class="sr-only" for="vuln-desc">Vuln description</label>
87 <textarea class="form-control" id="vuln-desc" placeholder="Description" ng-model="desc" required></textarea>
88 </div>
89 </div><!-- .form-group -->
90 <div class="form-group">
91 <div class="col-md-12">
92 <label class="sr-only" for="vuln-data">Vuln data</label>
93 <textarea class="form-control" id="vuln-data" placeholder="Data" ng-model="data"></textarea>
94 </div>
95 </div><!-- .form-group -->
96 <div class="form-group">
97 <div class="col-md-12 reference" ng-repeat="reference in refs">
98 <div class="input-group margin-bottom-sm">
99 <label class="sr-only" for="vuln-refs">References</label>
100 <input type="text" class="form-control" id="vuln-refs" placeholder="Reference" ng-model="reference.ref"/>
101 <span class="input-group-addon" ng-click="newReference($event)"><i class="fa fa-plus-circle"></i></span>
102 <span class="input-group-addon" ng-click="refs.splice($index, 1)" ng-hide="refs.length == 1"><i class="fa fa-minus-circle"></i></span>
103 </div>
104 </div>
105 </div><!-- .form-group -->
106 <div class="form-group">
107 <div class="col-md-12">
108 <label class="sr-only" for="vuln-resolution">Vuln Resolution</label>
109 <textarea class="form-control" id="vuln-resolution" placeholder="Resolution" ng-model="resolution"></textarea>
110 </div>
111 </div><!-- .form-group -->
112 </div>
113
114 <div class="animate-switch-container" ng-switch on="vuln_type">
115 <div class="animate-switch" ng-switch-when="VulnerabilityWeb">
116 <div class="form-horizontal">
117 <div class="form-group">
118 <div class="col-md-4">
119 <label class="sr-only control-label" for="vuln-method">Method</label>
120 <input type="text" class="form-control" id="vuln-method" placeholder="Method" ng-model="$parent.method"/>
121 </div>
122 <div class="col-md-3">
123 <label class="sr-only control-label" for="vuln-pname">Param Name</label>
124 <input type="text" class="form-control" id="vuln-pname" placeholder="Param name" ng-model="$parent.pname"/>
125 </div>
126 <div class="col-md-5">
127 <label class="sr-only control-label" for="vuln-params">Params</label>
128 <input type="text" class="form-control" id="vuln-params" placeholder="Params" ng-model="$parent.params"/>
129 </div>
130 </div><!-- .form-group -->
131 <div class="form-group">
132 <div class="col-md-4">
133 <label class="sr-only control-label" for="vuln-path">Path</label>
134 <input type="text" class="form-control" id="vuln-path" placeholder="Path" ng-model="$parent.path"/>
135 </div>
136 <div class="col-md-4">
137 <label class="sr-only control-label" for="vuln-query">Query</label>
138 <input type="text" class="form-control" id="vuln-query" placeholder="Query" ng-model="$parent.query"/>
139 </div>
140 <div class="col-md-4">
141 <label class="sr-only control-label" for="vuln-website">Website</label>
142 <input type="text" class="form-control" id="vuln-website" placeholder="Website" ng-model="$parent.website"/>
143 </div>
144 </div><!-- .form-group -->
145 <div class="form-group">
146 <div class="col-md-12">
147 <label class="sr-only control-label" for="vuln-request">Request</label>
148 <textarea class="form-control" id="vuln-request" placeholder="Request" ng-model="$parent.request"></textarea>
149 </div>
150 </div><!-- .form-group -->
151 <div class="form-group">
152 <div class="col-md-12">
153 <label class="sr-only control-label" for="vuln-response">Response</label>
154 <textarea class="form-control" id="vuln-response" placeholder="Response" ng-model="$parent.response"></textarea>
155 </div>
156 </div><!-- .form-group -->
157 </div><!-- .form-horizontal -->
158 </div><!-- .animate-switch -->
159 <div class="animate-switch" ng-switch-when="Vulnerability"></div>
160 </div><!-- .animate-switch-container -->
161
162 <h4>Impact</h4>
163 <div ng-repeat="(key, value) in impact" class="normal-size" style="cursor: pointer;">
164 <h4><span ng-class="{'label label-default': !value, 'label label-success': value}" ng-click="toggleImpact(key)">{{key}}</span></h4>
165 </div><!-- .normal-size -->
166
167 <h4>Evidence</h4>
168 <form>
169 <div class="alert alert-danger normal-size" role="alert" ng-if="file_name_error">
170 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
171 <span class="sr-only">Error:</span>
172 Cannot upload evidence starting with underscore, please choose a different name for the file.
173 </div>
174 <div class="form-group normal-size">
175 <input type="file" id="evidence" ng-file-select ng-multiple="true" resetOnClick="false" ng-file-change="selectedFiles($files, $event)"/>
176 <p class="help-block">Multiple files are allowed.</p>
177 </div><!-- .form-group -->
178 </form>
179 <div id="evidenceFiles" class="normal-size" ng-if="evidence">
180 <ul>
181 <li ng-repeat="e in evidence | orderObjectBy:'name':true | orderBy:'name'">
182 <div class="btn-group">
183 <button type="button" class="btn btn-default">
184 <span class="fa {{icons[e.name]}}" title="Evidence {{e.name}}"></span> {{e.name}}
185 </button><!-- ng-repeat -->
186 <button type="button" class="btn btn-danger" ng-click="removeEvidence(e.name)">
187 <span class="glyphicon glyphicon-trash"></span>
188 </button>
189 </div>
190 </li>
191 </ul>
192 </div><!-- #evidenceFiles -->
193 </div><!-- .modal-body -->
194 <div class="modal-footer">
195 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
196 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
197 </div>
198 </form>
+0
-210
views/reports/_attachments/scripts/partials/status_report.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5 <div class="right-main"><div id="reports-main" class="fila clearfix">
6 <div class="ws-label">
7 <h2><span id="ws-name" class="label label-default"
8 title="Current workspace">Status report for {{ workspace }} ({{vulns.length}} vulns)</span></h2><!-- WS name -->
9 </div><!-- .ws-label -->
10 <div id="ws-control" class="btn-group">
11 <button file-exporter="toCSV()" type="button" class="btn btn-success" title="Download CSV for current workspace">
12 <span class="glyphicon glyphicon-download"></span>
13 </button>
14 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()">
15 <span class="glyphicon glyphicon-refresh"></span>
16 </button>
17 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
18 Change workspace <span class="caret"></span>
19 </button>
20 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu">
21 <li ng-repeat="ws in workspaces"><a href="#/status/ws/{{ws}}" class="ws" >{{ws}}</a></li>
22 </ul><!-- WS navigation -->
23 </div><!-- #ws-control -->
24
25 <div class="button-control col-md-6 col-sm-6 col-xs-12">
26 <button id="delete" type="button" class="btn btn-default" title="Delete selected items" ng-click="delete()">
27 <span class="glyphicon glyphicon-trash"></span>
28 Delete
29 </button>
30 <button id="merge" type="button" class="btn btn-default" title="Edit selected vulns" ng-click="edit()">
31 <span class="glyphicon glyphicon-pencil"></span>
32 Edit
33 </button>
34 <button id="new" type="button" class="btn btn-success" title="New Vulns" ng-click="new()">
35 <span class="glyphicon glyphicon-plus-sign"></span>
36 New
37 </button>
38 </div><!-- .col-md-6 .col-sm-6 .col-xs-12 -->
39 <div class="reports">
40 <div class="row">
41 <div class="col-md-12 col-sm-3 col-xs-11">
42 <form role="form">
43 <div class="form-group">
44 <h4><span class="label label-default " title="Filter by">Filter by</span></h4>
45 <input type="text" class="form-control input-sm" id="filter-by" placeholder="enter keywords..." ng-model="query" ng-change="currentPage = 0">
46 </div>
47 </form>
48 </div>
49 <div class="col-md-12 col-sm-9 col-xs-12">
50 <h4><span class="label label-default" title="Add columns">Add columns</span></h4>
51 <ul class="label-list">
52 <li ng-repeat="(column, show) in columns">
53 <a href="" ng-click="toggleShow(column, show)" ng-show="!show">
54 <span class="label label-primary ws-name">{{column}}</span>
55 </a>
56 </li><!-- label-list -->
57 </ul>
58 </div>
59 </div>
60
61 <table class="csv-export status-report table table-responsive">
62 <thead>
63 <tr>
64 <th><input type="checkbox" ng-model="selectall" ng-click="checkAll()"/></th>
65 <th ng-if="columns.date">
66 <a href="" ng-click="toggleSort('date')">Date</a>
67 <a href="" ng-click="toggleShow('date', true)"><span class="glyphicon glyphicon-remove"></span></a>
68 </th>
69 <th ng-if="columns.target">
70 <a href="" ng-click="toggleSort('target')">Target</a>
71 <a href="" ng-click="toggleShow('target', true)"><span class="glyphicon glyphicon-remove"></span></a>
72 </th>
73 <th ng-if="columns.status">
74 <a href="" ng-click="toggleSort('status')">Status</a>
75 <a href="" ng-click="toggleShow('status', true)"><span class="glyphicon glyphicon-remove"></span></a>
76 </th>
77 <th ng-if="columns.severity">
78 <a href="" ng-click="toggleSort('severity')">Severity</a>
79 <a href="" ng-click="toggleShow('severity', true)"><span class="glyphicon glyphicon-remove"></span></a>
80 </th>
81 <th ng-if="columns.name">
82 <a href="" ng-click="toggleSort('name')">Name</a>
83 <a href="" ng-click="toggleShow('name', true)"><span class="glyphicon glyphicon-remove"></span></a>
84 </th>
85 <th ng-if="columns.desc">
86 <a href="" ng-click="toggleSort('desc')">Desc</a>
87 <a href="" ng-click="toggleShow('desc', true)"><span class="glyphicon glyphicon-remove"></span></a>
88 </th>
89 <th ng-if="columns.data">
90 <a href="" ng-click="toggleSort('data')">Data</a>
91 <a href="" ng-click="toggleShow('data', true)"><span class="glyphicon glyphicon-remove"></span></a>
92 </th>
93 <th ng-if="columns.method">
94 <a href="" ng-click="toggleSort('method')">Method</a>
95 <a href="" ng-click="toggleShow('method', true)"><span class="glyphicon glyphicon-remove"></span></a>
96 </th>
97 <th ng-if="columns.path">
98 <a href="" ng-click="toggleSort('path')">Path</a>
99 <a href="" ng-click="toggleShow('path', true)"><span class="glyphicon glyphicon-remove"></span></a>
100 </th>
101 <th ng-if="columns.pname">
102 <a href="" ng-click="toggleSort('pname')">Param Name</a>
103 <a href="" ng-click="toggleShow('pname', true)"><span class="glyphicon glyphicon-remove"></span></a>
104 </th>
105 <th ng-if="columns.params">
106 <a href="" ng-click="toggleSort('params')">Params</a>
107 <a href="" ng-click="toggleShow('params', true)"><span class="glyphicon glyphicon-remove"></span></a>
108 </th>
109 <th ng-if="columns.query">
110 <a href="" ng-click="toggleSort('query')">Query</a>
111 <a href="" ng-click="toggleShow('query', true)"><span class="glyphicon glyphicon-remove"></span></a>
112 </th>
113 <th ng-if="columns.request">
114 <a href="" ng-click="toggleSort('request')">Request</a>
115 <a href="" ng-click="toggleShow('request', true)"><span class="glyphicon glyphicon-remove"></span></a>
116 </th>
117 <th ng-if="columns.response">
118 <a href="" ng-click="toggleSort('response')">Response</a>
119 <a href="" ng-click="toggleShow('response', true)"><span class="glyphicon glyphicon-remove"></span></a>
120 </th>
121 <th ng-if="columns.resolution">
122 <a href="" ng-click="toggleSort('resolution')">Resolution</a>
123 <a href="" ng-click="toggleShow('resolution', true)"><span class="glyphicon glyphicon-remove"></span></a>
124 </th>
125 <th ng-if="columns.web">
126 <a href="" ng-click="toggleSort('web')">Web</a>
127 <a href="" ng-click="toggleShow('web', true)"><span class="glyphicon glyphicon-remove"></span></a>
128 </th>
129 <th ng-if="columns.website">
130 <a href="" ng-click="toggleSort('website')">Website</a>
131 <a href="" ng-click="toggleShow('website', true)"><span class="glyphicon glyphicon-remove"></span></a>
132 </th>
133 <th ng-if="columns.refs">
134 <a href="" ng-click="toggleSort('refs')">References</a>
135 <a href="" ng-click="toggleShow('refs', true)"><span class="glyphicon glyphicon-remove"></span></a>
136 <th ng-if="columns.evidence">
137 <a href="" ng-click="toggleSort('attachments')">Evidence</a>
138 <a href="" ng-click="toggleShow('evidence', true)"><span class="glyphicon glyphicon-remove"></span></a>
139 </th>
140 <th ng-if="columns.impact">
141 <a href="" ng-click="toggleSort('impact')">Impact</a>
142 <a href="" ng-click="toggleShow('impact', true)"><span class="glyphicon glyphicon-remove"></span></a>
143 </th>
144 <th ng-if="columns.easeofresolution">
145 <a href="" ng-click="toggleSort('easeofresolution')">Ease of Resolution</a>
146 <a href="" ng-click="toggleShow('easeofresolution', true)"><span class="glyphicon glyphicon-remove"></span></a>
147 </th>
148 </tr>
149 </thead>
150 <tbody>
151 <tr ng-repeat="v in vulns | filter:query | orderObjectBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize"
152 selection-model selection-model-type="checkbox"
153 selection-model-mode="multiple-additive"
154 selection-model-selected-class="multi-selected">
155 <td><input type="checkbox" name="{{v.id}}"/></td>
156 <td ng-if="columns.date">{{v.date | date:'MM/dd/yyyy'}}</td>
157 <td ng-if="columns.target">{{v.target}}</td>
158 <td ng-if="columns.status">Vulnerable</td>
159 <td ng-if="columns.severity"><span class="label vuln fondo-{{v.severity}}">{{v.severity}}</span></td>
160 <td ng-if="columns.name">{{v.name}}</td>
161 <td ng-if="columns.desc" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.desc}}"></td>
162 <td ng-if="columns.data" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.data}}"></td>
163 <td ng-if="columns.method">{{v.method}}</td>
164 <td ng-if="columns.path">{{v.path}}</td>
165 <td ng-if="columns.pname">{{v.pname}}</td>
166 <td ng-if="columns.params">{{v.params}}</td>
167 <td ng-if="columns.query">{{v.query}}</td>
168 <td ng-if="columns.request" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.request}}"></td>
169 <td ng-if="columns.response" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.response}}"></td>
170 <td ng-if="columns.resolution">{{v.resolution}}</td>
171 <td ng-if="columns.web">
172 <span class="glyphicon glyphicon-ok" ng-show="v.web"></span>
173 <span class="glyphicon glyphicon-remove" ng-show="!v.web"></span>
174 </td>
175 <td ng-if="columns.website">{{v.website}}</td>
176 <td ng-if="columns.refs"><p ng-repeat="refs in v.refs">{{refs}}</p></td>
177 <td ng-if="columns.evidence">
178 <div ng-repeat="e in v.attachments track by $index">
179 <a href="{{baseurl + workspace}}/{{ v.id}}/{{e}}" target="_blank">{{e}}</a>
180 </div>
181 </td>
182 <td ng-if="columns.impact">
183 <div ng-repeat="(impact, rating) in v.impact">
184 <p ng-if="rating">{{impact}}</p>
185 </div>
186 </td>
187 <td ng-if="columns.easeofresolution">{{v.easeofresolution}}</td>
188 </tr>
189 </tbody>
190 </table><!-- #hosts -->
191 <div class="showPagination" ng-show="showPagination">
192 <div class="form-group">
193 <ul class="pagination">
194 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
195 <li><a>{{currentPage}}/{{numberOfPages()}}</a></li>
196 <li><a ng-hide="currentPage >= numberOfPages()" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
197 </ul>
198 <form name="goToPage" id="goToPageStatus">
199 <div class="col-md-2">
200 <input type="number" min="0" max="{{numberOfPages()}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
201 </div>
202 <button class="btn btn-default" ng-click="go()">GO</button>
203 <input type="number" min="0" class="form-control vuln_per_page" ng-model="pagination" placeholder="Numbre page" />
204 </form>
205 </div>
206 </div>
207 </div><!-- .reports -->
208 </div><!-- #reports-main --></div><!-- .right-main -->
209 </section><!-- #main -->
+0
-31
views/reports/_attachments/scripts/partials/workspaces.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <section id="main" class="seccion clearfix">
4 <div class="right-main"><div id="reports-main" class="fila clearfix">
5 <div class="jumbotron">
6 <h1><b>Welcome!</b></h1>
7 <p>These are your available workspaces</p>
8 </div><!-- .jumbotron -->
9 <div class="reports normal-size">
10 <table class="status-report table-striped table-responsive table-hover">
11 <thead>
12 <tr>
13 <th>Name</th>
14 <th>Vulns</th>
15 <th>Hosts</th>
16 <th>Services</th>
17 </tr>
18 </thead>
19 <tbody>
20 <tr ng-repeat="ws in wss">
21 <td><a href="#/{{hash}}/ws/{{ws}}"><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'">{{ws}}</span></a></td>
22 <td ng-bind="objects[ws]['total vulns']"></td>
23 <td ng-bind="objects[ws]['hosts']"></td>
24 <td ng-bind="objects[ws]['services']"></td>
25 </tr>
26 </tbody>
27 </table>
28 </div><!-- .reports -->
29 </div><!-- #reports-main --></div><!-- .right-main -->
30 </section>
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('serviceModalEdit',
6 ['$scope', '$modalInstance', '$routeParams', 'services','service', 'servicesManager', 'commonsFact', 'dashboardSrv',
7 function($scope, $modalInstance, $routeParams, services, service, servicesManager, commons, dashboardSrv) {
8
9 init = function(){
10 // current Workspace
11 var ws = $routeParams.wsId;
12 // default scope (service)
13 $scope.service = {
14 "ports": []
15 };
16
17 if(service.length == 1){
18 $scope.service = {
19 "name": service[0].name,
20 "description": service[0].description,
21 "owned": service[0].owned,
22 "owner": service[0].owner,
23 "ports": commons.arrayToObject(service[0].ports),
24 "protocol": service[0].protocol,
25 "parent": service[0].parent,
26 "status": service[0].status,
27 "version": service[0].version,
28 };
29 }else{
30 $scope.services_selected = service;
31 }
32 };
33
34 $scope.ok = function() {
35 var ports = [];
36 var date = new Date(),
37 timestamp = date.getTime()/1000.0;
38
39 $scope.service.ports.forEach(function(port){
40 ports.push(port.key);
41 });
42
43 $scope.service.ports = ports.filter(Boolean);
44 $modalInstance.close($scope.service);
45 };
46
47 $scope.newPort = function($event){
48 $scope.service.ports.push({key:''});
49 $event.preventDefault();
50 };
51
52 $scope.call = function(service){
53 $scope.service = {
54 "name": service.name,
55 "description": service.description,
56 "owned": service.owned,
57 "ports": commons.arrayToObject(service.ports),
58 "protocol": service.protocol,
59 "status": service.status,
60 "version": service.version,
61 };
62 };
63
64 $scope.cancel = function() {
65 $modalInstance.dismiss('cancel');
66 };
67
68 init();
69 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('serviceModalNew',
6 ['$scope', '$modalInstance', '$routeParams', 'host', 'servicesManager', 'hostsManager',
7 function($scope, $modalInstance, $routeParams, host, servicesManager, hostsManager) {
8
9 init = function(){
10 $scope.service = {
11 "name": "",
12 "description": "",
13 "owned": false,
14 "owner": "",
15 "ports": [{key: ''}],
16 "protocol": "",
17 "parent": "",
18 "status": "",
19 "version": ""
20 };
21 // current Workspace
22 var ws = $routeParams.wsId;
23
24 hostsManager.getInterfaces(ws, host._id).then(function(resp){
25 $scope.service.parent = resp[0].value._id;
26 });
27 };
28
29 $scope.ok = function() {
30 var ports = [];
31 var date = new Date(),
32 timestamp = date.getTime()/1000.0;
33
34 $scope.service.ports.forEach(function(port){
35 ports.push(port.key);
36 });
37 $scope.service.metadata = {
38 "update_time": timestamp,
39 "update_user": "",
40 "update_action": 0,
41 "creator": "",
42 "create_time": timestamp,
43 "update_controller_action": "UI Web New",
44 "owner": ""
45 };
46
47 $scope.service.ports = ports.filter(Boolean);
48 $modalInstance.close($scope.service);
49 };
50
51 $scope.newPort = function($event){
52 $scope.service.ports.push({key:''});
53 $event.preventDefault();
54 };
55
56 $scope.cancel = function() {
57 $modalInstance.dismiss('cancel');
58 };
59
60 init();
61 }]);
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5
6 <div class="right-main"><div id="reports-main" class="fila clearfix">
7 <h2 class="ws-label">
8 <span id="ws-name" class="label label-default"
9 title="Hosts">Services from {{host.name}}</span><!-- WS name -->
10 <div id="ws-control" class="btn-group">
11 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()">
12 <span class="glyphicon glyphicon-refresh"></span>
13 </button>
14 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
15 Change workspace <span class="caret"></span>
16 </button>
17 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu">
18 <li ng-repeat="ws in workspaces"><a href="#/hosts/ws/{{ws}}" class="ws" >{{ws}}</a></li>
19 </ul><!-- WS navigation -->
20 </div><!-- #ws-control -->
21 </h2>
22 <div class="reports col-md-12 col-sm-12 col-xs-12">
23 <div class="form-horizontal">
24 <div class="form-group">
25 <div class="col-md-4">
26 <h5>Name</h5>
27 <label class="sr-only" for="name">Name</label>
28 <input type="text" class="form-control" id="name" placeholder="Name" ng-model="host.name" disabled/>
29 </div>
30 <div class="col-md-4">
31 <h5>Description</h5>
32 <label class="sr-only" for="description">Description</label>
33 <textarea class="form-control" id="description" placeholder="Description" ng-model="host.description" disabled></textarea>
34 </div>
35 <div class="col-md-4">
36 <h5>Operating System</h5>
37 <label class="sr-only" for="os">Operating System</label>
38 <input type="text" class="form-control" id="os" placeholder="Operating System" ng-model="host.os" disabled/>
39 </div>
40 </div><!-- .form-group -->
41 </div>
42 </div>
43 <h2 class="ws-label">
44 <button id="delete" type="button" class="btn btn-default" title="Delete selected hosts" ng-click="delete()">
45 <span class="glyphicon glyphicon-trash"></span>
46 Delete
47 </button>
48 <button id="merge" type="button" class="btn btn-default" title="Edit selected hosts" ng-click="edit()">
49 <span class="glyphicon glyphicon-pencil"></span>
50 Edit
51 </button>
52 <button id="merge" type="button" class="btn btn-success" title="New host" ng-click="new()">
53 <span class="glyphicon glyphicon-plus-sign"></span>
54 New
55 </button>
56 </h2><!-- .ws-label -->
57 <div class="reports col-md-9 col-sm-9 col-xs-12">
58 <table class="status-report hosts-list table table-responsive">
59 <thead>
60 <tr>
61 <th><input type="checkbox" ng-model="selectall" ng-click="checkAll()"/></th>
62 <th>
63 <a href="" ng-click="toggleSort('name')">Name</a>
64 </th>
65 <th>
66 <a href="" ng-click="toggleSort('description')">Description</a>
67 </th>
68 <th>
69 <a href="" ng-click="toggleSort('ports')">Ports</a>
70 </th>
71 <th>
72 <a href="" ng-click="toggleSort('protocol')">Protocol</a>
73 </th>
74 <th>
75 <a href="" ng-click="toggleSort('status')">Status</a>
76 </th>
77 </tr>
78 </thead>
79 <tbody>
80 <tr ng-repeat="service in services | filter:query | orderBy:sortField:reverse"
81 selection-model selection-model-type="checkbox"
82 selection-model-mode="multiple-additive"
83 selection-model-selected-class="multi-selected">
84 <td><input type="checkbox" name="{{s._id}}"/></td>
85 <td ng-bind="service.name"></td>
86 <td ng-bind="service.description || '-'"></td>
87 <td ng-bind="service.ports"></td>
88 <td ng-bind="service.protocol"></td>
89 <td ng-bind="service.status"></td>
90 </tr>
91 </tbody>
92 </table><!-- #hosts -->
93 </div><!-- .reports -->
94 </div><!-- #reports-main --></div><!-- .right-main -->
95 </section><!-- #main -->
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">Edit service</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <label class="sr-only" for="name">Name</label>
17 <input type="text" class="form-control" id="name" placeholder="Name" ng-model="service.name" required/>
18 </div>
19 </div><!-- .form-group -->
20 <div class="form-group">
21 <div class="col-md-12">
22 <label class="sr-only" for="description">Description</label>
23 <textarea class="form-control" id="description" placeholder="Description" ng-model="service.description"></textarea>
24 </div>
25 </div><!-- .form-group -->
26 <div class="form-group">
27 <div class="col-md-12">
28 <div class="checkbox">
29 <label>
30 <input type="checkbox" id="owned" ng-model="service.owned"/>
31 <span class="normal-size">Owned</span>
32 </label>
33 </div><!-- .checkbox -->
34 </div>
35 </div><!-- .form-group -->
36 <div class="form-group">
37 <div class="col-md-3">
38 <h5>Ports</h5>
39 <span class="input-group-addon button-radius" ng-click="newPort($event)">Add Port</span>
40 <div class="input-margin" ng-repeat="port in service.ports">
41 <div class="input-group margin-bottom-sm">
42 <label class="sr-only" for="port">Ports</label>
43 <input type="number" class="form-control" id="port" placeholder="Port" ng-model="port.key"/>
44 <span class="input-group-addon" ng-click="service.ports.splice($index, 1)" ng-hide="service.ports.length == 1"><i class="fa fa-minus-circle"></i></span>
45 </div>
46 </div>
47 </div>
48 <div class="col-md-3 protocol">
49 <h5>Protocol</h5>
50 <label class="sr-only" for="protocol">Protocol</label>
51 <input type="text" class="form-control" id="protocol" placeholder="Protocol" ng-model="service.protocol"/>
52 </div>
53 <div class="col-md-3">
54 <h5>Version</h5>
55 <label class="sr-only" for="version">Version</label>
56 <input type="text" class="form-control" id="version" placeholder="Version" ng-model="service.version"/>
57 </div>
58 <div class="col-md-3">
59 <h5>Status</h5>
60 <label class="sr-only" for="status">Status</label>
61 <input type="text" class="form-control" id="status" placeholder="Status" ng-model="service.status"/>
62 </div>
63 </div><!-- .form-group -->
64 <div class="form-group">
65 <div class="reports col-md-12 col-sm-12 col-xs-12" ng-if="services_selected">
66 <h5><small>
67 Services to update
68 </small></h5>
69 <table class="status-report hosts-list table table-responsive">
70 <thead>
71 <tr>
72 <th>
73 <a href="" ng-click="toggleSort('name')">Name</a>
74 </th>
75 <th>
76 <a href="" ng-click="toggleSort('description')">Description</a>
77 </th>
78 <th>
79 <a href="" ng-click="toggleSort('ports')">Ports</a>
80 </th>
81 <th>
82 <a href="" ng-click="toggleSort('protocol')">Protocol</a>
83 </th>
84 <th>
85 <a href="" ng-click="toggleSort('status')">Status</a>
86 </th>
87 <th>Copy</th>
88 </tr>
89 </thead>
90 <tbody>
91 <tr ng-repeat="service in services_selected | filter:query | orderBy:sortField:reverse">
92 <td ng-bind="service.name"></td>
93 <td ng-bind="service.description || '-'"></td>
94 <td ng-bind="service.ports"></td>
95 <td ng-bind="service.protocol"></td>
96 <td ng-bind="service.status"></td>
97 <td><i class="fa fa-copy copy-icon fa-lg" ng-click="call(service);"></i></td>
98 </tr>
99 </tbody>
100 </table><!-- #hosts -->
101 </div>
102 </div>
103 </div>
104 </div><!-- .modal-body -->
105 <div class="modal-footer">
106 <div class="modal-button">
107 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
108 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
109 </div>
110 </div>
111 </form>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">New service</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <label class="sr-only" for="service">Name</label>
17 <input type="text" class="form-control" id="service" placeholder="Name" ng-model="service.name" required/>
18 </div>
19 </div><!-- .form-group -->
20 <div class="form-group">
21 <div class="col-md-12">
22 <label class="sr-only" for="description">Description</label>
23 <textarea class="form-control" id="description" placeholder="Description" ng-model="service.description"></textarea>
24 </div>
25 </div><!-- .form-group -->
26 <div class="form-group">
27 <div class="col-md-12">
28 <div class="checkbox">
29 <label>
30 <input type="checkbox" id="owned" ng-model="service.owned"/>
31 <span class="normal-size">Owned</span>
32 </label>
33 </div><!-- .checkbox -->
34 </div>
35 </div><!-- .form-group -->
36 <div class="form-group">
37 <div class="col-md-3">
38 <h5>Ports</h5>
39 <span class="input-group-addon button-radius" ng-click="newPort($event)">Add Port</span>
40 <div class="input-margin" ng-repeat="port in service.ports">
41 <div class="input-group margin-bottom-sm">
42 <label class="sr-only" for="port">Ports</label>
43 <input type="number" class="form-control" id="port" placeholder="Port" ng-model="port.key" required/>
44 <span class="input-group-addon" ng-click="service.ports.splice($index, 1)" ng-hide="service.ports.length == 1"><i class="fa fa-minus-circle"></i></span>
45 </div>
46 </div>
47 </div>
48 <div class="col-md-3 protocol">
49 <h5>Protocol</h5>
50 <label class="sr-only" for="protocol">Protocol</label>
51 <input type="text" class="form-control" id="protocol" placeholder="Protocol" ng-model="service.protocol" required/>
52 </div>
53 <div class="col-md-3">
54 <h5>Version</h5>
55 <label class="sr-only" for="version">Version</label>
56 <input type="text" class="form-control" id="version" placeholder="Version" ng-model="service.version"/>
57 </div>
58 <div class="col-md-3">
59 <h5>Status</h5>
60 <label class="sr-only" for="status">Status</label>
61 <input type="text" class="form-control" id="status" placeholder="Status" ng-model="service.status"/>
62 </div>
63 </div><!-- .form-group -->
64 </div>
65 </div><!-- .modal-body -->
66 <div class="modal-footer">
67 <div class="modal-button">
68 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
69 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
70 </div>
71 </div>
72 </form>
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .factory('Service', ['BASEURL', '$http', function(BASEURL, $http) {
6 Service = function(data){
7 if(data) {
8 this.set(data);
9 }
10 };
11
12 Service.prototype = {
13 // TODO: instead of using angular.extend, we should check
14 // the attributes we're assigning to the Service
15 set: function(data) {
16 // if there's no ID, we need to generate it based on the Service name
17 if(data._id === undefined){
18 var ports = data.ports.toString().replace(/,/g,":");
19 data['_id'] = data.parent + "." + CryptoJS.SHA1(data.protocol+ "._." + ports).toString();
20 }
21 data.type = "Service";
22 angular.extend(this, data);
23 },
24 delete: function(ws) {
25 var self = this,
26 bulk = {docs:[]};
27 return $http.get(BASEURL + ws + '/_all_docs?startkey="' + self._id + '"&endkey="' + self._id + '.z"').then(function(all) {
28 all.data.rows.forEach(function(row) {
29 bulk.docs.push({
30 "_id": row.id,
31 "_rev": row.value.rev,
32 "_deleted": true
33 });
34 });
35
36 return $http.post(BASEURL + ws + "/_bulk_docs", JSON.stringify(bulk));
37 });
38 },
39 update: function(data, ws) {
40 angular.extend(this, data);
41 var self = this;
42
43 return ($http.put(BASEURL + ws + '/' + self._id + "?rev=" + self._rev, self).success(function(data) {
44 self._rev = data.rev;
45 }));
46 },
47 save: function(ws) {
48 var self = this;
49 return ($http.put(BASEURL + ws + '/' + self._id, self).success(function(data){
50 self._rev = data.rev;
51 }));
52 }
53 }
54
55 return Service;
56 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .factory('servicesManager', ['BASEURL', '$http', '$q', 'Service', function(BASEURL, $http, $q, Service) {
6 var servicesManager = {};
7
8 servicesManager._objects = {};
9 servicesManager._get = function(id, data) {
10 var service = this._objects[id];
11
12 if(service) {
13 service.set(data);
14 } else {
15 service = new Service(data);
16 this._objects[id] = service;
17 }
18
19 return service;
20 }
21
22 servicesManager._search = function(id) {
23 return this._objects[id];
24 }
25
26 servicesManager._load = function(id, ws, deferred) {
27 var self = this;
28 $http.get(BASEURL + '/' + ws + '/' + id)
29 .success(function(data){
30 var service = self._get(data._id, data);
31 deferred.resolve(service);
32 })
33 .error(function(){
34 deferred.reject();
35 });
36 }
37
38 servicesManager.getService = function(id, ws, force_reload) {
39 var deferred = $q.defer();
40 var service = this._search(id);
41 force_reload = force_reload || false;
42 if((service) && (!force_reload)) {
43 deferred.resolve(service);
44 } else {
45 this._load(id, ws, deferred);
46 }
47 return deferred.promise;
48 }
49
50 // async method - this is the one to use from now on!
51 servicesManager.getServices = function(ws) {
52 var deferred = $q.defer();
53 var self = this;
54 $http.get(BASEURL + '/' + ws + '/_design/services/_view/services')
55 .success(function(servicesArray){
56 var services = [];
57 servicesArray.rows.forEach(function(serviceData){
58 var service = self._get(serviceData.value._id, serviceData.value);
59 services.push(service);
60 });
61 deferred.resolve(services);
62 })
63 .error(function(){
64 deferred.reject();
65 })
66 return deferred.promise;
67 }
68
69 servicesManager.deleteServices = function(id, ws) {
70 var deferred = $q.defer();
71 var self = this;
72 this.getService(id, ws).then(function(service) {
73 service.delete(ws).then(function() {
74 delete self._objects[id];
75 deferred.resolve();
76 }, function(){
77 // host couldn't be deleted
78 deferred.reject("Error deleting service");
79 });
80 }, function(){
81 // host doesn't exist
82 deferred.reject("Service doesn't exist");
83 });
84 return deferred.promise;
85 }
86
87 servicesManager.createService = function(serviceData, ws) {
88 var deferred = $q.defer();
89 var self = this;
90
91 this.getServices(ws).then(function(services) {
92 var service = new Service(serviceData);
93 self.getService(service._id, ws).then(function(resp) {
94 deferred.reject("Service already exists");
95 }, function() {
96 // host doesn't exist, good to go
97 service.save(ws).then(function(){
98 service = self.getService(service._id, ws);
99 deferred.resolve(service);
100 }, function(){
101 // host couldn't be saved
102 deferred.reject("Error: host couldn't be saved");
103 })
104 });
105 });
106
107 return deferred.promise;
108 }
109
110 servicesManager.updateService = function(service, data, ws) {
111 var deferred = $q.defer();
112 var self = this;
113 this.getService(service._id, ws).then(function(resp) {
114 resp.update(data, ws).then(function() {
115 // we need to reload the service in order
116 // to update _rev
117 service = self._load(service._id, ws, deferred);
118 deferred.resolve(service);
119 })
120 }, function(){
121 // service doesn't exist
122 deferred.reject("Service doesn't exist");
123 });
124 return deferred.promise;
125 }
126
127 return servicesManager;
128 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalEditCtrl', ['$scope', '$modalInstance', 'EASEOFRESOLUTION', 'commonsFact', 'severities', 'vulns',
6 function($scope, $modalInstance, EASEOFRESOLUTION, commons, severities, vulns) {
7 $scope.easeofresolution = EASEOFRESOLUTION;
8 $scope.evidence = {};
9 $scope.icons = {};
10 $scope.severities = severities;
11 $scope.vulns = vulns;
12 $scope.web = false;
13 $scope.mixed = 0x00;
14 $scope.vulnc = 0;
15 $scope.vulnid = 0;
16 $scope.file_name_error = false;
17 $scope.p_impact = {
18 "accountability": false,
19 "availability": false,
20 "confidentiality": false,
21 "integrity": false
22 };
23 $scope.impact = {
24 "accountability": false,
25 "availability": false,
26 "confidentiality": false,
27 "integrity": false
28 };
29 var vuln_mask = {"VulnerabilityWeb": 0x01, "Vulnerability": 0x10};
30
31 $scope.pickVuln = function(v) {
32 $scope.p_name = v.name;
33 $scope.p_desc = v.desc;
34 $scope.p_data = v.data;
35 $scope.severitySelection = v.severity;
36 $scope.easeOfResolutionSelection = v.easeofresolution;
37 $scope.p_method = v.method;
38 $scope.p_pname = v.pname;
39 $scope.p_params = v.params;
40 $scope.p_path = v.path;
41 $scope.p_query = v.query;
42 $scope.p_website = v.website;
43 $scope.p_refs = v.refs;
44 $scope.p_request = v.request;
45 $scope.p_response = v.response;
46 $scope.p_resolution = v.resolution;
47
48 $scope.name = $scope.p_name;
49 $scope.data = $scope.p_data;
50 $scope.desc = $scope.p_desc;
51 $scope.method = $scope.p_method;
52 $scope.params = $scope.p_params;
53 $scope.path = $scope.p_path;
54 $scope.pname = $scope.p_pname;
55 $scope.query = $scope.p_query;
56 $scope.refs = $scope.p_refs;
57 $scope.request = $scope.p_request;
58 $scope.response = $scope.p_response;
59 $scope.resolution = $scope.p_resolution;
60 $scope.website = $scope.p_website;
61
62 for(var key in v.impact) {
63 $scope.impact[key] = v.impact[key];
64 $scope.p_impact[key] = v.impact[key];
65 }
66
67 };
68
69 $scope.toggleImpact = function(key) {
70 $scope.impact[key] = !$scope.impact[key];
71 };
72
73 $scope.call = function(){
74 $scope.refs = commons.arrayToObject($scope.refs);
75 };
76 vulnid_count=0
77 $scope.vulns.forEach(function(v) {
78 if(v.selected) {
79 if(typeof(v.attachments) != undefined && v.attachments != undefined) {
80 v.attachments.forEach(function(name) {
81 $scope.evidence[name] = {"name": name};
82 });
83 $scope.icons = commons.loadIcons($scope.evidence);
84 }
85 $scope.mixed = $scope.mixed | vuln_mask[v.type];
86 $scope.vulnc++;
87 $scope.vulnid = vulnid_count;
88 if (v.type === "VulnerabilityWeb") {
89 $scope.web = true;
90 //web
91 }
92
93 }
94 vulnid_count++;
95
96
97 });
98
99 if ($scope.vulnc == 1) {
100 $scope.pickVuln($scope.vulns[$scope.vulnid]);
101 }
102
103 $scope.unit = $scope.vulnc == 1;
104
105 if ($scope.vulnc > 1) {
106 $scope.p_name = "";
107 $scope.p_desc = "";
108 $scope.p_data = "";
109 $scope.p_method = "";
110 $scope.p_pname = "";
111 $scope.p_params = "";
112 $scope.p_path = "";
113 $scope.p_query = "";
114 $scope.p_website = "";
115 $scope.p_refs = "";
116 $scope.p_request = "";
117 $scope.p_response = "";
118 $scope.p_resolution = "";
119 }
120
121 if($scope.mixed == 0x11) {
122 $scope.mixed = true;
123 } else {
124 $scope.mixed = false;
125 }
126
127 $scope.isChecked = function(i) {
128 return i.selected;
129 };
130
131 $scope.ok = function() {
132 var res = {},
133 evidence = [];
134
135 for(var key in $scope.impact) {
136 $scope.impact[key] = Boolean($scope.impact[key]);
137 }
138
139 for(var key in $scope.evidence) {
140 if(Object.keys($scope.evidence[key]).length == 1) {
141 evidence.push(key);
142 } else {
143 evidence.push($scope.evidence[key]);
144 }
145 }
146
147 $scope.refs = commons.objectToArray($scope.refs);
148
149 if($scope.web) {
150 res = {
151 "data": $scope.data,
152 "desc": $scope.desc,
153 "easeofresolution": $scope.easeOfResolutionSelection,
154 "evidence": $scope.evidence,
155 "impact": $scope.impact,
156 "method": $scope.method,
157 "name": $scope.name,
158 "params": $scope.params,
159 "path": $scope.path,
160 "pname": $scope.pname,
161 "query": $scope.query,
162 "refs": $scope.refs,
163 "request": $scope.request,
164 "response": $scope.response,
165 "resolution": $scope.resolution,
166 "severity": $scope.severitySelection,
167 "vulns": $scope.vulns,
168 "website": $scope.website
169 };
170 } else {
171 res = {
172 "data": $scope.data,
173 "desc": $scope.desc,
174 "easeofresolution": $scope.easeOfResolutionSelection,
175 "evidence": $scope.evidence,
176 "impact": $scope.impact,
177 "name": $scope.name,
178 "refs": $scope.refs,
179 "resolution": $scope.resolution,
180 "severity": $scope.severitySelection,
181 "vulns": $scope.vulns
182 };
183 }
184
185 $modalInstance.close(res);
186 };
187
188 $scope.cancel = function() {
189 $modalInstance.dismiss('cancel');
190 };
191
192 $scope.refs = commons.arrayToObject($scope.refs);
193
194 $scope.newReference = function($event){
195 $scope.refs.push({key:''});
196 };
197
198 $scope.selectedFiles = function(files, e) {
199 files.forEach(function(file) {
200 if(file.name.charAt(0) != "_") {
201 if(!$scope.evidence.hasOwnProperty(file)) $scope.evidence[file.name] = file;
202 } else {
203 $scope.file_name_error = true;
204 }
205 });
206 $scope.icons = commons.loadIcons($scope.evidence);
207 }
208
209 $scope.removeEvidence = function(name) {
210 delete $scope.evidence[name];
211 delete $scope.icons[name];
212 }
213 }]);
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 angular.module('faradayApp')
5 .controller('modalNewCtrl',
6 ['$scope', '$modalInstance', '$filter', '$upload', 'EASEOFRESOLUTION', 'targetFact', 'commonsFact', 'severities', 'workspace', 'hostsManager',
7 function($scope, $modalInstance, $filter, $upload, EASEOFRESOLUTION, targetFact, commons, severities, workspace, hostsManager) {
8
9 $scope.typeOptions = [
10 {name:'Vulnerability', value:'Vulnerability'},
11 {name:'VulnerabilityWeb',value:'VulnerabilityWeb'}
12 ];
13
14 $scope.easeofresolution = EASEOFRESOLUTION;
15 $scope.vuln_type = $scope.typeOptions[0].value;
16 $scope.severities = severities;
17 $scope.workspace = workspace;
18 $scope.target_selected = null;
19 $scope.not_target_selected = false;
20 $scope.incompatible_vulnWeb = false;
21 $scope.refs = [{key:''}];
22 $scope.evidence = {};
23 $scope.icons = {};
24 $scope.showPagination = 1;
25 $scope.currentPage = 0;
26 $scope.pageSize = 5;
27 $scope.pagination = 10;
28 $scope.file_name_error = false;
29 $scope.impact = {
30 "accountability": false,
31 "availability": false,
32 "confidentiality": false,
33 "integrity": false
34 };
35
36
37 var name_selected,
38 host_selected,
39 d = {},
40 hosts = targetFact.getTarget($scope.workspace, true);
41
42 hosts.forEach(function(h) {
43 h.services = [];
44 d[h._id] = h;
45 });
46
47 hostsManager.getInterfaces($scope.workspace).then(function(resp){
48 $scope.interfaces = resp;
49 hosts.forEach(function(h){
50 $scope.interfaces.forEach(function(interface){
51 if(h._id == interface.value.parent){
52 h.hostnames = interface.value.hostnames;
53 }
54 });
55 });
56 });
57
58 var services = targetFact.getTarget($scope.workspace, false);
59
60 for(var i = 0; i < services.length; i++){
61 var host = [];
62 services[i].selected = false;
63 host = d[services[i].hid];
64 host.services.push(services[i]);
65 }
66
67 $scope.hosts_with_services = hosts;
68
69 $scope.numberOfPages = function() {
70 if(typeof(filteredData) == "undefined") return false;
71 var filteredData = $filter('filter')($scope.hosts_with_services,$scope.search_notes);
72 if (filteredData.length <= 10){
73 $scope.showPagination = 0;
74 } else {
75 $scope.showPagination = 1;
76 }
77
78 return Math.ceil(filteredData.length/$scope.pagination);
79 };
80
81 $scope.selectedFiles = function(files, e) {
82 files.forEach(function(file) {
83 if(file.name.charAt(0) != "_") {
84 if(!$scope.evidence.hasOwnProperty(file)) $scope.evidence[file.name] = file;
85 } else {
86 $scope.file_name_error = true;
87 }
88 });
89 $scope.icons = commons.loadIcons($scope.evidence);
90 };
91
92 $scope.removeEvidence = function(name) {
93 delete $scope.evidence[name];
94 delete $scope.icons[name];
95 };
96
97 $scope.toggleImpact = function(key) {
98 $scope.impact[key] = !$scope.impact[key];
99 };
100
101 $scope.ok = function() {
102 if($scope.vuln_type == "VulnerabilityWeb" && host_selected == true){
103 $scope.incompatible_vulnWeb = true;
104 } else {
105 var res = {},
106 id = $scope.target_selected._id + "." + CryptoJS.SHA1($scope.name + "." + $scope.desc).toString(),
107 sha = CryptoJS.SHA1($scope.name + "." + $scope.desc).toString(),
108 myDate = new Date(),
109 myEpoch = myDate.getTime()/1000.0,
110 extra_vulns_prop = {},
111 arrayReferences = [];
112
113 for(var key in $scope.impact) {
114 $scope.impact[key] = Boolean($scope.impact[key]);
115 }
116
117 $scope.refs.forEach(function(r){
118 arrayReferences.push(r.key);
119 });
120
121 arrayReferences.filter(Boolean);
122
123 var res = {
124 "id": id,
125 "data": $scope.data,
126 "date": myEpoch,
127 "desc": $scope.desc,
128 "easeofresolution": $scope.easeOfResolutionSelection,
129 "evidence": $scope.evidence,
130 "impact": $scope.impact,
131 "meta": {
132 'create_time': myEpoch,
133 "update_time": myEpoch,
134 "update_user": 'UI Web',
135 'update_action': 0,
136 'creator': 'UI Web',
137 'create_time': myEpoch,
138 'update_controller_action': 'UI Web New',
139 'owner': 'anonymous'
140 },
141 "name": $scope.name,
142 "oid": sha,
143 "owned": false,
144 "owner": "",
145 "couch_parent": $scope.target_selected._id,
146 "refs": arrayReferences,
147 "resolution": $scope.resolution,
148 "status": $scope.vuln_type,
149 "severity": $scope.severitySelection,
150 "target": name_selected,
151 "type": $scope.vuln_type
152 }
153
154 if($scope.vuln_type == "VulnerabilityWeb") {
155 extra_vulns_prop = {
156 "path": $scope.path,
157 "pname": $scope.pname,
158 "query": $scope.query,
159 "request": $scope.request,
160 "resolution": $scope.resolution,
161 "response": $scope.response,
162 "web": true,
163 "website": $scope.website
164 };
165 } else {
166 extra_vulns_prop = {
167 "web": false
168 };
169 }
170
171 for(var key in extra_vulns_prop) {
172 res[key] = extra_vulns_prop[key];
173 }
174
175 $modalInstance.close(res);
176 }
177 };
178
179 $scope.cancel = function() {
180 $modalInstance.dismiss('cancel');
181 };
182
183 $scope.$parent.isopen = ($scope.$parent.default === $scope.item);
184
185 $scope.$watch('isopen', function (newvalue, oldvalue, $scope) {
186 $scope.$parent.isopen = newvalue;
187 });
188
189 $scope.selected = function(i,j){
190 if($scope.target_selected){
191 $scope.target_selected.selected = false;
192 }
193 if(j != null){
194 host_selected = false;
195 $scope.target_selected = j;
196 name_selected = i.name;
197 }else{
198 host_selected = true;
199 $scope.target_selected = i;
200 name_selected = i.name;
201 }
202 $scope.target_selected.selected = true;
203 $scope.not_target_selected = true;
204 }
205
206 $scope.go = function(){
207 if($scope.go_page < $scope.numberOfPages()+2 && $scope.go_page > -1){
208 $scope.currentPage = $scope.go_page;
209 }
210 }
211
212 $scope.newReference = function($event){
213 $scope.refs.push({key:''});
214 $event.preventDefault();
215 }
216 }]);
33
44 angular.module('faradayApp')
55 .controller('statusReportCtrl',
6 ['$scope', '$filter', '$route', '$routeParams', '$modal', 'BASEURL', 'SEVERITIES', 'EASEOFRESOLUTION', 'statusReportFact',
7 function($scope, $filter, $route, $routeParams, $modal, BASEURL, SEVERITIES, EASEOFRESOLUTION, statusReportFact) {
8 $scope.baseurl = BASEURL;
9 $scope.severities = SEVERITIES;
10 $scope.easeofresolution = EASEOFRESOLUTION;
11
12 $scope.sortField = 'date';
13 $scope.reverse = true;
14 $scope.showPagination = 1;
15 $scope.currentPage = 0;
16 $scope.pageSize = 10;
17 $scope.pagination = 10;
18
19 // load all workspaces
20 statusReportFact.getWorkspaces().then(function(wss) {
21 $scope.workspaces = wss;
22 });
23
24 // current workspace
25 $scope.workspace = $routeParams.wsId;
26
27 // load all vulnerabilities
28 $scope.vulns = statusReportFact.getVulns($scope.workspace);
29
30 // toggles column show property
31 $scope.toggleShow = function(column, show) {
32 $scope.columns[column] = !show;
33 };
34
35 // toggles sort field and order
36 $scope.toggleSort = function(field) {
37 $scope.toggleSortField(field);
38 $scope.toggleReverse();
39 };
40
41 // toggles column sort field
42 $scope.toggleSortField = function(field) {
43 $scope.sortField = field;
44 };
45
46 // toggle column sort order
47 $scope.toggleReverse = function() {
48 $scope.reverse = !$scope.reverse;
49 }
50
51 // set columns to show and hide by default
52 $scope.columns = {
53 "data": true,
54 "date": true,
55 "desc": true,
56 "easeofresolution": false,
57 "evidence": false,
58 "impact": false,
59 "method": false,
60 "name": true,
61 "params": false,
62 "path": false,
63 "pname": false,
64 "query": false,
65 "refs": true,
66 "request": false,
67 "response": false,
68 "resolution": false,
69 "severity": true,
70 "status": false,
71 "target": true,
72 "web": false,
73 "website": false
6 ['$scope', '$filter', '$route', '$routeParams', '$location', '$modal', '$cookies','BASEURL', 'SEVERITIES', 'EASEOFRESOLUTION', 'statusReportFact', 'hostsManager',
7 function($scope, $filter, $route, $routeParams, $location, $modal, $cookies, BASEURL, SEVERITIES, EASEOFRESOLUTION, statusReportFact, hostsManager) {
8 init = function() {
9 $scope.baseurl = BASEURL;
10 $scope.severities = SEVERITIES;
11 $scope.easeofresolution = EASEOFRESOLUTION;
12
13 $scope.sortField = 'date';
14 $scope.reverse = true;
15 $scope.showPagination = 1;
16 $scope.currentPage = 0;
17 // set custom pagination if is possible
18 if(typeof($cookies.pageSize) == "undefined") {
19 $scope.pageSize = 10;
20 $scope.pagination = 10;
21 } else {
22 $scope.pageSize = parseInt($cookies.pageSize);
23 $scope.pagination = parseInt($cookies.pageSize);
24 }
25
26 // load all workspaces
27 statusReportFact.getWorkspaces().then(function(wss) {
28 $scope.workspaces = wss;
29 });
30
31 // current workspace
32 $scope.workspace = $routeParams.wsId;
33 $scope.interfaces = [];
34
35 $scope.getVulns = function() {
36 var vulnerabilities = statusReportFact.getVulns($scope.workspace);
37 hostsManager.getInterfaces($scope.workspace).then(function(interfaces){
38 interfaces.forEach(function(interface){
39 vulnerabilities.forEach(function(vuln){
40 if(vuln.parent == interface.value.parent){
41 vuln.hostnames = interface.value.hostnames;
42 }
43 });
44 });
45 });
46 return vulnerabilities;
47 };
48
49 // current search
50 $scope.search = $routeParams.search;
51 $scope.searchParams = "";
52 $scope.expression = {};
53 if($scope.search != "" && $scope.search != undefined && $scope.search.indexOf("=") > -1) {
54 // search expression for filter
55 $scope.expression = $scope.decodeSearch($scope.search);
56 // search params for search field, which shouldn't be used for filtering
57 $scope.searchParams = $scope.stringSearch($scope.expression);
58 }
59
60 // load all vulnerabilities
61 $scope.vulns = $filter('filter')($scope.getVulns(), $scope.expression);
62
63 // created object for columns cookie columns
64 if(typeof($cookies.SRcolumns) != 'undefined'){
65 var objectoSRColumns = {};
66 var arrayOfColumns = $cookies.SRcolumns.replace(/[{}"']/g, "").split(',');
67 arrayOfColumns.forEach(function(column){
68 var columnFinished = column.split(':');
69 if(columnFinished[1] == "true") objectoSRColumns[columnFinished[0]] = true; else objectoSRColumns[columnFinished[0]] = false;
70 });
71 }
72 // set columns to show and hide by default
73 $scope.columns = objectoSRColumns || {
74 "data": true,
75 "date": true,
76 "desc": true,
77 "easeofresolution": false,
78 "evidence": false,
79 "hostnames": false,
80 "impact": false,
81 "method": false,
82 "name": true,
83 "params": false,
84 "path": false,
85 "pname": false,
86 "query": false,
87 "refs": true,
88 "request": false,
89 "response": false,
90 "resolution": false,
91 "severity": true,
92 "status": false,
93 "target": true,
94 "web": false,
95 "website": false
96 };
7497 };
7598
7699 // returns scope vulns as CSV obj
246269
247270 if(selected) {
248271 var modal = $modal.open({
249 templateUrl: 'scripts/partials/modal-delete.html',
250 controller: 'modalDeleteCtrl',
272 templateUrl: 'scripts/commons/partials/modalDelete.html',
273 controller: 'commonsModalDelete',
251274 size: 'lg',
252275 resolve: {
253 amount: function() {
254 return i;
276 msg: function() {
277 var msg = "";
278 if(i == 1) {
279 msg = "A vulnerability will be deleted.";
280 } else {
281 msg = i + " vulnerabilities will be deleted.";
282 }
283 msg += " This action cannot be undone. Are you sure you want to proceed?";
284 return msg;
255285 }
256286 }
257287 });
261291 });
262292 } else {
263293 var modal = $modal.open({
264 templateUrl: 'scripts/partials/modal-ko.html',
265 controller: 'modalKoCtrl',
294 templateUrl: 'scripts/commons/partials/modalKO.html',
295 controller: 'commonsModalKoCtrl',
266296 resolve: {
267297 msg: function() {
268298 return 'No vulnerabilities were selected to delete';
282312
283313 if(selected) {
284314 var modal = $modal.open({
285 templateUrl: 'scripts/partials/modal-edit.html',
315 templateUrl: 'scripts/statusReport/partials/modalEdit.html',
286316 controller: 'modalEditCtrl',
287317 size: 'lg',
288318 resolve: {
300330 });
301331 } else {
302332 var modal = $modal.open({
303 templateUrl: 'scripts/partials/modal-ko.html',
304 controller: 'modalKoCtrl',
333 templateUrl: 'scripts/commons/partials/modalKO.html',
334 controller: 'commonsModalKoCtrl',
305335 resolve: {
306336 msg: function() {
307337 return 'At least one vulnerabilty must be selected in order to edit';
311341 }
312342 };
313343
314 $scope.insert = function(vuln){
344 $scope.insert = function(vuln) {
315345 statusReportFact.putVulns($scope.workspace, vuln, function(rev, evidence) {
316346 vuln.rev = rev;
317347 vuln.attachments = evidence;
322352 d = d.getDate() + "/" + (d.getMonth()+1) + "/" + d.getFullYear();
323353 vuln.date = d;
324354 $scope.vulns.push(vuln);
325 }
326
327 $scope.new = function(){
355 };
356
357 $scope.new = function() {
328358 var modal = $modal.open({
329 templateUrl: 'scripts/partials/modal-new.html',
359 templateUrl: 'scripts/statusReport/partials/modalNew.html',
330360 controller: 'modalNewCtrl',
331361 size: 'lg',
332362 resolve: {
351381 $scope.selectall = false;
352382 }
353383
354 angular.forEach($filter('filter')($scope.vulns, $scope.query), function(v) {
384 var orderObject = $filter('orderObjectBy')($scope.vulns, $scope.sortField, $scope.reverse);
385 var tmp_vulns = $filter('limitTo')(orderObject, $scope.pageSize, $scope.currentPage * $scope.pageSize);
386 angular.forEach($filter('filter')(tmp_vulns), function(v,k) {
355387 v.selected = $scope.selectall;
356388 });
357389 };
358390
359391 $scope.numberOfPages = function() {
360 $scope.filteredData = $filter('filter')($scope.vulns,$scope.query);
361 if ($scope.filteredData.length <= 10){
392 if($scope.vulns.length <= 10) {
362393 $scope.showPagination = 0;
363394 } else {
364395 $scope.showPagination = 1;
365 };
366 return parseInt($scope.filteredData.length/$scope.pageSize);
367 }
368
369 $scope.go = function(){
370 if($scope.go_page < $scope.numberOfPages()+1 && $scope.go_page > -1){
396 }
397 return parseInt($scope.vulns.length/$scope.pageSize);
398 };
399
400 $scope.go = function() {
401 if($scope.go_page < $scope.numberOfPages()+1 && $scope.go_page > -1) {
371402 $scope.currentPage = $scope.go_page;
372403 }
373404 $scope.pageSize = $scope.pagination;
374 if($scope.go_page > $scope.numberOfPages()){
405 if($scope.go_page > $scope.numberOfPages()) {
375406 $scope.currentPage = 0;
376407 }
377 }
408 $cookies.pageSize = $scope.pageSize;
409 };
410
411 // encodes search string in order to send it through URL
412 $scope.encodeSearch = function(search) {
413 var i = -1,
414 encode = "",
415 params = search.split(" "),
416 chunks = {};
417
418 params.forEach(function(chunk) {
419 i = chunk.indexOf(":");
420 if(i > 0) {
421 chunks[chunk.slice(0, i)] = chunk.slice(i+1);
422 } else {
423 if(!chunks.hasOwnProperty("free")) {
424 chunks.free = "";
425 }
426 chunks.free += " ".concat(chunk);
427 }
428 });
429
430 if(chunks.hasOwnProperty("free")) {
431 chunks.free = chunks.free.slice(1);
432 }
433
434 for(var prop in chunks) {
435 if(chunks.hasOwnProperty(prop)) {
436 if(chunks.prop != "") {
437 encode += "&" + prop + "=" + chunks[prop];
438 }
439 }
440 }
441 return encodeURI(encode.slice(1));
442 };
443
444 // decodes search parameters to object in order to use in filter
445 $scope.decodeSearch = function(search) {
446 var i = -1,
447 decode = {},
448 params = decodeURI(search).split("&");
449
450 params.forEach(function(param) {
451 i = param.indexOf("=");
452 decode[param.slice(0,i)] = param.slice(i+1);
453 });
454
455 if(decode.hasOwnProperty("free")) {
456 decode['$'] = decode.free;
457 delete decode.free;
458 }
459
460 return decode;
461 };
462
463 // converts current search object to string to be displayed in search field
464 $scope.stringSearch = function(obj) {
465 var search = "";
466
467 for(var prop in obj) {
468 if(obj.hasOwnProperty(prop)) {
469 if(search != "") {
470 search += " ";
471 }
472 if(prop == "$") {
473 search += obj[prop];
474 } else {
475 search += prop + ":" + obj[prop];
476 }
477 }
478 }
479
480 return search;
481 };
482
483 // changes the URL according to search params
484 $scope.searchFor = function(search, params) {
485 var url = "/status/ws/" + $routeParams.wsId;
486
487 if(search && params != "" && params != undefined) {
488 url += "/search/" + $scope.encodeSearch(params);
489 }
490
491 $location.path(url);
492 };
493
494 // toggles column show property
495 $scope.toggleShow = function(column, show) {
496 $scope.columns[column] = !show;
497 $cookies.SRcolumns = JSON.stringify($scope.columns);
498 };
499
500 // toggles sort field and order
501 $scope.toggleSort = function(field) {
502 $scope.toggleSortField(field);
503 $scope.toggleReverse();
504 };
505
506 // toggles column sort field
507 $scope.toggleSortField = function(field) {
508 $scope.sortField = field;
509 };
510
511 // toggle column sort order
512 $scope.toggleReverse = function() {
513 $scope.reverse = !$scope.reverse;
514 };
515
516 init();
378517 }]);
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="formEdit" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="formEdit.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">Bulk edit</h3>
11 </div>
12 <div class="modal-body">
13 <div ng-if="mixed">
14 <div class="alert alert-danger alert-dismissible" role="alert">
15 <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
16 <h5><strong>Hey!</strong> You have selected both vulnerabilities and web vulnerabilities. Please keep in mind that fields in red are specific for web vulnerabilities and these will not be added to regular vulnerabilities.</h5>
17 </div>
18 </div><!-- ng-if -->
19 <div ng-if="!web">
20 <h5><small>
21 Please choose a severity, name, description and data
22 </small></h5>
23 </div><!-- ng-if -->
24 <div class="form-horizontal">
25 <div class="form-group">
26 <div class="col-md-6">
27 <h5>Severity</h5>
28 <select class="form-control" ng-model="severitySelection" ng-options="s as s for s in severities">
29 </select>
30 </div>
31 <div class="col-md-6">
32 <h5>Ease of Resolution</h5>
33 <select class="form-control" ng-model="easeOfResolutionSelection" ng-options="e as e for e in easeofresolution">
34 <option value=""></option>
35 </select>
36 </div>
37 </div><!-- .form-group -->
38 <div class="form-group">
39 <div class="col-md-12">
40 <label class="sr-only" for="vuln-name">Vuln name</label>
41 <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="name"/>
42 </div>
43 </div><!-- .form-group -->
44 <div class="form-group">
45 <div class="col-md-12">
46 <label class="sr-only" for="vuln-desc">Vuln description</label>
47 <textarea class="form-control" id="vuln-desc" placeholder="Description" value="{{p_desc}}" ng-model="desc"></textarea>
48 </div>
49 </div><!-- .form-group -->
50 <div class="form-group">
51 <div class="col-md-12">
52 <label class="sr-only" for="vuln-data">Vuln data</label>
53 <textarea class="form-control" id="vuln-data" placeholder="Data" value="{{p_data}}" ng-model="data"></textarea>
54 </div>
55 </div><!-- .form-group -->
56 <div class="form-group">
57 <div class="col-md-12">
58 <h4>References</h4>
59 <span class="input-group-addon button-radius" ng-click="newReference($event)">Add Reference</span>
60 </div>
61 <div class="col-md-12 reference" ng-repeat="reference in refs">
62 <div class="input-group margin-bottom-sm">
63 <label class="sr-only" for="vuln-refs">References</label>
64 <input type="text" class="form-control" id="vuln-refs" placeholder="Reference" ng-model="reference.key"/>
65 <span class="input-group-addon" ng-click="refs.splice($index, 1)"><i class="fa fa-minus-circle"></i></span>
66 </div>
67 </div>
68 </div><!-- .form-group -->
69 <div class="form-group">
70 <div class="col-md-12">
71 <label class="sr-only" for="vuln-resolution">Vuln Resolution</label>
72 <textarea class="form-control" id="vuln-resolution" placeholder="Resolution" ng-model="resolution"></textarea>
73 </div>
74 </div><!-- .form-group -->
75 <div ng-if="web">
76 <div class="form-group">
77 <div class="col-md-4 has-error">
78 <label class="sr-only control-label" for="vuln-method">Method</label>
79 <input type="text" class="form-control input-error" id="vuln-method" value="{{p_method}}" placeholder="Method" ng-model="$parent.method"/>
80 </div>
81 <div class="col-md-3 has-error">
82 <label class="sr-only control-label" for="vuln-pname">Param Name</label>
83 <input type="text" class="form-control input-error" id="vuln-pname" value="{{p_pname}}" placeholder="Param name" ng-model="$parent.pname"/>
84 </div>
85 <div class="col-md-5 has-error">
86 <label class="sr-only control-label" for="vuln-params">Params</label>
87 <input type="text" class="form-control input-error" id="vuln-params" value="{{p_params}}" placeholder="Params" ng-model="$parent.params"/>
88 </div>
89 </div><!-- .form-group -->
90 <div class="form-group">
91 <div class="col-md-4 has-error">
92 <label class="sr-only control-label" for="vuln-path">Path</label>
93 <input type="text" class="form-control input-error" id="vuln-path" value="{{p_path}}" placeholder="Path" ng-model="$parent.path"/>
94 </div>
95 <div class="col-md-4 has-error">
96 <label class="sr-only control-label" for="vuln-query">Query</label>
97 <input type="text" class="form-control input-error" id="vuln-query" value="{{p_query}}"placeholder="Query" ng-model="$parent.query"/>
98 </div>
99 <div class="col-md-4 has-error">
100 <label class="sr-only control-label" for="vuln-website">Website</label>
101 <input type="text" class="form-control input-error" id="vuln-website" value="{{p_website}}" placeholder="Website" ng-model="$parent.website"/>
102 </div>
103 </div><!-- .form-group -->
104 <div class="form-group">
105 <div class="col-md-12 has-error">
106 <label class="sr-only control-label" for="vuln-request">Request</label>
107 <textarea class="form-control input-error" id="vuln-request" value="{{p_request}}" placeholder="Request" ng-model="$parent.request"></textarea>
108 </div>
109 </div><!-- .form-group -->
110 <div class="form-group">
111 <div class="col-md-12 has-error">
112 <label class="sr-only control-label" for="vuln-response">Response</label>
113 <textarea class="form-control input-error" id="vuln-response" value="{{p_response}}" placeholder="Response" ng-model="$parent.response"></textarea>
114 </div>
115 </div><!-- .form-group -->
116 </div><!-- ng-if -->
117 </div><!-- .form-horizontal -->
118
119 <h4>Impact</h4>
120 <div ng-repeat="(key, value) in impact" class="normal-size" style="cursor: pointer;">
121 <h4><span ng-class="{'label label-default': !value, 'label label-success': value}" ng-click="toggleImpact(key)">{{key}}</span></h4>
122 </div><!-- .normal-size -->
123
124 <div ng-if="vulnc == 1">
125 <h4>Evidence</h4>
126 <form>
127 <div class="alert alert-danger normal-size" role="alert" ng-if="file_name_error">
128 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
129 <span class="sr-only">Error:</span>
130 Cannot upload evidence starting with underscore, please choose a different name for the file.
131 </div>
132 <div class="form-group normal-size">
133 <input type="file" id="evidence" ng-file-select ng-multiple="true" resetOnClick="false" ng-file-change="selectedFiles($files, $event)"/>
134 <p class="help-block">Multiple files are allowed.</p>
135 </div><!-- .form-group -->
136 </form>
137 <div id="evidenceFiles" class="normal-size" ng-if="evidence">
138 <ul>
139 <li ng-repeat="e in evidence | orderObjectBy:'name':true | orderBy:'name'">
140 <div class="btn-group">
141 <button type="button" class="btn btn-default">
142 <span class="fa {{icons[e.name]}}" title="Evidence {{e.name}}"></span> {{e.name}}
143 </button><!-- ng-repeat -->
144 <button type="button" class="btn btn-danger" ng-click="removeEvidence(e.name)">
145 <span class="glyphicon glyphicon-trash"></span>
146 </button>
147 </div>
148 </li>
149 </ul>
150 </div><!-- #evidenceFiles -->
151 </div><!-- ng-if -->
152
153 <h5><small>
154 Vulnerabilities to update
155 </small></h5>
156 <table class="csv-export status-report">
157 <thead>
158 <tr>
159 <th><a href="" ng-click="sortField = 'date'; reverse = !reverse">Date</a></th>
160 <th><a href="" ng-click="sortField = 'web'; reverse = !reverse">Web</a></th>
161 <th><a href="" ng-click="sortField = 'status'; reverse = !reverse">Status</a></th>
162 <th><a href="" ng-click="sortField = 'severity'; reverse = !reverse">Severity</a></th>
163 <th><a href="" ng-click="sortField = 'name'; reverse = !reverse">Name</a></th>
164 <th><a href="" ng-click="sortField = 'target'; reverse = !reverse">Target</a></th>
165 <th><a href="" ng-click="sortField = 'desc'; reverse = !reverse">Desc</a></th>
166 <th><a href="">Copy</a></th>
167 </tr>
168 </thead>
169 <tbody>
170 <tr ng-repeat="v in vulns | filter:isChecked | orderBy:sortField:reverse">
171 <td>{{v.date | date:'MM/dd/yyyy'}}</td>
172 <td>
173 <span class="glyphicon glyphicon-ok" ng-show="v.web"></span>
174 <span class="glyphicon glyphicon-remove" ng-show="!v.web"></span>
175 </td>
176 <td>Vulnerable</td>
177 <td>{{v.severity}}</td>
178 <td>{{v.name}}</td>
179 <td>{{v.target}}</td>
180 <td text-collapse text-collapse-max-length="50" text-collapse-text="{{v.desc}}"></td>
181 <td><i class="fa fa-copy copy-icon fa-lg" ng-click="pickVuln(v);call();"></i></td>
182 </tr>
183 </tbody>
184 </table><!-- #hosts -->
185 </div><!-- .modal-body -->
186 <div class="modal-footer">
187 <div class="modal-button">
188 <button class="btn btn-success" ng-click="ok()" ng-disabled="formEdit.$invalid">OK</button>
189 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
190 </div>
191 </div>
192 </form>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-success" ng-click="ok()" ng-disabled="form.$invalid">OK</button>
8 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
9 </div>
10 <h3 class="modal-title">Vulnerability creation</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <input type="text" ng-model="search_notes" class="form-control input-sm" placeholder="Search" ng-change="currentPage = 0">
17 <accordion close-others="true">
18 <accordion-group is-open="isopen" ng-repeat="host in hosts_with_services | filter:search_notes | startFrom:currentPage*pageSize | limitTo:pageSize">
19 <accordion-heading>
20 <a ng-click="selected(host,null)" ng-class="{'multi-selected': host.selected == true}">{{host.name}} ({{host.hostnames[0]}})</a>
21 <i class="pull-right glyphicon"
22 ng-class="{'glyphicon glyphicon-minus-sign': isopen, 'glyphicon glyphicon-plus-sign': !isopen}"></i>
23 </accordion-heading>
24 <div class="panel-body" ng-repeat="service in host.services">
25 <a ng-model="service" ng-click="selected(host,service)" ng-class="{'multi-selected': service.selected == true}">{{service.name}}</a>
26 </div>
27 </accordion-group>
28 </accordion>
29 <div class="showPagination" ng-show="showPagination">
30 <div class="form-group">
31 <ul class="pagination">
32 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
33 <li><a>{{currentPage}}/{{numberOfPages()+1}}</a></li>
34 <li><a ng-hide="currentPage >= numberOfPages()+1" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
35 </ul>
36 <form name="goToPage">
37 <div class="col-md-2">
38 <input type="number" min="0" max="{{numberOfPages()+1}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
39 </div>
40 <button class="btn btn-danger" ng-click="go()">GO</button>
41 </form>
42 </div>
43 </div>
44 </div>
45 </div>
46 </div>
47
48 <div class="alert alert-danger target_not_selected" role="alert" ng-hide="not_target_selected">
49 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
50 <span class="sr-only">Error:</span>
51 There is no target selected
52 </div>
53 <div class="alert alert-danger target_not_selected" role="alert" ng-show="incompatible_vulnWeb">
54 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
55 <span class="sr-only">Error:</span>
56 Vulnerability incompatible with host target, please select a service
57 </div>
58
59 <div class="form-horizontal">
60 <div class="form-group">
61 <div class="col-md-4">
62 <h5>Type</h5>
63 <select class="form-control" ng-model="vuln_type" ng-options="option.value as option.name for option in typeOptions">
64 </select>
65 </div>
66 <div class="col-md-4">
67 <h5>Severity</h5>
68 <select class="form-control" ng-model="severitySelection" ng-options="s as s for s in severities" required>
69 </select>
70 </div>
71 <div class="col-md-4">
72 <h5>Ease of Resolution</h5>
73 <select class="form-control" ng-model="easeOfResolutionSelection" ng-options="e as e for e in easeofresolution">
74 <option value=""></option>
75 </select>
76 </div>
77 </div><!-- .form-group -->
78 <div class="form-group">
79 <div class="col-md-12">
80 <label class="sr-only" for="vuln-name">Vuln name</label>
81 <input type="text" class="form-control" id="vuln-name" placeholder="Name" ng-model="name" required/>
82 </div>
83 </div><!-- .form-group -->
84 <div class="form-group">
85 <div class="col-md-12">
86 <label class="sr-only" for="vuln-desc">Vuln description</label>
87 <textarea class="form-control" id="vuln-desc" placeholder="Description" ng-model="desc" required></textarea>
88 </div>
89 </div><!-- .form-group -->
90 <div class="form-group">
91 <div class="col-md-12">
92 <label class="sr-only" for="vuln-data">Vuln data</label>
93 <textarea class="form-control" id="vuln-data" placeholder="Data" ng-model="data"></textarea>
94 </div>
95 </div><!-- .form-group -->
96 <div class="form-group">
97 <div class="col-md-12 reference" ng-repeat="reference in refs">
98 <div class="input-group margin-bottom-sm">
99 <label class="sr-only" for="vuln-refs">References</label>
100 <input type="text" class="form-control" id="vuln-refs" placeholder="Reference" ng-model="reference.key"/>
101 <span class="input-group-addon" ng-click="newReference($event)"><i class="fa fa-plus-circle"></i></span>
102 <span class="input-group-addon" ng-click="refs.splice($index, 1)" ng-hide="refs.length == 1"><i class="fa fa-minus-circle"></i></span>
103 </div>
104 </div>
105 </div><!-- .form-group -->
106 <div class="form-group">
107 <div class="col-md-12">
108 <label class="sr-only" for="vuln-resolution">Vuln Resolution</label>
109 <textarea class="form-control" id="vuln-resolution" placeholder="Resolution" ng-model="resolution"></textarea>
110 </div>
111 </div><!-- .form-group -->
112 </div>
113
114 <div class="animate-switch-container" ng-switch on="vuln_type">
115 <div class="animate-switch" ng-switch-when="VulnerabilityWeb">
116 <div class="form-horizontal">
117 <div class="form-group">
118 <div class="col-md-4">
119 <label class="sr-only control-label" for="vuln-method">Method</label>
120 <input type="text" class="form-control" id="vuln-method" placeholder="Method" ng-model="$parent.method"/>
121 </div>
122 <div class="col-md-3">
123 <label class="sr-only control-label" for="vuln-pname">Param Name</label>
124 <input type="text" class="form-control" id="vuln-pname" placeholder="Param name" ng-model="$parent.pname"/>
125 </div>
126 <div class="col-md-5">
127 <label class="sr-only control-label" for="vuln-params">Params</label>
128 <input type="text" class="form-control" id="vuln-params" placeholder="Params" ng-model="$parent.params"/>
129 </div>
130 </div><!-- .form-group -->
131 <div class="form-group">
132 <div class="col-md-4">
133 <label class="sr-only control-label" for="vuln-path">Path</label>
134 <input type="text" class="form-control" id="vuln-path" placeholder="Path" ng-model="$parent.path"/>
135 </div>
136 <div class="col-md-4">
137 <label class="sr-only control-label" for="vuln-query">Query</label>
138 <input type="text" class="form-control" id="vuln-query" placeholder="Query" ng-model="$parent.query"/>
139 </div>
140 <div class="col-md-4">
141 <label class="sr-only control-label" for="vuln-website">Website</label>
142 <input type="text" class="form-control" id="vuln-website" placeholder="Website" ng-model="$parent.website"/>
143 </div>
144 </div><!-- .form-group -->
145 <div class="form-group">
146 <div class="col-md-12">
147 <label class="sr-only control-label" for="vuln-request">Request</label>
148 <textarea class="form-control" id="vuln-request" placeholder="Request" ng-model="$parent.request"></textarea>
149 </div>
150 </div><!-- .form-group -->
151 <div class="form-group">
152 <div class="col-md-12">
153 <label class="sr-only control-label" for="vuln-response">Response</label>
154 <textarea class="form-control" id="vuln-response" placeholder="Response" ng-model="$parent.response"></textarea>
155 </div>
156 </div><!-- .form-group -->
157 </div><!-- .form-horizontal -->
158 </div><!-- .animate-switch -->
159 <div class="animate-switch" ng-switch-when="Vulnerability"></div>
160 </div><!-- .animate-switch-container -->
161
162 <h4>Impact</h4>
163 <div ng-repeat="(key, value) in impact" class="normal-size" style="cursor: pointer;">
164 <h4><span ng-class="{'label label-default': !value, 'label label-success': value}" ng-click="toggleImpact(key)">{{key}}</span></h4>
165 </div><!-- .normal-size -->
166
167 <h4>Evidence</h4>
168 <form>
169 <div class="alert alert-danger normal-size" role="alert" ng-if="file_name_error">
170 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
171 <span class="sr-only">Error:</span>
172 Cannot upload evidence starting with underscore, please choose a different name for the file.
173 </div>
174 <div class="form-group normal-size">
175 <input type="file" id="evidence" ng-file-select ng-multiple="true" resetOnClick="false" ng-file-change="selectedFiles($files, $event)"/>
176 <p class="help-block">Multiple files are allowed.</p>
177 </div><!-- .form-group -->
178 </form>
179 <div id="evidenceFiles" class="normal-size" ng-if="evidence">
180 <ul>
181 <li ng-repeat="e in evidence | orderObjectBy:'name':true | orderBy:'name'">
182 <div class="btn-group">
183 <button type="button" class="btn btn-default">
184 <span class="fa {{icons[e.name]}}" title="Evidence {{e.name}}"></span> {{e.name}}
185 </button><!-- ng-repeat -->
186 <button type="button" class="btn btn-danger" ng-click="removeEvidence(e.name)">
187 <span class="glyphicon glyphicon-trash"></span>
188 </button>
189 </div>
190 </li>
191 </ul>
192 </div><!-- #evidenceFiles -->
193 </div><!-- .modal-body -->
194 <div class="modal-footer">
195 <div class="modal-button">
196 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="ok()">OK</button>
197 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
198 </div>
199 </div>
200 </form>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <section id="main" class="seccion clearfix">
5 <div class="right-main">
6 <div id="reports-main" class="fila clearfix">
7 <div class="ws-label">
8 <h2><span id="ws-name" class="label label-default"
9 title="Current workspace">Status report for {{ workspace }} ({{vulns.length}} vulns)</span></h2><!-- WS name -->
10 </div><!-- .ws-label -->
11 <div id="ws-control" class="btn-group btn-small-margin">
12 <button file-exporter="toCSV()" type="button" class="btn btn-success" title="Download CSV for current workspace">
13 <span class="glyphicon glyphicon-download"></span>
14 </button>
15 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace" ng-click="location.reload()">
16 <span class="glyphicon glyphicon-refresh"></span>
17 </button>
18 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
19 Change workspace <span class="caret"></span>
20 </button>
21 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu">
22 <li ng-repeat="ws in workspaces"><a href="#/status/ws/{{ws}}" class="ws" >{{ws}}</a></li>
23 </ul><!-- WS navigation -->
24 </div><!-- #ws-control -->
25
26 <div class="button-control col-md-6 col-sm-6 col-xs-12">
27 <button id="delete" type="button" class="btn btn-default" title="Delete selected items" ng-click="delete()">
28 <span class="glyphicon glyphicon-trash"></span>
29 Delete
30 </button>
31 <button id="merge" type="button" class="btn btn-default" title="Edit selected vulns" ng-click="edit()">
32 <span class="glyphicon glyphicon-pencil"></span>
33 Edit
34 </button>
35 <button id="new" type="button" class="btn btn-success" title="New Vulns" ng-click="new()">
36 <span class="glyphicon glyphicon-plus-sign"></span>
37 New
38 </button>
39 </div><!-- .col-md-6 .col-sm-6 .col-xs-12 -->
40 <div class="reports">
41 <div class="row">
42 <div class="col-md-6 col-sm-3 col-xs-11">
43 <form role="form" ng-submit="searchFor(true, searchParams)">
44 <div class="form-group">
45 <div class="input-group input-group-sm">
46 <span class="input-group-addon glyphicon-btn glyphicon glyphicon-remove" ng-click="searchFor(false, '')" ng-if="search"></span>
47 <input type="text" class="form-control" id="filter-by"
48 placeholder="enter keywords" ng-change="currentPage = 0" ng-model="searchParams" />
49 <span class="input-group-addon glyphicon-btn glyphicon glyphicon-search" ng-click="searchFor(true, searchParams)"></span>
50 </div>
51 </div>
52 </form>
53 </div>
54 <div class="col-md-12 col-sm-9 col-xs-12">
55 <h4><span class="label label-default" title="Add columns">Add columns</span></h4>
56 <ul class="label-list">
57 <li ng-repeat="(column, show) in columns">
58 <a href="" ng-click="toggleShow(column, show)" ng-show="!show">
59 <span class="label label-primary ws-name">{{column}}</span>
60 </a>
61 </li><!-- label-list -->
62 </ul>
63 </div>
64 </div>
65
66 <table class="csv-export status-report table table-responsive">
67 <thead>
68 <tr>
69 <th><input type="checkbox" ng-model="selectall" ng-click="checkAll()"/></th>
70 <th ng-if="columns.date">
71 <a href="" ng-click="toggleSort('date')">Date</a>
72 <a href="" ng-click="toggleShow('date', true)"><span class="glyphicon glyphicon-remove"></span></a>
73 </th>
74 <th ng-if="columns.target">
75 <a href="" ng-click="toggleSort('target')">Target</a>
76 <a href="" ng-click="toggleShow('target', true)"><span class="glyphicon glyphicon-remove"></span></a>
77 </th>
78 <th ng-if="columns.status">
79 <a href="" ng-click="toggleSort('status')">Status</a>
80 <a href="" ng-click="toggleShow('status', true)"><span class="glyphicon glyphicon-remove"></span></a>
81 </th>
82 <th ng-if="columns.severity">
83 <a href="" ng-click="toggleSort('severity')">Severity</a>
84 <a href="" ng-click="toggleShow('severity', true)"><span class="glyphicon glyphicon-remove"></span></a>
85 </th>
86 <th ng-if="columns.name">
87 <a href="" ng-click="toggleSort('name')">Name</a>
88 <a href="" ng-click="toggleShow('name', true)"><span class="glyphicon glyphicon-remove"></span></a>
89 </th>
90 <th ng-if="columns.desc">
91 <a href="" ng-click="toggleSort('desc')">Desc</a>
92 <a href="" ng-click="toggleShow('desc', true)"><span class="glyphicon glyphicon-remove"></span></a>
93 </th>
94 <th ng-if="columns.data">
95 <a href="" ng-click="toggleSort('data')">Data</a>
96 <a href="" ng-click="toggleShow('data', true)"><span class="glyphicon glyphicon-remove"></span></a>
97 </th>
98 <th ng-if="columns.method">
99 <a href="" ng-click="toggleSort('method')">Method</a>
100 <a href="" ng-click="toggleShow('method', true)"><span class="glyphicon glyphicon-remove"></span></a>
101 </th>
102 <th ng-if="columns.path">
103 <a href="" ng-click="toggleSort('path')">Path</a>
104 <a href="" ng-click="toggleShow('path', true)"><span class="glyphicon glyphicon-remove"></span></a>
105 </th>
106 <th ng-if="columns.pname">
107 <a href="" ng-click="toggleSort('pname')">Param Name</a>
108 <a href="" ng-click="toggleShow('pname', true)"><span class="glyphicon glyphicon-remove"></span></a>
109 </th>
110 <th ng-if="columns.params">
111 <a href="" ng-click="toggleSort('params')">Params</a>
112 <a href="" ng-click="toggleShow('params', true)"><span class="glyphicon glyphicon-remove"></span></a>
113 </th>
114 <th ng-if="columns.query">
115 <a href="" ng-click="toggleSort('query')">Query</a>
116 <a href="" ng-click="toggleShow('query', true)"><span class="glyphicon glyphicon-remove"></span></a>
117 </th>
118 <th ng-if="columns.request">
119 <a href="" ng-click="toggleSort('request')">Request</a>
120 <a href="" ng-click="toggleShow('request', true)"><span class="glyphicon glyphicon-remove"></span></a>
121 </th>
122 <th ng-if="columns.response">
123 <a href="" ng-click="toggleSort('response')">Response</a>
124 <a href="" ng-click="toggleShow('response', true)"><span class="glyphicon glyphicon-remove"></span></a>
125 </th>
126 <th ng-if="columns.resolution">
127 <a href="" ng-click="toggleSort('resolution')">Resolution</a>
128 <a href="" ng-click="toggleShow('resolution', true)"><span class="glyphicon glyphicon-remove"></span></a>
129 </th>
130 <th ng-if="columns.web">
131 <a href="" ng-click="toggleSort('web')">Web</a>
132 <a href="" ng-click="toggleShow('web', true)"><span class="glyphicon glyphicon-remove"></span></a>
133 </th>
134 <th ng-if="columns.website">
135 <a href="" ng-click="toggleSort('website')">Website</a>
136 <a href="" ng-click="toggleShow('website', true)"><span class="glyphicon glyphicon-remove"></span></a>
137 </th>
138 <th ng-if="columns.refs">
139 <a href="" ng-click="toggleSort('refs')">References</a>
140 <a href="" ng-click="toggleShow('refs', true)"><span class="glyphicon glyphicon-remove"></span></a>
141 </th>
142 <th ng-if="columns.evidence">
143 <a href="" ng-click="toggleSort('attachments')">Evidence</a>
144 <a href="" ng-click="toggleShow('evidence', true)"><span class="glyphicon glyphicon-remove"></span></a>
145 </th>
146 <th ng-if="columns.impact">
147 <a href="" ng-click="toggleSort('impact')">Impact</a>
148 <a href="" ng-click="toggleShow('impact', true)"><span class="glyphicon glyphicon-remove"></span></a>
149 </th>
150 <th ng-if="columns.easeofresolution">
151 <a href="" ng-click="toggleSort('easeofresolution')">Ease of Resolution</a>
152 <a href="" ng-click="toggleShow('easeofresolution', true)"><span class="glyphicon glyphicon-remove"></span></a>
153 </th>
154 <th ng-if="columns.hostnames">
155 <a href="" ng-click="toggleSort('hostnames')">Hostnames</a>
156 <a href="" ng-click="toggleShow('hostnames', true)"><span class="glyphicon glyphicon-remove"></span></a>
157 </th>
158 </tr>
159 </thead>
160 <tbody>
161 <tr ng-repeat="v in vulns | orderObjectBy:sortField:reverse | startFrom:currentPage*pageSize | limitTo:pageSize"
162 selection-model selection-model-type="checkbox"
163 selection-model-mode="multiple-additive"
164 selection-model-selected-class="multi-selected">
165 <td><input type="checkbox" name="{{v.id}}"/></td>
166 <td ng-if="columns.date">{{v.date | date:'MM/dd/yyyy'}}</td>
167 <td ng-if="columns.target">{{v.target}}</td>
168 <td ng-if="columns.status">Vulnerable</td>
169 <td ng-if="columns.severity"><span class="label vuln fondo-{{v.severity}}">{{v.severity}}</span></td>
170 <td ng-if="columns.name">{{v.name}}</td>
171 <td ng-if="columns.desc" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.desc}}"></td>
172 <td ng-if="columns.data" text-collapse text-collapse-max-length="150" text-collapse-text="{{v.data}}"></td>
173 <td ng-if="columns.method">{{v.method}}</td>
174 <td ng-if="columns.path">{{v.path}}</td>
175 <td ng-if="columns.pname">{{v.pname}}</td>
176 <td ng-if="columns.params">{{v.params}}</td>
177 <td ng-if="columns.query">{{v.query}}</td>
178 <td ng-if="columns.request" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.request}}"></td>
179 <td ng-if="columns.response" text-collapse text-collapse-max-length="100" text-collapse-text="{{v.response}}"></td>
180 <td ng-if="columns.resolution">{{v.resolution}}</td>
181 <td ng-if="columns.web">
182 <span class="glyphicon glyphicon-ok" ng-show="v.web"></span>
183 <span class="glyphicon glyphicon-remove" ng-show="!v.web"></span>
184 </td>
185 <td ng-if="columns.website">{{v.website}}</td>
186 <td ng-if="columns.refs"><p ng-repeat="refs in v.refs">{{refs}}</p></td>
187 <td ng-if="columns.evidence">
188 <div ng-repeat="e in v.attachments track by $index">
189 <a href="{{baseurl + workspace}}/{{ v.id}}/{{e}}" target="_blank">{{e}}</a>
190 </div>
191 </td>
192 <td ng-if="columns.impact">
193 <div ng-repeat="(impact, rating) in v.impact">
194 <p ng-if="rating">{{impact}}</p>
195 </div>
196 </td>
197 <td ng-if="columns.easeofresolution">{{v.easeofresolution}}</td>
198 <td ng-if="columns.hostnames"><p ng-repeat="hostname in v.hostnames">{{hostname}}</p></td>
199 </tr>
200 </tbody>
201 </table><!-- #hosts -->
202 <div class="showPagination" ng-show="showPagination">
203 <div class="form-group">
204 <ul class="pagination">
205 <li><a ng-hide="currentPage == 0" ng-click="currentPage=currentPage-1"><span aria-hidden="true">&laquo;</span><span class="sr-only">Previous</span></a></li>
206 <li><a>{{currentPage}}/{{numberOfPages()}}</a></li>
207 <li><a ng-hide="currentPage >= numberOfPages()" ng-click="currentPage=currentPage+1"><span aria-hidden="true">&raquo;</span><span class="sr-only">Next</span></a></li>
208 </ul>
209 <form name="goToPage" id="goToPageStatus">
210 <div class="col-md-2">
211 <input type="number" min="0" max="{{numberOfPages()}}" class="form-control" ng-model="go_page" placeholder="Go to page"/>
212 </div>
213 <button class="btn btn-default" ng-click="go()">GO</button>
214 <input type="number" min="0" class="form-control vuln_per_page" ng-model="pagination" placeholder="Numbre page" />
215 </form>
216 </div>
217 </div>
218 </div><!-- .reports -->
219 </div><!-- #reports-main --></div><!-- .right-main -->
220 </section><!-- #main -->
22 // See the file 'doc/LICENSE' for the license information
33
44 angular.module('faradayApp')
5 .factory('statusReportFact', ['vulnsFact', 'vulnsWebFact', 'hostsFact', 'workspacesFact', function(vulnsFact, vulnsWebFact, hostsFact, workspacesFact) {
5 .factory('statusReportFact', ['vulnsFact', 'vulnsWebFact', 'hostsManager', 'workspacesFact', function(vulnsFact, vulnsWebFact, hostsManager, workspacesFact) {
66 var statusReportFact = {};
77
88 statusReportFact.getVulns = function(ws) {
99 var vulns = vulnsFact.get(ws);
1010 var vulnsWeb = vulnsWebFact.get(ws);
11 var hosts = hostsFact.get(ws);
11 var hosts = hostsManager.get(ws);
1212 vulns.forEach(function(element, index, array) {
1313 if (element.parent in hosts) {
1414 element.target = hosts[element.parent].name;
1111
1212 $scope.onSuccessGet = function(workspace){
1313 if(workspace.sdate.toString().indexOf(".") != -1) workspace.sdate = workspace.sdate * 1000;
14 workspace.selected = false;
1415 $scope.workspaces.push(workspace);
1516 };
1617
2223
2324 $scope.onFailInsert = function(error){
2425 var modal = $modal.open({
25 templateUrl: 'scripts/partials/modal-ko.html',
26 controller: 'modalKoCtrl',
26 templateUrl: 'scripts/commons/partials/modalKO.html',
27 controller: 'commonsModalKoCtrl',
2728 resolve: {
2829 msg: function() {
2930 return error;
3536 $scope.onSuccessEdit = function(workspace){
3637 for(var i = 0; i < $scope.workspaces.length; i++) {
3738 if($scope.workspaces[i].name == workspace.name){
39 $scope.workspaces[i]._rev = workspace._rev;
3840 $scope.workspaces[i].description = workspace.description;
41 $scope.workspaces[i].duration.start = workspace.duration.start;
42 $scope.workspaces[i].duration.end = workspace.duration.end;
3943 break;
4044 }
4145 };
9296 case "dashboard":
9397 $scope.hash = "dashboard";
9498 break;
99 case "hosts":
100 $scope.hash = "hosts";
101 break;
95102 default:
96103 $scope.hash = "";
97104 }
106113 };
107114
108115 $scope.update = function(workspace){
116 if(typeof(workspace.duration.startDate) == "number") {
117 start = workspace.duration.startDate;
118 } else if(workspace.duration.startDate) {
119 start = workspace.duration.startDate.getTime();
120 } else {start = "";}
121 if(typeof(workspace.duration.endDate) == "number") {
122 end = workspace.duration.endDate;
123 } else if(workspace.duration.endDate) {
124 end = workspace.duration.endDate.getTime();
125 } else {end = "";}
126 duration = {'start': start, 'end': end};
127 workspace = {
128 "_id": workspace._id,
129 "_rev": workspace._rev,
130 "children": workspace.children,
131 "customer": workspace.customer,
132 "description": workspace.description,
133 "duration": duration,
134 "name": workspace.name,
135 "scope": workspace.scope,
136 "sdate": workspace.sdate,
137 "selected": workspace.selected,
138 "type": workspace.type
139 };
109140 workspacesFact.update(workspace, $scope.onSuccessEdit);
110141 };
111142
115146
116147 // Modals methods
117148 $scope.new = function(){
149 $scope.newworkspace = {};
118150
119151 $scope.modal = $modal.open({
120 templateUrl: 'scripts/workspaces/partials/modal-new.html',
152 templateUrl: 'scripts/workspaces/partials/modalNew.html',
121153 controller: 'workspacesCtrl',
122154 scope: $scope,
123155 size: 'lg'
124156 });
125157
126158 $scope.modal.result.then(function(workspace) {
127 workspace = $scope.create(workspace.name, workspace.description);
159 workspace = $scope.create(workspace.name, workspace.description, workspace.start, workspace.end, workspace.scope);
128160 $scope.insert(workspace);
129161 });
130162
147179 $scope.workspaces.forEach(function(w){
148180 if(w.selected){
149181 $scope.newworkspace = w;
182 if($scope.newworkspace.duration){
183 $scope.newworkspace.duration.startDate = w.duration.start;
184 $scope.newworkspace.duration.endDate = w.duration.end;
185 }
150186 }
151187 });
152188 $scope.modal = $modal.open({
153 templateUrl: 'scripts/workspaces/partials/modal-edit.html',
189 templateUrl: 'scripts/workspaces/partials/modalEdit.html',
154190 controller: 'workspacesCtrl',
155191 scope: $scope,
156192 size: 'lg'
161197 });
162198 } else {
163199 var modal = $modal.open({
164 templateUrl: 'scripts/partials/modal-ko.html',
165 controller: 'modalKoCtrl',
200 templateUrl: 'scripts/commons/partials/modalKO.html',
201 controller: 'commonsModalKoCtrl',
166202 resolve: {
167203 msg: function() {
168204 return 'No workspaces were selected to edit';
173209
174210 };
175211
176 $scope.okEdit = function(){
212 $scope.okEdit = function() {
177213 $scope.modal.close($scope.newworkspace);
178214 };
179215
180216
181 $scope.cancel = function(){
217 $scope.cancel = function() {
182218 $scope.modal.close();
183219 };
184220
185 $scope.delete = function(){
221 $scope.delete = function() {
186222 var selected = false;
187223
188224 $scope.workspaces.forEach(function(w) {
192228 }
193229 });
194230
195 if(selected){
231 if(selected) {
196232 $scope.modal = $modal.open({
197 templateUrl: 'scripts/workspaces/partials/modal-delete.html',
198 controller: 'workspacesCtrl',
199 scope: $scope,
200 size: 'lg'
233 templateUrl: 'scripts/commons/partials/modalDelete.html',
234 controller: 'commonsModalDelete',
235 size: 'lg',
236 resolve: {
237 msg: function() {
238 var msg = "A workspace will be deleted. This action cannot be undone. Are you sure you want to proceed?";
239 return msg;
240 }
241 }
201242 });
202243
203244 $scope.modal.result.then(function() {
204 $scope.workspaces.forEach(function(w){
245 $scope.workspaces.forEach(function(w) {
205246 if(w.selected == true)
206247 $scope.remove(w.name);
207248 });
208249 });
209250 } else {
210251 var modal = $modal.open({
211 templateUrl: 'scripts/partials/modal-ko.html',
212 controller: 'modalKoCtrl',
252 templateUrl: 'scripts/commons/partials/modalKO.html',
253 controller: 'commonsModalKoCtrl',
213254 resolve: {
214255 msg: function() {
215256 return 'No workspaces were selected to delete';
224265 };
225266 // end of modal context
226267
227 $scope.create = function(wname, wdesc){
268 $scope.create = function(wname, wdesc, start, end, scope){
269 if(end) end = end.getTime(); else end = "";
270 if(start) start = start.getTime(); else start = "";
228271 workspace = {
229272 "_id": wname,
230273 "_rev": "2-bd88abf79cf2b7e8b419cd4387c64bef",
235278 "type": "Workspace",
236279 "children": [
237280 ],
281 "duration": {"start": start, "end": end},
282 "scope": scope,
238283 "description": wdesc
239284 };
240285 return(workspace);
241286
242287 };
288
289 //DATE PICKER
290 $scope.today = function() {
291 $scope.dt = new Date();
292 };
293 $scope.today();
294
295 $scope.clear = function () {
296 $scope.dt = null;
297 };
298
299 $scope.minDate = new Date();
300
301 $scope.open = function($event, isStart) {
302 $event.preventDefault();
303 $event.stopPropagation();
304
305 if(isStart) $scope.openedStart = true; else $scope.openedEnd = true;
306 };
307
308 $scope.dateOptions = {
309 formatYear: 'yy',
310 startingDay: 1
311 };
312
243313 }]);
2525 <thead>
2626 <tr>
2727 <th>Name</th>
28 <th>Start Date</th>
29 <th>End Date</th>
2830 <th>Vulns</th>
2931 <th>Hosts</th>
3032 <th>Services</th>
3436 <tr ng-repeat="ws in workspaces | filter:query | orderBy:sortField:reverse"
3537 selection-model selection-model-selected-class="multi-selected">
3638 <td><span ng-class-even="'label label-unclassified'" ng-class-odd="'label label-high'">{{ws.name}}</span></td>
39 <td ng-bind="ws.duration.start || '-' | date:'MM/dd/yyyy'"></td>
40 <td ng-bind="ws.duration.end || '-' | date:'MM/dd/yyyy'"></td>
3741 <td ng-bind="objects[ws.name]['total vulns']"></td>
3842 <td ng-bind="objects[ws.name]['hosts']"></td>
3943 <td ng-bind="objects[ws.name]['services']"></td>
+0
-14
views/reports/_attachments/scripts/workspaces/partials/modal-delete.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Workspace Delete</h3>
6 </div>
7 <div class="modal-body">
8 <h5>You are about to remove {{workspacesCtrl.selectedItems.length}} Workspaces. Proceed?</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
12 <button class="btn btn-success" ng-click="okDelete()">OK</button>
13 </div>
+0
-23
views/reports/_attachments/scripts/workspaces/partials/modal-edit.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <div class="modal-button">
6 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
7 <button class="btn btn-success" ng-click="okEdit()">OK</button>
8 </div>
9 <h3 class="modal-title">Edit Workspace: {{newworkspace.name}}</h3>
10 </div>
11 <div class="modal-body">
12 <div class="form-horizontal">
13 <div class="form-group">
14 <div class="col-md-12">
15 <label class="sr-only" for="vuln-desc">Workspace Description</label>
16 <textarea class="form-control" id="vuln-desc"
17 placeholder="Description" value={{newworkspace.description}}
18 ng-model="newworkspace.description" required></textarea>
19 </div>
20 </div><!-- .form-group -->
21 </div><!-- .form-horizontal -->
22 </div><!-- .modal-body -->
+0
-40
views/reports/_attachments/scripts/workspaces/partials/modal-new.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <form name="form" novalidate>
5 <div class="modal-header">
6 <div class="modal-button">
7 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
8 <button class="btn btn-success" ng-disabled="form.$invalid" ng-click="okNew()">Save</button>
9 </div>
10 <h3 class="modal-title">New Workspace</h3>
11 </div>
12 <div class="modal-body">
13 <div class="form-horizontal">
14 <div class="form-group">
15 <div class="col-md-12">
16 <label class="sr-only" for="wsp-name">Workspace Name</label>
17 <input type="text" class="form-control"
18 ng-pattern=/^[a-z][a-z0-9\_\$\(\)\+\-\/]*$/ id="vuln-name" placeholder="Workspace Name"
19 ng-model="newworkspace.name" required/>
20 <div ng-if="form.$invalid">
21 <div class="alert alert-danger target_not_selected" role="alert" ng-hide="">
22 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
23 <span class="sr-only">Error:</span>
24 <button type="button" class="close" data-dismiss="alert"><span area-hidden="true">&times;</span><span class="sr-only">Close</span></button>
25 Workspace name should follow pattern [a-z][a-z0-9_$()+-/]*
26 </div>
27 </div>
28 </div>
29 <div class="col-md-12">
30 <label class="sr-only" for="vuln-desc">Workspace Description</label>
31 <textarea class="form-control" id="vuln-desc"
32 placeholder="Description" ng-model="newworkspace.description"
33 >
34 </textarea>
35 </div>
36 </div><!-- .form-group -->
37 </div><!-- .form-horizontal -->
38 </div><!-- .modal-body -->
39 </form>
+0
-13
views/reports/_attachments/scripts/workspaces/partials/modal-ok.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Oops!</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{ msg }}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-success" ng-click="ok()">OK</button>
12 </div>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Workspace Delete</h3>
6 </div>
7 <div class="modal-body">
8 <h5>You are about to remove {{workspacesCtrl.selectedItems.length}} Workspaces. Proceed?</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
12 <button class="btn btn-success" ng-click="okDelete()">OK</button>
13 </div>
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <div class="modal-button">
6 <button class="btn btn-success" ng-click="okEdit()">OK</button>
7 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
8 </div>
9 <h3 class="modal-title">Edit Workspace: {{newworkspace.name}}</h3>
10 </div>
11 <div class="modal-body">
12 <div class="form-horizontal">
13 <div class="form-group">
14 <div class="col-md-12">
15 <label class="sr-only" for="vuln-desc">Workspace Description</label>
16 <textarea class="form-control" id="vuln-desc"
17 placeholder="Description" value={{newworkspace.description}}
18 ng-model="newworkspace.description" required></textarea>
19 </div>
20 </div><!-- .form-group -->
21 <div class="form-group">
22 <div class="col-md-6">
23 <h5>Start Date</h5>
24 <label class="sr-only" for="work-start">Start Date</label>
25 <p class="input-group">
26 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="newworkspace.duration.startDate" is-open="openedStart" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="Start Date"/>
27 <span class="input-group-btn">
28 <button type="button" class="btn btn-default" ng-click="open($event,true)"><i class="glyphicon glyphicon-calendar"></i></button>
29 </span>
30 </p>
31 </div>
32 <div class="col-md-6">
33 <h5>End Date</h5>
34 <label class="sr-only" for="work-end">End Date</label>
35 <p class="input-group">
36 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="newworkspace.duration.endDate" is-open="openedEnd" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="End Date"/>
37 <span class="input-group-btn">
38 <button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
39 </span>
40 </p>
41 </div>
42
43 </div><!-- .form-group -->
44 <div class="form-group">
45 <div class="col-md-12">
46 <label class="sr-only" for="work-scope">Scope</label>
47 <textarea class="form-control" id="work-scope"
48 placeholder="Scope" ng-model="newworkspace.scope"
49 >
50 </textarea>
51 </div>
52 </div><!-- .form-group -->
53 </div><!-- .form-horizontal -->
54 </div><!-- .modal-body -->
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <div class="modal-button">
6 <button class="btn btn-success" ng-disabled="form.$invalid || date.$invalid" ng-click="okNew()">Save</button>
7 <button class="btn btn-danger" ng-click="cancel()">Cancel</button>
8 </div>
9 <h3 class="modal-title">New Workspace</h3>
10 </div>
11 <div class="modal-body">
12 <div class="form-horizontal">
13 <div class="form-group">
14 <div class="col-md-12">
15 <form name="form" novalidate>
16 <label class="sr-only" for="wsp-name">Workspace Name</label>
17 <input type="text" class="form-control"
18 ng-pattern=/^[a-z][a-z0-9\_\$\(\)\+\-\/]*$/ id="vuln-name" placeholder="Workspace Name"
19 ng-model="newworkspace.name" required/>
20 <div ng-if="form.$invalid">
21 <div class="alert alert-danger target_not_selected" role="alert" ng-hide="">
22 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
23 <span class="sr-only">Error:</span>
24 <button type="button" class="close" data-dismiss="alert"><span area-hidden="true">&times;</span><span class="sr-only">Close</span></button>
25 Workspace name should follow pattern [a-z][a-z0-9_$()+-/]*
26 </div>
27 </div>
28 </form>
29 </div>
30 </div><!-- .form-group -->
31 <div class="form-group">
32 <div class="col-md-12">
33 <label class="sr-only" for="vuln-desc">Workspace Description</label>
34 <textarea class="form-control" id="vuln-desc"
35 placeholder="Description" ng-model="newworkspace.description"
36 >
37 </textarea>
38 </div>
39 </div><!-- .form-group -->
40 <div class="form-group">
41 <form name="date" novalidate>
42 <div class="col-md-6">
43 <label class="sr-only" for="work-start">Start Date</label>
44 <p class="input-group">
45 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="newworkspace.start" is-open="openedStart" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="Start Date" />
46 <span class="input-group-btn">
47 <button type="button" class="btn btn-default" ng-click="open($event,true)"><i class="glyphicon glyphicon-calendar"></i></button>
48 </span>
49 </p>
50 </div>
51 <div class="col-md-6">
52 <label class="sr-only" for="work-end">End Date</label>
53 <p class="input-group">
54 <input type="text" class="form-control" datepicker-popup="MM/dd/yyyy" ng-model="newworkspace.end" is-open="openedEnd" datepicker-options="dateOptions" date-disabled="disabled(date, mode)" close-text="Close" placeholder="End Date" />
55 <span class="input-group-btn">
56 <button type="button" class="btn btn-default" ng-click="open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
57 </span>
58 </p>
59 </div>
60 <div ng-if="date.$invalid" class="col-md-12">
61 <div class="alert alert-danger target_not_selected" role="alert" ng-hide="">
62 <span class="glyphicon glyphicon-exclamation-sign" aria-hidden="true"></span>
63 <span class="sr-only">Error:</span>
64 <button type="button" class="close" data-dismiss="alert"><span area-hidden="true">&times;</span><span class="sr-only">Close</span></button>
65 Invalid Date
66 </div>
67 </div>
68 </form>
69 </div><!-- .form-group -->
70 <div class="form-group">
71 <div class="col-md-12">
72 <label class="sr-only" for="work-scope">Scope</label>
73 <textarea class="form-control" id="work-scope"
74 placeholder="Scope" ng-model="newworkspace.scope"
75 >
76 </textarea>
77 </div>
78 </div><!-- .form-group -->
79 </div><!-- .form-horizontal -->
80 </div><!-- .modal-body -->
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3
4 <div class="modal-header">
5 <h3 class="modal-title">Oops!</h3>
6 </div>
7 <div class="modal-body">
8 <h5>{{ msg }}</h5>
9 </div><!-- .modal-body -->
10 <div class="modal-footer">
11 <button class="btn btn-success" ng-click="ok()">OK</button>
12 </div>
7777 createWorkspaceDoc = function(response, workspace){
7878 $http.put(BASEURL + workspace.name + '/' + workspace.name, workspace).
7979 success(function(data){
80 workspace._rev = response.data.rev;
80 workspace._rev = data.rev;
8181 }).
8282 error(function(data) {
8383 errorHandler;
+0
-286
views/reports/_attachments/treemap.html less more
0 <!-- Faraday Penetration Test IDE -->
1 <!-- Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/) -->
2 <!-- See the file 'doc/LICENSE' for the license information -->
3 <!DOCTYPE html>
4 <!--[if lt IE 7 ]> <html lang="en" class="no-js ie6"> <![endif]-->
5 <!--[if IE 7 ]> <html lang="en" class="no-js ie7"> <![endif]-->
6 <!--[if IE 8 ]> <html lang="en" class="no-js ie8"> <![endif]-->
7 <!--[if IE 9 ]> <html lang="en" class="no-js ie9"> <![endif]-->
8 <!--[if (gt IE 9)|!(IE)]><!-->
9 <html id="no-overflow" lang="en" class="no-js"> <!--<![endif]-->
10 <head>
11 <meta charset="utf-8"/>
12 <!--[if IE]><![endif]-->
13 <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
14 <title>All services | Faraday</title>
15 <meta name="description" content=""/>
16 <meta name="keywords" content=""/>
17 <meta name="author" content=""/>
18
19 <!-- !CSS -->
20 <link rel="stylesheet" type="text/css" href="estilos.css" />
21 <link rel="stylesheet" type="text/css" href="script/jquery.qtip.css" />
22 <link rel="stylesheet" href="././././script/bootstrap.min.css">
23 <link rel="stylesheet" href="././././script/bootstrap-theme.min.css">
24 <link href="favicon.ico" rel="shortcut icon">
25 <link href="favicon.ico" type="image/vnd.microsoft.icon" rel="icon" />
26 <link href="images/site_preview.jpg" rel="image_src" />
27
28 <script src="./././script/d3.min.js"></script>
29 <script type="text/javascript" src="script/couch.js"></script>
30 <script src="script/common.js"></script>
31 <script type="text/javascript" src="/_utils/script/sha1.js"></script>
32 <script type="text/javascript" src="script/app.js"></script>
33 <script type="text/javascript" src="script/d3_summarized.js"></script>
34 <script type="text/javascript" src="script/reports_list.js"></script>
35 <script type="text/javascript" src="/_utils/script/json2.js"></script>
36 <script type="text/javascript" src="/_utils/script/jquery.js"></script>
37 <script type="text/javascript" src="/_utils/script/jquery.couch.js"></script>
38 <script type="text/javascript" src="script/jquery.tablesorter.min.js"></script>
39 <script src="././././script/bootstrap.min.js"></script>
40 <script src="script/jquery.qtip.min.js"></script>
41 </head>
42
43 <body>
44 <div id="cont">
45 <div class="wrapper">
46 <header class="head">
47 <a href="#" class="ws-dashboard"><img class="logo" src="images/logo-faraday.png" alt="Faraday home | WS Dashboard"/></a>
48 <!--
49 <nav>
50 <ul class="menu">
51 <li>
52 <a href="#">Opción 1</a>
53 </li>
54 <li>
55 <a href="#">Opción 1</a>
56 </li>
57 <li>
58 <a href="#" class="activo">Username</a>
59 <ul>
60 <li><a href="#">Sub opción 1</a></li>
61 <li><a href="#">Sub opción 2</a></li>
62 </ul>
63 </li>
64 </ul>
65 </nav>
66 -->
67 </header>
68
69 <section id="main" class="seccion clearfix">
70 <aside>
71 <nav class="left-nav">
72 <ul>
73 <li>
74 <a href="#" class="ws-dashboard" style="color: #ffffff !important" title="WS Dashboard">
75 <img src="images/ico-graph.png" alt="WS Dashboard"/>
76 </a>
77 </li>
78 <li>
79 <a href="#" class="status-report" style="color: #ffffff !important" title="Status Report">
80 <h2><span class="glyphicon glyphicon-list" title="Status Report"></span></h2>
81 </a>
82 </li>
83 </ul>
84 </nav>
85 </aside>
86
87 <div class="right-main">
88 <div id="reports-main" class="fila clearfix">
89 <h2 class="ws-label">
90 <span id="ws-name" class="label label-default" title="Current workspace"></span><!-- WS name -->
91
92 <div id="ws-control" class="btn-group">
93 <button id="refresh" type="button" class="btn btn-danger" title="Refresh current workspace">
94 <span class="glyphicon glyphicon-refresh"></span>
95 </button>
96 <button type="button" class="btn btn-danger dropdown-toggle" data-toggle="dropdown" title="Change current workspace">
97 Change workspace <span class="caret"></span>
98 </button>
99 <ul id="nav" class="dropdown-menu dropdown-menu-right" role="menu"></ul><!-- WS navigation -->
100 </div><!-- #ws-control -->
101 </h2><!-- .ws-label -->
102 </div><!-- #reports-main -->
103 <div class="seccion treemap">
104 <article id="chart">
105 <header>
106 <h2>All Services
107 <span class="glyphicon glyphicon-info-sign faraday-qtips" title="All services ordered by host amount"></span>
108 </h2>
109 </header>
110 </article>
111 </div>
112 </div><!-- .right-main -->
113 </section><!-- #main -->
114 </div><!--!/#wrapper -->
115 </div><!--!/#container -->
116 <script type="text/javascript">
117 $(document).ready(function() {
118 var counter = 0;
119 var h = 0;
120 $.couch.allDbs({
121 success : function(dbs) {
122
123 dbs.filter(function(db_name){
124 return db_name.search(/^_/) < 0 && db_name.search("reports") < 0;})
125 .forEach(function(db) {
126 // WSs list
127 $("#ws-control #nav").append('<li><a href="#'+db+'" class="ws" >'+db+'</a></li>');
128 // navigation between WSs
129
130 counter +=1;
131 });
132 if(counter < 1) {
133 $("#reports-main").empty();
134 $("#reports-main").append("<div id=\"no-workspace\" class=\"workspace\">No Workspaces installed, please start working to get some data</div><!-- .workspace -->");
135 }
136 }
137 });
138 //get workspace
139 dominio = location.href;
140 space = dominio.split("#");
141 workspace = space[1];
142
143 var navegador = navigator.userAgent;
144 if (navigator.userAgent.indexOf('MSIE') !=-1) {
145 $("#main.seccion").height(screen.height);
146 } else if (navigator.userAgent.indexOf('Firefox') !=-1) {
147 $("#main.seccion").height(screen.height - 5);
148 } else if (navigator.userAgent.indexOf('Chromium') !=-1) {
149 $("#main.seccion").height(screen.height);
150 } else if (navigator.userAgent.indexOf('Chrome') !=-1) {
151 $("#main.seccion").height(screen.height);
152 } else if (navigator.userAgent.indexOf('Opera') !=-1) {
153 $("#main.seccion").height(screen.height);
154 }
155
156 // navegacion entre WSs en el dropdown
157 $(document).on("click", "a.ws", function(e) {
158 e.preventDefault();
159 var hash = $(this).attr("href").substr(1);
160 var pathname = window.location.pathname;
161 var params = "?workspace="+ hash + "&design=hosts&view=byservices";
162 window.location.href = pathname + params + "#" + hash;
163 });
164 $(document).on("click", "#refresh", function() {
165 location.reload();
166 });
167 $(document).on("click", "a.ws-dashboard", function(e) {
168 e.preventDefault();
169 var url = "../././reports/index.html#" + workspace;
170 window.location.href = url;
171 });
172 $(document).on("click", "a.status-report", function(e) {
173 e.preventDefault();
174 var url = "../././reports/index.html#/status/ws/" + workspace;
175 window.location.href = url;
176 });
177
178 var ret = "Viewing "+ workspace;
179 $('#reports-main .ws-label span#ws-name').text(ret);
180 });
181 </script>
182 <style>
183 .node {
184 border: solid 1px white;
185 font: 13px Ubuntu;
186 line-height: 12px;
187 overflow: hidden;
188 font-weight: bold;
189 position: absolute;
190 text-indent: 2px;
191 }
192 </style>
193 <script>
194 var margin = {top: 10, left: 10, bottom: 10, right: 10}
195 , width = screen.width / 1.5
196 , mapRatio = .5
197 , height = width * mapRatio;
198
199 var color = d3.scale.category20c();
200
201 var treemap = d3.layout.treemap()
202 .size([width, height])
203 .sticky(true)
204 .value(function(d) {return d.value});
205
206 var div = d3.select("#chart").append("div")
207 .attr("class", "treemap-box")
208 .style("position", "relative")
209 .style("width", (width) + "px")
210 .style("height", (height) + "px")
211 .style("left", margin.left + "px")
212 .style("top", margin.top + "px");
213
214 var workspace = getParameterByName('workspace');
215 var design = getParameterByName('design');
216 var view = getParameterByName('view');
217
218 json_url = "/" + workspace + "/_design/" + design + "/_view/" + view + "?group=true";
219 d3.json(json_url, function(error, root) {
220 var jotason = {};
221 jotason["children"] = root["rows"];
222 var node = div.datum(jotason).selectAll(".node")
223 .data(treemap.nodes)
224 .enter().append("div")
225 .attr("class", "node")
226 .call(position)
227 .style("background", function(d) {return color(Math.floor(Math.random()*68)); })
228 .text(function(d, i) {
229 var total = d3.sum(jotason["children"], function(d){return d.value;});
230 return (d.key+ " ( " + d3.round(100* d.value / total, 1) + "% " + ")" ) ;
231 });
232 });
233
234 //return d.children ? null : d.key;
235 function position() {
236 this.style("left", function(d) { return d.x + "px"; })
237 .style("top", function(d) { return d.y + "px"; })
238 .style("width", function(d) { return Math.max(0, d.dx - 1) + "px"; })
239 .style("height", function(d) { return Math.max(0, d.dy - 1) + "px"; });
240 }
241
242 //function para traer parametros
243 function getParameterByName( name ){
244 name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
245 var regexS = "[\\?&]"+name+"=([^&#]*)";
246 var regex = new RegExp( regexS );
247 var results = regex.exec( window.location.href );
248 if( results == null )
249 return "";
250 else
251 return decodeURIComponent(results[1].replace(/\+/g, " "));
252 }
253
254 $("#chart").width(screen.width / 1.47);
255 $("#chart").height((screen.width /1.45) * (mapRatio + .05));
256
257 $('.treemap').on('mouseenter', '.faraday-qtips', function (event) {
258 $(this).qtip({
259 overwrite: false, // Don't overwrite tooltips already bound
260 show: {
261 event: event.type, // Use the same event type as above
262 ready: true // Show immediately - important!
263 },
264 hide: {
265 fixed: true,
266 delay: 300
267 },
268 content:{
269 text: function(event, api) {
270 var res = "<div id=\"contenido\">"+$(this).attr("title")+"</div>";
271 return res;
272 }
273 },
274 position:{
275 my: 'top center',
276 at: 'bottom center',
277 adjust: {
278 method: 'shift'
279 }
280 }
281 });
282 });
283 </script>
284 </body>
285 </html>
11 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
22 // See the file 'doc/LICENSE' for the license information
33 function(doc) {
4 if(doc.type=="Host"){
5 emit(doc._id, {"name": doc.name, "os": doc.os, "owned": doc.owned});
6 }
4 if(doc.type=="Host"){
5 emit(doc._id, {
6 "_id": doc._id,
7 "_rev": doc._rev,
8 "categories": doc.categories,
9 "default_gateway": doc.default_gateway,
10 "description": doc.description,
11 "metadata": doc.metadata,
12 "name": doc.name,
13 "os": doc.os,
14 "owned": doc.owned,
15 "owner": doc.owner
16 });
17 }
718 }
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3
4 function(doc) {
5 if(doc.type=="Interface"){
6 if(doc.parent != 'null') {
7 var hid = doc._id.substring(0, doc._id.indexOf('.'));
8 emit(hid, doc);
9 }
10 }
11 }
0 cuenta services totales, devuelve idService, nombreService, ports, protocol.
0 // Faraday Penetration Test IDE
1 // Copyright (C) 2013 Infobyte LLC (http://www.infobytesec.com/)
2 // See the file 'doc/LICENSE' for the license information
3 function(doc) {
4 if(doc.type=="Service"){
5 emit(doc._id, {
6 "_id": doc._id,
7 "_rev": doc._rev,
8 "description": doc.description,
9 "metadata": doc.metadata,
10 "name": doc.name,
11 "owned": doc.owned,
12 "owner": doc.owner,
13 "parent": doc.parent,
14 "ports": doc.ports,
15 "protocol": doc.protocol,
16 "status": doc.status,
17 "version": doc.version
18 });
19 }
20 }