Merge lp:~linaro-infrastructure/linaro-ci-dashboard/invoke-builds into lp:linaro-ci-dashboard

Proposed by Stevan Radaković
Status: Merged
Approved by: Stevan Radaković
Approved revision: 20
Merged at revision: 14
Proposed branch: lp:~linaro-infrastructure/linaro-ci-dashboard/invoke-builds
Merge into: lp:linaro-ci-dashboard
Diff against target: 10031 lines (+9660/-79)
19 files modified
dashboard/frontend/fixtures/initial_data.json (+9/-0)
dashboard/frontend/migrations/0001_initial.py (+0/-67)
dashboard/frontend/models/json_response_mixin.py (+43/-0)
dashboard/frontend/models/loop.py (+19/-0)
dashboard/frontend/models/loop_build.py (+4/-0)
dashboard/frontend/templates/header.html (+2/-1)
dashboard/frontend/templates/index.html (+23/-2)
dashboard/frontend/templates/loop_create.html (+1/-1)
dashboard/frontend/templates/loop_detail.html (+43/-0)
dashboard/frontend/urls.py (+7/-1)
dashboard/frontend/views/index_view.py (+2/-0)
dashboard/frontend/views/loop_build_view.py (+42/-0)
dashboard/frontend/views/loop_create_view.py (+7/-2)
dashboard/frontend/views/loop_detail_view.py (+42/-0)
dashboard/jenkinsserver/models/jenkins_server.py (+4/-0)
dashboard/jenkinsserver/tests/test_jenkins_server.py (+3/-5)
dashboard/js/jquery-1.7.2.js (+9404/-0)
dashboard/settings.py (+2/-0)
dashboard/urls.py (+3/-0)
To merge this branch: bzr merge lp:~linaro-infrastructure/linaro-ci-dashboard/invoke-builds
Reviewer Review Type Date Requested Status
James Tunnicliffe (community) Approve
Deepti B. Kalakeri (community) Approve
Данило Шеган Pending
Review via email: mp+115827@code.launchpad.net

Description of the change

Add list and detail page for CI loops.
Integrate jQuery and implement 'Schedule Build' functionality.

To post a comment you must log in.
Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

The loop forms looks good.

    +++ dashboard/frontend/models/loop_build.py 2012-07-19 20:33:19 +0000
    @@ -23,12 +23,15 @@
     class LoopBuild(models.Model):
         class Meta:
             app_label = 'frontend'
    + ordering = ['-id']

         BUILD_STATUS = (
    + ('scheduled', 'SCHEDULED'),

I hope by scheduled you mean to say the build is submitted , but not yet running ?
Another appropriate state would be Running.

In ashboard/frontend/views/loop_build_view.py, we have some commented lines, we can remove them
453 + def render_to_response(self, context):
454 + #if self.request.is_ajax():
455 + build = self.object.schedule_build()
456 + return JSONResponseMixin.render_to_response(self, build)
457 + #else:
458 + # raise NotImplementedError

I merged both my create_new_job changes and yours and tested after resolving merge conflicts.
Required features works fine with merged branch.
I do not have experience in Jquery handler, so I leave that part to Danilo for review.
Otherwise looks good.

review: Needs Fixing
20. By Stevan Radaković

Fixes from code review by deepti. Missed some commented lines and new build status.

Revision history for this message
Stevan Radaković (stevanr) wrote :

> The loop forms looks good.
>
> +++ dashboard/frontend/models/loop_build.py 2012-07-19 20:33:19 +0000
> @@ -23,12 +23,15 @@
> class LoopBuild(models.Model):
> class Meta:
> app_label = 'frontend'
> + ordering = ['-id']
>
> BUILD_STATUS = (
> + ('scheduled', 'SCHEDULED'),
>
> I hope by scheduled you mean to say the build is submitted , but not yet
> running ?
> Another appropriate state would be Running.
>
> In ashboard/frontend/views/loop_build_view.py, we have some commented lines,
> we can remove them
> 453 + def render_to_response(self, context):
> 454 + #if self.request.is_ajax():
> 455 + build = self.object.schedule_build()
> 456 + return JSONResponseMixin.render_to_response(self, build)
> 457 + #else:
> 458 + # raise NotImplementedError
>
> I merged both my create_new_job changes and yours and tested after resolving
> merge conflicts.
> Required features works fine with merged branch.
> I do not have experience in Jquery handler, so I leave that part to Danilo for
> review.
> Otherwise looks good.

Added new status. Removed comments, those lines where actually necessary but were removed for development purposes. Thanks for the review.

Revision history for this message
Deepti B. Kalakeri (deeptik) wrote :

The changes requested are addressed.
+1 approve.

review: Approve
Revision history for this message
James Tunnicliffe (dooferlad) wrote :

The jQuery stuff looks fine.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'dashboard/frontend/fixtures/initial_data.json'
2--- dashboard/frontend/fixtures/initial_data.json 2012-07-16 11:14:43 +0000
3+++ dashboard/frontend/fixtures/initial_data.json 2012-07-20 11:02:19 +0000
4@@ -12,5 +12,14 @@
5 "fields": {
6 "name": "linaro-infrastructure"
7 }
8+ },
9+ {
10+ "model": "jenkinsserver.JenkinsServer",
11+ "pk": 1,
12+ "fields": {
13+ "url": "http://localhost:9090/jenkins",
14+ "username": "admin",
15+ "password": "admin"
16+ }
17 }
18 ]
19\ No newline at end of file
20
21=== added file 'dashboard/frontend/migrations/0001_initial.py'
22--- dashboard/frontend/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
23+++ dashboard/frontend/migrations/0001_initial.py 2012-07-20 11:02:19 +0000
24@@ -0,0 +1,69 @@
25+# encoding: utf-8
26+import datetime
27+from south.db import db
28+from south.v2 import SchemaMigration
29+from django.db import models
30+
31+class Migration(SchemaMigration):
32+
33+ def forwards(self, orm):
34+
35+ # Adding model 'Loop'
36+ db.create_table('frontend_loop', (
37+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
38+ ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)),
39+ ('server', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jenkinsserver.JenkinsServer'])),
40+ ('branch', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
41+ ('precommand', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
42+ ('command', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
43+ ))
44+ db.send_create_signal('frontend', ['Loop'])
45+
46+ # Adding model 'LoopBuild'
47+ db.create_table('frontend_loopbuild', (
48+ ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
49+ ('loop', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['frontend.Loop'])),
50+ ('build_number', self.gf('django.db.models.fields.IntegerField')(null=True, blank=True)),
51+ ('status', self.gf('django.db.models.fields.CharField')(max_length=20)),
52+ ('duration', self.gf('django.db.models.fields.DecimalField')(max_digits=8, decimal_places=2)),
53+ ))
54+ db.send_create_signal('frontend', ['LoopBuild'])
55+
56+
57+ def backwards(self, orm):
58+
59+ # Deleting model 'Loop'
60+ db.delete_table('frontend_loop')
61+
62+ # Deleting model 'LoopBuild'
63+ db.delete_table('frontend_loopbuild')
64+
65+
66+ models = {
67+ 'frontend.loop': {
68+ 'Meta': {'object_name': 'Loop'},
69+ 'branch': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
70+ 'command': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
71+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
72+ 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
73+ 'precommand': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
74+ 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkinsserver.JenkinsServer']"})
75+ },
76+ 'frontend.loopbuild': {
77+ 'Meta': {'object_name': 'LoopBuild'},
78+ 'build_number': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
79+ 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
80+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
81+ 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}),
82+ 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
83+ },
84+ 'jenkinsserver.jenkinsserver': {
85+ 'Meta': {'object_name': 'JenkinsServer'},
86+ 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
87+ 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
88+ 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
89+ 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
90+ }
91+ }
92+
93+ complete_apps = ['frontend']
94
95=== removed file 'dashboard/frontend/migrations/0001_initial.py'
96--- dashboard/frontend/migrations/0001_initial.py 2012-07-17 11:25:13 +0000
97+++ dashboard/frontend/migrations/0001_initial.py 1970-01-01 00:00:00 +0000
98@@ -1,67 +0,0 @@
99-# encoding: utf-8
100-import datetime
101-from south.db import db
102-from south.v2 import SchemaMigration
103-from django.db import models
104-
105-class Migration(SchemaMigration):
106-
107- def forwards(self, orm):
108-
109- # Adding model 'Loop'
110- db.create_table('frontend_loop', (
111- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
112- ('name', self.gf('django.db.models.fields.CharField')(unique=True, max_length=200)),
113- ('server', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['jenkins.JenkinsServer'])),
114- ('branch', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
115- ('precommand', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
116- ('command', self.gf('django.db.models.fields.CharField')(max_length=200, null=True)),
117- ))
118- db.send_create_signal('frontend', ['Loop'])
119-
120- # Adding model 'LoopBuild'
121- db.create_table('frontend_loopbuild', (
122- ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
123- ('loop', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['frontend.Loop'])),
124- ('status', self.gf('django.db.models.fields.CharField')(max_length=20)),
125- ('duration', self.gf('django.db.models.fields.DecimalField')(max_digits=8, decimal_places=2)),
126- ))
127- db.send_create_signal('frontend', ['LoopBuild'])
128-
129-
130- def backwards(self, orm):
131-
132- # Deleting model 'Loop'
133- db.delete_table('frontend_loop')
134-
135- # Deleting model 'LoopBuild'
136- db.delete_table('frontend_loopbuild')
137-
138-
139- models = {
140- 'frontend.loop': {
141- 'Meta': {'object_name': 'Loop'},
142- 'branch': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
143- 'command': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
144- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
145- 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '200'}),
146- 'precommand': ('django.db.models.fields.CharField', [], {'max_length': '200', 'null': 'True'}),
147- 'server': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['jenkins.JenkinsServer']"})
148- },
149- 'frontend.loopbuild': {
150- 'Meta': {'object_name': 'LoopBuild'},
151- 'duration': ('django.db.models.fields.DecimalField', [], {'max_digits': '8', 'decimal_places': '2'}),
152- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
153- 'loop': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['frontend.Loop']"}),
154- 'status': ('django.db.models.fields.CharField', [], {'max_length': '20'})
155- },
156- 'jenkins.jenkinsserver': {
157- 'Meta': {'object_name': 'JenkinsServer'},
158- 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
159- 'password': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
160- 'url': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
161- 'username': ('django.db.models.fields.CharField', [], {'max_length': '100'})
162- }
163- }
164-
165- complete_apps = ['frontend']
166
167=== added file 'dashboard/frontend/models/json_response_mixin.py'
168--- dashboard/frontend/models/json_response_mixin.py 1970-01-01 00:00:00 +0000
169+++ dashboard/frontend/models/json_response_mixin.py 2012-07-20 11:02:19 +0000
170@@ -0,0 +1,43 @@
171+#!/usr/bin/env python
172+# Copyright (C) 2012 Linaro
173+#
174+# This file is part of linaro-ci-dashboard.
175+#
176+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
177+# it under the terms of the GNU Affero General Public License as published by
178+# the Free Software Foundation, either version 3 of the License, or
179+# (at your option) any later version.
180+#
181+# linaro-ci-dashboard is distributed in the hope that it will be useful,
182+# but WITHOUT ANY WARRANTY; without even the implied warranty of
183+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
184+# GNU Affero General Public License for more details.
185+#
186+# You should have received a copy of the GNU Affero General Public License
187+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
188+
189+import json
190+from django.http import HttpResponse
191+from django.core import serializers
192+
193+
194+class JSONResponseMixin(object):
195+ '''
196+ A mixin that can be used to render a JSON response.
197+ '''
198+ response_class = HttpResponse
199+
200+ def render_to_response(self, obj, **response_kwargs):
201+ '''
202+ Returns a JSON response, transforming 'context' to make the payload.
203+ '''
204+ response_kwargs['content_type'] = 'application/json'
205+ return self.response_class(
206+ self.convert_context_to_json(obj),
207+ **response_kwargs
208+ )
209+
210+ def convert_context_to_json(self, obj):
211+ '''Convert the context dictionary into a JSON object.'''
212+ json_obj = serializers.serialize('json', [obj])
213+ return json.dumps(json_obj)
214
215=== modified file 'dashboard/frontend/models/loop.py'
216--- dashboard/frontend/models/loop.py 2012-07-18 17:48:08 +0000
217+++ dashboard/frontend/models/loop.py 2012-07-20 11:02:19 +0000
218@@ -32,8 +32,27 @@
219
220 def save(self, *args, **kwargs):
221 server = JenkinsServer.objects.get(id=1)
222+ self.server = server
223 server.add_integration_loop()
224 if (True):
225 super(Loop, self).save(*args, **kwargs)
226 else:
227 raise
228+
229+ def schedule_build(self):
230+ '''Add a LoopBuild record for this Loop.
231+
232+ Also try to send a build job signal to the server, but if it fails,
233+ ignore it.
234+ '''
235+ from frontend.models.loop_build import LoopBuild
236+ build = LoopBuild()
237+ build.loop = self
238+ build.status = 'scheduled'
239+ build.duration = 0.00
240+ build.save()
241+ try:
242+ self.server.schedule_job_build(self.name)
243+ except:
244+ pass
245+ return build
246
247=== modified file 'dashboard/frontend/models/loop_build.py'
248--- dashboard/frontend/models/loop_build.py 2012-07-19 10:44:27 +0000
249+++ dashboard/frontend/models/loop_build.py 2012-07-20 11:02:19 +0000
250@@ -23,12 +23,16 @@
251 class LoopBuild(models.Model):
252 class Meta:
253 app_label = 'frontend'
254+ ordering = ['-id']
255
256 BUILD_STATUS = (
257+ ('scheduled', 'SCHEDULED'),
258+ ('running', 'RUNNING'),
259 ('failure', 'FAILURE'),
260 ('success', 'SUCCESS'),
261 ('aborted', 'ABORTED'),
262 )
263 loop = models.ForeignKey(Loop)
264+ build_number = models.IntegerField(null=True, blank=True)
265 status = models.CharField(max_length=20, choices=BUILD_STATUS)
266 duration = models.DecimalField(max_digits=8, decimal_places=2)
267
268=== modified file 'dashboard/frontend/templates/header.html'
269--- dashboard/frontend/templates/header.html 2012-07-17 08:17:05 +0000
270+++ dashboard/frontend/templates/header.html 2012-07-20 11:02:19 +0000
271@@ -1,7 +1,8 @@
272+<script type="text/javascript" src="{% if request.is_secure %}https{% else %}http{% endif %}://{{ request.get_host }}/js/jquery-1.7.2.js"></script>
273 {% if request.user.username %}
274 <ul>
275 <li>Welcome <b>{{ request.user.username }}!!!</b></li>
276- <p><a href="/logout">Sign out</a></p>
277+ <a href="/logout">Sign out</a>
278 </ul>
279 {% else %}
280 <ul>
281
282=== modified file 'dashboard/frontend/templates/index.html'
283--- dashboard/frontend/templates/index.html 2012-07-06 12:49:00 +0000
284+++ dashboard/frontend/templates/index.html 2012-07-20 11:02:19 +0000
285@@ -1,4 +1,25 @@
286+{% include "header.html" %}
287+
288 {% block content %}
289-<p>You are signed in as <strong>{{request.user.username}}</strong> ({{request.user.get_full_name}}) </p>
290-<a href="/logout">Sign out</a>
291+ <h2>CI Loops</h2>
292+
293+
294+ <div id="loops">
295+ {% for loop in loops %}
296+ <div id="loop_{{ loop.id }}">
297+ Loop: {{ loop.id }} ||
298+ <a href="{% url LoopDetail loop.name %}">{{ loop.name }}</a> ||
299+ {{ loop.precommand }} ||
300+ {{ loop.command }}
301+ </div>
302+ </div>
303+ {% endfor %}
304+
305+ <button type="button" id="create_button">Add new loop</button>
306 {% endblock %}
307+
308+<script type="text/javascript">
309+$("#create_button").click(function() {
310+ window.location.href = "{% url LoopCreate %}";
311+});
312+</script>
313
314=== modified file 'dashboard/frontend/templates/loop_create.html'
315--- dashboard/frontend/templates/loop_create.html 2012-07-17 08:17:05 +0000
316+++ dashboard/frontend/templates/loop_create.html 2012-07-20 11:02:19 +0000
317@@ -1,6 +1,6 @@
318 {% include "header.html" %}
319
320-<form action="/loopcreate/" method="post">{% csrf_token %}
321+<form action="/loop/create/" method="post">{% csrf_token %}
322 {{ form.as_p }}
323 <input type="submit" value="Submit" />
324 </form>
325
326=== added file 'dashboard/frontend/templates/loop_detail.html'
327--- dashboard/frontend/templates/loop_detail.html 1970-01-01 00:00:00 +0000
328+++ dashboard/frontend/templates/loop_detail.html 2012-07-20 11:02:19 +0000
329@@ -0,0 +1,43 @@
330+{% include "header.html" %}
331+
332+{% block content %}
333+ <h2>Loop {{ loop_detail.name }}</h2>
334+
335+ <h4>Details</h4>
336+ <div>
337+ Branch: {{ loop_detail.branch }}
338+ </div>
339+ <div>
340+ Pre-command: {{ loop_detail.precommand }}
341+ </div>
342+ <div>
343+ Test Command: {{ loop_detail.command }}
344+ </div>
345+
346+ <h4>Builds</h4>
347+ <div id="builds">
348+ {% for build in builds %}
349+ <div id="build_{{ build.id }}">
350+ Build: {{ build.id }} || {{ build.status }} || {{ build.duration }} || {{ build.build_number }}
351+ </div>
352+ </div>
353+ {% endfor %}
354+
355+ <button type="button" id="schedule_button">Schedule new build</button>
356+{% endblock %}
357+
358+<script type="text/javascript">
359+$("#schedule_button").click(function() {
360+ $.get("{% if request.is_secure %}https{% else %}http{% endif %}://{{ request.get_host }}/loop/build/{{ loop_detail.name }}/", function(data) {
361+ data = $.parseJSON(data);
362+ $("<div/>", {
363+ "class": "test",
364+ text: "Build: " + data[0].pk + " || " +
365+ data[0].fields.status + " || " +
366+ data[0].fields.duration + " || " +
367+ data[0].fields.build_number,
368+ })
369+ .prependTo($("#builds"));
370+ });
371+});
372+</script>
373
374=== modified file 'dashboard/frontend/urls.py'
375--- dashboard/frontend/urls.py 2012-07-17 08:17:05 +0000
376+++ dashboard/frontend/urls.py 2012-07-20 11:02:19 +0000
377@@ -20,10 +20,16 @@
378 from frontend.views.home_page_view import HomePageView
379 from frontend.views.index_view import IndexView
380 from frontend.views.loop_create_view import LoopCreateView
381+from frontend.views.loop_detail_view import LoopDetailView
382+from frontend.views.loop_build_view import LoopBuildView
383
384 urlpatterns = patterns('',
385 url(r'^$', IndexView.as_view(), name='Index'),
386 url(r'^index/$', IndexView.as_view(), name='Index'),
387 url(r'^home/$', HomePageView.as_view(), name='Home'),
388- url(r'^loopcreate/$', LoopCreateView.as_view(), name='LoopCreate'),
389+ url(r'^loop/create/$', LoopCreateView.as_view(), name='LoopCreate'),
390+ url(r'^loop/detail/(?P<slug>[-_\w]+)/$',
391+ LoopDetailView.as_view(), name='LoopDetail'),
392+ url(r'^loop/build/(?P<slug>[-_\w]+)/$',
393+ LoopBuildView.as_view(), name='LoopBuild'),
394 )
395
396=== modified file 'dashboard/frontend/views/index_view.py'
397--- dashboard/frontend/views/index_view.py 2012-07-18 17:48:08 +0000
398+++ dashboard/frontend/views/index_view.py 2012-07-20 11:02:19 +0000
399@@ -19,6 +19,7 @@
400 from django.views.generic.base import TemplateView
401 from django.utils.decorators import method_decorator
402 from django.contrib.auth.decorators import login_required
403+from frontend.models.loop import Loop
404
405
406 class IndexView(TemplateView):
407@@ -32,4 +33,5 @@
408 def get_context_data(self, **kwargs):
409 context = super(IndexView, self).get_context_data(**kwargs)
410 context['request'] = self.request
411+ context['loops'] = Loop.objects.all()
412 return context
413
414=== added file 'dashboard/frontend/views/loop_build_view.py'
415--- dashboard/frontend/views/loop_build_view.py 1970-01-01 00:00:00 +0000
416+++ dashboard/frontend/views/loop_build_view.py 2012-07-20 11:02:19 +0000
417@@ -0,0 +1,42 @@
418+#!/usr/bin/env python
419+# Copyright (C) 2012 Linaro
420+#
421+# This file is part of linaro-ci-dashboard.
422+#
423+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
424+# it under the terms of the GNU Affero General Public License as published by
425+# the Free Software Foundation, either version 3 of the License, or
426+# (at your option) any later version.
427+#
428+# linaro-ci-dashboard is distributed in the hope that it will be useful,
429+# but WITHOUT ANY WARRANTY; without even the implied warranty of
430+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
431+# GNU Affero General Public License for more details.
432+#
433+# You should have received a copy of the GNU Affero General Public License
434+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
435+
436+from django.contrib.auth.decorators import login_required
437+from django.core import serializers
438+from django.views.generic import View
439+from django.views.generic.detail import DetailView
440+from django.utils.decorators import method_decorator
441+from frontend.models.loop import Loop
442+from frontend.models.json_response_mixin import JSONResponseMixin
443+
444+
445+class LoopBuildView(JSONResponseMixin, DetailView):
446+
447+ model = Loop
448+ slug_field = "name"
449+
450+ @method_decorator(login_required)
451+ def dispatch(self, *args, **kwargs):
452+ return super(LoopBuildView, self).dispatch(*args, **kwargs)
453+
454+ def render_to_response(self, context):
455+ if self.request.is_ajax():
456+ build = self.object.schedule_build()
457+ return JSONResponseMixin.render_to_response(self, build)
458+ else:
459+ raise NotImplementedError
460
461=== modified file 'dashboard/frontend/views/loop_create_view.py'
462--- dashboard/frontend/views/loop_create_view.py 2012-07-17 11:23:55 +0000
463+++ dashboard/frontend/views/loop_create_view.py 2012-07-20 11:02:19 +0000
464@@ -17,18 +17,18 @@
465 # along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
466
467 from django.contrib.auth.decorators import login_required
468+from django.core.urlresolvers import reverse
469+from django.http import HttpResponseRedirect
470 from django.views.generic.edit import CreateView
471 from django.utils.decorators import method_decorator
472 from frontend.forms.loop_form import LoopForm
473 from frontend.models.loop import Loop
474
475-
476 class LoopCreateView(CreateView):
477
478 template_name = "loop_create.html"
479 form_class = LoopForm
480 model = Loop
481- success_url = '/index/'
482
483 def form_valid(self, form):
484 """This method is called when valid form data has been POSTed.
485@@ -42,6 +42,11 @@
486 return super(LoopCreateView, self).dispatch(*args, **kwargs)
487
488 def get_context_data(self, **kwargs):
489+ '''Get the context data passed to template.'''
490 context = super(LoopCreateView, self).get_context_data(**kwargs)
491 context['request'] = self.request
492 return context
493+
494+ def get_success_url(self):
495+ '''Return the URL when the form is valid.'''
496+ return reverse('LoopDetail', args=[self.object.name])
497
498=== added file 'dashboard/frontend/views/loop_detail_view.py'
499--- dashboard/frontend/views/loop_detail_view.py 1970-01-01 00:00:00 +0000
500+++ dashboard/frontend/views/loop_detail_view.py 2012-07-20 11:02:19 +0000
501@@ -0,0 +1,42 @@
502+#!/usr/bin/env python
503+# Copyright (C) 2012 Linaro
504+#
505+# This file is part of linaro-ci-dashboard.
506+#
507+# linaro-ci-dashboard is free software: you can redistribute it and/or modify
508+# it under the terms of the GNU Affero General Public License as published by
509+# the Free Software Foundation, either version 3 of the License, or
510+# (at your option) any later version.
511+#
512+# linaro-ci-dashboard is distributed in the hope that it will be useful,
513+# but WITHOUT ANY WARRANTY; without even the implied warranty of
514+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
515+# GNU Affero General Public License for more details.
516+#
517+# You should have received a copy of the GNU Affero General Public License
518+# along with linaro-ci-dashboard. If not, see <http://www.gnu.org/licenses/>.
519+
520+from django.contrib.auth.decorators import login_required
521+from django.views.generic.detail import DetailView
522+from django.utils.decorators import method_decorator
523+from frontend.forms.loop_form import LoopForm
524+from frontend.models.loop import Loop
525+
526+
527+class LoopDetailView(DetailView):
528+
529+ model = Loop
530+ context_object_name = 'loop_detail'
531+ slug_field = "name"
532+ template_name = "loop_detail.html"
533+
534+ @method_decorator(login_required)
535+ def dispatch(self, *args, **kwargs):
536+ return super(LoopDetailView, self).dispatch(*args, **kwargs)
537+
538+ def get_context_data(self, **kwargs):
539+ '''Get the context data passed to template.'''
540+ context = super(LoopDetailView, self).get_context_data(**kwargs)
541+ context['request'] = self.request
542+ context['builds'] = self.object.loopbuild_set.all()
543+ return context
544
545=== modified file 'dashboard/jenkinsserver/models/jenkins_server.py'
546--- dashboard/jenkinsserver/models/jenkins_server.py 2012-07-19 08:00:53 +0000
547+++ dashboard/jenkinsserver/models/jenkins_server.py 2012-07-20 11:02:19 +0000
548@@ -82,3 +82,7 @@
549 def unique_items(self, list_1, list_2):
550 """Return a list of items that are present in list_1 but not list_2."""
551 return [item for item in list_1 if item not in list_2]
552+
553+ def schedule_job_build(self, jobname):
554+ """Trigger build job on jenkins."""
555+ self.jenkins.build_job(jobname)
556
557=== modified file 'dashboard/jenkinsserver/tests/test_jenkins_server.py'
558--- dashboard/jenkinsserver/tests/test_jenkins_server.py 2012-07-19 10:44:27 +0000
559+++ dashboard/jenkinsserver/tests/test_jenkins_server.py 2012-07-20 11:02:19 +0000
560@@ -63,10 +63,8 @@
561
562 def test_sync_jobs(self):
563 self.server.sync_jobs()
564- loops = [loop.name for loop in
565- self.server.loop_set.all()]
566- jenkins_jobs = [job["name"] for job in
567- self.server.jenkins.get_jobs()]
568+ loops = [loop.name for loop in Loop.objects.all()]
569+ jenkins_jobs = [job["name"] for job in self.server.jenkins.get_jobs()]
570 jenkins_jobs.sort()
571 loops.sort()
572 self.assertEquals(loops, jenkins_jobs)
573@@ -82,7 +80,7 @@
574 jobs = [self.test_job_name]
575 self.server.graft_jobs(jobs)
576 loops = [loop.name.encode("utf8") for loop in
577- self.server.loop_set.all()]
578+ Loop.objects.all()]
579 loops.sort()
580 self.assertEquals([self.test_job_name], loops)
581
582
583=== added directory 'dashboard/js'
584=== added file 'dashboard/js/jquery-1.7.2.js'
585--- dashboard/js/jquery-1.7.2.js 1970-01-01 00:00:00 +0000
586+++ dashboard/js/jquery-1.7.2.js 2012-07-20 11:02:19 +0000
587@@ -0,0 +1,9404 @@
588+/*!
589+ * jQuery JavaScript Library v1.7.2
590+ * http://jquery.com/
591+ *
592+ * Copyright 2011, John Resig
593+ * Dual licensed under the MIT or GPL Version 2 licenses.
594+ * http://jquery.org/license
595+ *
596+ * Includes Sizzle.js
597+ * http://sizzlejs.com/
598+ * Copyright 2011, The Dojo Foundation
599+ * Released under the MIT, BSD, and GPL Licenses.
600+ *
601+ * Date: Wed Mar 21 12:46:34 2012 -0700
602+ */
603+(function( window, undefined ) {
604+
605+// Use the correct document accordingly with window argument (sandbox)
606+var document = window.document,
607+ navigator = window.navigator,
608+ location = window.location;
609+var jQuery = (function() {
610+
611+// Define a local copy of jQuery
612+var jQuery = function( selector, context ) {
613+ // The jQuery object is actually just the init constructor 'enhanced'
614+ return new jQuery.fn.init( selector, context, rootjQuery );
615+ },
616+
617+ // Map over jQuery in case of overwrite
618+ _jQuery = window.jQuery,
619+
620+ // Map over the $ in case of overwrite
621+ _$ = window.$,
622+
623+ // A central reference to the root jQuery(document)
624+ rootjQuery,
625+
626+ // A simple way to check for HTML strings or ID strings
627+ // Prioritize #id over <tag> to avoid XSS via location.hash (#9521)
628+ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
629+
630+ // Check if a string has a non-whitespace character in it
631+ rnotwhite = /\S/,
632+
633+ // Used for trimming whitespace
634+ trimLeft = /^\s+/,
635+ trimRight = /\s+$/,
636+
637+ // Match a standalone tag
638+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
639+
640+ // JSON RegExp
641+ rvalidchars = /^[\],:{}\s]*$/,
642+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
643+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
644+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
645+
646+ // Useragent RegExp
647+ rwebkit = /(webkit)[ \/]([\w.]+)/,
648+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
649+ rmsie = /(msie) ([\w.]+)/,
650+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
651+
652+ // Matches dashed string for camelizing
653+ rdashAlpha = /-([a-z]|[0-9])/ig,
654+ rmsPrefix = /^-ms-/,
655+
656+ // Used by jQuery.camelCase as callback to replace()
657+ fcamelCase = function( all, letter ) {
658+ return ( letter + "" ).toUpperCase();
659+ },
660+
661+ // Keep a UserAgent string for use with jQuery.browser
662+ userAgent = navigator.userAgent,
663+
664+ // For matching the engine and version of the browser
665+ browserMatch,
666+
667+ // The deferred used on DOM ready
668+ readyList,
669+
670+ // The ready event handler
671+ DOMContentLoaded,
672+
673+ // Save a reference to some core methods
674+ toString = Object.prototype.toString,
675+ hasOwn = Object.prototype.hasOwnProperty,
676+ push = Array.prototype.push,
677+ slice = Array.prototype.slice,
678+ trim = String.prototype.trim,
679+ indexOf = Array.prototype.indexOf,
680+
681+ // [[Class]] -> type pairs
682+ class2type = {};
683+
684+jQuery.fn = jQuery.prototype = {
685+ constructor: jQuery,
686+ init: function( selector, context, rootjQuery ) {
687+ var match, elem, ret, doc;
688+
689+ // Handle $(""), $(null), or $(undefined)
690+ if ( !selector ) {
691+ return this;
692+ }
693+
694+ // Handle $(DOMElement)
695+ if ( selector.nodeType ) {
696+ this.context = this[0] = selector;
697+ this.length = 1;
698+ return this;
699+ }
700+
701+ // The body element only exists once, optimize finding it
702+ if ( selector === "body" && !context && document.body ) {
703+ this.context = document;
704+ this[0] = document.body;
705+ this.selector = selector;
706+ this.length = 1;
707+ return this;
708+ }
709+
710+ // Handle HTML strings
711+ if ( typeof selector === "string" ) {
712+ // Are we dealing with HTML string or an ID?
713+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
714+ // Assume that strings that start and end with <> are HTML and skip the regex check
715+ match = [ null, selector, null ];
716+
717+ } else {
718+ match = quickExpr.exec( selector );
719+ }
720+
721+ // Verify a match, and that no context was specified for #id
722+ if ( match && (match[1] || !context) ) {
723+
724+ // HANDLE: $(html) -> $(array)
725+ if ( match[1] ) {
726+ context = context instanceof jQuery ? context[0] : context;
727+ doc = ( context ? context.ownerDocument || context : document );
728+
729+ // If a single string is passed in and it's a single tag
730+ // just do a createElement and skip the rest
731+ ret = rsingleTag.exec( selector );
732+
733+ if ( ret ) {
734+ if ( jQuery.isPlainObject( context ) ) {
735+ selector = [ document.createElement( ret[1] ) ];
736+ jQuery.fn.attr.call( selector, context, true );
737+
738+ } else {
739+ selector = [ doc.createElement( ret[1] ) ];
740+ }
741+
742+ } else {
743+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
744+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
745+ }
746+
747+ return jQuery.merge( this, selector );
748+
749+ // HANDLE: $("#id")
750+ } else {
751+ elem = document.getElementById( match[2] );
752+
753+ // Check parentNode to catch when Blackberry 4.6 returns
754+ // nodes that are no longer in the document #6963
755+ if ( elem && elem.parentNode ) {
756+ // Handle the case where IE and Opera return items
757+ // by name instead of ID
758+ if ( elem.id !== match[2] ) {
759+ return rootjQuery.find( selector );
760+ }
761+
762+ // Otherwise, we inject the element directly into the jQuery object
763+ this.length = 1;
764+ this[0] = elem;
765+ }
766+
767+ this.context = document;
768+ this.selector = selector;
769+ return this;
770+ }
771+
772+ // HANDLE: $(expr, $(...))
773+ } else if ( !context || context.jquery ) {
774+ return ( context || rootjQuery ).find( selector );
775+
776+ // HANDLE: $(expr, context)
777+ // (which is just equivalent to: $(context).find(expr)
778+ } else {
779+ return this.constructor( context ).find( selector );
780+ }
781+
782+ // HANDLE: $(function)
783+ // Shortcut for document ready
784+ } else if ( jQuery.isFunction( selector ) ) {
785+ return rootjQuery.ready( selector );
786+ }
787+
788+ if ( selector.selector !== undefined ) {
789+ this.selector = selector.selector;
790+ this.context = selector.context;
791+ }
792+
793+ return jQuery.makeArray( selector, this );
794+ },
795+
796+ // Start with an empty selector
797+ selector: "",
798+
799+ // The current version of jQuery being used
800+ jquery: "1.7.2",
801+
802+ // The default length of a jQuery object is 0
803+ length: 0,
804+
805+ // The number of elements contained in the matched element set
806+ size: function() {
807+ return this.length;
808+ },
809+
810+ toArray: function() {
811+ return slice.call( this, 0 );
812+ },
813+
814+ // Get the Nth element in the matched element set OR
815+ // Get the whole matched element set as a clean array
816+ get: function( num ) {
817+ return num == null ?
818+
819+ // Return a 'clean' array
820+ this.toArray() :
821+
822+ // Return just the object
823+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
824+ },
825+
826+ // Take an array of elements and push it onto the stack
827+ // (returning the new matched element set)
828+ pushStack: function( elems, name, selector ) {
829+ // Build a new jQuery matched element set
830+ var ret = this.constructor();
831+
832+ if ( jQuery.isArray( elems ) ) {
833+ push.apply( ret, elems );
834+
835+ } else {
836+ jQuery.merge( ret, elems );
837+ }
838+
839+ // Add the old object onto the stack (as a reference)
840+ ret.prevObject = this;
841+
842+ ret.context = this.context;
843+
844+ if ( name === "find" ) {
845+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
846+ } else if ( name ) {
847+ ret.selector = this.selector + "." + name + "(" + selector + ")";
848+ }
849+
850+ // Return the newly-formed element set
851+ return ret;
852+ },
853+
854+ // Execute a callback for every element in the matched set.
855+ // (You can seed the arguments with an array of args, but this is
856+ // only used internally.)
857+ each: function( callback, args ) {
858+ return jQuery.each( this, callback, args );
859+ },
860+
861+ ready: function( fn ) {
862+ // Attach the listeners
863+ jQuery.bindReady();
864+
865+ // Add the callback
866+ readyList.add( fn );
867+
868+ return this;
869+ },
870+
871+ eq: function( i ) {
872+ i = +i;
873+ return i === -1 ?
874+ this.slice( i ) :
875+ this.slice( i, i + 1 );
876+ },
877+
878+ first: function() {
879+ return this.eq( 0 );
880+ },
881+
882+ last: function() {
883+ return this.eq( -1 );
884+ },
885+
886+ slice: function() {
887+ return this.pushStack( slice.apply( this, arguments ),
888+ "slice", slice.call(arguments).join(",") );
889+ },
890+
891+ map: function( callback ) {
892+ return this.pushStack( jQuery.map(this, function( elem, i ) {
893+ return callback.call( elem, i, elem );
894+ }));
895+ },
896+
897+ end: function() {
898+ return this.prevObject || this.constructor(null);
899+ },
900+
901+ // For internal use only.
902+ // Behaves like an Array's method, not like a jQuery method.
903+ push: push,
904+ sort: [].sort,
905+ splice: [].splice
906+};
907+
908+// Give the init function the jQuery prototype for later instantiation
909+jQuery.fn.init.prototype = jQuery.fn;
910+
911+jQuery.extend = jQuery.fn.extend = function() {
912+ var options, name, src, copy, copyIsArray, clone,
913+ target = arguments[0] || {},
914+ i = 1,
915+ length = arguments.length,
916+ deep = false;
917+
918+ // Handle a deep copy situation
919+ if ( typeof target === "boolean" ) {
920+ deep = target;
921+ target = arguments[1] || {};
922+ // skip the boolean and the target
923+ i = 2;
924+ }
925+
926+ // Handle case when target is a string or something (possible in deep copy)
927+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
928+ target = {};
929+ }
930+
931+ // extend jQuery itself if only one argument is passed
932+ if ( length === i ) {
933+ target = this;
934+ --i;
935+ }
936+
937+ for ( ; i < length; i++ ) {
938+ // Only deal with non-null/undefined values
939+ if ( (options = arguments[ i ]) != null ) {
940+ // Extend the base object
941+ for ( name in options ) {
942+ src = target[ name ];
943+ copy = options[ name ];
944+
945+ // Prevent never-ending loop
946+ if ( target === copy ) {
947+ continue;
948+ }
949+
950+ // Recurse if we're merging plain objects or arrays
951+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
952+ if ( copyIsArray ) {
953+ copyIsArray = false;
954+ clone = src && jQuery.isArray(src) ? src : [];
955+
956+ } else {
957+ clone = src && jQuery.isPlainObject(src) ? src : {};
958+ }
959+
960+ // Never move original objects, clone them
961+ target[ name ] = jQuery.extend( deep, clone, copy );
962+
963+ // Don't bring in undefined values
964+ } else if ( copy !== undefined ) {
965+ target[ name ] = copy;
966+ }
967+ }
968+ }
969+ }
970+
971+ // Return the modified object
972+ return target;
973+};
974+
975+jQuery.extend({
976+ noConflict: function( deep ) {
977+ if ( window.$ === jQuery ) {
978+ window.$ = _$;
979+ }
980+
981+ if ( deep && window.jQuery === jQuery ) {
982+ window.jQuery = _jQuery;
983+ }
984+
985+ return jQuery;
986+ },
987+
988+ // Is the DOM ready to be used? Set to true once it occurs.
989+ isReady: false,
990+
991+ // A counter to track how many items to wait for before
992+ // the ready event fires. See #6781
993+ readyWait: 1,
994+
995+ // Hold (or release) the ready event
996+ holdReady: function( hold ) {
997+ if ( hold ) {
998+ jQuery.readyWait++;
999+ } else {
1000+ jQuery.ready( true );
1001+ }
1002+ },
1003+
1004+ // Handle when the DOM is ready
1005+ ready: function( wait ) {
1006+ // Either a released hold or an DOMready/load event and not yet ready
1007+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
1008+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1009+ if ( !document.body ) {
1010+ return setTimeout( jQuery.ready, 1 );
1011+ }
1012+
1013+ // Remember that the DOM is ready
1014+ jQuery.isReady = true;
1015+
1016+ // If a normal DOM Ready event fired, decrement, and wait if need be
1017+ if ( wait !== true && --jQuery.readyWait > 0 ) {
1018+ return;
1019+ }
1020+
1021+ // If there are functions bound, to execute
1022+ readyList.fireWith( document, [ jQuery ] );
1023+
1024+ // Trigger any bound ready events
1025+ if ( jQuery.fn.trigger ) {
1026+ jQuery( document ).trigger( "ready" ).off( "ready" );
1027+ }
1028+ }
1029+ },
1030+
1031+ bindReady: function() {
1032+ if ( readyList ) {
1033+ return;
1034+ }
1035+
1036+ readyList = jQuery.Callbacks( "once memory" );
1037+
1038+ // Catch cases where $(document).ready() is called after the
1039+ // browser event has already occurred.
1040+ if ( document.readyState === "complete" ) {
1041+ // Handle it asynchronously to allow scripts the opportunity to delay ready
1042+ return setTimeout( jQuery.ready, 1 );
1043+ }
1044+
1045+ // Mozilla, Opera and webkit nightlies currently support this event
1046+ if ( document.addEventListener ) {
1047+ // Use the handy event callback
1048+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
1049+
1050+ // A fallback to window.onload, that will always work
1051+ window.addEventListener( "load", jQuery.ready, false );
1052+
1053+ // If IE event model is used
1054+ } else if ( document.attachEvent ) {
1055+ // ensure firing before onload,
1056+ // maybe late but safe also for iframes
1057+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
1058+
1059+ // A fallback to window.onload, that will always work
1060+ window.attachEvent( "onload", jQuery.ready );
1061+
1062+ // If IE and not a frame
1063+ // continually check to see if the document is ready
1064+ var toplevel = false;
1065+
1066+ try {
1067+ toplevel = window.frameElement == null;
1068+ } catch(e) {}
1069+
1070+ if ( document.documentElement.doScroll && toplevel ) {
1071+ doScrollCheck();
1072+ }
1073+ }
1074+ },
1075+
1076+ // See test/unit/core.js for details concerning isFunction.
1077+ // Since version 1.3, DOM methods and functions like alert
1078+ // aren't supported. They return false on IE (#2968).
1079+ isFunction: function( obj ) {
1080+ return jQuery.type(obj) === "function";
1081+ },
1082+
1083+ isArray: Array.isArray || function( obj ) {
1084+ return jQuery.type(obj) === "array";
1085+ },
1086+
1087+ isWindow: function( obj ) {
1088+ return obj != null && obj == obj.window;
1089+ },
1090+
1091+ isNumeric: function( obj ) {
1092+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
1093+ },
1094+
1095+ type: function( obj ) {
1096+ return obj == null ?
1097+ String( obj ) :
1098+ class2type[ toString.call(obj) ] || "object";
1099+ },
1100+
1101+ isPlainObject: function( obj ) {
1102+ // Must be an Object.
1103+ // Because of IE, we also have to check the presence of the constructor property.
1104+ // Make sure that DOM nodes and window objects don't pass through, as well
1105+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
1106+ return false;
1107+ }
1108+
1109+ try {
1110+ // Not own constructor property must be Object
1111+ if ( obj.constructor &&
1112+ !hasOwn.call(obj, "constructor") &&
1113+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
1114+ return false;
1115+ }
1116+ } catch ( e ) {
1117+ // IE8,9 Will throw exceptions on certain host objects #9897
1118+ return false;
1119+ }
1120+
1121+ // Own properties are enumerated firstly, so to speed up,
1122+ // if last one is own, then all properties are own.
1123+
1124+ var key;
1125+ for ( key in obj ) {}
1126+
1127+ return key === undefined || hasOwn.call( obj, key );
1128+ },
1129+
1130+ isEmptyObject: function( obj ) {
1131+ for ( var name in obj ) {
1132+ return false;
1133+ }
1134+ return true;
1135+ },
1136+
1137+ error: function( msg ) {
1138+ throw new Error( msg );
1139+ },
1140+
1141+ parseJSON: function( data ) {
1142+ if ( typeof data !== "string" || !data ) {
1143+ return null;
1144+ }
1145+
1146+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
1147+ data = jQuery.trim( data );
1148+
1149+ // Attempt to parse using the native JSON parser first
1150+ if ( window.JSON && window.JSON.parse ) {
1151+ return window.JSON.parse( data );
1152+ }
1153+
1154+ // Make sure the incoming data is actual JSON
1155+ // Logic borrowed from http://json.org/json2.js
1156+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
1157+ .replace( rvalidtokens, "]" )
1158+ .replace( rvalidbraces, "")) ) {
1159+
1160+ return ( new Function( "return " + data ) )();
1161+
1162+ }
1163+ jQuery.error( "Invalid JSON: " + data );
1164+ },
1165+
1166+ // Cross-browser xml parsing
1167+ parseXML: function( data ) {
1168+ if ( typeof data !== "string" || !data ) {
1169+ return null;
1170+ }
1171+ var xml, tmp;
1172+ try {
1173+ if ( window.DOMParser ) { // Standard
1174+ tmp = new DOMParser();
1175+ xml = tmp.parseFromString( data , "text/xml" );
1176+ } else { // IE
1177+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
1178+ xml.async = "false";
1179+ xml.loadXML( data );
1180+ }
1181+ } catch( e ) {
1182+ xml = undefined;
1183+ }
1184+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
1185+ jQuery.error( "Invalid XML: " + data );
1186+ }
1187+ return xml;
1188+ },
1189+
1190+ noop: function() {},
1191+
1192+ // Evaluates a script in a global context
1193+ // Workarounds based on findings by Jim Driscoll
1194+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
1195+ globalEval: function( data ) {
1196+ if ( data && rnotwhite.test( data ) ) {
1197+ // We use execScript on Internet Explorer
1198+ // We use an anonymous function so that context is window
1199+ // rather than jQuery in Firefox
1200+ ( window.execScript || function( data ) {
1201+ window[ "eval" ].call( window, data );
1202+ } )( data );
1203+ }
1204+ },
1205+
1206+ // Convert dashed to camelCase; used by the css and data modules
1207+ // Microsoft forgot to hump their vendor prefix (#9572)
1208+ camelCase: function( string ) {
1209+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
1210+ },
1211+
1212+ nodeName: function( elem, name ) {
1213+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
1214+ },
1215+
1216+ // args is for internal usage only
1217+ each: function( object, callback, args ) {
1218+ var name, i = 0,
1219+ length = object.length,
1220+ isObj = length === undefined || jQuery.isFunction( object );
1221+
1222+ if ( args ) {
1223+ if ( isObj ) {
1224+ for ( name in object ) {
1225+ if ( callback.apply( object[ name ], args ) === false ) {
1226+ break;
1227+ }
1228+ }
1229+ } else {
1230+ for ( ; i < length; ) {
1231+ if ( callback.apply( object[ i++ ], args ) === false ) {
1232+ break;
1233+ }
1234+ }
1235+ }
1236+
1237+ // A special, fast, case for the most common use of each
1238+ } else {
1239+ if ( isObj ) {
1240+ for ( name in object ) {
1241+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
1242+ break;
1243+ }
1244+ }
1245+ } else {
1246+ for ( ; i < length; ) {
1247+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
1248+ break;
1249+ }
1250+ }
1251+ }
1252+ }
1253+
1254+ return object;
1255+ },
1256+
1257+ // Use native String.trim function wherever possible
1258+ trim: trim ?
1259+ function( text ) {
1260+ return text == null ?
1261+ "" :
1262+ trim.call( text );
1263+ } :
1264+
1265+ // Otherwise use our own trimming functionality
1266+ function( text ) {
1267+ return text == null ?
1268+ "" :
1269+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
1270+ },
1271+
1272+ // results is for internal usage only
1273+ makeArray: function( array, results ) {
1274+ var ret = results || [];
1275+
1276+ if ( array != null ) {
1277+ // The window, strings (and functions) also have 'length'
1278+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
1279+ var type = jQuery.type( array );
1280+
1281+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
1282+ push.call( ret, array );
1283+ } else {
1284+ jQuery.merge( ret, array );
1285+ }
1286+ }
1287+
1288+ return ret;
1289+ },
1290+
1291+ inArray: function( elem, array, i ) {
1292+ var len;
1293+
1294+ if ( array ) {
1295+ if ( indexOf ) {
1296+ return indexOf.call( array, elem, i );
1297+ }
1298+
1299+ len = array.length;
1300+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
1301+
1302+ for ( ; i < len; i++ ) {
1303+ // Skip accessing in sparse arrays
1304+ if ( i in array && array[ i ] === elem ) {
1305+ return i;
1306+ }
1307+ }
1308+ }
1309+
1310+ return -1;
1311+ },
1312+
1313+ merge: function( first, second ) {
1314+ var i = first.length,
1315+ j = 0;
1316+
1317+ if ( typeof second.length === "number" ) {
1318+ for ( var l = second.length; j < l; j++ ) {
1319+ first[ i++ ] = second[ j ];
1320+ }
1321+
1322+ } else {
1323+ while ( second[j] !== undefined ) {
1324+ first[ i++ ] = second[ j++ ];
1325+ }
1326+ }
1327+
1328+ first.length = i;
1329+
1330+ return first;
1331+ },
1332+
1333+ grep: function( elems, callback, inv ) {
1334+ var ret = [], retVal;
1335+ inv = !!inv;
1336+
1337+ // Go through the array, only saving the items
1338+ // that pass the validator function
1339+ for ( var i = 0, length = elems.length; i < length; i++ ) {
1340+ retVal = !!callback( elems[ i ], i );
1341+ if ( inv !== retVal ) {
1342+ ret.push( elems[ i ] );
1343+ }
1344+ }
1345+
1346+ return ret;
1347+ },
1348+
1349+ // arg is for internal usage only
1350+ map: function( elems, callback, arg ) {
1351+ var value, key, ret = [],
1352+ i = 0,
1353+ length = elems.length,
1354+ // jquery objects are treated as arrays
1355+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
1356+
1357+ // Go through the array, translating each of the items to their
1358+ if ( isArray ) {
1359+ for ( ; i < length; i++ ) {
1360+ value = callback( elems[ i ], i, arg );
1361+
1362+ if ( value != null ) {
1363+ ret[ ret.length ] = value;
1364+ }
1365+ }
1366+
1367+ // Go through every key on the object,
1368+ } else {
1369+ for ( key in elems ) {
1370+ value = callback( elems[ key ], key, arg );
1371+
1372+ if ( value != null ) {
1373+ ret[ ret.length ] = value;
1374+ }
1375+ }
1376+ }
1377+
1378+ // Flatten any nested arrays
1379+ return ret.concat.apply( [], ret );
1380+ },
1381+
1382+ // A global GUID counter for objects
1383+ guid: 1,
1384+
1385+ // Bind a function to a context, optionally partially applying any
1386+ // arguments.
1387+ proxy: function( fn, context ) {
1388+ if ( typeof context === "string" ) {
1389+ var tmp = fn[ context ];
1390+ context = fn;
1391+ fn = tmp;
1392+ }
1393+
1394+ // Quick check to determine if target is callable, in the spec
1395+ // this throws a TypeError, but we will just return undefined.
1396+ if ( !jQuery.isFunction( fn ) ) {
1397+ return undefined;
1398+ }
1399+
1400+ // Simulated bind
1401+ var args = slice.call( arguments, 2 ),
1402+ proxy = function() {
1403+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
1404+ };
1405+
1406+ // Set the guid of unique handler to the same of original handler, so it can be removed
1407+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
1408+
1409+ return proxy;
1410+ },
1411+
1412+ // Mutifunctional method to get and set values to a collection
1413+ // The value/s can optionally be executed if it's a function
1414+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
1415+ var exec,
1416+ bulk = key == null,
1417+ i = 0,
1418+ length = elems.length;
1419+
1420+ // Sets many values
1421+ if ( key && typeof key === "object" ) {
1422+ for ( i in key ) {
1423+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
1424+ }
1425+ chainable = 1;
1426+
1427+ // Sets one value
1428+ } else if ( value !== undefined ) {
1429+ // Optionally, function values get executed if exec is true
1430+ exec = pass === undefined && jQuery.isFunction( value );
1431+
1432+ if ( bulk ) {
1433+ // Bulk operations only iterate when executing function values
1434+ if ( exec ) {
1435+ exec = fn;
1436+ fn = function( elem, key, value ) {
1437+ return exec.call( jQuery( elem ), value );
1438+ };
1439+
1440+ // Otherwise they run against the entire set
1441+ } else {
1442+ fn.call( elems, value );
1443+ fn = null;
1444+ }
1445+ }
1446+
1447+ if ( fn ) {
1448+ for (; i < length; i++ ) {
1449+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
1450+ }
1451+ }
1452+
1453+ chainable = 1;
1454+ }
1455+
1456+ return chainable ?
1457+ elems :
1458+
1459+ // Gets
1460+ bulk ?
1461+ fn.call( elems ) :
1462+ length ? fn( elems[0], key ) : emptyGet;
1463+ },
1464+
1465+ now: function() {
1466+ return ( new Date() ).getTime();
1467+ },
1468+
1469+ // Use of jQuery.browser is frowned upon.
1470+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
1471+ uaMatch: function( ua ) {
1472+ ua = ua.toLowerCase();
1473+
1474+ var match = rwebkit.exec( ua ) ||
1475+ ropera.exec( ua ) ||
1476+ rmsie.exec( ua ) ||
1477+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
1478+ [];
1479+
1480+ return { browser: match[1] || "", version: match[2] || "0" };
1481+ },
1482+
1483+ sub: function() {
1484+ function jQuerySub( selector, context ) {
1485+ return new jQuerySub.fn.init( selector, context );
1486+ }
1487+ jQuery.extend( true, jQuerySub, this );
1488+ jQuerySub.superclass = this;
1489+ jQuerySub.fn = jQuerySub.prototype = this();
1490+ jQuerySub.fn.constructor = jQuerySub;
1491+ jQuerySub.sub = this.sub;
1492+ jQuerySub.fn.init = function init( selector, context ) {
1493+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
1494+ context = jQuerySub( context );
1495+ }
1496+
1497+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
1498+ };
1499+ jQuerySub.fn.init.prototype = jQuerySub.fn;
1500+ var rootjQuerySub = jQuerySub(document);
1501+ return jQuerySub;
1502+ },
1503+
1504+ browser: {}
1505+});
1506+
1507+// Populate the class2type map
1508+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
1509+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
1510+});
1511+
1512+browserMatch = jQuery.uaMatch( userAgent );
1513+if ( browserMatch.browser ) {
1514+ jQuery.browser[ browserMatch.browser ] = true;
1515+ jQuery.browser.version = browserMatch.version;
1516+}
1517+
1518+// Deprecated, use jQuery.browser.webkit instead
1519+if ( jQuery.browser.webkit ) {
1520+ jQuery.browser.safari = true;
1521+}
1522+
1523+// IE doesn't match non-breaking spaces with \s
1524+if ( rnotwhite.test( "\xA0" ) ) {
1525+ trimLeft = /^[\s\xA0]+/;
1526+ trimRight = /[\s\xA0]+$/;
1527+}
1528+
1529+// All jQuery objects should point back to these
1530+rootjQuery = jQuery(document);
1531+
1532+// Cleanup functions for the document ready method
1533+if ( document.addEventListener ) {
1534+ DOMContentLoaded = function() {
1535+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
1536+ jQuery.ready();
1537+ };
1538+
1539+} else if ( document.attachEvent ) {
1540+ DOMContentLoaded = function() {
1541+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
1542+ if ( document.readyState === "complete" ) {
1543+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
1544+ jQuery.ready();
1545+ }
1546+ };
1547+}
1548+
1549+// The DOM ready check for Internet Explorer
1550+function doScrollCheck() {
1551+ if ( jQuery.isReady ) {
1552+ return;
1553+ }
1554+
1555+ try {
1556+ // If IE is used, use the trick by Diego Perini
1557+ // http://javascript.nwbox.com/IEContentLoaded/
1558+ document.documentElement.doScroll("left");
1559+ } catch(e) {
1560+ setTimeout( doScrollCheck, 1 );
1561+ return;
1562+ }
1563+
1564+ // and execute any waiting functions
1565+ jQuery.ready();
1566+}
1567+
1568+return jQuery;
1569+
1570+})();
1571+
1572+
1573+// String to Object flags format cache
1574+var flagsCache = {};
1575+
1576+// Convert String-formatted flags into Object-formatted ones and store in cache
1577+function createFlags( flags ) {
1578+ var object = flagsCache[ flags ] = {},
1579+ i, length;
1580+ flags = flags.split( /\s+/ );
1581+ for ( i = 0, length = flags.length; i < length; i++ ) {
1582+ object[ flags[i] ] = true;
1583+ }
1584+ return object;
1585+}
1586+
1587+/*
1588+ * Create a callback list using the following parameters:
1589+ *
1590+ * flags: an optional list of space-separated flags that will change how
1591+ * the callback list behaves
1592+ *
1593+ * By default a callback list will act like an event callback list and can be
1594+ * "fired" multiple times.
1595+ *
1596+ * Possible flags:
1597+ *
1598+ * once: will ensure the callback list can only be fired once (like a Deferred)
1599+ *
1600+ * memory: will keep track of previous values and will call any callback added
1601+ * after the list has been fired right away with the latest "memorized"
1602+ * values (like a Deferred)
1603+ *
1604+ * unique: will ensure a callback can only be added once (no duplicate in the list)
1605+ *
1606+ * stopOnFalse: interrupt callings when a callback returns false
1607+ *
1608+ */
1609+jQuery.Callbacks = function( flags ) {
1610+
1611+ // Convert flags from String-formatted to Object-formatted
1612+ // (we check in cache first)
1613+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
1614+
1615+ var // Actual callback list
1616+ list = [],
1617+ // Stack of fire calls for repeatable lists
1618+ stack = [],
1619+ // Last fire value (for non-forgettable lists)
1620+ memory,
1621+ // Flag to know if list was already fired
1622+ fired,
1623+ // Flag to know if list is currently firing
1624+ firing,
1625+ // First callback to fire (used internally by add and fireWith)
1626+ firingStart,
1627+ // End of the loop when firing
1628+ firingLength,
1629+ // Index of currently firing callback (modified by remove if needed)
1630+ firingIndex,
1631+ // Add one or several callbacks to the list
1632+ add = function( args ) {
1633+ var i,
1634+ length,
1635+ elem,
1636+ type,
1637+ actual;
1638+ for ( i = 0, length = args.length; i < length; i++ ) {
1639+ elem = args[ i ];
1640+ type = jQuery.type( elem );
1641+ if ( type === "array" ) {
1642+ // Inspect recursively
1643+ add( elem );
1644+ } else if ( type === "function" ) {
1645+ // Add if not in unique mode and callback is not in
1646+ if ( !flags.unique || !self.has( elem ) ) {
1647+ list.push( elem );
1648+ }
1649+ }
1650+ }
1651+ },
1652+ // Fire callbacks
1653+ fire = function( context, args ) {
1654+ args = args || [];
1655+ memory = !flags.memory || [ context, args ];
1656+ fired = true;
1657+ firing = true;
1658+ firingIndex = firingStart || 0;
1659+ firingStart = 0;
1660+ firingLength = list.length;
1661+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
1662+ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
1663+ memory = true; // Mark as halted
1664+ break;
1665+ }
1666+ }
1667+ firing = false;
1668+ if ( list ) {
1669+ if ( !flags.once ) {
1670+ if ( stack && stack.length ) {
1671+ memory = stack.shift();
1672+ self.fireWith( memory[ 0 ], memory[ 1 ] );
1673+ }
1674+ } else if ( memory === true ) {
1675+ self.disable();
1676+ } else {
1677+ list = [];
1678+ }
1679+ }
1680+ },
1681+ // Actual Callbacks object
1682+ self = {
1683+ // Add a callback or a collection of callbacks to the list
1684+ add: function() {
1685+ if ( list ) {
1686+ var length = list.length;
1687+ add( arguments );
1688+ // Do we need to add the callbacks to the
1689+ // current firing batch?
1690+ if ( firing ) {
1691+ firingLength = list.length;
1692+ // With memory, if we're not firing then
1693+ // we should call right away, unless previous
1694+ // firing was halted (stopOnFalse)
1695+ } else if ( memory && memory !== true ) {
1696+ firingStart = length;
1697+ fire( memory[ 0 ], memory[ 1 ] );
1698+ }
1699+ }
1700+ return this;
1701+ },
1702+ // Remove a callback from the list
1703+ remove: function() {
1704+ if ( list ) {
1705+ var args = arguments,
1706+ argIndex = 0,
1707+ argLength = args.length;
1708+ for ( ; argIndex < argLength ; argIndex++ ) {
1709+ for ( var i = 0; i < list.length; i++ ) {
1710+ if ( args[ argIndex ] === list[ i ] ) {
1711+ // Handle firingIndex and firingLength
1712+ if ( firing ) {
1713+ if ( i <= firingLength ) {
1714+ firingLength--;
1715+ if ( i <= firingIndex ) {
1716+ firingIndex--;
1717+ }
1718+ }
1719+ }
1720+ // Remove the element
1721+ list.splice( i--, 1 );
1722+ // If we have some unicity property then
1723+ // we only need to do this once
1724+ if ( flags.unique ) {
1725+ break;
1726+ }
1727+ }
1728+ }
1729+ }
1730+ }
1731+ return this;
1732+ },
1733+ // Control if a given callback is in the list
1734+ has: function( fn ) {
1735+ if ( list ) {
1736+ var i = 0,
1737+ length = list.length;
1738+ for ( ; i < length; i++ ) {
1739+ if ( fn === list[ i ] ) {
1740+ return true;
1741+ }
1742+ }
1743+ }
1744+ return false;
1745+ },
1746+ // Remove all callbacks from the list
1747+ empty: function() {
1748+ list = [];
1749+ return this;
1750+ },
1751+ // Have the list do nothing anymore
1752+ disable: function() {
1753+ list = stack = memory = undefined;
1754+ return this;
1755+ },
1756+ // Is it disabled?
1757+ disabled: function() {
1758+ return !list;
1759+ },
1760+ // Lock the list in its current state
1761+ lock: function() {
1762+ stack = undefined;
1763+ if ( !memory || memory === true ) {
1764+ self.disable();
1765+ }
1766+ return this;
1767+ },
1768+ // Is it locked?
1769+ locked: function() {
1770+ return !stack;
1771+ },
1772+ // Call all callbacks with the given context and arguments
1773+ fireWith: function( context, args ) {
1774+ if ( stack ) {
1775+ if ( firing ) {
1776+ if ( !flags.once ) {
1777+ stack.push( [ context, args ] );
1778+ }
1779+ } else if ( !( flags.once && memory ) ) {
1780+ fire( context, args );
1781+ }
1782+ }
1783+ return this;
1784+ },
1785+ // Call all the callbacks with the given arguments
1786+ fire: function() {
1787+ self.fireWith( this, arguments );
1788+ return this;
1789+ },
1790+ // To know if the callbacks have already been called at least once
1791+ fired: function() {
1792+ return !!fired;
1793+ }
1794+ };
1795+
1796+ return self;
1797+};
1798+
1799+
1800+
1801+
1802+var // Static reference to slice
1803+ sliceDeferred = [].slice;
1804+
1805+jQuery.extend({
1806+
1807+ Deferred: function( func ) {
1808+ var doneList = jQuery.Callbacks( "once memory" ),
1809+ failList = jQuery.Callbacks( "once memory" ),
1810+ progressList = jQuery.Callbacks( "memory" ),
1811+ state = "pending",
1812+ lists = {
1813+ resolve: doneList,
1814+ reject: failList,
1815+ notify: progressList
1816+ },
1817+ promise = {
1818+ done: doneList.add,
1819+ fail: failList.add,
1820+ progress: progressList.add,
1821+
1822+ state: function() {
1823+ return state;
1824+ },
1825+
1826+ // Deprecated
1827+ isResolved: doneList.fired,
1828+ isRejected: failList.fired,
1829+
1830+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
1831+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
1832+ return this;
1833+ },
1834+ always: function() {
1835+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
1836+ return this;
1837+ },
1838+ pipe: function( fnDone, fnFail, fnProgress ) {
1839+ return jQuery.Deferred(function( newDefer ) {
1840+ jQuery.each( {
1841+ done: [ fnDone, "resolve" ],
1842+ fail: [ fnFail, "reject" ],
1843+ progress: [ fnProgress, "notify" ]
1844+ }, function( handler, data ) {
1845+ var fn = data[ 0 ],
1846+ action = data[ 1 ],
1847+ returned;
1848+ if ( jQuery.isFunction( fn ) ) {
1849+ deferred[ handler ](function() {
1850+ returned = fn.apply( this, arguments );
1851+ if ( returned && jQuery.isFunction( returned.promise ) ) {
1852+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
1853+ } else {
1854+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
1855+ }
1856+ });
1857+ } else {
1858+ deferred[ handler ]( newDefer[ action ] );
1859+ }
1860+ });
1861+ }).promise();
1862+ },
1863+ // Get a promise for this deferred
1864+ // If obj is provided, the promise aspect is added to the object
1865+ promise: function( obj ) {
1866+ if ( obj == null ) {
1867+ obj = promise;
1868+ } else {
1869+ for ( var key in promise ) {
1870+ obj[ key ] = promise[ key ];
1871+ }
1872+ }
1873+ return obj;
1874+ }
1875+ },
1876+ deferred = promise.promise({}),
1877+ key;
1878+
1879+ for ( key in lists ) {
1880+ deferred[ key ] = lists[ key ].fire;
1881+ deferred[ key + "With" ] = lists[ key ].fireWith;
1882+ }
1883+
1884+ // Handle state
1885+ deferred.done( function() {
1886+ state = "resolved";
1887+ }, failList.disable, progressList.lock ).fail( function() {
1888+ state = "rejected";
1889+ }, doneList.disable, progressList.lock );
1890+
1891+ // Call given func if any
1892+ if ( func ) {
1893+ func.call( deferred, deferred );
1894+ }
1895+
1896+ // All done!
1897+ return deferred;
1898+ },
1899+
1900+ // Deferred helper
1901+ when: function( firstParam ) {
1902+ var args = sliceDeferred.call( arguments, 0 ),
1903+ i = 0,
1904+ length = args.length,
1905+ pValues = new Array( length ),
1906+ count = length,
1907+ pCount = length,
1908+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
1909+ firstParam :
1910+ jQuery.Deferred(),
1911+ promise = deferred.promise();
1912+ function resolveFunc( i ) {
1913+ return function( value ) {
1914+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
1915+ if ( !( --count ) ) {
1916+ deferred.resolveWith( deferred, args );
1917+ }
1918+ };
1919+ }
1920+ function progressFunc( i ) {
1921+ return function( value ) {
1922+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
1923+ deferred.notifyWith( promise, pValues );
1924+ };
1925+ }
1926+ if ( length > 1 ) {
1927+ for ( ; i < length; i++ ) {
1928+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
1929+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
1930+ } else {
1931+ --count;
1932+ }
1933+ }
1934+ if ( !count ) {
1935+ deferred.resolveWith( deferred, args );
1936+ }
1937+ } else if ( deferred !== firstParam ) {
1938+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
1939+ }
1940+ return promise;
1941+ }
1942+});
1943+
1944+
1945+
1946+
1947+jQuery.support = (function() {
1948+
1949+ var support,
1950+ all,
1951+ a,
1952+ select,
1953+ opt,
1954+ input,
1955+ fragment,
1956+ tds,
1957+ events,
1958+ eventName,
1959+ i,
1960+ isSupported,
1961+ div = document.createElement( "div" ),
1962+ documentElement = document.documentElement;
1963+
1964+ // Preliminary tests
1965+ div.setAttribute("className", "t");
1966+ div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
1967+
1968+ all = div.getElementsByTagName( "*" );
1969+ a = div.getElementsByTagName( "a" )[ 0 ];
1970+
1971+ // Can't get basic test support
1972+ if ( !all || !all.length || !a ) {
1973+ return {};
1974+ }
1975+
1976+ // First batch of supports tests
1977+ select = document.createElement( "select" );
1978+ opt = select.appendChild( document.createElement("option") );
1979+ input = div.getElementsByTagName( "input" )[ 0 ];
1980+
1981+ support = {
1982+ // IE strips leading whitespace when .innerHTML is used
1983+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
1984+
1985+ // Make sure that tbody elements aren't automatically inserted
1986+ // IE will insert them into empty tables
1987+ tbody: !div.getElementsByTagName("tbody").length,
1988+
1989+ // Make sure that link elements get serialized correctly by innerHTML
1990+ // This requires a wrapper element in IE
1991+ htmlSerialize: !!div.getElementsByTagName("link").length,
1992+
1993+ // Get the style information from getAttribute
1994+ // (IE uses .cssText instead)
1995+ style: /top/.test( a.getAttribute("style") ),
1996+
1997+ // Make sure that URLs aren't manipulated
1998+ // (IE normalizes it by default)
1999+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
2000+
2001+ // Make sure that element opacity exists
2002+ // (IE uses filter instead)
2003+ // Use a regex to work around a WebKit issue. See #5145
2004+ opacity: /^0.55/.test( a.style.opacity ),
2005+
2006+ // Verify style float existence
2007+ // (IE uses styleFloat instead of cssFloat)
2008+ cssFloat: !!a.style.cssFloat,
2009+
2010+ // Make sure that if no value is specified for a checkbox
2011+ // that it defaults to "on".
2012+ // (WebKit defaults to "" instead)
2013+ checkOn: ( input.value === "on" ),
2014+
2015+ // Make sure that a selected-by-default option has a working selected property.
2016+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
2017+ optSelected: opt.selected,
2018+
2019+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
2020+ getSetAttribute: div.className !== "t",
2021+
2022+ // Tests for enctype support on a form(#6743)
2023+ enctype: !!document.createElement("form").enctype,
2024+
2025+ // Makes sure cloning an html5 element does not cause problems
2026+ // Where outerHTML is undefined, this still works
2027+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>",
2028+
2029+ // Will be defined later
2030+ submitBubbles: true,
2031+ changeBubbles: true,
2032+ focusinBubbles: false,
2033+ deleteExpando: true,
2034+ noCloneEvent: true,
2035+ inlineBlockNeedsLayout: false,
2036+ shrinkWrapBlocks: false,
2037+ reliableMarginRight: true,
2038+ pixelMargin: true
2039+ };
2040+
2041+ // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
2042+ jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
2043+
2044+ // Make sure checked status is properly cloned
2045+ input.checked = true;
2046+ support.noCloneChecked = input.cloneNode( true ).checked;
2047+
2048+ // Make sure that the options inside disabled selects aren't marked as disabled
2049+ // (WebKit marks them as disabled)
2050+ select.disabled = true;
2051+ support.optDisabled = !opt.disabled;
2052+
2053+ // Test to see if it's possible to delete an expando from an element
2054+ // Fails in Internet Explorer
2055+ try {
2056+ delete div.test;
2057+ } catch( e ) {
2058+ support.deleteExpando = false;
2059+ }
2060+
2061+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
2062+ div.attachEvent( "onclick", function() {
2063+ // Cloning a node shouldn't copy over any
2064+ // bound event handlers (IE does this)
2065+ support.noCloneEvent = false;
2066+ });
2067+ div.cloneNode( true ).fireEvent( "onclick" );
2068+ }
2069+
2070+ // Check if a radio maintains its value
2071+ // after being appended to the DOM
2072+ input = document.createElement("input");
2073+ input.value = "t";
2074+ input.setAttribute("type", "radio");
2075+ support.radioValue = input.value === "t";
2076+
2077+ input.setAttribute("checked", "checked");
2078+
2079+ // #11217 - WebKit loses check when the name is after the checked attribute
2080+ input.setAttribute( "name", "t" );
2081+
2082+ div.appendChild( input );
2083+ fragment = document.createDocumentFragment();
2084+ fragment.appendChild( div.lastChild );
2085+
2086+ // WebKit doesn't clone checked state correctly in fragments
2087+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
2088+
2089+ // Check if a disconnected checkbox will retain its checked
2090+ // value of true after appended to the DOM (IE6/7)
2091+ support.appendChecked = input.checked;
2092+
2093+ fragment.removeChild( input );
2094+ fragment.appendChild( div );
2095+
2096+ // Technique from Juriy Zaytsev
2097+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
2098+ // We only care about the case where non-standard event systems
2099+ // are used, namely in IE. Short-circuiting here helps us to
2100+ // avoid an eval call (in setAttribute) which can cause CSP
2101+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
2102+ if ( div.attachEvent ) {
2103+ for ( i in {
2104+ submit: 1,
2105+ change: 1,
2106+ focusin: 1
2107+ }) {
2108+ eventName = "on" + i;
2109+ isSupported = ( eventName in div );
2110+ if ( !isSupported ) {
2111+ div.setAttribute( eventName, "return;" );
2112+ isSupported = ( typeof div[ eventName ] === "function" );
2113+ }
2114+ support[ i + "Bubbles" ] = isSupported;
2115+ }
2116+ }
2117+
2118+ fragment.removeChild( div );
2119+
2120+ // Null elements to avoid leaks in IE
2121+ fragment = select = opt = div = input = null;
2122+
2123+ // Run tests that need a body at doc ready
2124+ jQuery(function() {
2125+ var container, outer, inner, table, td, offsetSupport,
2126+ marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
2127+ paddingMarginBorderVisibility, paddingMarginBorder,
2128+ body = document.getElementsByTagName("body")[0];
2129+
2130+ if ( !body ) {
2131+ // Return for frameset docs that don't have a body
2132+ return;
2133+ }
2134+
2135+ conMarginTop = 1;
2136+ paddingMarginBorder = "padding:0;margin:0;border:";
2137+ positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
2138+ paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
2139+ style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
2140+ html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" +
2141+ "<table " + style + "' cellpadding='0' cellspacing='0'>" +
2142+ "<tr><td></td></tr></table>";
2143+
2144+ container = document.createElement("div");
2145+ container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
2146+ body.insertBefore( container, body.firstChild );
2147+
2148+ // Construct the test element
2149+ div = document.createElement("div");
2150+ container.appendChild( div );
2151+
2152+ // Check if table cells still have offsetWidth/Height when they are set
2153+ // to display:none and there are still other visible table cells in a
2154+ // table row; if so, offsetWidth/Height are not reliable for use when
2155+ // determining if an element has been hidden directly using
2156+ // display:none (it is still safe to use offsets if a parent element is
2157+ // hidden; don safety goggles and see bug #4512 for more information).
2158+ // (only IE 8 fails this test)
2159+ div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>";
2160+ tds = div.getElementsByTagName( "td" );
2161+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
2162+
2163+ tds[ 0 ].style.display = "";
2164+ tds[ 1 ].style.display = "none";
2165+
2166+ // Check if empty table cells still have offsetWidth/Height
2167+ // (IE <= 8 fail this test)
2168+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
2169+
2170+ // Check if div with explicit width and no margin-right incorrectly
2171+ // gets computed margin-right based on width of container. For more
2172+ // info see bug #3333
2173+ // Fails in WebKit before Feb 2011 nightlies
2174+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
2175+ if ( window.getComputedStyle ) {
2176+ div.innerHTML = "";
2177+ marginDiv = document.createElement( "div" );
2178+ marginDiv.style.width = "0";
2179+ marginDiv.style.marginRight = "0";
2180+ div.style.width = "2px";
2181+ div.appendChild( marginDiv );
2182+ support.reliableMarginRight =
2183+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
2184+ }
2185+
2186+ if ( typeof div.style.zoom !== "undefined" ) {
2187+ // Check if natively block-level elements act like inline-block
2188+ // elements when setting their display to 'inline' and giving
2189+ // them layout
2190+ // (IE < 8 does this)
2191+ div.innerHTML = "";
2192+ div.style.width = div.style.padding = "1px";
2193+ div.style.border = 0;
2194+ div.style.overflow = "hidden";
2195+ div.style.display = "inline";
2196+ div.style.zoom = 1;
2197+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
2198+
2199+ // Check if elements with layout shrink-wrap their children
2200+ // (IE 6 does this)
2201+ div.style.display = "block";
2202+ div.style.overflow = "visible";
2203+ div.innerHTML = "<div style='width:5px;'></div>";
2204+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
2205+ }
2206+
2207+ div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
2208+ div.innerHTML = html;
2209+
2210+ outer = div.firstChild;
2211+ inner = outer.firstChild;
2212+ td = outer.nextSibling.firstChild.firstChild;
2213+
2214+ offsetSupport = {
2215+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
2216+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
2217+ };
2218+
2219+ inner.style.position = "fixed";
2220+ inner.style.top = "20px";
2221+
2222+ // safari subtracts parent border width here which is 5px
2223+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
2224+ inner.style.position = inner.style.top = "";
2225+
2226+ outer.style.overflow = "hidden";
2227+ outer.style.position = "relative";
2228+
2229+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
2230+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
2231+
2232+ if ( window.getComputedStyle ) {
2233+ div.style.marginTop = "1%";
2234+ support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
2235+ }
2236+
2237+ if ( typeof container.style.zoom !== "undefined" ) {
2238+ container.style.zoom = 1;
2239+ }
2240+
2241+ body.removeChild( container );
2242+ marginDiv = div = container = null;
2243+
2244+ jQuery.extend( support, offsetSupport );
2245+ });
2246+
2247+ return support;
2248+})();
2249+
2250+
2251+
2252+
2253+var rbrace = /^(?:\{.*\}|\[.*\])$/,
2254+ rmultiDash = /([A-Z])/g;
2255+
2256+jQuery.extend({
2257+ cache: {},
2258+
2259+ // Please use with caution
2260+ uuid: 0,
2261+
2262+ // Unique for each copy of jQuery on the page
2263+ // Non-digits removed to match rinlinejQuery
2264+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
2265+
2266+ // The following elements throw uncatchable exceptions if you
2267+ // attempt to add expando properties to them.
2268+ noData: {
2269+ "embed": true,
2270+ // Ban all objects except for Flash (which handle expandos)
2271+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
2272+ "applet": true
2273+ },
2274+
2275+ hasData: function( elem ) {
2276+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
2277+ return !!elem && !isEmptyDataObject( elem );
2278+ },
2279+
2280+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
2281+ if ( !jQuery.acceptData( elem ) ) {
2282+ return;
2283+ }
2284+
2285+ var privateCache, thisCache, ret,
2286+ internalKey = jQuery.expando,
2287+ getByName = typeof name === "string",
2288+
2289+ // We have to handle DOM nodes and JS objects differently because IE6-7
2290+ // can't GC object references properly across the DOM-JS boundary
2291+ isNode = elem.nodeType,
2292+
2293+ // Only DOM nodes need the global jQuery cache; JS object data is
2294+ // attached directly to the object so GC can occur automatically
2295+ cache = isNode ? jQuery.cache : elem,
2296+
2297+ // Only defining an ID for JS objects if its cache already exists allows
2298+ // the code to shortcut on the same path as a DOM node with no cache
2299+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
2300+ isEvents = name === "events";
2301+
2302+ // Avoid doing any more work than we need to when trying to get data on an
2303+ // object that has no data at all
2304+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
2305+ return;
2306+ }
2307+
2308+ if ( !id ) {
2309+ // Only DOM nodes need a new unique ID for each element since their data
2310+ // ends up in the global cache
2311+ if ( isNode ) {
2312+ elem[ internalKey ] = id = ++jQuery.uuid;
2313+ } else {
2314+ id = internalKey;
2315+ }
2316+ }
2317+
2318+ if ( !cache[ id ] ) {
2319+ cache[ id ] = {};
2320+
2321+ // Avoids exposing jQuery metadata on plain JS objects when the object
2322+ // is serialized using JSON.stringify
2323+ if ( !isNode ) {
2324+ cache[ id ].toJSON = jQuery.noop;
2325+ }
2326+ }
2327+
2328+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
2329+ // shallow copied over onto the existing cache
2330+ if ( typeof name === "object" || typeof name === "function" ) {
2331+ if ( pvt ) {
2332+ cache[ id ] = jQuery.extend( cache[ id ], name );
2333+ } else {
2334+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
2335+ }
2336+ }
2337+
2338+ privateCache = thisCache = cache[ id ];
2339+
2340+ // jQuery data() is stored in a separate object inside the object's internal data
2341+ // cache in order to avoid key collisions between internal data and user-defined
2342+ // data.
2343+ if ( !pvt ) {
2344+ if ( !thisCache.data ) {
2345+ thisCache.data = {};
2346+ }
2347+
2348+ thisCache = thisCache.data;
2349+ }
2350+
2351+ if ( data !== undefined ) {
2352+ thisCache[ jQuery.camelCase( name ) ] = data;
2353+ }
2354+
2355+ // Users should not attempt to inspect the internal events object using jQuery.data,
2356+ // it is undocumented and subject to change. But does anyone listen? No.
2357+ if ( isEvents && !thisCache[ name ] ) {
2358+ return privateCache.events;
2359+ }
2360+
2361+ // Check for both converted-to-camel and non-converted data property names
2362+ // If a data property was specified
2363+ if ( getByName ) {
2364+
2365+ // First Try to find as-is property data
2366+ ret = thisCache[ name ];
2367+
2368+ // Test for null|undefined property data
2369+ if ( ret == null ) {
2370+
2371+ // Try to find the camelCased property
2372+ ret = thisCache[ jQuery.camelCase( name ) ];
2373+ }
2374+ } else {
2375+ ret = thisCache;
2376+ }
2377+
2378+ return ret;
2379+ },
2380+
2381+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
2382+ if ( !jQuery.acceptData( elem ) ) {
2383+ return;
2384+ }
2385+
2386+ var thisCache, i, l,
2387+
2388+ // Reference to internal data cache key
2389+ internalKey = jQuery.expando,
2390+
2391+ isNode = elem.nodeType,
2392+
2393+ // See jQuery.data for more information
2394+ cache = isNode ? jQuery.cache : elem,
2395+
2396+ // See jQuery.data for more information
2397+ id = isNode ? elem[ internalKey ] : internalKey;
2398+
2399+ // If there is already no cache entry for this object, there is no
2400+ // purpose in continuing
2401+ if ( !cache[ id ] ) {
2402+ return;
2403+ }
2404+
2405+ if ( name ) {
2406+
2407+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
2408+
2409+ if ( thisCache ) {
2410+
2411+ // Support array or space separated string names for data keys
2412+ if ( !jQuery.isArray( name ) ) {
2413+
2414+ // try the string as a key before any manipulation
2415+ if ( name in thisCache ) {
2416+ name = [ name ];
2417+ } else {
2418+
2419+ // split the camel cased version by spaces unless a key with the spaces exists
2420+ name = jQuery.camelCase( name );
2421+ if ( name in thisCache ) {
2422+ name = [ name ];
2423+ } else {
2424+ name = name.split( " " );
2425+ }
2426+ }
2427+ }
2428+
2429+ for ( i = 0, l = name.length; i < l; i++ ) {
2430+ delete thisCache[ name[i] ];
2431+ }
2432+
2433+ // If there is no data left in the cache, we want to continue
2434+ // and let the cache object itself get destroyed
2435+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
2436+ return;
2437+ }
2438+ }
2439+ }
2440+
2441+ // See jQuery.data for more information
2442+ if ( !pvt ) {
2443+ delete cache[ id ].data;
2444+
2445+ // Don't destroy the parent cache unless the internal data object
2446+ // had been the only thing left in it
2447+ if ( !isEmptyDataObject(cache[ id ]) ) {
2448+ return;
2449+ }
2450+ }
2451+
2452+ // Browsers that fail expando deletion also refuse to delete expandos on
2453+ // the window, but it will allow it on all other JS objects; other browsers
2454+ // don't care
2455+ // Ensure that `cache` is not a window object #10080
2456+ if ( jQuery.support.deleteExpando || !cache.setInterval ) {
2457+ delete cache[ id ];
2458+ } else {
2459+ cache[ id ] = null;
2460+ }
2461+
2462+ // We destroyed the cache and need to eliminate the expando on the node to avoid
2463+ // false lookups in the cache for entries that no longer exist
2464+ if ( isNode ) {
2465+ // IE does not allow us to delete expando properties from nodes,
2466+ // nor does it have a removeAttribute function on Document nodes;
2467+ // we must handle all of these cases
2468+ if ( jQuery.support.deleteExpando ) {
2469+ delete elem[ internalKey ];
2470+ } else if ( elem.removeAttribute ) {
2471+ elem.removeAttribute( internalKey );
2472+ } else {
2473+ elem[ internalKey ] = null;
2474+ }
2475+ }
2476+ },
2477+
2478+ // For internal use only.
2479+ _data: function( elem, name, data ) {
2480+ return jQuery.data( elem, name, data, true );
2481+ },
2482+
2483+ // A method for determining if a DOM node can handle the data expando
2484+ acceptData: function( elem ) {
2485+ if ( elem.nodeName ) {
2486+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
2487+
2488+ if ( match ) {
2489+ return !(match === true || elem.getAttribute("classid") !== match);
2490+ }
2491+ }
2492+
2493+ return true;
2494+ }
2495+});
2496+
2497+jQuery.fn.extend({
2498+ data: function( key, value ) {
2499+ var parts, part, attr, name, l,
2500+ elem = this[0],
2501+ i = 0,
2502+ data = null;
2503+
2504+ // Gets all values
2505+ if ( key === undefined ) {
2506+ if ( this.length ) {
2507+ data = jQuery.data( elem );
2508+
2509+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
2510+ attr = elem.attributes;
2511+ for ( l = attr.length; i < l; i++ ) {
2512+ name = attr[i].name;
2513+
2514+ if ( name.indexOf( "data-" ) === 0 ) {
2515+ name = jQuery.camelCase( name.substring(5) );
2516+
2517+ dataAttr( elem, name, data[ name ] );
2518+ }
2519+ }
2520+ jQuery._data( elem, "parsedAttrs", true );
2521+ }
2522+ }
2523+
2524+ return data;
2525+ }
2526+
2527+ // Sets multiple values
2528+ if ( typeof key === "object" ) {
2529+ return this.each(function() {
2530+ jQuery.data( this, key );
2531+ });
2532+ }
2533+
2534+ parts = key.split( ".", 2 );
2535+ parts[1] = parts[1] ? "." + parts[1] : "";
2536+ part = parts[1] + "!";
2537+
2538+ return jQuery.access( this, function( value ) {
2539+
2540+ if ( value === undefined ) {
2541+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
2542+
2543+ // Try to fetch any internally stored data first
2544+ if ( data === undefined && elem ) {
2545+ data = jQuery.data( elem, key );
2546+ data = dataAttr( elem, key, data );
2547+ }
2548+
2549+ return data === undefined && parts[1] ?
2550+ this.data( parts[0] ) :
2551+ data;
2552+ }
2553+
2554+ parts[1] = value;
2555+ this.each(function() {
2556+ var self = jQuery( this );
2557+
2558+ self.triggerHandler( "setData" + part, parts );
2559+ jQuery.data( this, key, value );
2560+ self.triggerHandler( "changeData" + part, parts );
2561+ });
2562+ }, null, value, arguments.length > 1, null, false );
2563+ },
2564+
2565+ removeData: function( key ) {
2566+ return this.each(function() {
2567+ jQuery.removeData( this, key );
2568+ });
2569+ }
2570+});
2571+
2572+function dataAttr( elem, key, data ) {
2573+ // If nothing was found internally, try to fetch any
2574+ // data from the HTML5 data-* attribute
2575+ if ( data === undefined && elem.nodeType === 1 ) {
2576+
2577+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
2578+
2579+ data = elem.getAttribute( name );
2580+
2581+ if ( typeof data === "string" ) {
2582+ try {
2583+ data = data === "true" ? true :
2584+ data === "false" ? false :
2585+ data === "null" ? null :
2586+ jQuery.isNumeric( data ) ? +data :
2587+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
2588+ data;
2589+ } catch( e ) {}
2590+
2591+ // Make sure we set the data so it isn't changed later
2592+ jQuery.data( elem, key, data );
2593+
2594+ } else {
2595+ data = undefined;
2596+ }
2597+ }
2598+
2599+ return data;
2600+}
2601+
2602+// checks a cache object for emptiness
2603+function isEmptyDataObject( obj ) {
2604+ for ( var name in obj ) {
2605+
2606+ // if the public data object is empty, the private is still empty
2607+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
2608+ continue;
2609+ }
2610+ if ( name !== "toJSON" ) {
2611+ return false;
2612+ }
2613+ }
2614+
2615+ return true;
2616+}
2617+
2618+
2619+
2620+
2621+function handleQueueMarkDefer( elem, type, src ) {
2622+ var deferDataKey = type + "defer",
2623+ queueDataKey = type + "queue",
2624+ markDataKey = type + "mark",
2625+ defer = jQuery._data( elem, deferDataKey );
2626+ if ( defer &&
2627+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
2628+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
2629+ // Give room for hard-coded callbacks to fire first
2630+ // and eventually mark/queue something else on the element
2631+ setTimeout( function() {
2632+ if ( !jQuery._data( elem, queueDataKey ) &&
2633+ !jQuery._data( elem, markDataKey ) ) {
2634+ jQuery.removeData( elem, deferDataKey, true );
2635+ defer.fire();
2636+ }
2637+ }, 0 );
2638+ }
2639+}
2640+
2641+jQuery.extend({
2642+
2643+ _mark: function( elem, type ) {
2644+ if ( elem ) {
2645+ type = ( type || "fx" ) + "mark";
2646+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
2647+ }
2648+ },
2649+
2650+ _unmark: function( force, elem, type ) {
2651+ if ( force !== true ) {
2652+ type = elem;
2653+ elem = force;
2654+ force = false;
2655+ }
2656+ if ( elem ) {
2657+ type = type || "fx";
2658+ var key = type + "mark",
2659+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
2660+ if ( count ) {
2661+ jQuery._data( elem, key, count );
2662+ } else {
2663+ jQuery.removeData( elem, key, true );
2664+ handleQueueMarkDefer( elem, type, "mark" );
2665+ }
2666+ }
2667+ },
2668+
2669+ queue: function( elem, type, data ) {
2670+ var q;
2671+ if ( elem ) {
2672+ type = ( type || "fx" ) + "queue";
2673+ q = jQuery._data( elem, type );
2674+
2675+ // Speed up dequeue by getting out quickly if this is just a lookup
2676+ if ( data ) {
2677+ if ( !q || jQuery.isArray(data) ) {
2678+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
2679+ } else {
2680+ q.push( data );
2681+ }
2682+ }
2683+ return q || [];
2684+ }
2685+ },
2686+
2687+ dequeue: function( elem, type ) {
2688+ type = type || "fx";
2689+
2690+ var queue = jQuery.queue( elem, type ),
2691+ fn = queue.shift(),
2692+ hooks = {};
2693+
2694+ // If the fx queue is dequeued, always remove the progress sentinel
2695+ if ( fn === "inprogress" ) {
2696+ fn = queue.shift();
2697+ }
2698+
2699+ if ( fn ) {
2700+ // Add a progress sentinel to prevent the fx queue from being
2701+ // automatically dequeued
2702+ if ( type === "fx" ) {
2703+ queue.unshift( "inprogress" );
2704+ }
2705+
2706+ jQuery._data( elem, type + ".run", hooks );
2707+ fn.call( elem, function() {
2708+ jQuery.dequeue( elem, type );
2709+ }, hooks );
2710+ }
2711+
2712+ if ( !queue.length ) {
2713+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
2714+ handleQueueMarkDefer( elem, type, "queue" );
2715+ }
2716+ }
2717+});
2718+
2719+jQuery.fn.extend({
2720+ queue: function( type, data ) {
2721+ var setter = 2;
2722+
2723+ if ( typeof type !== "string" ) {
2724+ data = type;
2725+ type = "fx";
2726+ setter--;
2727+ }
2728+
2729+ if ( arguments.length < setter ) {
2730+ return jQuery.queue( this[0], type );
2731+ }
2732+
2733+ return data === undefined ?
2734+ this :
2735+ this.each(function() {
2736+ var queue = jQuery.queue( this, type, data );
2737+
2738+ if ( type === "fx" && queue[0] !== "inprogress" ) {
2739+ jQuery.dequeue( this, type );
2740+ }
2741+ });
2742+ },
2743+ dequeue: function( type ) {
2744+ return this.each(function() {
2745+ jQuery.dequeue( this, type );
2746+ });
2747+ },
2748+ // Based off of the plugin by Clint Helfers, with permission.
2749+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
2750+ delay: function( time, type ) {
2751+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
2752+ type = type || "fx";
2753+
2754+ return this.queue( type, function( next, hooks ) {
2755+ var timeout = setTimeout( next, time );
2756+ hooks.stop = function() {
2757+ clearTimeout( timeout );
2758+ };
2759+ });
2760+ },
2761+ clearQueue: function( type ) {
2762+ return this.queue( type || "fx", [] );
2763+ },
2764+ // Get a promise resolved when queues of a certain type
2765+ // are emptied (fx is the type by default)
2766+ promise: function( type, object ) {
2767+ if ( typeof type !== "string" ) {
2768+ object = type;
2769+ type = undefined;
2770+ }
2771+ type = type || "fx";
2772+ var defer = jQuery.Deferred(),
2773+ elements = this,
2774+ i = elements.length,
2775+ count = 1,
2776+ deferDataKey = type + "defer",
2777+ queueDataKey = type + "queue",
2778+ markDataKey = type + "mark",
2779+ tmp;
2780+ function resolve() {
2781+ if ( !( --count ) ) {
2782+ defer.resolveWith( elements, [ elements ] );
2783+ }
2784+ }
2785+ while( i-- ) {
2786+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
2787+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
2788+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
2789+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
2790+ count++;
2791+ tmp.add( resolve );
2792+ }
2793+ }
2794+ resolve();
2795+ return defer.promise( object );
2796+ }
2797+});
2798+
2799+
2800+
2801+
2802+var rclass = /[\n\t\r]/g,
2803+ rspace = /\s+/,
2804+ rreturn = /\r/g,
2805+ rtype = /^(?:button|input)$/i,
2806+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
2807+ rclickable = /^a(?:rea)?$/i,
2808+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
2809+ getSetAttribute = jQuery.support.getSetAttribute,
2810+ nodeHook, boolHook, fixSpecified;
2811+
2812+jQuery.fn.extend({
2813+ attr: function( name, value ) {
2814+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
2815+ },
2816+
2817+ removeAttr: function( name ) {
2818+ return this.each(function() {
2819+ jQuery.removeAttr( this, name );
2820+ });
2821+ },
2822+
2823+ prop: function( name, value ) {
2824+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
2825+ },
2826+
2827+ removeProp: function( name ) {
2828+ name = jQuery.propFix[ name ] || name;
2829+ return this.each(function() {
2830+ // try/catch handles cases where IE balks (such as removing a property on window)
2831+ try {
2832+ this[ name ] = undefined;
2833+ delete this[ name ];
2834+ } catch( e ) {}
2835+ });
2836+ },
2837+
2838+ addClass: function( value ) {
2839+ var classNames, i, l, elem,
2840+ setClass, c, cl;
2841+
2842+ if ( jQuery.isFunction( value ) ) {
2843+ return this.each(function( j ) {
2844+ jQuery( this ).addClass( value.call(this, j, this.className) );
2845+ });
2846+ }
2847+
2848+ if ( value && typeof value === "string" ) {
2849+ classNames = value.split( rspace );
2850+
2851+ for ( i = 0, l = this.length; i < l; i++ ) {
2852+ elem = this[ i ];
2853+
2854+ if ( elem.nodeType === 1 ) {
2855+ if ( !elem.className && classNames.length === 1 ) {
2856+ elem.className = value;
2857+
2858+ } else {
2859+ setClass = " " + elem.className + " ";
2860+
2861+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
2862+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
2863+ setClass += classNames[ c ] + " ";
2864+ }
2865+ }
2866+ elem.className = jQuery.trim( setClass );
2867+ }
2868+ }
2869+ }
2870+ }
2871+
2872+ return this;
2873+ },
2874+
2875+ removeClass: function( value ) {
2876+ var classNames, i, l, elem, className, c, cl;
2877+
2878+ if ( jQuery.isFunction( value ) ) {
2879+ return this.each(function( j ) {
2880+ jQuery( this ).removeClass( value.call(this, j, this.className) );
2881+ });
2882+ }
2883+
2884+ if ( (value && typeof value === "string") || value === undefined ) {
2885+ classNames = ( value || "" ).split( rspace );
2886+
2887+ for ( i = 0, l = this.length; i < l; i++ ) {
2888+ elem = this[ i ];
2889+
2890+ if ( elem.nodeType === 1 && elem.className ) {
2891+ if ( value ) {
2892+ className = (" " + elem.className + " ").replace( rclass, " " );
2893+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
2894+ className = className.replace(" " + classNames[ c ] + " ", " ");
2895+ }
2896+ elem.className = jQuery.trim( className );
2897+
2898+ } else {
2899+ elem.className = "";
2900+ }
2901+ }
2902+ }
2903+ }
2904+
2905+ return this;
2906+ },
2907+
2908+ toggleClass: function( value, stateVal ) {
2909+ var type = typeof value,
2910+ isBool = typeof stateVal === "boolean";
2911+
2912+ if ( jQuery.isFunction( value ) ) {
2913+ return this.each(function( i ) {
2914+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
2915+ });
2916+ }
2917+
2918+ return this.each(function() {
2919+ if ( type === "string" ) {
2920+ // toggle individual class names
2921+ var className,
2922+ i = 0,
2923+ self = jQuery( this ),
2924+ state = stateVal,
2925+ classNames = value.split( rspace );
2926+
2927+ while ( (className = classNames[ i++ ]) ) {
2928+ // check each className given, space seperated list
2929+ state = isBool ? state : !self.hasClass( className );
2930+ self[ state ? "addClass" : "removeClass" ]( className );
2931+ }
2932+
2933+ } else if ( type === "undefined" || type === "boolean" ) {
2934+ if ( this.className ) {
2935+ // store className if set
2936+ jQuery._data( this, "__className__", this.className );
2937+ }
2938+
2939+ // toggle whole className
2940+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
2941+ }
2942+ });
2943+ },
2944+
2945+ hasClass: function( selector ) {
2946+ var className = " " + selector + " ",
2947+ i = 0,
2948+ l = this.length;
2949+ for ( ; i < l; i++ ) {
2950+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
2951+ return true;
2952+ }
2953+ }
2954+
2955+ return false;
2956+ },
2957+
2958+ val: function( value ) {
2959+ var hooks, ret, isFunction,
2960+ elem = this[0];
2961+
2962+ if ( !arguments.length ) {
2963+ if ( elem ) {
2964+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
2965+
2966+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
2967+ return ret;
2968+ }
2969+
2970+ ret = elem.value;
2971+
2972+ return typeof ret === "string" ?
2973+ // handle most common string cases
2974+ ret.replace(rreturn, "") :
2975+ // handle cases where value is null/undef or number
2976+ ret == null ? "" : ret;
2977+ }
2978+
2979+ return;
2980+ }
2981+
2982+ isFunction = jQuery.isFunction( value );
2983+
2984+ return this.each(function( i ) {
2985+ var self = jQuery(this), val;
2986+
2987+ if ( this.nodeType !== 1 ) {
2988+ return;
2989+ }
2990+
2991+ if ( isFunction ) {
2992+ val = value.call( this, i, self.val() );
2993+ } else {
2994+ val = value;
2995+ }
2996+
2997+ // Treat null/undefined as ""; convert numbers to string
2998+ if ( val == null ) {
2999+ val = "";
3000+ } else if ( typeof val === "number" ) {
3001+ val += "";
3002+ } else if ( jQuery.isArray( val ) ) {
3003+ val = jQuery.map(val, function ( value ) {
3004+ return value == null ? "" : value + "";
3005+ });
3006+ }
3007+
3008+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
3009+
3010+ // If set returns undefined, fall back to normal setting
3011+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
3012+ this.value = val;
3013+ }
3014+ });
3015+ }
3016+});
3017+
3018+jQuery.extend({
3019+ valHooks: {
3020+ option: {
3021+ get: function( elem ) {
3022+ // attributes.value is undefined in Blackberry 4.7 but
3023+ // uses .value. See #6932
3024+ var val = elem.attributes.value;
3025+ return !val || val.specified ? elem.value : elem.text;
3026+ }
3027+ },
3028+ select: {
3029+ get: function( elem ) {
3030+ var value, i, max, option,
3031+ index = elem.selectedIndex,
3032+ values = [],
3033+ options = elem.options,
3034+ one = elem.type === "select-one";
3035+
3036+ // Nothing was selected
3037+ if ( index < 0 ) {
3038+ return null;
3039+ }
3040+
3041+ // Loop through all the selected options
3042+ i = one ? index : 0;
3043+ max = one ? index + 1 : options.length;
3044+ for ( ; i < max; i++ ) {
3045+ option = options[ i ];
3046+
3047+ // Don't return options that are disabled or in a disabled optgroup
3048+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
3049+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
3050+
3051+ // Get the specific value for the option
3052+ value = jQuery( option ).val();
3053+
3054+ // We don't need an array for one selects
3055+ if ( one ) {
3056+ return value;
3057+ }
3058+
3059+ // Multi-Selects return an array
3060+ values.push( value );
3061+ }
3062+ }
3063+
3064+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
3065+ if ( one && !values.length && options.length ) {
3066+ return jQuery( options[ index ] ).val();
3067+ }
3068+
3069+ return values;
3070+ },
3071+
3072+ set: function( elem, value ) {
3073+ var values = jQuery.makeArray( value );
3074+
3075+ jQuery(elem).find("option").each(function() {
3076+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
3077+ });
3078+
3079+ if ( !values.length ) {
3080+ elem.selectedIndex = -1;
3081+ }
3082+ return values;
3083+ }
3084+ }
3085+ },
3086+
3087+ attrFn: {
3088+ val: true,
3089+ css: true,
3090+ html: true,
3091+ text: true,
3092+ data: true,
3093+ width: true,
3094+ height: true,
3095+ offset: true
3096+ },
3097+
3098+ attr: function( elem, name, value, pass ) {
3099+ var ret, hooks, notxml,
3100+ nType = elem.nodeType;
3101+
3102+ // don't get/set attributes on text, comment and attribute nodes
3103+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
3104+ return;
3105+ }
3106+
3107+ if ( pass && name in jQuery.attrFn ) {
3108+ return jQuery( elem )[ name ]( value );
3109+ }
3110+
3111+ // Fallback to prop when attributes are not supported
3112+ if ( typeof elem.getAttribute === "undefined" ) {
3113+ return jQuery.prop( elem, name, value );
3114+ }
3115+
3116+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
3117+
3118+ // All attributes are lowercase
3119+ // Grab necessary hook if one is defined
3120+ if ( notxml ) {
3121+ name = name.toLowerCase();
3122+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
3123+ }
3124+
3125+ if ( value !== undefined ) {
3126+
3127+ if ( value === null ) {
3128+ jQuery.removeAttr( elem, name );
3129+ return;
3130+
3131+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
3132+ return ret;
3133+
3134+ } else {
3135+ elem.setAttribute( name, "" + value );
3136+ return value;
3137+ }
3138+
3139+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
3140+ return ret;
3141+
3142+ } else {
3143+
3144+ ret = elem.getAttribute( name );
3145+
3146+ // Non-existent attributes return null, we normalize to undefined
3147+ return ret === null ?
3148+ undefined :
3149+ ret;
3150+ }
3151+ },
3152+
3153+ removeAttr: function( elem, value ) {
3154+ var propName, attrNames, name, l, isBool,
3155+ i = 0;
3156+
3157+ if ( value && elem.nodeType === 1 ) {
3158+ attrNames = value.toLowerCase().split( rspace );
3159+ l = attrNames.length;
3160+
3161+ for ( ; i < l; i++ ) {
3162+ name = attrNames[ i ];
3163+
3164+ if ( name ) {
3165+ propName = jQuery.propFix[ name ] || name;
3166+ isBool = rboolean.test( name );
3167+
3168+ // See #9699 for explanation of this approach (setting first, then removal)
3169+ // Do not do this for boolean attributes (see #10870)
3170+ if ( !isBool ) {
3171+ jQuery.attr( elem, name, "" );
3172+ }
3173+ elem.removeAttribute( getSetAttribute ? name : propName );
3174+
3175+ // Set corresponding property to false for boolean attributes
3176+ if ( isBool && propName in elem ) {
3177+ elem[ propName ] = false;
3178+ }
3179+ }
3180+ }
3181+ }
3182+ },
3183+
3184+ attrHooks: {
3185+ type: {
3186+ set: function( elem, value ) {
3187+ // We can't allow the type property to be changed (since it causes problems in IE)
3188+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
3189+ jQuery.error( "type property can't be changed" );
3190+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
3191+ // Setting the type on a radio button after the value resets the value in IE6-9
3192+ // Reset value to it's default in case type is set after value
3193+ // This is for element creation
3194+ var val = elem.value;
3195+ elem.setAttribute( "type", value );
3196+ if ( val ) {
3197+ elem.value = val;
3198+ }
3199+ return value;
3200+ }
3201+ }
3202+ },
3203+ // Use the value property for back compat
3204+ // Use the nodeHook for button elements in IE6/7 (#1954)
3205+ value: {
3206+ get: function( elem, name ) {
3207+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
3208+ return nodeHook.get( elem, name );
3209+ }
3210+ return name in elem ?
3211+ elem.value :
3212+ null;
3213+ },
3214+ set: function( elem, value, name ) {
3215+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
3216+ return nodeHook.set( elem, value, name );
3217+ }
3218+ // Does not return so that setAttribute is also used
3219+ elem.value = value;
3220+ }
3221+ }
3222+ },
3223+
3224+ propFix: {
3225+ tabindex: "tabIndex",
3226+ readonly: "readOnly",
3227+ "for": "htmlFor",
3228+ "class": "className",
3229+ maxlength: "maxLength",
3230+ cellspacing: "cellSpacing",
3231+ cellpadding: "cellPadding",
3232+ rowspan: "rowSpan",
3233+ colspan: "colSpan",
3234+ usemap: "useMap",
3235+ frameborder: "frameBorder",
3236+ contenteditable: "contentEditable"
3237+ },
3238+
3239+ prop: function( elem, name, value ) {
3240+ var ret, hooks, notxml,
3241+ nType = elem.nodeType;
3242+
3243+ // don't get/set properties on text, comment and attribute nodes
3244+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
3245+ return;
3246+ }
3247+
3248+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
3249+
3250+ if ( notxml ) {
3251+ // Fix name and attach hooks
3252+ name = jQuery.propFix[ name ] || name;
3253+ hooks = jQuery.propHooks[ name ];
3254+ }
3255+
3256+ if ( value !== undefined ) {
3257+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
3258+ return ret;
3259+
3260+ } else {
3261+ return ( elem[ name ] = value );
3262+ }
3263+
3264+ } else {
3265+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
3266+ return ret;
3267+
3268+ } else {
3269+ return elem[ name ];
3270+ }
3271+ }
3272+ },
3273+
3274+ propHooks: {
3275+ tabIndex: {
3276+ get: function( elem ) {
3277+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
3278+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
3279+ var attributeNode = elem.getAttributeNode("tabindex");
3280+
3281+ return attributeNode && attributeNode.specified ?
3282+ parseInt( attributeNode.value, 10 ) :
3283+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
3284+ 0 :
3285+ undefined;
3286+ }
3287+ }
3288+ }
3289+});
3290+
3291+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
3292+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
3293+
3294+// Hook for boolean attributes
3295+boolHook = {
3296+ get: function( elem, name ) {
3297+ // Align boolean attributes with corresponding properties
3298+ // Fall back to attribute presence where some booleans are not supported
3299+ var attrNode,
3300+ property = jQuery.prop( elem, name );
3301+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
3302+ name.toLowerCase() :
3303+ undefined;
3304+ },
3305+ set: function( elem, value, name ) {
3306+ var propName;
3307+ if ( value === false ) {
3308+ // Remove boolean attributes when set to false
3309+ jQuery.removeAttr( elem, name );
3310+ } else {
3311+ // value is true since we know at this point it's type boolean and not false
3312+ // Set boolean attributes to the same name and set the DOM property
3313+ propName = jQuery.propFix[ name ] || name;
3314+ if ( propName in elem ) {
3315+ // Only set the IDL specifically if it already exists on the element
3316+ elem[ propName ] = true;
3317+ }
3318+
3319+ elem.setAttribute( name, name.toLowerCase() );
3320+ }
3321+ return name;
3322+ }
3323+};
3324+
3325+// IE6/7 do not support getting/setting some attributes with get/setAttribute
3326+if ( !getSetAttribute ) {
3327+
3328+ fixSpecified = {
3329+ name: true,
3330+ id: true,
3331+ coords: true
3332+ };
3333+
3334+ // Use this for any attribute in IE6/7
3335+ // This fixes almost every IE6/7 issue
3336+ nodeHook = jQuery.valHooks.button = {
3337+ get: function( elem, name ) {
3338+ var ret;
3339+ ret = elem.getAttributeNode( name );
3340+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
3341+ ret.nodeValue :
3342+ undefined;
3343+ },
3344+ set: function( elem, value, name ) {
3345+ // Set the existing or create a new attribute node
3346+ var ret = elem.getAttributeNode( name );
3347+ if ( !ret ) {
3348+ ret = document.createAttribute( name );
3349+ elem.setAttributeNode( ret );
3350+ }
3351+ return ( ret.nodeValue = value + "" );
3352+ }
3353+ };
3354+
3355+ // Apply the nodeHook to tabindex
3356+ jQuery.attrHooks.tabindex.set = nodeHook.set;
3357+
3358+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
3359+ // This is for removals
3360+ jQuery.each([ "width", "height" ], function( i, name ) {
3361+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
3362+ set: function( elem, value ) {
3363+ if ( value === "" ) {
3364+ elem.setAttribute( name, "auto" );
3365+ return value;
3366+ }
3367+ }
3368+ });
3369+ });
3370+
3371+ // Set contenteditable to false on removals(#10429)
3372+ // Setting to empty string throws an error as an invalid value
3373+ jQuery.attrHooks.contenteditable = {
3374+ get: nodeHook.get,
3375+ set: function( elem, value, name ) {
3376+ if ( value === "" ) {
3377+ value = "false";
3378+ }
3379+ nodeHook.set( elem, value, name );
3380+ }
3381+ };
3382+}
3383+
3384+
3385+// Some attributes require a special call on IE
3386+if ( !jQuery.support.hrefNormalized ) {
3387+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
3388+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
3389+ get: function( elem ) {
3390+ var ret = elem.getAttribute( name, 2 );
3391+ return ret === null ? undefined : ret;
3392+ }
3393+ });
3394+ });
3395+}
3396+
3397+if ( !jQuery.support.style ) {
3398+ jQuery.attrHooks.style = {
3399+ get: function( elem ) {
3400+ // Return undefined in the case of empty string
3401+ // Normalize to lowercase since IE uppercases css property names
3402+ return elem.style.cssText.toLowerCase() || undefined;
3403+ },
3404+ set: function( elem, value ) {
3405+ return ( elem.style.cssText = "" + value );
3406+ }
3407+ };
3408+}
3409+
3410+// Safari mis-reports the default selected property of an option
3411+// Accessing the parent's selectedIndex property fixes it
3412+if ( !jQuery.support.optSelected ) {
3413+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
3414+ get: function( elem ) {
3415+ var parent = elem.parentNode;
3416+
3417+ if ( parent ) {
3418+ parent.selectedIndex;
3419+
3420+ // Make sure that it also works with optgroups, see #5701
3421+ if ( parent.parentNode ) {
3422+ parent.parentNode.selectedIndex;
3423+ }
3424+ }
3425+ return null;
3426+ }
3427+ });
3428+}
3429+
3430+// IE6/7 call enctype encoding
3431+if ( !jQuery.support.enctype ) {
3432+ jQuery.propFix.enctype = "encoding";
3433+}
3434+
3435+// Radios and checkboxes getter/setter
3436+if ( !jQuery.support.checkOn ) {
3437+ jQuery.each([ "radio", "checkbox" ], function() {
3438+ jQuery.valHooks[ this ] = {
3439+ get: function( elem ) {
3440+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
3441+ return elem.getAttribute("value") === null ? "on" : elem.value;
3442+ }
3443+ };
3444+ });
3445+}
3446+jQuery.each([ "radio", "checkbox" ], function() {
3447+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
3448+ set: function( elem, value ) {
3449+ if ( jQuery.isArray( value ) ) {
3450+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
3451+ }
3452+ }
3453+ });
3454+});
3455+
3456+
3457+
3458+
3459+var rformElems = /^(?:textarea|input|select)$/i,
3460+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
3461+ rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
3462+ rkeyEvent = /^key/,
3463+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
3464+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
3465+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
3466+ quickParse = function( selector ) {
3467+ var quick = rquickIs.exec( selector );
3468+ if ( quick ) {
3469+ // 0 1 2 3
3470+ // [ _, tag, id, class ]
3471+ quick[1] = ( quick[1] || "" ).toLowerCase();
3472+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
3473+ }
3474+ return quick;
3475+ },
3476+ quickIs = function( elem, m ) {
3477+ var attrs = elem.attributes || {};
3478+ return (
3479+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
3480+ (!m[2] || (attrs.id || {}).value === m[2]) &&
3481+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
3482+ );
3483+ },
3484+ hoverHack = function( events ) {
3485+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
3486+ };
3487+
3488+/*
3489+ * Helper functions for managing events -- not part of the public interface.
3490+ * Props to Dean Edwards' addEvent library for many of the ideas.
3491+ */
3492+jQuery.event = {
3493+
3494+ add: function( elem, types, handler, data, selector ) {
3495+
3496+ var elemData, eventHandle, events,
3497+ t, tns, type, namespaces, handleObj,
3498+ handleObjIn, quick, handlers, special;
3499+
3500+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
3501+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
3502+ return;
3503+ }
3504+
3505+ // Caller can pass in an object of custom data in lieu of the handler
3506+ if ( handler.handler ) {
3507+ handleObjIn = handler;
3508+ handler = handleObjIn.handler;
3509+ selector = handleObjIn.selector;
3510+ }
3511+
3512+ // Make sure that the handler has a unique ID, used to find/remove it later
3513+ if ( !handler.guid ) {
3514+ handler.guid = jQuery.guid++;
3515+ }
3516+
3517+ // Init the element's event structure and main handler, if this is the first
3518+ events = elemData.events;
3519+ if ( !events ) {
3520+ elemData.events = events = {};
3521+ }
3522+ eventHandle = elemData.handle;
3523+ if ( !eventHandle ) {
3524+ elemData.handle = eventHandle = function( e ) {
3525+ // Discard the second event of a jQuery.event.trigger() and
3526+ // when an event is called after a page has unloaded
3527+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
3528+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
3529+ undefined;
3530+ };
3531+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
3532+ eventHandle.elem = elem;
3533+ }
3534+
3535+ // Handle multiple events separated by a space
3536+ // jQuery(...).bind("mouseover mouseout", fn);
3537+ types = jQuery.trim( hoverHack(types) ).split( " " );
3538+ for ( t = 0; t < types.length; t++ ) {
3539+
3540+ tns = rtypenamespace.exec( types[t] ) || [];
3541+ type = tns[1];
3542+ namespaces = ( tns[2] || "" ).split( "." ).sort();
3543+
3544+ // If event changes its type, use the special event handlers for the changed type
3545+ special = jQuery.event.special[ type ] || {};
3546+
3547+ // If selector defined, determine special event api type, otherwise given type
3548+ type = ( selector ? special.delegateType : special.bindType ) || type;
3549+
3550+ // Update special based on newly reset type
3551+ special = jQuery.event.special[ type ] || {};
3552+
3553+ // handleObj is passed to all event handlers
3554+ handleObj = jQuery.extend({
3555+ type: type,
3556+ origType: tns[1],
3557+ data: data,
3558+ handler: handler,
3559+ guid: handler.guid,
3560+ selector: selector,
3561+ quick: selector && quickParse( selector ),
3562+ namespace: namespaces.join(".")
3563+ }, handleObjIn );
3564+
3565+ // Init the event handler queue if we're the first
3566+ handlers = events[ type ];
3567+ if ( !handlers ) {
3568+ handlers = events[ type ] = [];
3569+ handlers.delegateCount = 0;
3570+
3571+ // Only use addEventListener/attachEvent if the special events handler returns false
3572+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
3573+ // Bind the global event handler to the element
3574+ if ( elem.addEventListener ) {
3575+ elem.addEventListener( type, eventHandle, false );
3576+
3577+ } else if ( elem.attachEvent ) {
3578+ elem.attachEvent( "on" + type, eventHandle );
3579+ }
3580+ }
3581+ }
3582+
3583+ if ( special.add ) {
3584+ special.add.call( elem, handleObj );
3585+
3586+ if ( !handleObj.handler.guid ) {
3587+ handleObj.handler.guid = handler.guid;
3588+ }
3589+ }
3590+
3591+ // Add to the element's handler list, delegates in front
3592+ if ( selector ) {
3593+ handlers.splice( handlers.delegateCount++, 0, handleObj );
3594+ } else {
3595+ handlers.push( handleObj );
3596+ }
3597+
3598+ // Keep track of which events have ever been used, for event optimization
3599+ jQuery.event.global[ type ] = true;
3600+ }
3601+
3602+ // Nullify elem to prevent memory leaks in IE
3603+ elem = null;
3604+ },
3605+
3606+ global: {},
3607+
3608+ // Detach an event or set of events from an element
3609+ remove: function( elem, types, handler, selector, mappedTypes ) {
3610+
3611+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
3612+ t, tns, type, origType, namespaces, origCount,
3613+ j, events, special, handle, eventType, handleObj;
3614+
3615+ if ( !elemData || !(events = elemData.events) ) {
3616+ return;
3617+ }
3618+
3619+ // Once for each type.namespace in types; type may be omitted
3620+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
3621+ for ( t = 0; t < types.length; t++ ) {
3622+ tns = rtypenamespace.exec( types[t] ) || [];
3623+ type = origType = tns[1];
3624+ namespaces = tns[2];
3625+
3626+ // Unbind all events (on this namespace, if provided) for the element
3627+ if ( !type ) {
3628+ for ( type in events ) {
3629+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
3630+ }
3631+ continue;
3632+ }
3633+
3634+ special = jQuery.event.special[ type ] || {};
3635+ type = ( selector? special.delegateType : special.bindType ) || type;
3636+ eventType = events[ type ] || [];
3637+ origCount = eventType.length;
3638+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
3639+
3640+ // Remove matching events
3641+ for ( j = 0; j < eventType.length; j++ ) {
3642+ handleObj = eventType[ j ];
3643+
3644+ if ( ( mappedTypes || origType === handleObj.origType ) &&
3645+ ( !handler || handler.guid === handleObj.guid ) &&
3646+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
3647+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
3648+ eventType.splice( j--, 1 );
3649+
3650+ if ( handleObj.selector ) {
3651+ eventType.delegateCount--;
3652+ }
3653+ if ( special.remove ) {
3654+ special.remove.call( elem, handleObj );
3655+ }
3656+ }
3657+ }
3658+
3659+ // Remove generic event handler if we removed something and no more handlers exist
3660+ // (avoids potential for endless recursion during removal of special event handlers)
3661+ if ( eventType.length === 0 && origCount !== eventType.length ) {
3662+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
3663+ jQuery.removeEvent( elem, type, elemData.handle );
3664+ }
3665+
3666+ delete events[ type ];
3667+ }
3668+ }
3669+
3670+ // Remove the expando if it's no longer used
3671+ if ( jQuery.isEmptyObject( events ) ) {
3672+ handle = elemData.handle;
3673+ if ( handle ) {
3674+ handle.elem = null;
3675+ }
3676+
3677+ // removeData also checks for emptiness and clears the expando if empty
3678+ // so use it instead of delete
3679+ jQuery.removeData( elem, [ "events", "handle" ], true );
3680+ }
3681+ },
3682+
3683+ // Events that are safe to short-circuit if no handlers are attached.
3684+ // Native DOM events should not be added, they may have inline handlers.
3685+ customEvent: {
3686+ "getData": true,
3687+ "setData": true,
3688+ "changeData": true
3689+ },
3690+
3691+ trigger: function( event, data, elem, onlyHandlers ) {
3692+ // Don't do events on text and comment nodes
3693+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
3694+ return;
3695+ }
3696+
3697+ // Event object or event type
3698+ var type = event.type || event,
3699+ namespaces = [],
3700+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
3701+
3702+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
3703+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
3704+ return;
3705+ }
3706+
3707+ if ( type.indexOf( "!" ) >= 0 ) {
3708+ // Exclusive events trigger only for the exact event (no namespaces)
3709+ type = type.slice(0, -1);
3710+ exclusive = true;
3711+ }
3712+
3713+ if ( type.indexOf( "." ) >= 0 ) {
3714+ // Namespaced trigger; create a regexp to match event type in handle()
3715+ namespaces = type.split(".");
3716+ type = namespaces.shift();
3717+ namespaces.sort();
3718+ }
3719+
3720+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
3721+ // No jQuery handlers for this event type, and it can't have inline handlers
3722+ return;
3723+ }
3724+
3725+ // Caller can pass in an Event, Object, or just an event type string
3726+ event = typeof event === "object" ?
3727+ // jQuery.Event object
3728+ event[ jQuery.expando ] ? event :
3729+ // Object literal
3730+ new jQuery.Event( type, event ) :
3731+ // Just the event type (string)
3732+ new jQuery.Event( type );
3733+
3734+ event.type = type;
3735+ event.isTrigger = true;
3736+ event.exclusive = exclusive;
3737+ event.namespace = namespaces.join( "." );
3738+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
3739+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
3740+
3741+ // Handle a global trigger
3742+ if ( !elem ) {
3743+
3744+ // TODO: Stop taunting the data cache; remove global events and always attach to document
3745+ cache = jQuery.cache;
3746+ for ( i in cache ) {
3747+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
3748+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
3749+ }
3750+ }
3751+ return;
3752+ }
3753+
3754+ // Clean up the event in case it is being reused
3755+ event.result = undefined;
3756+ if ( !event.target ) {
3757+ event.target = elem;
3758+ }
3759+
3760+ // Clone any incoming data and prepend the event, creating the handler arg list
3761+ data = data != null ? jQuery.makeArray( data ) : [];
3762+ data.unshift( event );
3763+
3764+ // Allow special events to draw outside the lines
3765+ special = jQuery.event.special[ type ] || {};
3766+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
3767+ return;
3768+ }
3769+
3770+ // Determine event propagation path in advance, per W3C events spec (#9951)
3771+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
3772+ eventPath = [[ elem, special.bindType || type ]];
3773+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
3774+
3775+ bubbleType = special.delegateType || type;
3776+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
3777+ old = null;
3778+ for ( ; cur; cur = cur.parentNode ) {
3779+ eventPath.push([ cur, bubbleType ]);
3780+ old = cur;
3781+ }
3782+
3783+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
3784+ if ( old && old === elem.ownerDocument ) {
3785+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
3786+ }
3787+ }
3788+
3789+ // Fire handlers on the event path
3790+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
3791+
3792+ cur = eventPath[i][0];
3793+ event.type = eventPath[i][1];
3794+
3795+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
3796+ if ( handle ) {
3797+ handle.apply( cur, data );
3798+ }
3799+ // Note that this is a bare JS function and not a jQuery handler
3800+ handle = ontype && cur[ ontype ];
3801+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
3802+ event.preventDefault();
3803+ }
3804+ }
3805+ event.type = type;
3806+
3807+ // If nobody prevented the default action, do it now
3808+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
3809+
3810+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
3811+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
3812+
3813+ // Call a native DOM method on the target with the same name name as the event.
3814+ // Can't use an .isFunction() check here because IE6/7 fails that test.
3815+ // Don't do default actions on window, that's where global variables be (#6170)
3816+ // IE<9 dies on focus/blur to hidden element (#1486)
3817+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
3818+
3819+ // Don't re-trigger an onFOO event when we call its FOO() method
3820+ old = elem[ ontype ];
3821+
3822+ if ( old ) {
3823+ elem[ ontype ] = null;
3824+ }
3825+
3826+ // Prevent re-triggering of the same event, since we already bubbled it above
3827+ jQuery.event.triggered = type;
3828+ elem[ type ]();
3829+ jQuery.event.triggered = undefined;
3830+
3831+ if ( old ) {
3832+ elem[ ontype ] = old;
3833+ }
3834+ }
3835+ }
3836+ }
3837+
3838+ return event.result;
3839+ },
3840+
3841+ dispatch: function( event ) {
3842+
3843+ // Make a writable jQuery.Event from the native event object
3844+ event = jQuery.event.fix( event || window.event );
3845+
3846+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
3847+ delegateCount = handlers.delegateCount,
3848+ args = [].slice.call( arguments, 0 ),
3849+ run_all = !event.exclusive && !event.namespace,
3850+ special = jQuery.event.special[ event.type ] || {},
3851+ handlerQueue = [],
3852+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
3853+
3854+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
3855+ args[0] = event;
3856+ event.delegateTarget = this;
3857+
3858+ // Call the preDispatch hook for the mapped type, and let it bail if desired
3859+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
3860+ return;
3861+ }
3862+
3863+ // Determine handlers that should run if there are delegated events
3864+ // Avoid non-left-click bubbling in Firefox (#3861)
3865+ if ( delegateCount && !(event.button && event.type === "click") ) {
3866+
3867+ // Pregenerate a single jQuery object for reuse with .is()
3868+ jqcur = jQuery(this);
3869+ jqcur.context = this.ownerDocument || this;
3870+
3871+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
3872+
3873+ // Don't process events on disabled elements (#6911, #8165)
3874+ if ( cur.disabled !== true ) {
3875+ selMatch = {};
3876+ matches = [];
3877+ jqcur[0] = cur;
3878+ for ( i = 0; i < delegateCount; i++ ) {
3879+ handleObj = handlers[ i ];
3880+ sel = handleObj.selector;
3881+
3882+ if ( selMatch[ sel ] === undefined ) {
3883+ selMatch[ sel ] = (
3884+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
3885+ );
3886+ }
3887+ if ( selMatch[ sel ] ) {
3888+ matches.push( handleObj );
3889+ }
3890+ }
3891+ if ( matches.length ) {
3892+ handlerQueue.push({ elem: cur, matches: matches });
3893+ }
3894+ }
3895+ }
3896+ }
3897+
3898+ // Add the remaining (directly-bound) handlers
3899+ if ( handlers.length > delegateCount ) {
3900+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
3901+ }
3902+
3903+ // Run delegates first; they may want to stop propagation beneath us
3904+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
3905+ matched = handlerQueue[ i ];
3906+ event.currentTarget = matched.elem;
3907+
3908+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
3909+ handleObj = matched.matches[ j ];
3910+
3911+ // Triggered event must either 1) be non-exclusive and have no namespace, or
3912+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
3913+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
3914+
3915+ event.data = handleObj.data;
3916+ event.handleObj = handleObj;
3917+
3918+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
3919+ .apply( matched.elem, args );
3920+
3921+ if ( ret !== undefined ) {
3922+ event.result = ret;
3923+ if ( ret === false ) {
3924+ event.preventDefault();
3925+ event.stopPropagation();
3926+ }
3927+ }
3928+ }
3929+ }
3930+ }
3931+
3932+ // Call the postDispatch hook for the mapped type
3933+ if ( special.postDispatch ) {
3934+ special.postDispatch.call( this, event );
3935+ }
3936+
3937+ return event.result;
3938+ },
3939+
3940+ // Includes some event props shared by KeyEvent and MouseEvent
3941+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
3942+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
3943+
3944+ fixHooks: {},
3945+
3946+ keyHooks: {
3947+ props: "char charCode key keyCode".split(" "),
3948+ filter: function( event, original ) {
3949+
3950+ // Add which for key events
3951+ if ( event.which == null ) {
3952+ event.which = original.charCode != null ? original.charCode : original.keyCode;
3953+ }
3954+
3955+ return event;
3956+ }
3957+ },
3958+
3959+ mouseHooks: {
3960+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
3961+ filter: function( event, original ) {
3962+ var eventDoc, doc, body,
3963+ button = original.button,
3964+ fromElement = original.fromElement;
3965+
3966+ // Calculate pageX/Y if missing and clientX/Y available
3967+ if ( event.pageX == null && original.clientX != null ) {
3968+ eventDoc = event.target.ownerDocument || document;
3969+ doc = eventDoc.documentElement;
3970+ body = eventDoc.body;
3971+
3972+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
3973+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
3974+ }
3975+
3976+ // Add relatedTarget, if necessary
3977+ if ( !event.relatedTarget && fromElement ) {
3978+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
3979+ }
3980+
3981+ // Add which for click: 1 === left; 2 === middle; 3 === right
3982+ // Note: button is not normalized, so don't use it
3983+ if ( !event.which && button !== undefined ) {
3984+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
3985+ }
3986+
3987+ return event;
3988+ }
3989+ },
3990+
3991+ fix: function( event ) {
3992+ if ( event[ jQuery.expando ] ) {
3993+ return event;
3994+ }
3995+
3996+ // Create a writable copy of the event object and normalize some properties
3997+ var i, prop,
3998+ originalEvent = event,
3999+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
4000+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
4001+
4002+ event = jQuery.Event( originalEvent );
4003+
4004+ for ( i = copy.length; i; ) {
4005+ prop = copy[ --i ];
4006+ event[ prop ] = originalEvent[ prop ];
4007+ }
4008+
4009+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
4010+ if ( !event.target ) {
4011+ event.target = originalEvent.srcElement || document;
4012+ }
4013+
4014+ // Target should not be a text node (#504, Safari)
4015+ if ( event.target.nodeType === 3 ) {
4016+ event.target = event.target.parentNode;
4017+ }
4018+
4019+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
4020+ if ( event.metaKey === undefined ) {
4021+ event.metaKey = event.ctrlKey;
4022+ }
4023+
4024+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
4025+ },
4026+
4027+ special: {
4028+ ready: {
4029+ // Make sure the ready event is setup
4030+ setup: jQuery.bindReady
4031+ },
4032+
4033+ load: {
4034+ // Prevent triggered image.load events from bubbling to window.load
4035+ noBubble: true
4036+ },
4037+
4038+ focus: {
4039+ delegateType: "focusin"
4040+ },
4041+ blur: {
4042+ delegateType: "focusout"
4043+ },
4044+
4045+ beforeunload: {
4046+ setup: function( data, namespaces, eventHandle ) {
4047+ // We only want to do this special case on windows
4048+ if ( jQuery.isWindow( this ) ) {
4049+ this.onbeforeunload = eventHandle;
4050+ }
4051+ },
4052+
4053+ teardown: function( namespaces, eventHandle ) {
4054+ if ( this.onbeforeunload === eventHandle ) {
4055+ this.onbeforeunload = null;
4056+ }
4057+ }
4058+ }
4059+ },
4060+
4061+ simulate: function( type, elem, event, bubble ) {
4062+ // Piggyback on a donor event to simulate a different one.
4063+ // Fake originalEvent to avoid donor's stopPropagation, but if the
4064+ // simulated event prevents default then we do the same on the donor.
4065+ var e = jQuery.extend(
4066+ new jQuery.Event(),
4067+ event,
4068+ { type: type,
4069+ isSimulated: true,
4070+ originalEvent: {}
4071+ }
4072+ );
4073+ if ( bubble ) {
4074+ jQuery.event.trigger( e, null, elem );
4075+ } else {
4076+ jQuery.event.dispatch.call( elem, e );
4077+ }
4078+ if ( e.isDefaultPrevented() ) {
4079+ event.preventDefault();
4080+ }
4081+ }
4082+};
4083+
4084+// Some plugins are using, but it's undocumented/deprecated and will be removed.
4085+// The 1.7 special event interface should provide all the hooks needed now.
4086+jQuery.event.handle = jQuery.event.dispatch;
4087+
4088+jQuery.removeEvent = document.removeEventListener ?
4089+ function( elem, type, handle ) {
4090+ if ( elem.removeEventListener ) {
4091+ elem.removeEventListener( type, handle, false );
4092+ }
4093+ } :
4094+ function( elem, type, handle ) {
4095+ if ( elem.detachEvent ) {
4096+ elem.detachEvent( "on" + type, handle );
4097+ }
4098+ };
4099+
4100+jQuery.Event = function( src, props ) {
4101+ // Allow instantiation without the 'new' keyword
4102+ if ( !(this instanceof jQuery.Event) ) {
4103+ return new jQuery.Event( src, props );
4104+ }
4105+
4106+ // Event object
4107+ if ( src && src.type ) {
4108+ this.originalEvent = src;
4109+ this.type = src.type;
4110+
4111+ // Events bubbling up the document may have been marked as prevented
4112+ // by a handler lower down the tree; reflect the correct value.
4113+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
4114+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
4115+
4116+ // Event type
4117+ } else {
4118+ this.type = src;
4119+ }
4120+
4121+ // Put explicitly provided properties onto the event object
4122+ if ( props ) {
4123+ jQuery.extend( this, props );
4124+ }
4125+
4126+ // Create a timestamp if incoming event doesn't have one
4127+ this.timeStamp = src && src.timeStamp || jQuery.now();
4128+
4129+ // Mark it as fixed
4130+ this[ jQuery.expando ] = true;
4131+};
4132+
4133+function returnFalse() {
4134+ return false;
4135+}
4136+function returnTrue() {
4137+ return true;
4138+}
4139+
4140+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
4141+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
4142+jQuery.Event.prototype = {
4143+ preventDefault: function() {
4144+ this.isDefaultPrevented = returnTrue;
4145+
4146+ var e = this.originalEvent;
4147+ if ( !e ) {
4148+ return;
4149+ }
4150+
4151+ // if preventDefault exists run it on the original event
4152+ if ( e.preventDefault ) {
4153+ e.preventDefault();
4154+
4155+ // otherwise set the returnValue property of the original event to false (IE)
4156+ } else {
4157+ e.returnValue = false;
4158+ }
4159+ },
4160+ stopPropagation: function() {
4161+ this.isPropagationStopped = returnTrue;
4162+
4163+ var e = this.originalEvent;
4164+ if ( !e ) {
4165+ return;
4166+ }
4167+ // if stopPropagation exists run it on the original event
4168+ if ( e.stopPropagation ) {
4169+ e.stopPropagation();
4170+ }
4171+ // otherwise set the cancelBubble property of the original event to true (IE)
4172+ e.cancelBubble = true;
4173+ },
4174+ stopImmediatePropagation: function() {
4175+ this.isImmediatePropagationStopped = returnTrue;
4176+ this.stopPropagation();
4177+ },
4178+ isDefaultPrevented: returnFalse,
4179+ isPropagationStopped: returnFalse,
4180+ isImmediatePropagationStopped: returnFalse
4181+};
4182+
4183+// Create mouseenter/leave events using mouseover/out and event-time checks
4184+jQuery.each({
4185+ mouseenter: "mouseover",
4186+ mouseleave: "mouseout"
4187+}, function( orig, fix ) {
4188+ jQuery.event.special[ orig ] = {
4189+ delegateType: fix,
4190+ bindType: fix,
4191+
4192+ handle: function( event ) {
4193+ var target = this,
4194+ related = event.relatedTarget,
4195+ handleObj = event.handleObj,
4196+ selector = handleObj.selector,
4197+ ret;
4198+
4199+ // For mousenter/leave call the handler if related is outside the target.
4200+ // NB: No relatedTarget if the mouse left/entered the browser window
4201+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
4202+ event.type = handleObj.origType;
4203+ ret = handleObj.handler.apply( this, arguments );
4204+ event.type = fix;
4205+ }
4206+ return ret;
4207+ }
4208+ };
4209+});
4210+
4211+// IE submit delegation
4212+if ( !jQuery.support.submitBubbles ) {
4213+
4214+ jQuery.event.special.submit = {
4215+ setup: function() {
4216+ // Only need this for delegated form submit events
4217+ if ( jQuery.nodeName( this, "form" ) ) {
4218+ return false;
4219+ }
4220+
4221+ // Lazy-add a submit handler when a descendant form may potentially be submitted
4222+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
4223+ // Node name check avoids a VML-related crash in IE (#9807)
4224+ var elem = e.target,
4225+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
4226+ if ( form && !form._submit_attached ) {
4227+ jQuery.event.add( form, "submit._submit", function( event ) {
4228+ event._submit_bubble = true;
4229+ });
4230+ form._submit_attached = true;
4231+ }
4232+ });
4233+ // return undefined since we don't need an event listener
4234+ },
4235+
4236+ postDispatch: function( event ) {
4237+ // If form was submitted by the user, bubble the event up the tree
4238+ if ( event._submit_bubble ) {
4239+ delete event._submit_bubble;
4240+ if ( this.parentNode && !event.isTrigger ) {
4241+ jQuery.event.simulate( "submit", this.parentNode, event, true );
4242+ }
4243+ }
4244+ },
4245+
4246+ teardown: function() {
4247+ // Only need this for delegated form submit events
4248+ if ( jQuery.nodeName( this, "form" ) ) {
4249+ return false;
4250+ }
4251+
4252+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
4253+ jQuery.event.remove( this, "._submit" );
4254+ }
4255+ };
4256+}
4257+
4258+// IE change delegation and checkbox/radio fix
4259+if ( !jQuery.support.changeBubbles ) {
4260+
4261+ jQuery.event.special.change = {
4262+
4263+ setup: function() {
4264+
4265+ if ( rformElems.test( this.nodeName ) ) {
4266+ // IE doesn't fire change on a check/radio until blur; trigger it on click
4267+ // after a propertychange. Eat the blur-change in special.change.handle.
4268+ // This still fires onchange a second time for check/radio after blur.
4269+ if ( this.type === "checkbox" || this.type === "radio" ) {
4270+ jQuery.event.add( this, "propertychange._change", function( event ) {
4271+ if ( event.originalEvent.propertyName === "checked" ) {
4272+ this._just_changed = true;
4273+ }
4274+ });
4275+ jQuery.event.add( this, "click._change", function( event ) {
4276+ if ( this._just_changed && !event.isTrigger ) {
4277+ this._just_changed = false;
4278+ jQuery.event.simulate( "change", this, event, true );
4279+ }
4280+ });
4281+ }
4282+ return false;
4283+ }
4284+ // Delegated event; lazy-add a change handler on descendant inputs
4285+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
4286+ var elem = e.target;
4287+
4288+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
4289+ jQuery.event.add( elem, "change._change", function( event ) {
4290+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
4291+ jQuery.event.simulate( "change", this.parentNode, event, true );
4292+ }
4293+ });
4294+ elem._change_attached = true;
4295+ }
4296+ });
4297+ },
4298+
4299+ handle: function( event ) {
4300+ var elem = event.target;
4301+
4302+ // Swallow native change events from checkbox/radio, we already triggered them above
4303+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
4304+ return event.handleObj.handler.apply( this, arguments );
4305+ }
4306+ },
4307+
4308+ teardown: function() {
4309+ jQuery.event.remove( this, "._change" );
4310+
4311+ return rformElems.test( this.nodeName );
4312+ }
4313+ };
4314+}
4315+
4316+// Create "bubbling" focus and blur events
4317+if ( !jQuery.support.focusinBubbles ) {
4318+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
4319+
4320+ // Attach a single capturing handler while someone wants focusin/focusout
4321+ var attaches = 0,
4322+ handler = function( event ) {
4323+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
4324+ };
4325+
4326+ jQuery.event.special[ fix ] = {
4327+ setup: function() {
4328+ if ( attaches++ === 0 ) {
4329+ document.addEventListener( orig, handler, true );
4330+ }
4331+ },
4332+ teardown: function() {
4333+ if ( --attaches === 0 ) {
4334+ document.removeEventListener( orig, handler, true );
4335+ }
4336+ }
4337+ };
4338+ });
4339+}
4340+
4341+jQuery.fn.extend({
4342+
4343+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
4344+ var origFn, type;
4345+
4346+ // Types can be a map of types/handlers
4347+ if ( typeof types === "object" ) {
4348+ // ( types-Object, selector, data )
4349+ if ( typeof selector !== "string" ) { // && selector != null
4350+ // ( types-Object, data )
4351+ data = data || selector;
4352+ selector = undefined;
4353+ }
4354+ for ( type in types ) {
4355+ this.on( type, selector, data, types[ type ], one );
4356+ }
4357+ return this;
4358+ }
4359+
4360+ if ( data == null && fn == null ) {
4361+ // ( types, fn )
4362+ fn = selector;
4363+ data = selector = undefined;
4364+ } else if ( fn == null ) {
4365+ if ( typeof selector === "string" ) {
4366+ // ( types, selector, fn )
4367+ fn = data;
4368+ data = undefined;
4369+ } else {
4370+ // ( types, data, fn )
4371+ fn = data;
4372+ data = selector;
4373+ selector = undefined;
4374+ }
4375+ }
4376+ if ( fn === false ) {
4377+ fn = returnFalse;
4378+ } else if ( !fn ) {
4379+ return this;
4380+ }
4381+
4382+ if ( one === 1 ) {
4383+ origFn = fn;
4384+ fn = function( event ) {
4385+ // Can use an empty set, since event contains the info
4386+ jQuery().off( event );
4387+ return origFn.apply( this, arguments );
4388+ };
4389+ // Use same guid so caller can remove using origFn
4390+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
4391+ }
4392+ return this.each( function() {
4393+ jQuery.event.add( this, types, fn, data, selector );
4394+ });
4395+ },
4396+ one: function( types, selector, data, fn ) {
4397+ return this.on( types, selector, data, fn, 1 );
4398+ },
4399+ off: function( types, selector, fn ) {
4400+ if ( types && types.preventDefault && types.handleObj ) {
4401+ // ( event ) dispatched jQuery.Event
4402+ var handleObj = types.handleObj;
4403+ jQuery( types.delegateTarget ).off(
4404+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
4405+ handleObj.selector,
4406+ handleObj.handler
4407+ );
4408+ return this;
4409+ }
4410+ if ( typeof types === "object" ) {
4411+ // ( types-object [, selector] )
4412+ for ( var type in types ) {
4413+ this.off( type, selector, types[ type ] );
4414+ }
4415+ return this;
4416+ }
4417+ if ( selector === false || typeof selector === "function" ) {
4418+ // ( types [, fn] )
4419+ fn = selector;
4420+ selector = undefined;
4421+ }
4422+ if ( fn === false ) {
4423+ fn = returnFalse;
4424+ }
4425+ return this.each(function() {
4426+ jQuery.event.remove( this, types, fn, selector );
4427+ });
4428+ },
4429+
4430+ bind: function( types, data, fn ) {
4431+ return this.on( types, null, data, fn );
4432+ },
4433+ unbind: function( types, fn ) {
4434+ return this.off( types, null, fn );
4435+ },
4436+
4437+ live: function( types, data, fn ) {
4438+ jQuery( this.context ).on( types, this.selector, data, fn );
4439+ return this;
4440+ },
4441+ die: function( types, fn ) {
4442+ jQuery( this.context ).off( types, this.selector || "**", fn );
4443+ return this;
4444+ },
4445+
4446+ delegate: function( selector, types, data, fn ) {
4447+ return this.on( types, selector, data, fn );
4448+ },
4449+ undelegate: function( selector, types, fn ) {
4450+ // ( namespace ) or ( selector, types [, fn] )
4451+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
4452+ },
4453+
4454+ trigger: function( type, data ) {
4455+ return this.each(function() {
4456+ jQuery.event.trigger( type, data, this );
4457+ });
4458+ },
4459+ triggerHandler: function( type, data ) {
4460+ if ( this[0] ) {
4461+ return jQuery.event.trigger( type, data, this[0], true );
4462+ }
4463+ },
4464+
4465+ toggle: function( fn ) {
4466+ // Save reference to arguments for access in closure
4467+ var args = arguments,
4468+ guid = fn.guid || jQuery.guid++,
4469+ i = 0,
4470+ toggler = function( event ) {
4471+ // Figure out which function to execute
4472+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
4473+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
4474+
4475+ // Make sure that clicks stop
4476+ event.preventDefault();
4477+
4478+ // and execute the function
4479+ return args[ lastToggle ].apply( this, arguments ) || false;
4480+ };
4481+
4482+ // link all the functions, so any of them can unbind this click handler
4483+ toggler.guid = guid;
4484+ while ( i < args.length ) {
4485+ args[ i++ ].guid = guid;
4486+ }
4487+
4488+ return this.click( toggler );
4489+ },
4490+
4491+ hover: function( fnOver, fnOut ) {
4492+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
4493+ }
4494+});
4495+
4496+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
4497+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
4498+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
4499+
4500+ // Handle event binding
4501+ jQuery.fn[ name ] = function( data, fn ) {
4502+ if ( fn == null ) {
4503+ fn = data;
4504+ data = null;
4505+ }
4506+
4507+ return arguments.length > 0 ?
4508+ this.on( name, null, data, fn ) :
4509+ this.trigger( name );
4510+ };
4511+
4512+ if ( jQuery.attrFn ) {
4513+ jQuery.attrFn[ name ] = true;
4514+ }
4515+
4516+ if ( rkeyEvent.test( name ) ) {
4517+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
4518+ }
4519+
4520+ if ( rmouseEvent.test( name ) ) {
4521+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
4522+ }
4523+});
4524+
4525+
4526+
4527+/*!
4528+ * Sizzle CSS Selector Engine
4529+ * Copyright 2011, The Dojo Foundation
4530+ * Released under the MIT, BSD, and GPL Licenses.
4531+ * More information: http://sizzlejs.com/
4532+ */
4533+(function(){
4534+
4535+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
4536+ expando = "sizcache" + (Math.random() + '').replace('.', ''),
4537+ done = 0,
4538+ toString = Object.prototype.toString,
4539+ hasDuplicate = false,
4540+ baseHasDuplicate = true,
4541+ rBackslash = /\\/g,
4542+ rReturn = /\r\n/g,
4543+ rNonWord = /\W/;
4544+
4545+// Here we check if the JavaScript engine is using some sort of
4546+// optimization where it does not always call our comparision
4547+// function. If that is the case, discard the hasDuplicate value.
4548+// Thus far that includes Google Chrome.
4549+[0, 0].sort(function() {
4550+ baseHasDuplicate = false;
4551+ return 0;
4552+});
4553+
4554+var Sizzle = function( selector, context, results, seed ) {
4555+ results = results || [];
4556+ context = context || document;
4557+
4558+ var origContext = context;
4559+
4560+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
4561+ return [];
4562+ }
4563+
4564+ if ( !selector || typeof selector !== "string" ) {
4565+ return results;
4566+ }
4567+
4568+ var m, set, checkSet, extra, ret, cur, pop, i,
4569+ prune = true,
4570+ contextXML = Sizzle.isXML( context ),
4571+ parts = [],
4572+ soFar = selector;
4573+
4574+ // Reset the position of the chunker regexp (start from head)
4575+ do {
4576+ chunker.exec( "" );
4577+ m = chunker.exec( soFar );
4578+
4579+ if ( m ) {
4580+ soFar = m[3];
4581+
4582+ parts.push( m[1] );
4583+
4584+ if ( m[2] ) {
4585+ extra = m[3];
4586+ break;
4587+ }
4588+ }
4589+ } while ( m );
4590+
4591+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
4592+
4593+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
4594+ set = posProcess( parts[0] + parts[1], context, seed );
4595+
4596+ } else {
4597+ set = Expr.relative[ parts[0] ] ?
4598+ [ context ] :
4599+ Sizzle( parts.shift(), context );
4600+
4601+ while ( parts.length ) {
4602+ selector = parts.shift();
4603+
4604+ if ( Expr.relative[ selector ] ) {
4605+ selector += parts.shift();
4606+ }
4607+
4608+ set = posProcess( selector, set, seed );
4609+ }
4610+ }
4611+
4612+ } else {
4613+ // Take a shortcut and set the context if the root selector is an ID
4614+ // (but not if it'll be faster if the inner selector is an ID)
4615+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
4616+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
4617+
4618+ ret = Sizzle.find( parts.shift(), context, contextXML );
4619+ context = ret.expr ?
4620+ Sizzle.filter( ret.expr, ret.set )[0] :
4621+ ret.set[0];
4622+ }
4623+
4624+ if ( context ) {
4625+ ret = seed ?
4626+ { expr: parts.pop(), set: makeArray(seed) } :
4627+ Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
4628+
4629+ set = ret.expr ?
4630+ Sizzle.filter( ret.expr, ret.set ) :
4631+ ret.set;
4632+
4633+ if ( parts.length > 0 ) {
4634+ checkSet = makeArray( set );
4635+
4636+ } else {
4637+ prune = false;
4638+ }
4639+
4640+ while ( parts.length ) {
4641+ cur = parts.pop();
4642+ pop = cur;
4643+
4644+ if ( !Expr.relative[ cur ] ) {
4645+ cur = "";
4646+ } else {
4647+ pop = parts.pop();
4648+ }
4649+
4650+ if ( pop == null ) {
4651+ pop = context;
4652+ }
4653+
4654+ Expr.relative[ cur ]( checkSet, pop, contextXML );
4655+ }
4656+
4657+ } else {
4658+ checkSet = parts = [];
4659+ }
4660+ }
4661+
4662+ if ( !checkSet ) {
4663+ checkSet = set;
4664+ }
4665+
4666+ if ( !checkSet ) {
4667+ Sizzle.error( cur || selector );
4668+ }
4669+
4670+ if ( toString.call(checkSet) === "[object Array]" ) {
4671+ if ( !prune ) {
4672+ results.push.apply( results, checkSet );
4673+
4674+ } else if ( context && context.nodeType === 1 ) {
4675+ for ( i = 0; checkSet[i] != null; i++ ) {
4676+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
4677+ results.push( set[i] );
4678+ }
4679+ }
4680+
4681+ } else {
4682+ for ( i = 0; checkSet[i] != null; i++ ) {
4683+ if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
4684+ results.push( set[i] );
4685+ }
4686+ }
4687+ }
4688+
4689+ } else {
4690+ makeArray( checkSet, results );
4691+ }
4692+
4693+ if ( extra ) {
4694+ Sizzle( extra, origContext, results, seed );
4695+ Sizzle.uniqueSort( results );
4696+ }
4697+
4698+ return results;
4699+};
4700+
4701+Sizzle.uniqueSort = function( results ) {
4702+ if ( sortOrder ) {
4703+ hasDuplicate = baseHasDuplicate;
4704+ results.sort( sortOrder );
4705+
4706+ if ( hasDuplicate ) {
4707+ for ( var i = 1; i < results.length; i++ ) {
4708+ if ( results[i] === results[ i - 1 ] ) {
4709+ results.splice( i--, 1 );
4710+ }
4711+ }
4712+ }
4713+ }
4714+
4715+ return results;
4716+};
4717+
4718+Sizzle.matches = function( expr, set ) {
4719+ return Sizzle( expr, null, null, set );
4720+};
4721+
4722+Sizzle.matchesSelector = function( node, expr ) {
4723+ return Sizzle( expr, null, null, [node] ).length > 0;
4724+};
4725+
4726+Sizzle.find = function( expr, context, isXML ) {
4727+ var set, i, len, match, type, left;
4728+
4729+ if ( !expr ) {
4730+ return [];
4731+ }
4732+
4733+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
4734+ type = Expr.order[i];
4735+
4736+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
4737+ left = match[1];
4738+ match.splice( 1, 1 );
4739+
4740+ if ( left.substr( left.length - 1 ) !== "\\" ) {
4741+ match[1] = (match[1] || "").replace( rBackslash, "" );
4742+ set = Expr.find[ type ]( match, context, isXML );
4743+
4744+ if ( set != null ) {
4745+ expr = expr.replace( Expr.match[ type ], "" );
4746+ break;
4747+ }
4748+ }
4749+ }
4750+ }
4751+
4752+ if ( !set ) {
4753+ set = typeof context.getElementsByTagName !== "undefined" ?
4754+ context.getElementsByTagName( "*" ) :
4755+ [];
4756+ }
4757+
4758+ return { set: set, expr: expr };
4759+};
4760+
4761+Sizzle.filter = function( expr, set, inplace, not ) {
4762+ var match, anyFound,
4763+ type, found, item, filter, left,
4764+ i, pass,
4765+ old = expr,
4766+ result = [],
4767+ curLoop = set,
4768+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
4769+
4770+ while ( expr && set.length ) {
4771+ for ( type in Expr.filter ) {
4772+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
4773+ filter = Expr.filter[ type ];
4774+ left = match[1];
4775+
4776+ anyFound = false;
4777+
4778+ match.splice(1,1);
4779+
4780+ if ( left.substr( left.length - 1 ) === "\\" ) {
4781+ continue;
4782+ }
4783+
4784+ if ( curLoop === result ) {
4785+ result = [];
4786+ }
4787+
4788+ if ( Expr.preFilter[ type ] ) {
4789+ match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
4790+
4791+ if ( !match ) {
4792+ anyFound = found = true;
4793+
4794+ } else if ( match === true ) {
4795+ continue;
4796+ }
4797+ }
4798+
4799+ if ( match ) {
4800+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
4801+ if ( item ) {
4802+ found = filter( item, match, i, curLoop );
4803+ pass = not ^ found;
4804+
4805+ if ( inplace && found != null ) {
4806+ if ( pass ) {
4807+ anyFound = true;
4808+
4809+ } else {
4810+ curLoop[i] = false;
4811+ }
4812+
4813+ } else if ( pass ) {
4814+ result.push( item );
4815+ anyFound = true;
4816+ }
4817+ }
4818+ }
4819+ }
4820+
4821+ if ( found !== undefined ) {
4822+ if ( !inplace ) {
4823+ curLoop = result;
4824+ }
4825+
4826+ expr = expr.replace( Expr.match[ type ], "" );
4827+
4828+ if ( !anyFound ) {
4829+ return [];
4830+ }
4831+
4832+ break;
4833+ }
4834+ }
4835+ }
4836+
4837+ // Improper expression
4838+ if ( expr === old ) {
4839+ if ( anyFound == null ) {
4840+ Sizzle.error( expr );
4841+
4842+ } else {
4843+ break;
4844+ }
4845+ }
4846+
4847+ old = expr;
4848+ }
4849+
4850+ return curLoop;
4851+};
4852+
4853+Sizzle.error = function( msg ) {
4854+ throw new Error( "Syntax error, unrecognized expression: " + msg );
4855+};
4856+
4857+/**
4858+ * Utility function for retreiving the text value of an array of DOM nodes
4859+ * @param {Array|Element} elem
4860+ */
4861+var getText = Sizzle.getText = function( elem ) {
4862+ var i, node,
4863+ nodeType = elem.nodeType,
4864+ ret = "";
4865+
4866+ if ( nodeType ) {
4867+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
4868+ // Use textContent || innerText for elements
4869+ if ( typeof elem.textContent === 'string' ) {
4870+ return elem.textContent;
4871+ } else if ( typeof elem.innerText === 'string' ) {
4872+ // Replace IE's carriage returns
4873+ return elem.innerText.replace( rReturn, '' );
4874+ } else {
4875+ // Traverse it's children
4876+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
4877+ ret += getText( elem );
4878+ }
4879+ }
4880+ } else if ( nodeType === 3 || nodeType === 4 ) {
4881+ return elem.nodeValue;
4882+ }
4883+ } else {
4884+
4885+ // If no nodeType, this is expected to be an array
4886+ for ( i = 0; (node = elem[i]); i++ ) {
4887+ // Do not traverse comment nodes
4888+ if ( node.nodeType !== 8 ) {
4889+ ret += getText( node );
4890+ }
4891+ }
4892+ }
4893+ return ret;
4894+};
4895+
4896+var Expr = Sizzle.selectors = {
4897+ order: [ "ID", "NAME", "TAG" ],
4898+
4899+ match: {
4900+ ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4901+ CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
4902+ NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
4903+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
4904+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
4905+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
4906+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
4907+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
4908+ },
4909+
4910+ leftMatch: {},
4911+
4912+ attrMap: {
4913+ "class": "className",
4914+ "for": "htmlFor"
4915+ },
4916+
4917+ attrHandle: {
4918+ href: function( elem ) {
4919+ return elem.getAttribute( "href" );
4920+ },
4921+ type: function( elem ) {
4922+ return elem.getAttribute( "type" );
4923+ }
4924+ },
4925+
4926+ relative: {
4927+ "+": function(checkSet, part){
4928+ var isPartStr = typeof part === "string",
4929+ isTag = isPartStr && !rNonWord.test( part ),
4930+ isPartStrNotTag = isPartStr && !isTag;
4931+
4932+ if ( isTag ) {
4933+ part = part.toLowerCase();
4934+ }
4935+
4936+ for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
4937+ if ( (elem = checkSet[i]) ) {
4938+ while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
4939+
4940+ checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
4941+ elem || false :
4942+ elem === part;
4943+ }
4944+ }
4945+
4946+ if ( isPartStrNotTag ) {
4947+ Sizzle.filter( part, checkSet, true );
4948+ }
4949+ },
4950+
4951+ ">": function( checkSet, part ) {
4952+ var elem,
4953+ isPartStr = typeof part === "string",
4954+ i = 0,
4955+ l = checkSet.length;
4956+
4957+ if ( isPartStr && !rNonWord.test( part ) ) {
4958+ part = part.toLowerCase();
4959+
4960+ for ( ; i < l; i++ ) {
4961+ elem = checkSet[i];
4962+
4963+ if ( elem ) {
4964+ var parent = elem.parentNode;
4965+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
4966+ }
4967+ }
4968+
4969+ } else {
4970+ for ( ; i < l; i++ ) {
4971+ elem = checkSet[i];
4972+
4973+ if ( elem ) {
4974+ checkSet[i] = isPartStr ?
4975+ elem.parentNode :
4976+ elem.parentNode === part;
4977+ }
4978+ }
4979+
4980+ if ( isPartStr ) {
4981+ Sizzle.filter( part, checkSet, true );
4982+ }
4983+ }
4984+ },
4985+
4986+ "": function(checkSet, part, isXML){
4987+ var nodeCheck,
4988+ doneName = done++,
4989+ checkFn = dirCheck;
4990+
4991+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
4992+ part = part.toLowerCase();
4993+ nodeCheck = part;
4994+ checkFn = dirNodeCheck;
4995+ }
4996+
4997+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
4998+ },
4999+
5000+ "~": function( checkSet, part, isXML ) {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches

to all changes: