Commit f60d5698a796fd0bd27f044887940247a4140139
Exists in
master
and in
1 other branch
Fixed merge conflict in SettingsController.js
Showing 15 changed files Side-by-side Diff
- .gitignore
- Makefile
- casper_test.js
- config.js
- home.html
- scripts/CardGridController.js
- scripts/ResetPasswordController.js
- scripts/SettingsController.js
- scripts/StudyController.js
- scripts/UserService.js
- scripts/bootstrap.js
- static/js/angular-contenteditable.js
- static/js/angular-websocket.js
- test.png
- ws_debug.html
Makefile
View file @
f60d569
1 | 1 | all: styles/materialize.css styles/flashier.css fixjsstyle |
2 | 2 | |
3 | +JS_SOURCES = config.js $(wildcard scripts/*Controller.js) $(wildcard scripts/*Directive.js) $(wildcard scripts/*Service.js) | |
4 | + | |
5 | +print-%: | |
6 | + @echo '$*=$($*)' | |
7 | + | |
3 | 8 | styles/materialize.css: .PHONY |
4 | 9 | sassc sass/materialize.scss styles/materialize.css |
5 | 10 | |
... | ... | @@ -7,6 +12,7 @@ |
7 | 12 | sassc sass/flashier.scss styles/flashier.css |
8 | 13 | |
9 | 14 | fixjsstyle: .PHONY |
15 | + $(foreach sourcefile,$(JS_SOURCES),expand $(sourcefile) | sponge $(sourcefile);) | |
10 | 16 | fixjsstyle --flagfile gjslint.conf config.js scripts/*{Controller,Directive,Service}.js |
11 | 17 | |
12 | 18 | .PHONY: |
casper_test.js
View file @
f60d569
1 | -phantom.casperPath = 'path/to/node_modules/casperjs'; | |
2 | -phantom.injectJs('path/to/node_modules/casperjs/bin/bootstrap.js'); | |
3 | - | |
4 | -phantom.casperTest = true; | |
5 | - | |
6 | - | |
7 | -//var casper = require('casper'); | |
8 | -var x = require('casper').selectXPath; | |
9 | - | |
10 | -casper.start('http://google.com/', function() { | |
11 | - this.echo(this.getTitle()); | |
12 | - this.assertExists(x('//*[@id="gbqfbb"]'), 'the element exists'); | |
13 | -}); | |
14 | - | |
15 | -casper.run(); | |
16 | - | |
17 | - |
config.js
View file @
f60d569
... | ... | @@ -52,7 +52,6 @@ |
52 | 52 | $urlRouterProvider.otherwise('/404'); |
53 | 53 | var auth_resolve = { |
54 | 54 | authorize: function($q, $rootScope, $state, $stateParams, UserService) { |
55 | - | |
56 | 55 | console.log('do we need to authorize a user for', $rootScope.nextState.name); |
57 | 56 | if (UserService.noAuthRequired($rootScope.nextState)) { |
58 | 57 | console.log('no auth required for', $rootScope.nextState.name); |
home.html
View file @
f60d569
... | ... | @@ -151,8 +151,8 @@ |
151 | 151 | <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-aria.min.js"></script> |
152 | 152 | <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-resource.min.js"></script> |
153 | 153 | <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.15/angular-sanitize.js"></script> |
154 | -<script src="https://cdn.rawgit.com/gdi2290/angular-websocket/v1.0.9/angular-websocket.min.js"></script> | |
155 | -<script src="https://cdn.rawgit.com/akatov/angular-contenteditable/master/angular-contenteditable.js"></script> | |
154 | +<script src="static/js/angular-websocket.js"></script> | |
155 | +<script src="static/js/angular-contenteditable.js"></script> | |
156 | 156 | <script src="https://cdnjs.cloudflare.com/ajax/libs/angular-filter/0.5.4/angular-filter.js"></script> |
157 | 157 | |
158 | 158 |
scripts/CardGridController.js
View file @
f60d569
... | ... | @@ -117,9 +117,11 @@ |
117 | 117 | $timeout($scope.refreshColumnWidth); |
118 | 118 | }; |
119 | 119 | |
120 | + | |
121 | + $scope.deck_cards = []; | |
120 | 122 | $http.get('/api/sections/' + $scope.sectionId + '/deck/'). |
121 | 123 | success(function(data) { |
122 | - $scope.deck_cards = []; | |
124 | + | |
123 | 125 | for (i in data) $scope.deck_cards[data[i].id] = data[i]; |
124 | 126 | console.log("got user's deck"); |
125 | 127 | }). |
scripts/ResetPasswordController.js
View file @
f60d569
... | ... | @@ -15,38 +15,38 @@ |
15 | 15 | }*/ |
16 | 16 | console.log('RESETTING'); |
17 | 17 | $scope.confirmResetPass = function() { |
18 | - if ($scope.newPassword.length < 8) { | |
19 | - $scope.unacceptable = true; | |
20 | - return; | |
21 | - } | |
22 | - if ($scope.newPassword != $scope.confirmPassword) { | |
23 | - $scope.mismatch = true; | |
24 | - $scope.confirmPassword.$setPristine(); | |
25 | - console.log('mismatch'); | |
26 | - return; | |
27 | - } | |
28 | - /*password passes local tests*/ | |
29 | - $http.post('/api/reset_password/', JSON.stringify({ | |
30 | - 'uid': uid, | |
31 | - 'token': token, | |
32 | - 'new_password': $scope.newPassword | |
33 | - })) | |
34 | - .success(function(data) { | |
35 | - $scope.error = false; | |
36 | - $scope.success = true; | |
37 | - //$state.go('resetpasssuccess'); | |
38 | - $timeout(function($state) { | |
39 | - $state.go('login'); | |
40 | - }, 1000); | |
41 | - console.log(data); | |
42 | - }) | |
43 | - .error(function(data, status, header, config) { | |
44 | - $scope.error = true; | |
45 | - $scope.success = false; | |
46 | - $scope.mismatch = false; | |
47 | - $scope.unacceptable = false; | |
48 | - console.log(data); | |
49 | - }); | |
18 | + if ($scope.newPassword.length < 8) { | |
19 | + $scope.unacceptable = true; | |
20 | + return; | |
21 | + } | |
22 | + if ($scope.newPassword != $scope.confirmPassword) { | |
23 | + $scope.mismatch = true; | |
24 | + $scope.confirmPassword.$setPristine(); | |
25 | + console.log('mismatch'); | |
26 | + return; | |
27 | + } | |
28 | + /*password passes local tests*/ | |
29 | + $http.post('/api/reset_password/', JSON.stringify({ | |
30 | + 'uid': uid, | |
31 | + 'token': token, | |
32 | + 'new_password': $scope.newPassword | |
33 | + })) | |
34 | + .success(function(data) { | |
35 | + $scope.error = false; | |
36 | + $scope.success = true; | |
37 | + //$state.go('resetpasssuccess'); | |
38 | + $timeout(function($state) { | |
39 | + $state.go('login'); | |
40 | + }, 1000); | |
41 | + console.log(data); | |
42 | + }) | |
43 | + .error(function(data, status, header, config) { | |
44 | + $scope.error = true; | |
45 | + $scope.success = false; | |
46 | + $scope.mismatch = false; | |
47 | + $scope.unacceptable = false; | |
48 | + console.log(data); | |
49 | + }); | |
50 | 50 | }; |
51 | 51 | $scope.cancelReset = function() { |
52 | 52 | $state.go('login'); |
scripts/SettingsController.js
View file @
f60d569
scripts/StudyController.js
View file @
f60d569
... | ... | @@ -4,7 +4,7 @@ |
4 | 4 | function($scope, $stateParams, $state, $http, UserService) { |
5 | 5 | console.log('Flashy study controller content in this file. also hell0'); |
6 | 6 | sectionId = $stateParams.sectionId; |
7 | - $scope.isParamOpen = true; | |
7 | + $scope.isParamOpen = true; | |
8 | 8 | |
9 | 9 | $(document).ready(function() { |
10 | 10 | $('.datepicker').pickadate({ |
scripts/UserService.js
View file @
f60d569
... | ... | @@ -5,7 +5,7 @@ |
5 | 5 | var login = function(data) { |
6 | 6 | if (data.locked) { |
7 | 7 | $rootScope.UserService.showLockedMessage(); |
8 | - return deferred.reject('account locked'); | |
8 | + return deferred.resolve(data); | |
9 | 9 | } |
10 | 10 | if (!data.is_confirmed) { |
11 | 11 | Materialize.toast('Please verify your email address! ' + |
... | ... | @@ -93,10 +93,8 @@ |
93 | 93 | 'Resend Verification Email</a>', 4000); |
94 | 94 | }; |
95 | 95 | this.noAuthRequired = function(state) { |
96 | - if (['verifyemail', 'login'].indexOf(state.name) >= 0) { | |
97 | - return true; | |
98 | - } | |
99 | - return false; | |
96 | + return ['verifyemail', 'login'].indexOf(state.name) >= 0; | |
97 | + | |
100 | 98 | }; |
101 | 99 | this.resendConfirmationEmail = function() { |
102 | 100 | console.log('Requesting resend of confirmation email'); |
scripts/bootstrap.js
View file @
f60d569
Diff suppressed. Click to show
1 | -/*! | |
2 | - * Bootstrap v3.3.4 (http://getbootstrap.com) | |
3 | - * Copyright 2011-2015 Twitter, Inc. | |
4 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
5 | - */ | |
6 | - | |
7 | -if (typeof jQuery === 'undefined') { | |
8 | - throw new Error('Bootstrap\'s JavaScript requires jQuery') | |
9 | -} | |
10 | - | |
11 | -+function ($) { | |
12 | - 'use strict'; | |
13 | - var version = $.fn.jquery.split(' ')[0].split('.') | |
14 | - if ((version[0] < 2 && version[1] < 9) || (version[0] == 1 && version[1] == 9 && version[2] < 1)) { | |
15 | - throw new Error('Bootstrap\'s JavaScript requires jQuery version 1.9.1 or higher') | |
16 | - } | |
17 | -}(jQuery); | |
18 | - | |
19 | -/* ======================================================================== | |
20 | - * Bootstrap: transition.js v3.3.4 | |
21 | - * http://getbootstrap.com/javascript/#transitions | |
22 | - * ======================================================================== | |
23 | - * Copyright 2011-2015 Twitter, Inc. | |
24 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
25 | - * ======================================================================== */ | |
26 | - | |
27 | - | |
28 | -+function ($) { | |
29 | - 'use strict'; | |
30 | - | |
31 | - // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/) | |
32 | - // ============================================================ | |
33 | - | |
34 | - function transitionEnd() { | |
35 | - var el = document.createElement('bootstrap') | |
36 | - | |
37 | - var transEndEventNames = { | |
38 | - WebkitTransition : 'webkitTransitionEnd', | |
39 | - MozTransition : 'transitionend', | |
40 | - OTransition : 'oTransitionEnd otransitionend', | |
41 | - transition : 'transitionend' | |
42 | - } | |
43 | - | |
44 | - for (var name in transEndEventNames) { | |
45 | - if (el.style[name] !== undefined) { | |
46 | - return { end: transEndEventNames[name] } | |
47 | - } | |
48 | - } | |
49 | - | |
50 | - return false // explicit for ie8 ( ._.) | |
51 | - } | |
52 | - | |
53 | - // http://blog.alexmaccaw.com/css-transitions | |
54 | - $.fn.emulateTransitionEnd = function (duration) { | |
55 | - var called = false | |
56 | - var $el = this | |
57 | - $(this).one('bsTransitionEnd', function () { called = true }) | |
58 | - var callback = function () { if (!called) $($el).trigger($.support.transition.end) } | |
59 | - setTimeout(callback, duration) | |
60 | - return this | |
61 | - } | |
62 | - | |
63 | - $(function () { | |
64 | - $.support.transition = transitionEnd() | |
65 | - | |
66 | - if (!$.support.transition) return | |
67 | - | |
68 | - $.event.special.bsTransitionEnd = { | |
69 | - bindType: $.support.transition.end, | |
70 | - delegateType: $.support.transition.end, | |
71 | - handle: function (e) { | |
72 | - if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments) | |
73 | - } | |
74 | - } | |
75 | - }) | |
76 | - | |
77 | -}(jQuery); | |
78 | - | |
79 | -/* ======================================================================== | |
80 | - * Bootstrap: alert.js v3.3.4 | |
81 | - * http://getbootstrap.com/javascript/#alerts | |
82 | - * ======================================================================== | |
83 | - * Copyright 2011-2015 Twitter, Inc. | |
84 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
85 | - * ======================================================================== */ | |
86 | - | |
87 | - | |
88 | -+function ($) { | |
89 | - 'use strict'; | |
90 | - | |
91 | - // ALERT CLASS DEFINITION | |
92 | - // ====================== | |
93 | - | |
94 | - var dismiss = '[data-dismiss="alert"]' | |
95 | - var Alert = function (el) { | |
96 | - $(el).on('click', dismiss, this.close) | |
97 | - } | |
98 | - | |
99 | - Alert.VERSION = '3.3.4' | |
100 | - | |
101 | - Alert.TRANSITION_DURATION = 150 | |
102 | - | |
103 | - Alert.prototype.close = function (e) { | |
104 | - var $this = $(this) | |
105 | - var selector = $this.attr('data-target') | |
106 | - | |
107 | - if (!selector) { | |
108 | - selector = $this.attr('href') | |
109 | - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 | |
110 | - } | |
111 | - | |
112 | - var $parent = $(selector) | |
113 | - | |
114 | - if (e) e.preventDefault() | |
115 | - | |
116 | - if (!$parent.length) { | |
117 | - $parent = $this.closest('.alert') | |
118 | - } | |
119 | - | |
120 | - $parent.trigger(e = $.Event('close.bs.alert')) | |
121 | - | |
122 | - if (e.isDefaultPrevented()) return | |
123 | - | |
124 | - $parent.removeClass('in') | |
125 | - | |
126 | - function removeElement() { | |
127 | - // detach from parent, fire event then clean up data | |
128 | - $parent.detach().trigger('closed.bs.alert').remove() | |
129 | - } | |
130 | - | |
131 | - $.support.transition && $parent.hasClass('fade') ? | |
132 | - $parent | |
133 | - .one('bsTransitionEnd', removeElement) | |
134 | - .emulateTransitionEnd(Alert.TRANSITION_DURATION) : | |
135 | - removeElement() | |
136 | - } | |
137 | - | |
138 | - | |
139 | - // ALERT PLUGIN DEFINITION | |
140 | - // ======================= | |
141 | - | |
142 | - function Plugin(option) { | |
143 | - return this.each(function () { | |
144 | - var $this = $(this) | |
145 | - var data = $this.data('bs.alert') | |
146 | - | |
147 | - if (!data) $this.data('bs.alert', (data = new Alert(this))) | |
148 | - if (typeof option == 'string') data[option].call($this) | |
149 | - }) | |
150 | - } | |
151 | - | |
152 | - var old = $.fn.alert | |
153 | - | |
154 | - $.fn.alert = Plugin | |
155 | - $.fn.alert.Constructor = Alert | |
156 | - | |
157 | - | |
158 | - // ALERT NO CONFLICT | |
159 | - // ================= | |
160 | - | |
161 | - $.fn.alert.noConflict = function () { | |
162 | - $.fn.alert = old | |
163 | - return this | |
164 | - } | |
165 | - | |
166 | - | |
167 | - // ALERT DATA-API | |
168 | - // ============== | |
169 | - | |
170 | - $(document).on('click.bs.alert.data-api', dismiss, Alert.prototype.close) | |
171 | - | |
172 | -}(jQuery); | |
173 | - | |
174 | -/* ======================================================================== | |
175 | - * Bootstrap: button.js v3.3.4 | |
176 | - * http://getbootstrap.com/javascript/#buttons | |
177 | - * ======================================================================== | |
178 | - * Copyright 2011-2015 Twitter, Inc. | |
179 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
180 | - * ======================================================================== */ | |
181 | - | |
182 | - | |
183 | -+function ($) { | |
184 | - 'use strict'; | |
185 | - | |
186 | - // BUTTON PUBLIC CLASS DEFINITION | |
187 | - // ============================== | |
188 | - | |
189 | - var Button = function (element, options) { | |
190 | - this.$element = $(element) | |
191 | - this.options = $.extend({}, Button.DEFAULTS, options) | |
192 | - this.isLoading = false | |
193 | - } | |
194 | - | |
195 | - Button.VERSION = '3.3.4' | |
196 | - | |
197 | - Button.DEFAULTS = { | |
198 | - loadingText: 'loading...' | |
199 | - } | |
200 | - | |
201 | - Button.prototype.setState = function (state) { | |
202 | - var d = 'disabled' | |
203 | - var $el = this.$element | |
204 | - var val = $el.is('input') ? 'val' : 'html' | |
205 | - var data = $el.data() | |
206 | - | |
207 | - state = state + 'Text' | |
208 | - | |
209 | - if (data.resetText == null) $el.data('resetText', $el[val]()) | |
210 | - | |
211 | - // push to event loop to allow forms to submit | |
212 | - setTimeout($.proxy(function () { | |
213 | - $el[val](data[state] == null ? this.options[state] : data[state]) | |
214 | - | |
215 | - if (state == 'loadingText') { | |
216 | - this.isLoading = true | |
217 | - $el.addClass(d).attr(d, d) | |
218 | - } else if (this.isLoading) { | |
219 | - this.isLoading = false | |
220 | - $el.removeClass(d).removeAttr(d) | |
221 | - } | |
222 | - }, this), 0) | |
223 | - } | |
224 | - | |
225 | - Button.prototype.toggle = function () { | |
226 | - var changed = true | |
227 | - var $parent = this.$element.closest('[data-toggle="buttons"]') | |
228 | - | |
229 | - if ($parent.length) { | |
230 | - var $input = this.$element.find('input') | |
231 | - if ($input.prop('type') == 'radio') { | |
232 | - if ($input.prop('checked') && this.$element.hasClass('active')) changed = false | |
233 | - else $parent.find('.active').removeClass('active') | |
234 | - } | |
235 | - if (changed) $input.prop('checked', !this.$element.hasClass('active')).trigger('change') | |
236 | - } else { | |
237 | - this.$element.attr('aria-pressed', !this.$element.hasClass('active')) | |
238 | - } | |
239 | - | |
240 | - if (changed) this.$element.toggleClass('active') | |
241 | - } | |
242 | - | |
243 | - | |
244 | - // BUTTON PLUGIN DEFINITION | |
245 | - // ======================== | |
246 | - | |
247 | - function Plugin(option) { | |
248 | - return this.each(function () { | |
249 | - var $this = $(this) | |
250 | - var data = $this.data('bs.button') | |
251 | - var options = typeof option == 'object' && option | |
252 | - | |
253 | - if (!data) $this.data('bs.button', (data = new Button(this, options))) | |
254 | - | |
255 | - if (option == 'toggle') data.toggle() | |
256 | - else if (option) data.setState(option) | |
257 | - }) | |
258 | - } | |
259 | - | |
260 | - var old = $.fn.button | |
261 | - | |
262 | - $.fn.button = Plugin | |
263 | - $.fn.button.Constructor = Button | |
264 | - | |
265 | - | |
266 | - // BUTTON NO CONFLICT | |
267 | - // ================== | |
268 | - | |
269 | - $.fn.button.noConflict = function () { | |
270 | - $.fn.button = old | |
271 | - return this | |
272 | - } | |
273 | - | |
274 | - | |
275 | - // BUTTON DATA-API | |
276 | - // =============== | |
277 | - | |
278 | - $(document) | |
279 | - .on('click.bs.button.data-api', '[data-toggle^="button"]', function (e) { | |
280 | - var $btn = $(e.target) | |
281 | - if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') | |
282 | - Plugin.call($btn, 'toggle') | |
283 | - e.preventDefault() | |
284 | - }) | |
285 | - .on('focus.bs.button.data-api blur.bs.button.data-api', '[data-toggle^="button"]', function (e) { | |
286 | - $(e.target).closest('.btn').toggleClass('focus', /^focus(in)?$/.test(e.type)) | |
287 | - }) | |
288 | - | |
289 | -}(jQuery); | |
290 | - | |
291 | -/* ======================================================================== | |
292 | - * Bootstrap: carousel.js v3.3.4 | |
293 | - * http://getbootstrap.com/javascript/#carousel | |
294 | - * ======================================================================== | |
295 | - * Copyright 2011-2015 Twitter, Inc. | |
296 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
297 | - * ======================================================================== */ | |
298 | - | |
299 | - | |
300 | -+function ($) { | |
301 | - 'use strict'; | |
302 | - | |
303 | - // CAROUSEL CLASS DEFINITION | |
304 | - // ========================= | |
305 | - | |
306 | - var Carousel = function (element, options) { | |
307 | - this.$element = $(element) | |
308 | - this.$indicators = this.$element.find('.carousel-indicators') | |
309 | - this.options = options | |
310 | - this.paused = null | |
311 | - this.sliding = null | |
312 | - this.interval = null | |
313 | - this.$active = null | |
314 | - this.$items = null | |
315 | - | |
316 | - this.options.keyboard && this.$element.on('keydown.bs.carousel', $.proxy(this.keydown, this)) | |
317 | - | |
318 | - this.options.pause == 'hover' && !('ontouchstart' in document.documentElement) && this.$element | |
319 | - .on('mouseenter.bs.carousel', $.proxy(this.pause, this)) | |
320 | - .on('mouseleave.bs.carousel', $.proxy(this.cycle, this)) | |
321 | - } | |
322 | - | |
323 | - Carousel.VERSION = '3.3.4' | |
324 | - | |
325 | - Carousel.TRANSITION_DURATION = 600 | |
326 | - | |
327 | - Carousel.DEFAULTS = { | |
328 | - interval: 5000, | |
329 | - pause: 'hover', | |
330 | - wrap: true, | |
331 | - keyboard: true | |
332 | - } | |
333 | - | |
334 | - Carousel.prototype.keydown = function (e) { | |
335 | - if (/input|textarea/i.test(e.target.tagName)) return | |
336 | - switch (e.which) { | |
337 | - case 37: this.prev(); break | |
338 | - case 39: this.next(); break | |
339 | - default: return | |
340 | - } | |
341 | - | |
342 | - e.preventDefault() | |
343 | - } | |
344 | - | |
345 | - Carousel.prototype.cycle = function (e) { | |
346 | - e || (this.paused = false) | |
347 | - | |
348 | - this.interval && clearInterval(this.interval) | |
349 | - | |
350 | - this.options.interval | |
351 | - && !this.paused | |
352 | - && (this.interval = setInterval($.proxy(this.next, this), this.options.interval)) | |
353 | - | |
354 | - return this | |
355 | - } | |
356 | - | |
357 | - Carousel.prototype.getItemIndex = function (item) { | |
358 | - this.$items = item.parent().children('.item') | |
359 | - return this.$items.index(item || this.$active) | |
360 | - } | |
361 | - | |
362 | - Carousel.prototype.getItemForDirection = function (direction, active) { | |
363 | - var activeIndex = this.getItemIndex(active) | |
364 | - var willWrap = (direction == 'prev' && activeIndex === 0) | |
365 | - || (direction == 'next' && activeIndex == (this.$items.length - 1)) | |
366 | - if (willWrap && !this.options.wrap) return active | |
367 | - var delta = direction == 'prev' ? -1 : 1 | |
368 | - var itemIndex = (activeIndex + delta) % this.$items.length | |
369 | - return this.$items.eq(itemIndex) | |
370 | - } | |
371 | - | |
372 | - Carousel.prototype.to = function (pos) { | |
373 | - var that = this | |
374 | - var activeIndex = this.getItemIndex(this.$active = this.$element.find('.item.active')) | |
375 | - | |
376 | - if (pos > (this.$items.length - 1) || pos < 0) return | |
377 | - | |
378 | - if (this.sliding) return this.$element.one('slid.bs.carousel', function () { that.to(pos) }) // yes, "slid" | |
379 | - if (activeIndex == pos) return this.pause().cycle() | |
380 | - | |
381 | - return this.slide(pos > activeIndex ? 'next' : 'prev', this.$items.eq(pos)) | |
382 | - } | |
383 | - | |
384 | - Carousel.prototype.pause = function (e) { | |
385 | - e || (this.paused = true) | |
386 | - | |
387 | - if (this.$element.find('.next, .prev').length && $.support.transition) { | |
388 | - this.$element.trigger($.support.transition.end) | |
389 | - this.cycle(true) | |
390 | - } | |
391 | - | |
392 | - this.interval = clearInterval(this.interval) | |
393 | - | |
394 | - return this | |
395 | - } | |
396 | - | |
397 | - Carousel.prototype.next = function () { | |
398 | - if (this.sliding) return | |
399 | - return this.slide('next') | |
400 | - } | |
401 | - | |
402 | - Carousel.prototype.prev = function () { | |
403 | - if (this.sliding) return | |
404 | - return this.slide('prev') | |
405 | - } | |
406 | - | |
407 | - Carousel.prototype.slide = function (type, next) { | |
408 | - var $active = this.$element.find('.item.active') | |
409 | - var $next = next || this.getItemForDirection(type, $active) | |
410 | - var isCycling = this.interval | |
411 | - var direction = type == 'next' ? 'left' : 'right' | |
412 | - var that = this | |
413 | - | |
414 | - if ($next.hasClass('active')) return (this.sliding = false) | |
415 | - | |
416 | - var relatedTarget = $next[0] | |
417 | - var slideEvent = $.Event('slide.bs.carousel', { | |
418 | - relatedTarget: relatedTarget, | |
419 | - direction: direction | |
420 | - }) | |
421 | - this.$element.trigger(slideEvent) | |
422 | - if (slideEvent.isDefaultPrevented()) return | |
423 | - | |
424 | - this.sliding = true | |
425 | - | |
426 | - isCycling && this.pause() | |
427 | - | |
428 | - if (this.$indicators.length) { | |
429 | - this.$indicators.find('.active').removeClass('active') | |
430 | - var $nextIndicator = $(this.$indicators.children()[this.getItemIndex($next)]) | |
431 | - $nextIndicator && $nextIndicator.addClass('active') | |
432 | - } | |
433 | - | |
434 | - var slidEvent = $.Event('slid.bs.carousel', { relatedTarget: relatedTarget, direction: direction }) // yes, "slid" | |
435 | - if ($.support.transition && this.$element.hasClass('slide')) { | |
436 | - $next.addClass(type) | |
437 | - $next[0].offsetWidth // force reflow | |
438 | - $active.addClass(direction) | |
439 | - $next.addClass(direction) | |
440 | - $active | |
441 | - .one('bsTransitionEnd', function () { | |
442 | - $next.removeClass([type, direction].join(' ')).addClass('active') | |
443 | - $active.removeClass(['active', direction].join(' ')) | |
444 | - that.sliding = false | |
445 | - setTimeout(function () { | |
446 | - that.$element.trigger(slidEvent) | |
447 | - }, 0) | |
448 | - }) | |
449 | - .emulateTransitionEnd(Carousel.TRANSITION_DURATION) | |
450 | - } else { | |
451 | - $active.removeClass('active') | |
452 | - $next.addClass('active') | |
453 | - this.sliding = false | |
454 | - this.$element.trigger(slidEvent) | |
455 | - } | |
456 | - | |
457 | - isCycling && this.cycle() | |
458 | - | |
459 | - return this | |
460 | - } | |
461 | - | |
462 | - | |
463 | - // CAROUSEL PLUGIN DEFINITION | |
464 | - // ========================== | |
465 | - | |
466 | - function Plugin(option) { | |
467 | - return this.each(function () { | |
468 | - var $this = $(this) | |
469 | - var data = $this.data('bs.carousel') | |
470 | - var options = $.extend({}, Carousel.DEFAULTS, $this.data(), typeof option == 'object' && option) | |
471 | - var action = typeof option == 'string' ? option : options.slide | |
472 | - | |
473 | - if (!data) $this.data('bs.carousel', (data = new Carousel(this, options))) | |
474 | - if (typeof option == 'number') data.to(option) | |
475 | - else if (action) data[action]() | |
476 | - else if (options.interval) data.pause().cycle() | |
477 | - }) | |
478 | - } | |
479 | - | |
480 | - var old = $.fn.carousel | |
481 | - | |
482 | - $.fn.carousel = Plugin | |
483 | - $.fn.carousel.Constructor = Carousel | |
484 | - | |
485 | - | |
486 | - // CAROUSEL NO CONFLICT | |
487 | - // ==================== | |
488 | - | |
489 | - $.fn.carousel.noConflict = function () { | |
490 | - $.fn.carousel = old | |
491 | - return this | |
492 | - } | |
493 | - | |
494 | - | |
495 | - // CAROUSEL DATA-API | |
496 | - // ================= | |
497 | - | |
498 | - var clickHandler = function (e) { | |
499 | - var href | |
500 | - var $this = $(this) | |
501 | - var $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) // strip for ie7 | |
502 | - if (!$target.hasClass('carousel')) return | |
503 | - var options = $.extend({}, $target.data(), $this.data()) | |
504 | - var slideIndex = $this.attr('data-slide-to') | |
505 | - if (slideIndex) options.interval = false | |
506 | - | |
507 | - Plugin.call($target, options) | |
508 | - | |
509 | - if (slideIndex) { | |
510 | - $target.data('bs.carousel').to(slideIndex) | |
511 | - } | |
512 | - | |
513 | - e.preventDefault() | |
514 | - } | |
515 | - | |
516 | - $(document) | |
517 | - .on('click.bs.carousel.data-api', '[data-slide]', clickHandler) | |
518 | - .on('click.bs.carousel.data-api', '[data-slide-to]', clickHandler) | |
519 | - | |
520 | - $(window).on('load', function () { | |
521 | - $('[data-ride="carousel"]').each(function () { | |
522 | - var $carousel = $(this) | |
523 | - Plugin.call($carousel, $carousel.data()) | |
524 | - }) | |
525 | - }) | |
526 | - | |
527 | -}(jQuery); | |
528 | - | |
529 | -/* ======================================================================== | |
530 | - * Bootstrap: collapse.js v3.3.4 | |
531 | - * http://getbootstrap.com/javascript/#collapse | |
532 | - * ======================================================================== | |
533 | - * Copyright 2011-2015 Twitter, Inc. | |
534 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
535 | - * ======================================================================== */ | |
536 | - | |
537 | - | |
538 | -+function ($) { | |
539 | - 'use strict'; | |
540 | - | |
541 | - // COLLAPSE PUBLIC CLASS DEFINITION | |
542 | - // ================================ | |
543 | - | |
544 | - var Collapse = function (element, options) { | |
545 | - this.$element = $(element) | |
546 | - this.options = $.extend({}, Collapse.DEFAULTS, options) | |
547 | - this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' + | |
548 | - '[data-toggle="collapse"][data-target="#' + element.id + '"]') | |
549 | - this.transitioning = null | |
550 | - | |
551 | - if (this.options.parent) { | |
552 | - this.$parent = this.getParent() | |
553 | - } else { | |
554 | - this.addAriaAndCollapsedClass(this.$element, this.$trigger) | |
555 | - } | |
556 | - | |
557 | - if (this.options.toggle) this.toggle() | |
558 | - } | |
559 | - | |
560 | - Collapse.VERSION = '3.3.4' | |
561 | - | |
562 | - Collapse.TRANSITION_DURATION = 350 | |
563 | - | |
564 | - Collapse.DEFAULTS = { | |
565 | - toggle: true | |
566 | - } | |
567 | - | |
568 | - Collapse.prototype.dimension = function () { | |
569 | - var hasWidth = this.$element.hasClass('width') | |
570 | - return hasWidth ? 'width' : 'height' | |
571 | - } | |
572 | - | |
573 | - Collapse.prototype.show = function () { | |
574 | - if (this.transitioning || this.$element.hasClass('in')) return | |
575 | - | |
576 | - var activesData | |
577 | - var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing') | |
578 | - | |
579 | - if (actives && actives.length) { | |
580 | - activesData = actives.data('bs.collapse') | |
581 | - if (activesData && activesData.transitioning) return | |
582 | - } | |
583 | - | |
584 | - var startEvent = $.Event('show.bs.collapse') | |
585 | - this.$element.trigger(startEvent) | |
586 | - if (startEvent.isDefaultPrevented()) return | |
587 | - | |
588 | - if (actives && actives.length) { | |
589 | - Plugin.call(actives, 'hide') | |
590 | - activesData || actives.data('bs.collapse', null) | |
591 | - } | |
592 | - | |
593 | - var dimension = this.dimension() | |
594 | - | |
595 | - this.$element | |
596 | - .removeClass('collapse') | |
597 | - .addClass('collapsing')[dimension](0) | |
598 | - .attr('aria-expanded', true) | |
599 | - | |
600 | - this.$trigger | |
601 | - .removeClass('collapsed') | |
602 | - .attr('aria-expanded', true) | |
603 | - | |
604 | - this.transitioning = 1 | |
605 | - | |
606 | - var complete = function () { | |
607 | - this.$element | |
608 | - .removeClass('collapsing') | |
609 | - .addClass('collapse in')[dimension]('') | |
610 | - this.transitioning = 0 | |
611 | - this.$element | |
612 | - .trigger('shown.bs.collapse') | |
613 | - } | |
614 | - | |
615 | - if (!$.support.transition) return complete.call(this) | |
616 | - | |
617 | - var scrollSize = $.camelCase(['scroll', dimension].join('-')) | |
618 | - | |
619 | - this.$element | |
620 | - .one('bsTransitionEnd', $.proxy(complete, this)) | |
621 | - .emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]) | |
622 | - } | |
623 | - | |
624 | - Collapse.prototype.hide = function () { | |
625 | - if (this.transitioning || !this.$element.hasClass('in')) return | |
626 | - | |
627 | - var startEvent = $.Event('hide.bs.collapse') | |
628 | - this.$element.trigger(startEvent) | |
629 | - if (startEvent.isDefaultPrevented()) return | |
630 | - | |
631 | - var dimension = this.dimension() | |
632 | - | |
633 | - this.$element[dimension](this.$element[dimension]())[0].offsetHeight | |
634 | - | |
635 | - this.$element | |
636 | - .addClass('collapsing') | |
637 | - .removeClass('collapse in') | |
638 | - .attr('aria-expanded', false) | |
639 | - | |
640 | - this.$trigger | |
641 | - .addClass('collapsed') | |
642 | - .attr('aria-expanded', false) | |
643 | - | |
644 | - this.transitioning = 1 | |
645 | - | |
646 | - var complete = function () { | |
647 | - this.transitioning = 0 | |
648 | - this.$element | |
649 | - .removeClass('collapsing') | |
650 | - .addClass('collapse') | |
651 | - .trigger('hidden.bs.collapse') | |
652 | - } | |
653 | - | |
654 | - if (!$.support.transition) return complete.call(this) | |
655 | - | |
656 | - this.$element | |
657 | - [dimension](0) | |
658 | - .one('bsTransitionEnd', $.proxy(complete, this)) | |
659 | - .emulateTransitionEnd(Collapse.TRANSITION_DURATION) | |
660 | - } | |
661 | - | |
662 | - Collapse.prototype.toggle = function () { | |
663 | - this[this.$element.hasClass('in') ? 'hide' : 'show']() | |
664 | - } | |
665 | - | |
666 | - Collapse.prototype.getParent = function () { | |
667 | - return $(this.options.parent) | |
668 | - .find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]') | |
669 | - .each($.proxy(function (i, element) { | |
670 | - var $element = $(element) | |
671 | - this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element) | |
672 | - }, this)) | |
673 | - .end() | |
674 | - } | |
675 | - | |
676 | - Collapse.prototype.addAriaAndCollapsedClass = function ($element, $trigger) { | |
677 | - var isOpen = $element.hasClass('in') | |
678 | - | |
679 | - $element.attr('aria-expanded', isOpen) | |
680 | - $trigger | |
681 | - .toggleClass('collapsed', !isOpen) | |
682 | - .attr('aria-expanded', isOpen) | |
683 | - } | |
684 | - | |
685 | - function getTargetFromTrigger($trigger) { | |
686 | - var href | |
687 | - var target = $trigger.attr('data-target') | |
688 | - || (href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') // strip for ie7 | |
689 | - | |
690 | - return $(target) | |
691 | - } | |
692 | - | |
693 | - | |
694 | - // COLLAPSE PLUGIN DEFINITION | |
695 | - // ========================== | |
696 | - | |
697 | - function Plugin(option) { | |
698 | - return this.each(function () { | |
699 | - var $this = $(this) | |
700 | - var data = $this.data('bs.collapse') | |
701 | - var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option) | |
702 | - | |
703 | - if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false | |
704 | - if (!data) $this.data('bs.collapse', (data = new Collapse(this, options))) | |
705 | - if (typeof option == 'string') data[option]() | |
706 | - }) | |
707 | - } | |
708 | - | |
709 | - var old = $.fn.collapse | |
710 | - | |
711 | - $.fn.collapse = Plugin | |
712 | - $.fn.collapse.Constructor = Collapse | |
713 | - | |
714 | - | |
715 | - // COLLAPSE NO CONFLICT | |
716 | - // ==================== | |
717 | - | |
718 | - $.fn.collapse.noConflict = function () { | |
719 | - $.fn.collapse = old | |
720 | - return this | |
721 | - } | |
722 | - | |
723 | - | |
724 | - // COLLAPSE DATA-API | |
725 | - // ================= | |
726 | - | |
727 | - $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) { | |
728 | - var $this = $(this) | |
729 | - | |
730 | - if (!$this.attr('data-target')) e.preventDefault() | |
731 | - | |
732 | - var $target = getTargetFromTrigger($this) | |
733 | - var data = $target.data('bs.collapse') | |
734 | - var option = data ? 'toggle' : $this.data() | |
735 | - | |
736 | - Plugin.call($target, option) | |
737 | - }) | |
738 | - | |
739 | -}(jQuery); | |
740 | - | |
741 | -/* ======================================================================== | |
742 | - * Bootstrap: dropdown.js v3.3.4 | |
743 | - * http://getbootstrap.com/javascript/#dropdowns | |
744 | - * ======================================================================== | |
745 | - * Copyright 2011-2015 Twitter, Inc. | |
746 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
747 | - * ======================================================================== */ | |
748 | - | |
749 | - | |
750 | -+function ($) { | |
751 | - 'use strict'; | |
752 | - | |
753 | - // DROPDOWN CLASS DEFINITION | |
754 | - // ========================= | |
755 | - | |
756 | - var backdrop = '.dropdown-backdrop' | |
757 | - var toggle = '[data-toggle="dropdown"]' | |
758 | - var Dropdown = function (element) { | |
759 | - $(element).on('click.bs.dropdown', this.toggle) | |
760 | - } | |
761 | - | |
762 | - Dropdown.VERSION = '3.3.4' | |
763 | - | |
764 | - Dropdown.prototype.toggle = function (e) { | |
765 | - var $this = $(this) | |
766 | - | |
767 | - if ($this.is('.disabled, :disabled')) return | |
768 | - | |
769 | - var $parent = getParent($this) | |
770 | - var isActive = $parent.hasClass('open') | |
771 | - | |
772 | - clearMenus() | |
773 | - | |
774 | - if (!isActive) { | |
775 | - if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) { | |
776 | - // if mobile we use a backdrop because click events don't delegate | |
777 | - $('<div class="dropdown-backdrop"/>').insertAfter($(this)).on('click', clearMenus) | |
778 | - } | |
779 | - | |
780 | - var relatedTarget = { relatedTarget: this } | |
781 | - $parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget)) | |
782 | - | |
783 | - if (e.isDefaultPrevented()) return | |
784 | - | |
785 | - $this | |
786 | - .trigger('focus') | |
787 | - .attr('aria-expanded', 'true') | |
788 | - | |
789 | - $parent | |
790 | - .toggleClass('open') | |
791 | - .trigger('shown.bs.dropdown', relatedTarget) | |
792 | - } | |
793 | - | |
794 | - return false | |
795 | - } | |
796 | - | |
797 | - Dropdown.prototype.keydown = function (e) { | |
798 | - if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return | |
799 | - | |
800 | - var $this = $(this) | |
801 | - | |
802 | - e.preventDefault() | |
803 | - e.stopPropagation() | |
804 | - | |
805 | - if ($this.is('.disabled, :disabled')) return | |
806 | - | |
807 | - var $parent = getParent($this) | |
808 | - var isActive = $parent.hasClass('open') | |
809 | - | |
810 | - if ((!isActive && e.which != 27) || (isActive && e.which == 27)) { | |
811 | - if (e.which == 27) $parent.find(toggle).trigger('focus') | |
812 | - return $this.trigger('click') | |
813 | - } | |
814 | - | |
815 | - var desc = ' li:not(.disabled):visible a' | |
816 | - var $items = $parent.find('[role="menu"]' + desc + ', [role="listbox"]' + desc) | |
817 | - | |
818 | - if (!$items.length) return | |
819 | - | |
820 | - var index = $items.index(e.target) | |
821 | - | |
822 | - if (e.which == 38 && index > 0) index-- // up | |
823 | - if (e.which == 40 && index < $items.length - 1) index++ // down | |
824 | - if (!~index) index = 0 | |
825 | - | |
826 | - $items.eq(index).trigger('focus') | |
827 | - } | |
828 | - | |
829 | - function clearMenus(e) { | |
830 | - if (e && e.which === 3) return | |
831 | - $(backdrop).remove() | |
832 | - $(toggle).each(function () { | |
833 | - var $this = $(this) | |
834 | - var $parent = getParent($this) | |
835 | - var relatedTarget = { relatedTarget: this } | |
836 | - | |
837 | - if (!$parent.hasClass('open')) return | |
838 | - | |
839 | - $parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget)) | |
840 | - | |
841 | - if (e.isDefaultPrevented()) return | |
842 | - | |
843 | - $this.attr('aria-expanded', 'false') | |
844 | - $parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget) | |
845 | - }) | |
846 | - } | |
847 | - | |
848 | - function getParent($this) { | |
849 | - var selector = $this.attr('data-target') | |
850 | - | |
851 | - if (!selector) { | |
852 | - selector = $this.attr('href') | |
853 | - selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 | |
854 | - } | |
855 | - | |
856 | - var $parent = selector && $(selector) | |
857 | - | |
858 | - return $parent && $parent.length ? $parent : $this.parent() | |
859 | - } | |
860 | - | |
861 | - | |
862 | - // DROPDOWN PLUGIN DEFINITION | |
863 | - // ========================== | |
864 | - | |
865 | - function Plugin(option) { | |
866 | - return this.each(function () { | |
867 | - var $this = $(this) | |
868 | - var data = $this.data('bs.dropdown') | |
869 | - | |
870 | - if (!data) $this.data('bs.dropdown', (data = new Dropdown(this))) | |
871 | - if (typeof option == 'string') data[option].call($this) | |
872 | - }) | |
873 | - } | |
874 | - | |
875 | - var old = $.fn.dropdown | |
876 | - | |
877 | - $.fn.dropdown = Plugin | |
878 | - $.fn.dropdown.Constructor = Dropdown | |
879 | - | |
880 | - | |
881 | - // DROPDOWN NO CONFLICT | |
882 | - // ==================== | |
883 | - | |
884 | - $.fn.dropdown.noConflict = function () { | |
885 | - $.fn.dropdown = old | |
886 | - return this | |
887 | - } | |
888 | - | |
889 | - | |
890 | - // APPLY TO STANDARD DROPDOWN ELEMENTS | |
891 | - // =================================== | |
892 | - | |
893 | - $(document) | |
894 | - .on('click.bs.dropdown.data-api', clearMenus) | |
895 | - .on('click.bs.dropdown.data-api', '.dropdown form', function (e) { e.stopPropagation() }) | |
896 | - .on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle) | |
897 | - .on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown) | |
898 | - .on('keydown.bs.dropdown.data-api', '[role="menu"]', Dropdown.prototype.keydown) | |
899 | - .on('keydown.bs.dropdown.data-api', '[role="listbox"]', Dropdown.prototype.keydown) | |
900 | - | |
901 | -}(jQuery); | |
902 | - | |
903 | -/* ======================================================================== | |
904 | - * Bootstrap: modal.js v3.3.4 | |
905 | - * http://getbootstrap.com/javascript/#modals | |
906 | - * ======================================================================== | |
907 | - * Copyright 2011-2015 Twitter, Inc. | |
908 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
909 | - * ======================================================================== */ | |
910 | - | |
911 | - | |
912 | -+function ($) { | |
913 | - 'use strict'; | |
914 | - | |
915 | - // MODAL CLASS DEFINITION | |
916 | - // ====================== | |
917 | - | |
918 | - var Modal = function (element, options) { | |
919 | - this.options = options | |
920 | - this.$body = $(document.body) | |
921 | - this.$element = $(element) | |
922 | - this.$dialog = this.$element.find('.modal-dialog') | |
923 | - this.$backdrop = null | |
924 | - this.isShown = null | |
925 | - this.originalBodyPad = null | |
926 | - this.scrollbarWidth = 0 | |
927 | - this.ignoreBackdropClick = false | |
928 | - | |
929 | - if (this.options.remote) { | |
930 | - this.$element | |
931 | - .find('.modal-content') | |
932 | - .load(this.options.remote, $.proxy(function () { | |
933 | - this.$element.trigger('loaded.bs.modal') | |
934 | - }, this)) | |
935 | - } | |
936 | - } | |
937 | - | |
938 | - Modal.VERSION = '3.3.4' | |
939 | - | |
940 | - Modal.TRANSITION_DURATION = 300 | |
941 | - Modal.BACKDROP_TRANSITION_DURATION = 150 | |
942 | - | |
943 | - Modal.DEFAULTS = { | |
944 | - backdrop: true, | |
945 | - keyboard: true, | |
946 | - show: true | |
947 | - } | |
948 | - | |
949 | - Modal.prototype.toggle = function (_relatedTarget) { | |
950 | - return this.isShown ? this.hide() : this.show(_relatedTarget) | |
951 | - } | |
952 | - | |
953 | - Modal.prototype.show = function (_relatedTarget) { | |
954 | - var that = this | |
955 | - var e = $.Event('show.bs.modal', { relatedTarget: _relatedTarget }) | |
956 | - | |
957 | - this.$element.trigger(e) | |
958 | - | |
959 | - if (this.isShown || e.isDefaultPrevented()) return | |
960 | - | |
961 | - this.isShown = true | |
962 | - | |
963 | - this.checkScrollbar() | |
964 | - this.setScrollbar() | |
965 | - this.$body.addClass('modal-open') | |
966 | - | |
967 | - this.escape() | |
968 | - this.resize() | |
969 | - | |
970 | - this.$element.on('click.dismiss.bs.modal', '[data-dismiss="modal"]', $.proxy(this.hide, this)) | |
971 | - | |
972 | - this.$dialog.on('mousedown.dismiss.bs.modal', function () { | |
973 | - that.$element.one('mouseup.dismiss.bs.modal', function (e) { | |
974 | - if ($(e.target).is(that.$element)) that.ignoreBackdropClick = true | |
975 | - }) | |
976 | - }) | |
977 | - | |
978 | - this.backdrop(function () { | |
979 | - var transition = $.support.transition && that.$element.hasClass('fade') | |
980 | - | |
981 | - if (!that.$element.parent().length) { | |
982 | - that.$element.appendTo(that.$body) // don't move modals dom position | |
983 | - } | |
984 | - | |
985 | - that.$element | |
986 | - .show() | |
987 | - .scrollTop(0) | |
988 | - | |
989 | - that.adjustDialog() | |
990 | - | |
991 | - if (transition) { | |
992 | - that.$element[0].offsetWidth // force reflow | |
993 | - } | |
994 | - | |
995 | - that.$element | |
996 | - .addClass('in') | |
997 | - .attr('aria-hidden', false) | |
998 | - | |
999 | - that.enforceFocus() | |
1000 | - | |
1001 | - var e = $.Event('shown.bs.modal', { relatedTarget: _relatedTarget }) | |
1002 | - | |
1003 | - transition ? | |
1004 | - that.$dialog // wait for modal to slide in | |
1005 | - .one('bsTransitionEnd', function () { | |
1006 | - that.$element.trigger('focus').trigger(e) | |
1007 | - }) | |
1008 | - .emulateTransitionEnd(Modal.TRANSITION_DURATION) : | |
1009 | - that.$element.trigger('focus').trigger(e) | |
1010 | - }) | |
1011 | - } | |
1012 | - | |
1013 | - Modal.prototype.hide = function (e) { | |
1014 | - if (e) e.preventDefault() | |
1015 | - | |
1016 | - e = $.Event('hide.bs.modal') | |
1017 | - | |
1018 | - this.$element.trigger(e) | |
1019 | - | |
1020 | - if (!this.isShown || e.isDefaultPrevented()) return | |
1021 | - | |
1022 | - this.isShown = false | |
1023 | - | |
1024 | - this.escape() | |
1025 | - this.resize() | |
1026 | - | |
1027 | - $(document).off('focusin.bs.modal') | |
1028 | - | |
1029 | - this.$element | |
1030 | - .removeClass('in') | |
1031 | - .attr('aria-hidden', true) | |
1032 | - .off('click.dismiss.bs.modal') | |
1033 | - .off('mouseup.dismiss.bs.modal') | |
1034 | - | |
1035 | - this.$dialog.off('mousedown.dismiss.bs.modal') | |
1036 | - | |
1037 | - $.support.transition && this.$element.hasClass('fade') ? | |
1038 | - this.$element | |
1039 | - .one('bsTransitionEnd', $.proxy(this.hideModal, this)) | |
1040 | - .emulateTransitionEnd(Modal.TRANSITION_DURATION) : | |
1041 | - this.hideModal() | |
1042 | - } | |
1043 | - | |
1044 | - Modal.prototype.enforceFocus = function () { | |
1045 | - $(document) | |
1046 | - .off('focusin.bs.modal') // guard against infinite focus loop | |
1047 | - .on('focusin.bs.modal', $.proxy(function (e) { | |
1048 | - if (this.$element[0] !== e.target && !this.$element.has(e.target).length) { | |
1049 | - this.$element.trigger('focus') | |
1050 | - } | |
1051 | - }, this)) | |
1052 | - } | |
1053 | - | |
1054 | - Modal.prototype.escape = function () { | |
1055 | - if (this.isShown && this.options.keyboard) { | |
1056 | - this.$element.on('keydown.dismiss.bs.modal', $.proxy(function (e) { | |
1057 | - e.which == 27 && this.hide() | |
1058 | - }, this)) | |
1059 | - } else if (!this.isShown) { | |
1060 | - this.$element.off('keydown.dismiss.bs.modal') | |
1061 | - } | |
1062 | - } | |
1063 | - | |
1064 | - Modal.prototype.resize = function () { | |
1065 | - if (this.isShown) { | |
1066 | - $(window).on('resize.bs.modal', $.proxy(this.handleUpdate, this)) | |
1067 | - } else { | |
1068 | - $(window).off('resize.bs.modal') | |
1069 | - } | |
1070 | - } | |
1071 | - | |
1072 | - Modal.prototype.hideModal = function () { | |
1073 | - var that = this | |
1074 | - this.$element.hide() | |
1075 | - this.backdrop(function () { | |
1076 | - that.$body.removeClass('modal-open') | |
1077 | - that.resetAdjustments() | |
1078 | - that.resetScrollbar() | |
1079 | - that.$element.trigger('hidden.bs.modal') | |
1080 | - }) | |
1081 | - } | |
1082 | - | |
1083 | - Modal.prototype.removeBackdrop = function () { | |
1084 | - this.$backdrop && this.$backdrop.remove() | |
1085 | - this.$backdrop = null | |
1086 | - } | |
1087 | - | |
1088 | - Modal.prototype.backdrop = function (callback) { | |
1089 | - var that = this | |
1090 | - var animate = this.$element.hasClass('fade') ? 'fade' : '' | |
1091 | - | |
1092 | - if (this.isShown && this.options.backdrop) { | |
1093 | - var doAnimate = $.support.transition && animate | |
1094 | - | |
1095 | - this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />') | |
1096 | - .appendTo(this.$body) | |
1097 | - | |
1098 | - this.$element.on('click.dismiss.bs.modal', $.proxy(function (e) { | |
1099 | - if (this.ignoreBackdropClick) { | |
1100 | - this.ignoreBackdropClick = false | |
1101 | - return | |
1102 | - } | |
1103 | - if (e.target !== e.currentTarget) return | |
1104 | - this.options.backdrop == 'static' | |
1105 | - ? this.$element[0].focus() | |
1106 | - : this.hide() | |
1107 | - }, this)) | |
1108 | - | |
1109 | - if (doAnimate) this.$backdrop[0].offsetWidth // force reflow | |
1110 | - | |
1111 | - this.$backdrop.addClass('in') | |
1112 | - | |
1113 | - if (!callback) return | |
1114 | - | |
1115 | - doAnimate ? | |
1116 | - this.$backdrop | |
1117 | - .one('bsTransitionEnd', callback) | |
1118 | - .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : | |
1119 | - callback() | |
1120 | - | |
1121 | - } else if (!this.isShown && this.$backdrop) { | |
1122 | - this.$backdrop.removeClass('in') | |
1123 | - | |
1124 | - var callbackRemove = function () { | |
1125 | - that.removeBackdrop() | |
1126 | - callback && callback() | |
1127 | - } | |
1128 | - $.support.transition && this.$element.hasClass('fade') ? | |
1129 | - this.$backdrop | |
1130 | - .one('bsTransitionEnd', callbackRemove) | |
1131 | - .emulateTransitionEnd(Modal.BACKDROP_TRANSITION_DURATION) : | |
1132 | - callbackRemove() | |
1133 | - | |
1134 | - } else if (callback) { | |
1135 | - callback() | |
1136 | - } | |
1137 | - } | |
1138 | - | |
1139 | - // these following methods are used to handle overflowing modals | |
1140 | - | |
1141 | - Modal.prototype.handleUpdate = function () { | |
1142 | - this.adjustDialog() | |
1143 | - } | |
1144 | - | |
1145 | - Modal.prototype.adjustDialog = function () { | |
1146 | - var modalIsOverflowing = this.$element[0].scrollHeight > document.documentElement.clientHeight | |
1147 | - | |
1148 | - this.$element.css({ | |
1149 | - paddingLeft: !this.bodyIsOverflowing && modalIsOverflowing ? this.scrollbarWidth : '', | |
1150 | - paddingRight: this.bodyIsOverflowing && !modalIsOverflowing ? this.scrollbarWidth : '' | |
1151 | - }) | |
1152 | - } | |
1153 | - | |
1154 | - Modal.prototype.resetAdjustments = function () { | |
1155 | - this.$element.css({ | |
1156 | - paddingLeft: '', | |
1157 | - paddingRight: '' | |
1158 | - }) | |
1159 | - } | |
1160 | - | |
1161 | - Modal.prototype.checkScrollbar = function () { | |
1162 | - var fullWindowWidth = window.innerWidth | |
1163 | - if (!fullWindowWidth) { // workaround for missing window.innerWidth in IE8 | |
1164 | - var documentElementRect = document.documentElement.getBoundingClientRect() | |
1165 | - fullWindowWidth = documentElementRect.right - Math.abs(documentElementRect.left) | |
1166 | - } | |
1167 | - this.bodyIsOverflowing = document.body.clientWidth < fullWindowWidth | |
1168 | - this.scrollbarWidth = this.measureScrollbar() | |
1169 | - } | |
1170 | - | |
1171 | - Modal.prototype.setScrollbar = function () { | |
1172 | - var bodyPad = parseInt((this.$body.css('padding-right') || 0), 10) | |
1173 | - this.originalBodyPad = document.body.style.paddingRight || '' | |
1174 | - if (this.bodyIsOverflowing) this.$body.css('padding-right', bodyPad + this.scrollbarWidth) | |
1175 | - } | |
1176 | - | |
1177 | - Modal.prototype.resetScrollbar = function () { | |
1178 | - this.$body.css('padding-right', this.originalBodyPad) | |
1179 | - } | |
1180 | - | |
1181 | - Modal.prototype.measureScrollbar = function () { // thx walsh | |
1182 | - var scrollDiv = document.createElement('div') | |
1183 | - scrollDiv.className = 'modal-scrollbar-measure' | |
1184 | - this.$body.append(scrollDiv) | |
1185 | - var scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth | |
1186 | - this.$body[0].removeChild(scrollDiv) | |
1187 | - return scrollbarWidth | |
1188 | - } | |
1189 | - | |
1190 | - | |
1191 | - // MODAL PLUGIN DEFINITION | |
1192 | - // ======================= | |
1193 | - | |
1194 | - function Plugin(option, _relatedTarget) { | |
1195 | - return this.each(function () { | |
1196 | - var $this = $(this) | |
1197 | - var data = $this.data('bs.modal') | |
1198 | - var options = $.extend({}, Modal.DEFAULTS, $this.data(), typeof option == 'object' && option) | |
1199 | - | |
1200 | - if (!data) $this.data('bs.modal', (data = new Modal(this, options))) | |
1201 | - if (typeof option == 'string') data[option](_relatedTarget) | |
1202 | - else if (options.show) data.show(_relatedTarget) | |
1203 | - }) | |
1204 | - } | |
1205 | - | |
1206 | - var old = $.fn.modal | |
1207 | - | |
1208 | - $.fn.modal = Plugin | |
1209 | - $.fn.modal.Constructor = Modal | |
1210 | - | |
1211 | - | |
1212 | - // MODAL NO CONFLICT | |
1213 | - // ================= | |
1214 | - | |
1215 | - $.fn.modal.noConflict = function () { | |
1216 | - $.fn.modal = old | |
1217 | - return this | |
1218 | - } | |
1219 | - | |
1220 | - | |
1221 | - // MODAL DATA-API | |
1222 | - // ============== | |
1223 | - | |
1224 | - $(document).on('click.bs.modal.data-api', '[data-toggle="modal"]', function (e) { | |
1225 | - var $this = $(this) | |
1226 | - var href = $this.attr('href') | |
1227 | - var $target = $($this.attr('data-target') || (href && href.replace(/.*(?=#[^\s]+$)/, ''))) // strip for ie7 | |
1228 | - var option = $target.data('bs.modal') ? 'toggle' : $.extend({ remote: !/#/.test(href) && href }, $target.data(), $this.data()) | |
1229 | - | |
1230 | - if ($this.is('a')) e.preventDefault() | |
1231 | - | |
1232 | - $target.one('show.bs.modal', function (showEvent) { | |
1233 | - if (showEvent.isDefaultPrevented()) return // only register focus restorer if modal will actually get shown | |
1234 | - $target.one('hidden.bs.modal', function () { | |
1235 | - $this.is(':visible') && $this.trigger('focus') | |
1236 | - }) | |
1237 | - }) | |
1238 | - Plugin.call($target, option, this) | |
1239 | - }) | |
1240 | - | |
1241 | -}(jQuery); | |
1242 | - | |
1243 | -/* ======================================================================== | |
1244 | - * Bootstrap: tooltip.js v3.3.4 | |
1245 | - * http://getbootstrap.com/javascript/#tooltip | |
1246 | - * Inspired by the original jQuery.tipsy by Jason Frame | |
1247 | - * ======================================================================== | |
1248 | - * Copyright 2011-2015 Twitter, Inc. | |
1249 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
1250 | - * ======================================================================== */ | |
1251 | - | |
1252 | - | |
1253 | -+function ($) { | |
1254 | - 'use strict'; | |
1255 | - | |
1256 | - // TOOLTIP PUBLIC CLASS DEFINITION | |
1257 | - // =============================== | |
1258 | - | |
1259 | - var Tooltip = function (element, options) { | |
1260 | - this.type = null | |
1261 | - this.options = null | |
1262 | - this.enabled = null | |
1263 | - this.timeout = null | |
1264 | - this.hoverState = null | |
1265 | - this.$element = null | |
1266 | - | |
1267 | - this.init('tooltip', element, options) | |
1268 | - } | |
1269 | - | |
1270 | - Tooltip.VERSION = '3.3.4' | |
1271 | - | |
1272 | - Tooltip.TRANSITION_DURATION = 150 | |
1273 | - | |
1274 | - Tooltip.DEFAULTS = { | |
1275 | - animation: true, | |
1276 | - placement: 'top', | |
1277 | - selector: false, | |
1278 | - template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>', | |
1279 | - trigger: 'hover focus', | |
1280 | - title: '', | |
1281 | - delay: 0, | |
1282 | - html: false, | |
1283 | - container: false, | |
1284 | - viewport: { | |
1285 | - selector: 'body', | |
1286 | - padding: 0 | |
1287 | - } | |
1288 | - } | |
1289 | - | |
1290 | - Tooltip.prototype.init = function (type, element, options) { | |
1291 | - this.enabled = true | |
1292 | - this.type = type | |
1293 | - this.$element = $(element) | |
1294 | - this.options = this.getOptions(options) | |
1295 | - this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport) | |
1296 | - | |
1297 | - if (this.$element[0] instanceof document.constructor && !this.options.selector) { | |
1298 | - throw new Error('`selector` option must be specified when initializing ' + this.type + ' on the window.document object!') | |
1299 | - } | |
1300 | - | |
1301 | - var triggers = this.options.trigger.split(' ') | |
1302 | - | |
1303 | - for (var i = triggers.length; i--;) { | |
1304 | - var trigger = triggers[i] | |
1305 | - | |
1306 | - if (trigger == 'click') { | |
1307 | - this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this)) | |
1308 | - } else if (trigger != 'manual') { | |
1309 | - var eventIn = trigger == 'hover' ? 'mouseenter' : 'focusin' | |
1310 | - var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout' | |
1311 | - | |
1312 | - this.$element.on(eventIn + '.' + this.type, this.options.selector, $.proxy(this.enter, this)) | |
1313 | - this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this)) | |
1314 | - } | |
1315 | - } | |
1316 | - | |
1317 | - this.options.selector ? | |
1318 | - (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) : | |
1319 | - this.fixTitle() | |
1320 | - } | |
1321 | - | |
1322 | - Tooltip.prototype.getDefaults = function () { | |
1323 | - return Tooltip.DEFAULTS | |
1324 | - } | |
1325 | - | |
1326 | - Tooltip.prototype.getOptions = function (options) { | |
1327 | - options = $.extend({}, this.getDefaults(), this.$element.data(), options) | |
1328 | - | |
1329 | - if (options.delay && typeof options.delay == 'number') { | |
1330 | - options.delay = { | |
1331 | - show: options.delay, | |
1332 | - hide: options.delay | |
1333 | - } | |
1334 | - } | |
1335 | - | |
1336 | - return options | |
1337 | - } | |
1338 | - | |
1339 | - Tooltip.prototype.getDelegateOptions = function () { | |
1340 | - var options = {} | |
1341 | - var defaults = this.getDefaults() | |
1342 | - | |
1343 | - this._options && $.each(this._options, function (key, value) { | |
1344 | - if (defaults[key] != value) options[key] = value | |
1345 | - }) | |
1346 | - | |
1347 | - return options | |
1348 | - } | |
1349 | - | |
1350 | - Tooltip.prototype.enter = function (obj) { | |
1351 | - var self = obj instanceof this.constructor ? | |
1352 | - obj : $(obj.currentTarget).data('bs.' + this.type) | |
1353 | - | |
1354 | - if (self && self.$tip && self.$tip.is(':visible')) { | |
1355 | - self.hoverState = 'in' | |
1356 | - return | |
1357 | - } | |
1358 | - | |
1359 | - if (!self) { | |
1360 | - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) | |
1361 | - $(obj.currentTarget).data('bs.' + this.type, self) | |
1362 | - } | |
1363 | - | |
1364 | - clearTimeout(self.timeout) | |
1365 | - | |
1366 | - self.hoverState = 'in' | |
1367 | - | |
1368 | - if (!self.options.delay || !self.options.delay.show) return self.show() | |
1369 | - | |
1370 | - self.timeout = setTimeout(function () { | |
1371 | - if (self.hoverState == 'in') self.show() | |
1372 | - }, self.options.delay.show) | |
1373 | - } | |
1374 | - | |
1375 | - Tooltip.prototype.leave = function (obj) { | |
1376 | - var self = obj instanceof this.constructor ? | |
1377 | - obj : $(obj.currentTarget).data('bs.' + this.type) | |
1378 | - | |
1379 | - if (!self) { | |
1380 | - self = new this.constructor(obj.currentTarget, this.getDelegateOptions()) | |
1381 | - $(obj.currentTarget).data('bs.' + this.type, self) | |
1382 | - } | |
1383 | - | |
1384 | - clearTimeout(self.timeout) | |
1385 | - | |
1386 | - self.hoverState = 'out' | |
1387 | - | |
1388 | - if (!self.options.delay || !self.options.delay.hide) return self.hide() | |
1389 | - | |
1390 | - self.timeout = setTimeout(function () { | |
1391 | - if (self.hoverState == 'out') self.hide() | |
1392 | - }, self.options.delay.hide) | |
1393 | - } | |
1394 | - | |
1395 | - Tooltip.prototype.show = function () { | |
1396 | - var e = $.Event('show.bs.' + this.type) | |
1397 | - | |
1398 | - if (this.hasContent() && this.enabled) { | |
1399 | - this.$element.trigger(e) | |
1400 | - | |
1401 | - var inDom = $.contains(this.$element[0].ownerDocument.documentElement, this.$element[0]) | |
1402 | - if (e.isDefaultPrevented() || !inDom) return | |
1403 | - var that = this | |
1404 | - | |
1405 | - var $tip = this.tip() | |
1406 | - | |
1407 | - var tipId = this.getUID(this.type) | |
1408 | - | |
1409 | - this.setContent() | |
1410 | - $tip.attr('id', tipId) | |
1411 | - this.$element.attr('aria-describedby', tipId) | |
1412 | - | |
1413 | - if (this.options.animation) $tip.addClass('fade') | |
1414 | - | |
1415 | - var placement = typeof this.options.placement == 'function' ? | |
1416 | - this.options.placement.call(this, $tip[0], this.$element[0]) : | |
1417 | - this.options.placement | |
1418 | - | |
1419 | - var autoToken = /\s?auto?\s?/i | |
1420 | - var autoPlace = autoToken.test(placement) | |
1421 | - if (autoPlace) placement = placement.replace(autoToken, '') || 'top' | |
1422 | - | |
1423 | - $tip | |
1424 | - .detach() | |
1425 | - .css({ top: 0, left: 0, display: 'block' }) | |
1426 | - .addClass(placement) | |
1427 | - .data('bs.' + this.type, this) | |
1428 | - | |
1429 | - this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element) | |
1430 | - | |
1431 | - var pos = this.getPosition() | |
1432 | - var actualWidth = $tip[0].offsetWidth | |
1433 | - var actualHeight = $tip[0].offsetHeight | |
1434 | - | |
1435 | - if (autoPlace) { | |
1436 | - var orgPlacement = placement | |
1437 | - var $container = this.options.container ? $(this.options.container) : this.$element.parent() | |
1438 | - var containerDim = this.getPosition($container) | |
1439 | - | |
1440 | - placement = placement == 'bottom' && pos.bottom + actualHeight > containerDim.bottom ? 'top' : | |
1441 | - placement == 'top' && pos.top - actualHeight < containerDim.top ? 'bottom' : | |
1442 | - placement == 'right' && pos.right + actualWidth > containerDim.width ? 'left' : | |
1443 | - placement == 'left' && pos.left - actualWidth < containerDim.left ? 'right' : | |
1444 | - placement | |
1445 | - | |
1446 | - $tip | |
1447 | - .removeClass(orgPlacement) | |
1448 | - .addClass(placement) | |
1449 | - } | |
1450 | - | |
1451 | - var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight) | |
1452 | - | |
1453 | - this.applyPlacement(calculatedOffset, placement) | |
1454 | - | |
1455 | - var complete = function () { | |
1456 | - var prevHoverState = that.hoverState | |
1457 | - that.$element.trigger('shown.bs.' + that.type) | |
1458 | - that.hoverState = null | |
1459 | - | |
1460 | - if (prevHoverState == 'out') that.leave(that) | |
1461 | - } | |
1462 | - | |
1463 | - $.support.transition && this.$tip.hasClass('fade') ? | |
1464 | - $tip | |
1465 | - .one('bsTransitionEnd', complete) | |
1466 | - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : | |
1467 | - complete() | |
1468 | - } | |
1469 | - } | |
1470 | - | |
1471 | - Tooltip.prototype.applyPlacement = function (offset, placement) { | |
1472 | - var $tip = this.tip() | |
1473 | - var width = $tip[0].offsetWidth | |
1474 | - var height = $tip[0].offsetHeight | |
1475 | - | |
1476 | - // manually read margins because getBoundingClientRect includes difference | |
1477 | - var marginTop = parseInt($tip.css('margin-top'), 10) | |
1478 | - var marginLeft = parseInt($tip.css('margin-left'), 10) | |
1479 | - | |
1480 | - // we must check for NaN for ie 8/9 | |
1481 | - if (isNaN(marginTop)) marginTop = 0 | |
1482 | - if (isNaN(marginLeft)) marginLeft = 0 | |
1483 | - | |
1484 | - offset.top = offset.top + marginTop | |
1485 | - offset.left = offset.left + marginLeft | |
1486 | - | |
1487 | - // $.fn.offset doesn't round pixel values | |
1488 | - // so we use setOffset directly with our own function B-0 | |
1489 | - $.offset.setOffset($tip[0], $.extend({ | |
1490 | - using: function (props) { | |
1491 | - $tip.css({ | |
1492 | - top: Math.round(props.top), | |
1493 | - left: Math.round(props.left) | |
1494 | - }) | |
1495 | - } | |
1496 | - }, offset), 0) | |
1497 | - | |
1498 | - $tip.addClass('in') | |
1499 | - | |
1500 | - // check to see if placing tip in new offset caused the tip to resize itself | |
1501 | - var actualWidth = $tip[0].offsetWidth | |
1502 | - var actualHeight = $tip[0].offsetHeight | |
1503 | - | |
1504 | - if (placement == 'top' && actualHeight != height) { | |
1505 | - offset.top = offset.top + height - actualHeight | |
1506 | - } | |
1507 | - | |
1508 | - var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight) | |
1509 | - | |
1510 | - if (delta.left) offset.left += delta.left | |
1511 | - else offset.top += delta.top | |
1512 | - | |
1513 | - var isVertical = /top|bottom/.test(placement) | |
1514 | - var arrowDelta = isVertical ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight | |
1515 | - var arrowOffsetPosition = isVertical ? 'offsetWidth' : 'offsetHeight' | |
1516 | - | |
1517 | - $tip.offset(offset) | |
1518 | - this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], isVertical) | |
1519 | - } | |
1520 | - | |
1521 | - Tooltip.prototype.replaceArrow = function (delta, dimension, isVertical) { | |
1522 | - this.arrow() | |
1523 | - .css(isVertical ? 'left' : 'top', 50 * (1 - delta / dimension) + '%') | |
1524 | - .css(isVertical ? 'top' : 'left', '') | |
1525 | - } | |
1526 | - | |
1527 | - Tooltip.prototype.setContent = function () { | |
1528 | - var $tip = this.tip() | |
1529 | - var title = this.getTitle() | |
1530 | - | |
1531 | - $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title) | |
1532 | - $tip.removeClass('fade in top bottom left right') | |
1533 | - } | |
1534 | - | |
1535 | - Tooltip.prototype.hide = function (callback) { | |
1536 | - var that = this | |
1537 | - var $tip = $(this.$tip) | |
1538 | - var e = $.Event('hide.bs.' + this.type) | |
1539 | - | |
1540 | - function complete() { | |
1541 | - if (that.hoverState != 'in') $tip.detach() | |
1542 | - that.$element | |
1543 | - .removeAttr('aria-describedby') | |
1544 | - .trigger('hidden.bs.' + that.type) | |
1545 | - callback && callback() | |
1546 | - } | |
1547 | - | |
1548 | - this.$element.trigger(e) | |
1549 | - | |
1550 | - if (e.isDefaultPrevented()) return | |
1551 | - | |
1552 | - $tip.removeClass('in') | |
1553 | - | |
1554 | - $.support.transition && $tip.hasClass('fade') ? | |
1555 | - $tip | |
1556 | - .one('bsTransitionEnd', complete) | |
1557 | - .emulateTransitionEnd(Tooltip.TRANSITION_DURATION) : | |
1558 | - complete() | |
1559 | - | |
1560 | - this.hoverState = null | |
1561 | - | |
1562 | - return this | |
1563 | - } | |
1564 | - | |
1565 | - Tooltip.prototype.fixTitle = function () { | |
1566 | - var $e = this.$element | |
1567 | - if ($e.attr('title') || typeof ($e.attr('data-original-title')) != 'string') { | |
1568 | - $e.attr('data-original-title', $e.attr('title') || '').attr('title', '') | |
1569 | - } | |
1570 | - } | |
1571 | - | |
1572 | - Tooltip.prototype.hasContent = function () { | |
1573 | - return this.getTitle() | |
1574 | - } | |
1575 | - | |
1576 | - Tooltip.prototype.getPosition = function ($element) { | |
1577 | - $element = $element || this.$element | |
1578 | - | |
1579 | - var el = $element[0] | |
1580 | - var isBody = el.tagName == 'BODY' | |
1581 | - | |
1582 | - var elRect = el.getBoundingClientRect() | |
1583 | - if (elRect.width == null) { | |
1584 | - // width and height are missing in IE8, so compute them manually; see https://github.com/twbs/bootstrap/issues/14093 | |
1585 | - elRect = $.extend({}, elRect, { width: elRect.right - elRect.left, height: elRect.bottom - elRect.top }) | |
1586 | - } | |
1587 | - var elOffset = isBody ? { top: 0, left: 0 } : $element.offset() | |
1588 | - var scroll = { scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop() } | |
1589 | - var outerDims = isBody ? { width: $(window).width(), height: $(window).height() } : null | |
1590 | - | |
1591 | - return $.extend({}, elRect, scroll, outerDims, elOffset) | |
1592 | - } | |
1593 | - | |
1594 | - Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) { | |
1595 | - return placement == 'bottom' ? { top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2 } : | |
1596 | - placement == 'top' ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2 } : | |
1597 | - placement == 'left' ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } : | |
1598 | - /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width } | |
1599 | - | |
1600 | - } | |
1601 | - | |
1602 | - Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) { | |
1603 | - var delta = { top: 0, left: 0 } | |
1604 | - if (!this.$viewport) return delta | |
1605 | - | |
1606 | - var viewportPadding = this.options.viewport && this.options.viewport.padding || 0 | |
1607 | - var viewportDimensions = this.getPosition(this.$viewport) | |
1608 | - | |
1609 | - if (/right|left/.test(placement)) { | |
1610 | - var topEdgeOffset = pos.top - viewportPadding - viewportDimensions.scroll | |
1611 | - var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight | |
1612 | - if (topEdgeOffset < viewportDimensions.top) { // top overflow | |
1613 | - delta.top = viewportDimensions.top - topEdgeOffset | |
1614 | - } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow | |
1615 | - delta.top = viewportDimensions.top + viewportDimensions.height - bottomEdgeOffset | |
1616 | - } | |
1617 | - } else { | |
1618 | - var leftEdgeOffset = pos.left - viewportPadding | |
1619 | - var rightEdgeOffset = pos.left + viewportPadding + actualWidth | |
1620 | - if (leftEdgeOffset < viewportDimensions.left) { // left overflow | |
1621 | - delta.left = viewportDimensions.left - leftEdgeOffset | |
1622 | - } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow | |
1623 | - delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset | |
1624 | - } | |
1625 | - } | |
1626 | - | |
1627 | - return delta | |
1628 | - } | |
1629 | - | |
1630 | - Tooltip.prototype.getTitle = function () { | |
1631 | - var title | |
1632 | - var $e = this.$element | |
1633 | - var o = this.options | |
1634 | - | |
1635 | - title = $e.attr('data-original-title') | |
1636 | - || (typeof o.title == 'function' ? o.title.call($e[0]) : o.title) | |
1637 | - | |
1638 | - return title | |
1639 | - } | |
1640 | - | |
1641 | - Tooltip.prototype.getUID = function (prefix) { | |
1642 | - do prefix += ~~(Math.random() * 1000000) | |
1643 | - while (document.getElementById(prefix)) | |
1644 | - return prefix | |
1645 | - } | |
1646 | - | |
1647 | - Tooltip.prototype.tip = function () { | |
1648 | - return (this.$tip = this.$tip || $(this.options.template)) | |
1649 | - } | |
1650 | - | |
1651 | - Tooltip.prototype.arrow = function () { | |
1652 | - return (this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')) | |
1653 | - } | |
1654 | - | |
1655 | - Tooltip.prototype.enable = function () { | |
1656 | - this.enabled = true | |
1657 | - } | |
1658 | - | |
1659 | - Tooltip.prototype.disable = function () { | |
1660 | - this.enabled = false | |
1661 | - } | |
1662 | - | |
1663 | - Tooltip.prototype.toggleEnabled = function () { | |
1664 | - this.enabled = !this.enabled | |
1665 | - } | |
1666 | - | |
1667 | - Tooltip.prototype.toggle = function (e) { | |
1668 | - var self = this | |
1669 | - if (e) { | |
1670 | - self = $(e.currentTarget).data('bs.' + this.type) | |
1671 | - if (!self) { | |
1672 | - self = new this.constructor(e.currentTarget, this.getDelegateOptions()) | |
1673 | - $(e.currentTarget).data('bs.' + this.type, self) | |
1674 | - } | |
1675 | - } | |
1676 | - | |
1677 | - self.tip().hasClass('in') ? self.leave(self) : self.enter(self) | |
1678 | - } | |
1679 | - | |
1680 | - Tooltip.prototype.destroy = function () { | |
1681 | - var that = this | |
1682 | - clearTimeout(this.timeout) | |
1683 | - this.hide(function () { | |
1684 | - that.$element.off('.' + that.type).removeData('bs.' + that.type) | |
1685 | - }) | |
1686 | - } | |
1687 | - | |
1688 | - | |
1689 | - // TOOLTIP PLUGIN DEFINITION | |
1690 | - // ========================= | |
1691 | - | |
1692 | - function Plugin(option) { | |
1693 | - return this.each(function () { | |
1694 | - var $this = $(this) | |
1695 | - var data = $this.data('bs.tooltip') | |
1696 | - var options = typeof option == 'object' && option | |
1697 | - | |
1698 | - if (!data && /destroy|hide/.test(option)) return | |
1699 | - if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options))) | |
1700 | - if (typeof option == 'string') data[option]() | |
1701 | - }) | |
1702 | - } | |
1703 | - | |
1704 | - var old = $.fn.tooltip | |
1705 | - | |
1706 | - $.fn.tooltip = Plugin | |
1707 | - $.fn.tooltip.Constructor = Tooltip | |
1708 | - | |
1709 | - | |
1710 | - // TOOLTIP NO CONFLICT | |
1711 | - // =================== | |
1712 | - | |
1713 | - $.fn.tooltip.noConflict = function () { | |
1714 | - $.fn.tooltip = old | |
1715 | - return this | |
1716 | - } | |
1717 | - | |
1718 | -}(jQuery); | |
1719 | - | |
1720 | -/* ======================================================================== | |
1721 | - * Bootstrap: popover.js v3.3.4 | |
1722 | - * http://getbootstrap.com/javascript/#popovers | |
1723 | - * ======================================================================== | |
1724 | - * Copyright 2011-2015 Twitter, Inc. | |
1725 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
1726 | - * ======================================================================== */ | |
1727 | - | |
1728 | - | |
1729 | -+function ($) { | |
1730 | - 'use strict'; | |
1731 | - | |
1732 | - // POPOVER PUBLIC CLASS DEFINITION | |
1733 | - // =============================== | |
1734 | - | |
1735 | - var Popover = function (element, options) { | |
1736 | - this.init('popover', element, options) | |
1737 | - } | |
1738 | - | |
1739 | - if (!$.fn.tooltip) throw new Error('Popover requires tooltip.js') | |
1740 | - | |
1741 | - Popover.VERSION = '3.3.4' | |
1742 | - | |
1743 | - Popover.DEFAULTS = $.extend({}, $.fn.tooltip.Constructor.DEFAULTS, { | |
1744 | - placement: 'right', | |
1745 | - trigger: 'click', | |
1746 | - content: '', | |
1747 | - template: '<div class="popover" role="tooltip"><div class="arrow"></div><h3 class="popover-title"></h3><div class="popover-content"></div></div>' | |
1748 | - }) | |
1749 | - | |
1750 | - | |
1751 | - // NOTE: POPOVER EXTENDS tooltip.js | |
1752 | - // ================================ | |
1753 | - | |
1754 | - Popover.prototype = $.extend({}, $.fn.tooltip.Constructor.prototype) | |
1755 | - | |
1756 | - Popover.prototype.constructor = Popover | |
1757 | - | |
1758 | - Popover.prototype.getDefaults = function () { | |
1759 | - return Popover.DEFAULTS | |
1760 | - } | |
1761 | - | |
1762 | - Popover.prototype.setContent = function () { | |
1763 | - var $tip = this.tip() | |
1764 | - var title = this.getTitle() | |
1765 | - var content = this.getContent() | |
1766 | - | |
1767 | - $tip.find('.popover-title')[this.options.html ? 'html' : 'text'](title) | |
1768 | - $tip.find('.popover-content').children().detach().end()[ // we use append for html objects to maintain js events | |
1769 | - this.options.html ? (typeof content == 'string' ? 'html' : 'append') : 'text' | |
1770 | - ](content) | |
1771 | - | |
1772 | - $tip.removeClass('fade top bottom left right in') | |
1773 | - | |
1774 | - // IE8 doesn't accept hiding via the `:empty` pseudo selector, we have to do | |
1775 | - // this manually by checking the contents. | |
1776 | - if (!$tip.find('.popover-title').html()) $tip.find('.popover-title').hide() | |
1777 | - } | |
1778 | - | |
1779 | - Popover.prototype.hasContent = function () { | |
1780 | - return this.getTitle() || this.getContent() | |
1781 | - } | |
1782 | - | |
1783 | - Popover.prototype.getContent = function () { | |
1784 | - var $e = this.$element | |
1785 | - var o = this.options | |
1786 | - | |
1787 | - return $e.attr('data-content') | |
1788 | - || (typeof o.content == 'function' ? | |
1789 | - o.content.call($e[0]) : | |
1790 | - o.content) | |
1791 | - } | |
1792 | - | |
1793 | - Popover.prototype.arrow = function () { | |
1794 | - return (this.$arrow = this.$arrow || this.tip().find('.arrow')) | |
1795 | - } | |
1796 | - | |
1797 | - | |
1798 | - // POPOVER PLUGIN DEFINITION | |
1799 | - // ========================= | |
1800 | - | |
1801 | - function Plugin(option) { | |
1802 | - return this.each(function () { | |
1803 | - var $this = $(this) | |
1804 | - var data = $this.data('bs.popover') | |
1805 | - var options = typeof option == 'object' && option | |
1806 | - | |
1807 | - if (!data && /destroy|hide/.test(option)) return | |
1808 | - if (!data) $this.data('bs.popover', (data = new Popover(this, options))) | |
1809 | - if (typeof option == 'string') data[option]() | |
1810 | - }) | |
1811 | - } | |
1812 | - | |
1813 | - var old = $.fn.popover | |
1814 | - | |
1815 | - $.fn.popover = Plugin | |
1816 | - $.fn.popover.Constructor = Popover | |
1817 | - | |
1818 | - | |
1819 | - // POPOVER NO CONFLICT | |
1820 | - // =================== | |
1821 | - | |
1822 | - $.fn.popover.noConflict = function () { | |
1823 | - $.fn.popover = old | |
1824 | - return this | |
1825 | - } | |
1826 | - | |
1827 | -}(jQuery); | |
1828 | - | |
1829 | -/* ======================================================================== | |
1830 | - * Bootstrap: scrollspy.js v3.3.4 | |
1831 | - * http://getbootstrap.com/javascript/#scrollspy | |
1832 | - * ======================================================================== | |
1833 | - * Copyright 2011-2015 Twitter, Inc. | |
1834 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
1835 | - * ======================================================================== */ | |
1836 | - | |
1837 | - | |
1838 | -+function ($) { | |
1839 | - 'use strict'; | |
1840 | - | |
1841 | - // SCROLLSPY CLASS DEFINITION | |
1842 | - // ========================== | |
1843 | - | |
1844 | - function ScrollSpy(element, options) { | |
1845 | - this.$body = $(document.body) | |
1846 | - this.$scrollElement = $(element).is(document.body) ? $(window) : $(element) | |
1847 | - this.options = $.extend({}, ScrollSpy.DEFAULTS, options) | |
1848 | - this.selector = (this.options.target || '') + ' .nav li > a' | |
1849 | - this.offsets = [] | |
1850 | - this.targets = [] | |
1851 | - this.activeTarget = null | |
1852 | - this.scrollHeight = 0 | |
1853 | - | |
1854 | - this.$scrollElement.on('scroll.bs.scrollspy', $.proxy(this.process, this)) | |
1855 | - this.refresh() | |
1856 | - this.process() | |
1857 | - } | |
1858 | - | |
1859 | - ScrollSpy.VERSION = '3.3.4' | |
1860 | - | |
1861 | - ScrollSpy.DEFAULTS = { | |
1862 | - offset: 10 | |
1863 | - } | |
1864 | - | |
1865 | - ScrollSpy.prototype.getScrollHeight = function () { | |
1866 | - return this.$scrollElement[0].scrollHeight || Math.max(this.$body[0].scrollHeight, document.documentElement.scrollHeight) | |
1867 | - } | |
1868 | - | |
1869 | - ScrollSpy.prototype.refresh = function () { | |
1870 | - var that = this | |
1871 | - var offsetMethod = 'offset' | |
1872 | - var offsetBase = 0 | |
1873 | - | |
1874 | - this.offsets = [] | |
1875 | - this.targets = [] | |
1876 | - this.scrollHeight = this.getScrollHeight() | |
1877 | - | |
1878 | - if (!$.isWindow(this.$scrollElement[0])) { | |
1879 | - offsetMethod = 'position' | |
1880 | - offsetBase = this.$scrollElement.scrollTop() | |
1881 | - } | |
1882 | - | |
1883 | - this.$body | |
1884 | - .find(this.selector) | |
1885 | - .map(function () { | |
1886 | - var $el = $(this) | |
1887 | - var href = $el.data('target') || $el.attr('href') | |
1888 | - var $href = /^#./.test(href) && $(href) | |
1889 | - | |
1890 | - return ($href | |
1891 | - && $href.length | |
1892 | - && $href.is(':visible') | |
1893 | - && [[$href[offsetMethod]().top + offsetBase, href]]) || null | |
1894 | - }) | |
1895 | - .sort(function (a, b) { return a[0] - b[0] }) | |
1896 | - .each(function () { | |
1897 | - that.offsets.push(this[0]) | |
1898 | - that.targets.push(this[1]) | |
1899 | - }) | |
1900 | - } | |
1901 | - | |
1902 | - ScrollSpy.prototype.process = function () { | |
1903 | - var scrollTop = this.$scrollElement.scrollTop() + this.options.offset | |
1904 | - var scrollHeight = this.getScrollHeight() | |
1905 | - var maxScroll = this.options.offset + scrollHeight - this.$scrollElement.height() | |
1906 | - var offsets = this.offsets | |
1907 | - var targets = this.targets | |
1908 | - var activeTarget = this.activeTarget | |
1909 | - var i | |
1910 | - | |
1911 | - if (this.scrollHeight != scrollHeight) { | |
1912 | - this.refresh() | |
1913 | - } | |
1914 | - | |
1915 | - if (scrollTop >= maxScroll) { | |
1916 | - return activeTarget != (i = targets[targets.length - 1]) && this.activate(i) | |
1917 | - } | |
1918 | - | |
1919 | - if (activeTarget && scrollTop < offsets[0]) { | |
1920 | - this.activeTarget = null | |
1921 | - return this.clear() | |
1922 | - } | |
1923 | - | |
1924 | - for (i = offsets.length; i--;) { | |
1925 | - activeTarget != targets[i] | |
1926 | - && scrollTop >= offsets[i] | |
1927 | - && (offsets[i + 1] === undefined || scrollTop < offsets[i + 1]) | |
1928 | - && this.activate(targets[i]) | |
1929 | - } | |
1930 | - } | |
1931 | - | |
1932 | - ScrollSpy.prototype.activate = function (target) { | |
1933 | - this.activeTarget = target | |
1934 | - | |
1935 | - this.clear() | |
1936 | - | |
1937 | - var selector = this.selector + | |
1938 | - '[data-target="' + target + '"],' + | |
1939 | - this.selector + '[href="' + target + '"]' | |
1940 | - | |
1941 | - var active = $(selector) | |
1942 | - .parents('li') | |
1943 | - .addClass('active') | |
1944 | - | |
1945 | - if (active.parent('.dropdown-menu').length) { | |
1946 | - active = active | |
1947 | - .closest('li.dropdown') | |
1948 | - .addClass('active') | |
1949 | - } | |
1950 | - | |
1951 | - active.trigger('activate.bs.scrollspy') | |
1952 | - } | |
1953 | - | |
1954 | - ScrollSpy.prototype.clear = function () { | |
1955 | - $(this.selector) | |
1956 | - .parentsUntil(this.options.target, '.active') | |
1957 | - .removeClass('active') | |
1958 | - } | |
1959 | - | |
1960 | - | |
1961 | - // SCROLLSPY PLUGIN DEFINITION | |
1962 | - // =========================== | |
1963 | - | |
1964 | - function Plugin(option) { | |
1965 | - return this.each(function () { | |
1966 | - var $this = $(this) | |
1967 | - var data = $this.data('bs.scrollspy') | |
1968 | - var options = typeof option == 'object' && option | |
1969 | - | |
1970 | - if (!data) $this.data('bs.scrollspy', (data = new ScrollSpy(this, options))) | |
1971 | - if (typeof option == 'string') data[option]() | |
1972 | - }) | |
1973 | - } | |
1974 | - | |
1975 | - var old = $.fn.scrollspy | |
1976 | - | |
1977 | - $.fn.scrollspy = Plugin | |
1978 | - $.fn.scrollspy.Constructor = ScrollSpy | |
1979 | - | |
1980 | - | |
1981 | - // SCROLLSPY NO CONFLICT | |
1982 | - // ===================== | |
1983 | - | |
1984 | - $.fn.scrollspy.noConflict = function () { | |
1985 | - $.fn.scrollspy = old | |
1986 | - return this | |
1987 | - } | |
1988 | - | |
1989 | - | |
1990 | - // SCROLLSPY DATA-API | |
1991 | - // ================== | |
1992 | - | |
1993 | - $(window).on('load.bs.scrollspy.data-api', function () { | |
1994 | - $('[data-spy="scroll"]').each(function () { | |
1995 | - var $spy = $(this) | |
1996 | - Plugin.call($spy, $spy.data()) | |
1997 | - }) | |
1998 | - }) | |
1999 | - | |
2000 | -}(jQuery); | |
2001 | - | |
2002 | -/* ======================================================================== | |
2003 | - * Bootstrap: tab.js v3.3.4 | |
2004 | - * http://getbootstrap.com/javascript/#tabs | |
2005 | - * ======================================================================== | |
2006 | - * Copyright 2011-2015 Twitter, Inc. | |
2007 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
2008 | - * ======================================================================== */ | |
2009 | - | |
2010 | - | |
2011 | -+function ($) { | |
2012 | - 'use strict'; | |
2013 | - | |
2014 | - // TAB CLASS DEFINITION | |
2015 | - // ==================== | |
2016 | - | |
2017 | - var Tab = function (element) { | |
2018 | - this.element = $(element) | |
2019 | - } | |
2020 | - | |
2021 | - Tab.VERSION = '3.3.4' | |
2022 | - | |
2023 | - Tab.TRANSITION_DURATION = 150 | |
2024 | - | |
2025 | - Tab.prototype.show = function () { | |
2026 | - var $this = this.element | |
2027 | - var $ul = $this.closest('ul:not(.dropdown-menu)') | |
2028 | - var selector = $this.data('target') | |
2029 | - | |
2030 | - if (!selector) { | |
2031 | - selector = $this.attr('href') | |
2032 | - selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') // strip for ie7 | |
2033 | - } | |
2034 | - | |
2035 | - if ($this.parent('li').hasClass('active')) return | |
2036 | - | |
2037 | - var $previous = $ul.find('.active:last a') | |
2038 | - var hideEvent = $.Event('hide.bs.tab', { | |
2039 | - relatedTarget: $this[0] | |
2040 | - }) | |
2041 | - var showEvent = $.Event('show.bs.tab', { | |
2042 | - relatedTarget: $previous[0] | |
2043 | - }) | |
2044 | - | |
2045 | - $previous.trigger(hideEvent) | |
2046 | - $this.trigger(showEvent) | |
2047 | - | |
2048 | - if (showEvent.isDefaultPrevented() || hideEvent.isDefaultPrevented()) return | |
2049 | - | |
2050 | - var $target = $(selector) | |
2051 | - | |
2052 | - this.activate($this.closest('li'), $ul) | |
2053 | - this.activate($target, $target.parent(), function () { | |
2054 | - $previous.trigger({ | |
2055 | - type: 'hidden.bs.tab', | |
2056 | - relatedTarget: $this[0] | |
2057 | - }) | |
2058 | - $this.trigger({ | |
2059 | - type: 'shown.bs.tab', | |
2060 | - relatedTarget: $previous[0] | |
2061 | - }) | |
2062 | - }) | |
2063 | - } | |
2064 | - | |
2065 | - Tab.prototype.activate = function (element, container, callback) { | |
2066 | - var $active = container.find('> .active') | |
2067 | - var transition = callback | |
2068 | - && $.support.transition | |
2069 | - && (($active.length && $active.hasClass('fade')) || !!container.find('> .fade').length) | |
2070 | - | |
2071 | - function next() { | |
2072 | - $active | |
2073 | - .removeClass('active') | |
2074 | - .find('> .dropdown-menu > .active') | |
2075 | - .removeClass('active') | |
2076 | - .end() | |
2077 | - .find('[data-toggle="tab"]') | |
2078 | - .attr('aria-expanded', false) | |
2079 | - | |
2080 | - element | |
2081 | - .addClass('active') | |
2082 | - .find('[data-toggle="tab"]') | |
2083 | - .attr('aria-expanded', true) | |
2084 | - | |
2085 | - if (transition) { | |
2086 | - element[0].offsetWidth // reflow for transition | |
2087 | - element.addClass('in') | |
2088 | - } else { | |
2089 | - element.removeClass('fade') | |
2090 | - } | |
2091 | - | |
2092 | - if (element.parent('.dropdown-menu').length) { | |
2093 | - element | |
2094 | - .closest('li.dropdown') | |
2095 | - .addClass('active') | |
2096 | - .end() | |
2097 | - .find('[data-toggle="tab"]') | |
2098 | - .attr('aria-expanded', true) | |
2099 | - } | |
2100 | - | |
2101 | - callback && callback() | |
2102 | - } | |
2103 | - | |
2104 | - $active.length && transition ? | |
2105 | - $active | |
2106 | - .one('bsTransitionEnd', next) | |
2107 | - .emulateTransitionEnd(Tab.TRANSITION_DURATION) : | |
2108 | - next() | |
2109 | - | |
2110 | - $active.removeClass('in') | |
2111 | - } | |
2112 | - | |
2113 | - | |
2114 | - // TAB PLUGIN DEFINITION | |
2115 | - // ===================== | |
2116 | - | |
2117 | - function Plugin(option) { | |
2118 | - return this.each(function () { | |
2119 | - var $this = $(this) | |
2120 | - var data = $this.data('bs.tab') | |
2121 | - | |
2122 | - if (!data) $this.data('bs.tab', (data = new Tab(this))) | |
2123 | - if (typeof option == 'string') data[option]() | |
2124 | - }) | |
2125 | - } | |
2126 | - | |
2127 | - var old = $.fn.tab | |
2128 | - | |
2129 | - $.fn.tab = Plugin | |
2130 | - $.fn.tab.Constructor = Tab | |
2131 | - | |
2132 | - | |
2133 | - // TAB NO CONFLICT | |
2134 | - // =============== | |
2135 | - | |
2136 | - $.fn.tab.noConflict = function () { | |
2137 | - $.fn.tab = old | |
2138 | - return this | |
2139 | - } | |
2140 | - | |
2141 | - | |
2142 | - // TAB DATA-API | |
2143 | - // ============ | |
2144 | - | |
2145 | - var clickHandler = function (e) { | |
2146 | - e.preventDefault() | |
2147 | - Plugin.call($(this), 'show') | |
2148 | - } | |
2149 | - | |
2150 | - $(document) | |
2151 | - .on('click.bs.tab.data-api', '[data-toggle="tab"]', clickHandler) | |
2152 | - .on('click.bs.tab.data-api', '[data-toggle="pill"]', clickHandler) | |
2153 | - | |
2154 | -}(jQuery); | |
2155 | - | |
2156 | -/* ======================================================================== | |
2157 | - * Bootstrap: affix.js v3.3.4 | |
2158 | - * http://getbootstrap.com/javascript/#affix | |
2159 | - * ======================================================================== | |
2160 | - * Copyright 2011-2015 Twitter, Inc. | |
2161 | - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) | |
2162 | - * ======================================================================== */ | |
2163 | - | |
2164 | - | |
2165 | -+function ($) { | |
2166 | - 'use strict'; | |
2167 | - | |
2168 | - // AFFIX CLASS DEFINITION | |
2169 | - // ====================== | |
2170 | - | |
2171 | - var Affix = function (element, options) { | |
2172 | - this.options = $.extend({}, Affix.DEFAULTS, options) | |
2173 | - | |
2174 | - this.$target = $(this.options.target) | |
2175 | - .on('scroll.bs.affix.data-api', $.proxy(this.checkPosition, this)) | |
2176 | - .on('click.bs.affix.data-api', $.proxy(this.checkPositionWithEventLoop, this)) | |
2177 | - | |
2178 | - this.$element = $(element) | |
2179 | - this.affixed = null | |
2180 | - this.unpin = null | |
2181 | - this.pinnedOffset = null | |
2182 | - | |
2183 | - this.checkPosition() | |
2184 | - } | |
2185 | - | |
2186 | - Affix.VERSION = '3.3.4' | |
2187 | - | |
2188 | - Affix.RESET = 'affix affix-top affix-bottom' | |
2189 | - | |
2190 | - Affix.DEFAULTS = { | |
2191 | - offset: 0, | |
2192 | - target: window | |
2193 | - } | |
2194 | - | |
2195 | - Affix.prototype.getState = function (scrollHeight, height, offsetTop, offsetBottom) { | |
2196 | - var scrollTop = this.$target.scrollTop() | |
2197 | - var position = this.$element.offset() | |
2198 | - var targetHeight = this.$target.height() | |
2199 | - | |
2200 | - if (offsetTop != null && this.affixed == 'top') return scrollTop < offsetTop ? 'top' : false | |
2201 | - | |
2202 | - if (this.affixed == 'bottom') { | |
2203 | - if (offsetTop != null) return (scrollTop + this.unpin <= position.top) ? false : 'bottom' | |
2204 | - return (scrollTop + targetHeight <= scrollHeight - offsetBottom) ? false : 'bottom' | |
2205 | - } | |
2206 | - | |
2207 | - var initializing = this.affixed == null | |
2208 | - var colliderTop = initializing ? scrollTop : position.top | |
2209 | - var colliderHeight = initializing ? targetHeight : height | |
2210 | - | |
2211 | - if (offsetTop != null && scrollTop <= offsetTop) return 'top' | |
2212 | - if (offsetBottom != null && (colliderTop + colliderHeight >= scrollHeight - offsetBottom)) return 'bottom' | |
2213 | - | |
2214 | - return false | |
2215 | - } | |
2216 | - | |
2217 | - Affix.prototype.getPinnedOffset = function () { | |
2218 | - if (this.pinnedOffset) return this.pinnedOffset | |
2219 | - this.$element.removeClass(Affix.RESET).addClass('affix') | |
2220 | - var scrollTop = this.$target.scrollTop() | |
2221 | - var position = this.$element.offset() | |
2222 | - return (this.pinnedOffset = position.top - scrollTop) | |
2223 | - } | |
2224 | - | |
2225 | - Affix.prototype.checkPositionWithEventLoop = function () { | |
2226 | - setTimeout($.proxy(this.checkPosition, this), 1) | |
2227 | - } | |
2228 | - | |
2229 | - Affix.prototype.checkPosition = function () { | |
2230 | - if (!this.$element.is(':visible')) return | |
2231 | - | |
2232 | - var height = this.$element.height() | |
2233 | - var offset = this.options.offset | |
2234 | - var offsetTop = offset.top | |
2235 | - var offsetBottom = offset.bottom | |
2236 | - var scrollHeight = $(document.body).height() | |
2237 | - | |
2238 | - if (typeof offset != 'object') offsetBottom = offsetTop = offset | |
2239 | - if (typeof offsetTop == 'function') offsetTop = offset.top(this.$element) | |
2240 | - if (typeof offsetBottom == 'function') offsetBottom = offset.bottom(this.$element) | |
2241 | - | |
2242 | - var affix = this.getState(scrollHeight, height, offsetTop, offsetBottom) | |
2243 | - | |
2244 | - if (this.affixed != affix) { | |
2245 | - if (this.unpin != null) this.$element.css('top', '') | |
2246 | - | |
2247 | - var affixType = 'affix' + (affix ? '-' + affix : '') | |
2248 | - var e = $.Event(affixType + '.bs.affix') | |
2249 | - | |
2250 | - this.$element.trigger(e) | |
2251 | - | |
2252 | - if (e.isDefaultPrevented()) return | |
2253 | - | |
2254 | - this.affixed = affix | |
2255 | - this.unpin = affix == 'bottom' ? this.getPinnedOffset() : null | |
2256 | - | |
2257 | - this.$element | |
2258 | - .removeClass(Affix.RESET) | |
2259 | - .addClass(affixType) | |
2260 | - .trigger(affixType.replace('affix', 'affixed') + '.bs.affix') | |
2261 | - } | |
2262 | - | |
2263 | - if (affix == 'bottom') { | |
2264 | - this.$element.offset({ | |
2265 | - top: scrollHeight - height - offsetBottom | |
2266 | - }) | |
2267 | - } | |
2268 | - } | |
2269 | - | |
2270 | - | |
2271 | - // AFFIX PLUGIN DEFINITION | |
2272 | - // ======================= | |
2273 | - | |
2274 | - function Plugin(option) { | |
2275 | - return this.each(function () { | |
2276 | - var $this = $(this) | |
2277 | - var data = $this.data('bs.affix') | |
2278 | - var options = typeof option == 'object' && option | |
2279 | - | |
2280 | - if (!data) $this.data('bs.affix', (data = new Affix(this, options))) | |
2281 | - if (typeof option == 'string') data[option]() | |
2282 | - }) | |
2283 | - } | |
2284 | - | |
2285 | - var old = $.fn.affix | |
2286 | - | |
2287 | - $.fn.affix = Plugin | |
2288 | - $.fn.affix.Constructor = Affix | |
2289 | - | |
2290 | - | |
2291 | - // AFFIX NO CONFLICT | |
2292 | - // ================= | |
2293 | - | |
2294 | - $.fn.affix.noConflict = function () { | |
2295 | - $.fn.affix = old | |
2296 | - return this | |
2297 | - } | |
2298 | - | |
2299 | - | |
2300 | - // AFFIX DATA-API | |
2301 | - // ============== | |
2302 | - | |
2303 | - $(window).on('load', function () { | |
2304 | - $('[data-spy="affix"]').each(function () { | |
2305 | - var $spy = $(this) | |
2306 | - var data = $spy.data() | |
2307 | - | |
2308 | - data.offset = data.offset || {} | |
2309 | - | |
2310 | - if (data.offsetBottom != null) data.offset.bottom = data.offsetBottom | |
2311 | - if (data.offsetTop != null) data.offset.top = data.offsetTop | |
2312 | - | |
2313 | - Plugin.call($spy, data) | |
2314 | - }) | |
2315 | - }) | |
2316 | - | |
2317 | -}(jQuery); |
static/js/angular-contenteditable.js
View file @
f60d569
1 | +/** | |
2 | + * @see http://docs.angularjs.org/guide/concepts | |
3 | + * @see http://docs.angularjs.org/api/ng.directive:ngModel.NgModelController | |
4 | + * @see https://github.com/angular/angular.js/issues/528#issuecomment-7573166 | |
5 | + */ | |
6 | + | |
7 | +angular.module('contenteditable', []) | |
8 | + .directive('contenteditable', ['$timeout', function($timeout) { return { | |
9 | + restrict: 'A', | |
10 | + require: '?ngModel', | |
11 | + link: function(scope, element, attrs, ngModel) { | |
12 | + // don't do anything unless this is actually bound to a model | |
13 | + if (!ngModel) { | |
14 | + return | |
15 | + } | |
16 | + | |
17 | + // options | |
18 | + var opts = {} | |
19 | + angular.forEach([ | |
20 | + 'stripBr', | |
21 | + 'noLineBreaks', | |
22 | + 'selectNonEditable', | |
23 | + 'moveCaretToEndOnChange', | |
24 | + ], function(opt) { | |
25 | + var o = attrs[opt] | |
26 | + opts[opt] = o && o !== 'false' | |
27 | + }) | |
28 | + | |
29 | + // view -> model | |
30 | + element.bind('input', function(e) { | |
31 | + scope.$apply(function() { | |
32 | + var html, html2, rerender | |
33 | + html = element.html() | |
34 | + rerender = false | |
35 | + if (opts.stripBr) { | |
36 | + html = html.replace(/<br>$/, '') | |
37 | + } | |
38 | + if (opts.noLineBreaks) { | |
39 | + html2 = html.replace(/<div>/g, '').replace(/<br>/g, '').replace(/<\/div>/g, '') | |
40 | + if (html2 !== html) { | |
41 | + rerender = true | |
42 | + html = html2 | |
43 | + } | |
44 | + } | |
45 | + ngModel.$setViewValue(html) | |
46 | + if (rerender) { | |
47 | + ngModel.$render() | |
48 | + } | |
49 | + if (html === '') { | |
50 | + // the cursor disappears if the contents is empty | |
51 | + // so we need to refocus | |
52 | + $timeout(function(){ | |
53 | + element[0].blur() | |
54 | + element[0].focus() | |
55 | + }) | |
56 | + } | |
57 | + }) | |
58 | + }) | |
59 | + | |
60 | + // model -> view | |
61 | + var oldRender = ngModel.$render | |
62 | + ngModel.$render = function() { | |
63 | + var el, el2, range, sel | |
64 | + if (!!oldRender) { | |
65 | + oldRender() | |
66 | + } | |
67 | + element.html(ngModel.$viewValue || '') | |
68 | + if (opts.moveCaretToEndOnChange) { | |
69 | + el = element[0] | |
70 | + range = document.createRange() | |
71 | + sel = window.getSelection() | |
72 | + if (el.childNodes.length > 0) { | |
73 | + el2 = el.childNodes[el.childNodes.length - 1] | |
74 | + range.setStartAfter(el2) | |
75 | + } else { | |
76 | + range.setStartAfter(el) | |
77 | + } | |
78 | + range.collapse(true) | |
79 | + sel.removeAllRanges() | |
80 | + sel.addRange(range) | |
81 | + } | |
82 | + } | |
83 | + if (opts.selectNonEditable) { | |
84 | + element.bind('click', function(e) { | |
85 | + var range, sel, target | |
86 | + target = e.toElement | |
87 | + if (target !== this && angular.element(target).attr('contenteditable') === 'false') { | |
88 | + range = document.createRange() | |
89 | + sel = window.getSelection() | |
90 | + range.setStartBefore(target) | |
91 | + range.setEndAfter(target) | |
92 | + sel.removeAllRanges() | |
93 | + sel.addRange(range) | |
94 | + } | |
95 | + }) | |
96 | + } | |
97 | + } | |
98 | + }}]); |
static/js/angular-websocket.js
View file @
f60d569
1 | +(function() { | |
2 | + 'use strict'; | |
3 | + | |
4 | + var noop = angular.noop; | |
5 | + var objectFreeze = (Object.freeze) ? Object.freeze : noop; | |
6 | + var objectDefineProperty = Object.defineProperty; | |
7 | + var isString = angular.isString; | |
8 | + var isFunction = angular.isFunction; | |
9 | + var isDefined = angular.isDefined; | |
10 | + var isObject = angular.isObject; | |
11 | + var isArray = angular.isArray; | |
12 | + var forEach = angular.forEach; | |
13 | + var arraySlice = Array.prototype.slice; | |
14 | + // ie8 wat | |
15 | + if (!Array.prototype.indexOf) { | |
16 | + Array.prototype.indexOf = function(elt /*, from*/) { | |
17 | + var len = this.length >>> 0; | |
18 | + var from = Number(arguments[1]) || 0; | |
19 | + from = (from < 0) ? Math.ceil(from) : Math.floor(from); | |
20 | + if (from < 0) { | |
21 | + from += len; | |
22 | + } | |
23 | + | |
24 | + for (; from < len; from++) { | |
25 | + if (from in this && this[from] === elt) { return from; } | |
26 | + } | |
27 | + return -1; | |
28 | + }; | |
29 | + } | |
30 | + | |
31 | + // $WebSocketProvider.$inject = ['$rootScope', '$q', '$timeout', '$websocketBackend']; | |
32 | + function $WebSocketProvider($rootScope, $q, $timeout, $websocketBackend) { | |
33 | + | |
34 | + function $WebSocket(url, protocols, options) { | |
35 | + if (!options && isObject(protocols) && !isArray(protocols)) { | |
36 | + options = protocols; | |
37 | + protocols = undefined; | |
38 | + } | |
39 | + | |
40 | + this.protocols = protocols; | |
41 | + this.url = url || 'Missing URL'; | |
42 | + this.ssl = /(wss)/i.test(this.url); | |
43 | + | |
44 | + // this.binaryType = ''; | |
45 | + // this.extensions = ''; | |
46 | + // this.bufferedAmount = 0; | |
47 | + // this.trasnmitting = false; | |
48 | + // this.buffer = []; | |
49 | + | |
50 | + // TODO: refactor options to use isDefined | |
51 | + this.scope = options && options.scope || $rootScope; | |
52 | + this.rootScopeFailover = options && options.rootScopeFailover && true; | |
53 | + this.useApplyAsync = options && options.useApplyAsync || false; | |
54 | + this.initialTimeout = options && options.initialTimeout || 500; // 500ms | |
55 | + this.maxTimeout = options && options.maxTimeout || 5 * 60 * 1000; // 5 minutes | |
56 | + this.reconnectIfNotNormalClose = options && options.reconnectIfNotNormalClose || false; | |
57 | + | |
58 | + this._reconnectAttempts = 0; | |
59 | + this.sendQueue = []; | |
60 | + this.onOpenCallbacks = []; | |
61 | + this.onMessageCallbacks = []; | |
62 | + this.onErrorCallbacks = []; | |
63 | + this.onCloseCallbacks = []; | |
64 | + | |
65 | + objectFreeze(this._readyStateConstants); | |
66 | + | |
67 | + if (url) { | |
68 | + this._connect(); | |
69 | + } else { | |
70 | + this._setInternalState(0); | |
71 | + } | |
72 | + | |
73 | + } | |
74 | + | |
75 | + | |
76 | + $WebSocket.prototype._readyStateConstants = { | |
77 | + 'CONNECTING': 0, | |
78 | + 'OPEN': 1, | |
79 | + 'CLOSING': 2, | |
80 | + 'CLOSED': 3, | |
81 | + 'RECONNECT_ABORTED': 4 | |
82 | + }; | |
83 | + | |
84 | + $WebSocket.prototype._normalCloseCode = 1000; | |
85 | + | |
86 | + $WebSocket.prototype._reconnectableStatusCodes = [ | |
87 | + 4000 | |
88 | + ]; | |
89 | + | |
90 | + $WebSocket.prototype.safeDigest = function safeDigest(autoApply) { | |
91 | + if (autoApply && !this.scope.$$phase) { | |
92 | + this.scope.$digest(); | |
93 | + } | |
94 | + }; | |
95 | + | |
96 | + $WebSocket.prototype.bindToScope = function bindToScope(scope) { | |
97 | + var self = this; | |
98 | + if (scope) { | |
99 | + this.scope = scope; | |
100 | + if (this.rootScopeFailover) { | |
101 | + this.scope.$on('$destroy', function() { | |
102 | + self.scope = $rootScope; | |
103 | + }); | |
104 | + } | |
105 | + } | |
106 | + return self; | |
107 | + }; | |
108 | + | |
109 | + $WebSocket.prototype._connect = function _connect(force) { | |
110 | + if (force || !this.socket || this.socket.readyState !== this._readyStateConstants.OPEN) { | |
111 | + this.socket = $websocketBackend.create(this.url, this.protocols); | |
112 | + this.socket.onmessage = angular.bind(this, this._onMessageHandler); | |
113 | + this.socket.onopen = angular.bind(this, this._onOpenHandler); | |
114 | + this.socket.onerror = angular.bind(this, this._onErrorHandler); | |
115 | + this.socket.onclose = angular.bind(this, this._onCloseHandler); | |
116 | + } | |
117 | + }; | |
118 | + | |
119 | + $WebSocket.prototype.fireQueue = function fireQueue() { | |
120 | + while (this.sendQueue.length && this.socket.readyState === this._readyStateConstants.OPEN) { | |
121 | + var data = this.sendQueue.shift(); | |
122 | + | |
123 | + this.socket.send( | |
124 | + isString(data.message) ? data.message : JSON.stringify(data.message) | |
125 | + ); | |
126 | + data.deferred.resolve(); | |
127 | + } | |
128 | + }; | |
129 | + | |
130 | + $WebSocket.prototype.notifyOpenCallbacks = function notifyOpenCallbacks(event) { | |
131 | + for (var i = 0; i < this.onOpenCallbacks.length; i++) { | |
132 | + this.onOpenCallbacks[i].call(this, event); | |
133 | + } | |
134 | + }; | |
135 | + | |
136 | + $WebSocket.prototype.notifyCloseCallbacks = function notifyCloseCallbacks(event) { | |
137 | + for (var i = 0; i < this.onCloseCallbacks.length; i++) { | |
138 | + this.onCloseCallbacks[i].call(this, event); | |
139 | + } | |
140 | + }; | |
141 | + | |
142 | + $WebSocket.prototype.notifyErrorCallbacks = function notifyErrorCallbacks(event) { | |
143 | + for (var i = 0; i < this.onErrorCallbacks.length; i++) { | |
144 | + this.onErrorCallbacks[i].call(this, event); | |
145 | + } | |
146 | + }; | |
147 | + | |
148 | + $WebSocket.prototype.onOpen = function onOpen(cb) { | |
149 | + this.onOpenCallbacks.push(cb); | |
150 | + return this; | |
151 | + }; | |
152 | + | |
153 | + $WebSocket.prototype.onClose = function onClose(cb) { | |
154 | + this.onCloseCallbacks.push(cb); | |
155 | + return this; | |
156 | + }; | |
157 | + | |
158 | + $WebSocket.prototype.onError = function onError(cb) { | |
159 | + this.onErrorCallbacks.push(cb); | |
160 | + return this; | |
161 | + }; | |
162 | + | |
163 | + | |
164 | + $WebSocket.prototype.onMessage = function onMessage(callback, options) { | |
165 | + if (!isFunction(callback)) { | |
166 | + throw new Error('Callback must be a function'); | |
167 | + } | |
168 | + | |
169 | + if (options && isDefined(options.filter) && !isString(options.filter) && !(options.filter instanceof RegExp)) { | |
170 | + throw new Error('Pattern must be a string or regular expression'); | |
171 | + } | |
172 | + | |
173 | + this.onMessageCallbacks.push({ | |
174 | + fn: callback, | |
175 | + pattern: options ? options.filter : undefined, | |
176 | + autoApply: options ? options.autoApply : true | |
177 | + }); | |
178 | + return this; | |
179 | + }; | |
180 | + | |
181 | + $WebSocket.prototype._onOpenHandler = function _onOpenHandler(event) { | |
182 | + this._reconnectAttempts = 0; | |
183 | + this.notifyOpenCallbacks(event); | |
184 | + this.fireQueue(); | |
185 | + }; | |
186 | + | |
187 | + $WebSocket.prototype._onCloseHandler = function _onCloseHandler(event) { | |
188 | + this.notifyCloseCallbacks(event); | |
189 | + if ((this.reconnectIfNotNormalClose && event.code !== this._normalCloseCode) || this._reconnectableStatusCodes.indexOf(event.code) > -1) { | |
190 | + this.reconnect(); | |
191 | + } | |
192 | + }; | |
193 | + | |
194 | + $WebSocket.prototype._onErrorHandler = function _onErrorHandler(event) { | |
195 | + this.notifyErrorCallbacks(event); | |
196 | + }; | |
197 | + | |
198 | + $WebSocket.prototype._onMessageHandler = function _onMessageHandler(message) { | |
199 | + var pattern; | |
200 | + var self = this; | |
201 | + var currentCallback; | |
202 | + for (var i = 0; i < self.onMessageCallbacks.length; i++) { | |
203 | + currentCallback = self.onMessageCallbacks[i]; | |
204 | + pattern = currentCallback.pattern; | |
205 | + if (pattern) { | |
206 | + if (isString(pattern) && message.data === pattern) { | |
207 | + applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message); | |
208 | + } | |
209 | + else if (pattern instanceof RegExp && pattern.exec(message.data)) { | |
210 | + applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message); | |
211 | + } | |
212 | + } | |
213 | + else { | |
214 | + applyAsyncOrDigest(currentCallback.fn, currentCallback.autoApply, message); | |
215 | + } | |
216 | + } | |
217 | + | |
218 | + function applyAsyncOrDigest(callback, autoApply, args) { | |
219 | + args = arraySlice.call(arguments, 2); | |
220 | + if (self.useApplyAsync) { | |
221 | + self.scope.$applyAsync(function() { | |
222 | + callback.apply(self, args); | |
223 | + }); | |
224 | + } else { | |
225 | + callback.apply(self, args); | |
226 | + self.safeDigest(autoApply); | |
227 | + } | |
228 | + } | |
229 | + | |
230 | + }; | |
231 | + | |
232 | + $WebSocket.prototype.close = function close(force) { | |
233 | + if (force || !this.socket.bufferedAmount) { | |
234 | + this.socket.close(); | |
235 | + } | |
236 | + return this; | |
237 | + }; | |
238 | + | |
239 | + $WebSocket.prototype.send = function send(data) { | |
240 | + var deferred = $q.defer(); | |
241 | + var self = this; | |
242 | + var promise = cancelableify(deferred.promise); | |
243 | + | |
244 | + if (self.readyState === self._readyStateConstants.RECONNECT_ABORTED) { | |
245 | + deferred.reject('Socket connection has been closed'); | |
246 | + } | |
247 | + else { | |
248 | + self.sendQueue.push({ | |
249 | + message: data, | |
250 | + deferred: deferred | |
251 | + }); | |
252 | + self.fireQueue(); | |
253 | + } | |
254 | + | |
255 | + // Credit goes to @btford | |
256 | + function cancelableify(promise) { | |
257 | + promise.cancel = cancel; | |
258 | + var then = promise.then; | |
259 | + promise.then = function() { | |
260 | + var newPromise = then.apply(this, arguments); | |
261 | + return cancelableify(newPromise); | |
262 | + }; | |
263 | + return promise; | |
264 | + } | |
265 | + | |
266 | + function cancel(reason) { | |
267 | + self.sendQueue.splice(self.sendQueue.indexOf(data), 1); | |
268 | + deferred.reject(reason); | |
269 | + return self; | |
270 | + } | |
271 | + | |
272 | + if ($websocketBackend.isMocked && $websocketBackend.isMocked() && | |
273 | + $websocketBackend.isConnected(this.url)) { | |
274 | + this._onMessageHandler($websocketBackend.mockSend()); | |
275 | + } | |
276 | + | |
277 | + return promise; | |
278 | + }; | |
279 | + | |
280 | + $WebSocket.prototype.reconnect = function reconnect() { | |
281 | + this.close(); | |
282 | + | |
283 | + var backoffDelay = this._getBackoffDelay(++this._reconnectAttempts); | |
284 | + | |
285 | + var backoffDelaySeconds = backoffDelay / 1000; | |
286 | + console.log('Reconnecting in ' + backoffDelaySeconds + ' seconds'); | |
287 | + | |
288 | + $timeout(angular.bind(this, this._connect), backoffDelay); | |
289 | + | |
290 | + return this; | |
291 | + }; | |
292 | + // Exponential Backoff Formula by Prof. Douglas Thain | |
293 | + // http://dthain.blogspot.co.uk/2009/02/exponential-backoff-in-distributed.html | |
294 | + $WebSocket.prototype._getBackoffDelay = function _getBackoffDelay(attempt) { | |
295 | + var R = Math.random() + 1; | |
296 | + var T = this.initialTimeout; | |
297 | + var F = 2; | |
298 | + var N = attempt; | |
299 | + var M = this.maxTimeout; | |
300 | + | |
301 | + return Math.floor(Math.min(R * T * Math.pow(F, N), M)); | |
302 | + }; | |
303 | + | |
304 | + $WebSocket.prototype._setInternalState = function _setInternalState(state) { | |
305 | + if (Math.floor(state) !== state || state < 0 || state > 4) { | |
306 | + throw new Error('state must be an integer between 0 and 4, got: ' + state); | |
307 | + } | |
308 | + | |
309 | + // ie8 wat | |
310 | + if (!objectDefineProperty) { | |
311 | + this.readyState = state || this.socket.readyState; | |
312 | + } | |
313 | + this._internalConnectionState = state; | |
314 | + | |
315 | + | |
316 | + forEach(this.sendQueue, function(pending) { | |
317 | + pending.deferred.reject('Message cancelled due to closed socket connection'); | |
318 | + }); | |
319 | + }; | |
320 | + | |
321 | + // Read only .readyState | |
322 | + if (objectDefineProperty) { | |
323 | + objectDefineProperty($WebSocket.prototype, 'readyState', { | |
324 | + get: function() { | |
325 | + return this._internalConnectionState || this.socket.readyState; | |
326 | + }, | |
327 | + set: function() { | |
328 | + throw new Error('The readyState property is read-only'); | |
329 | + } | |
330 | + }); | |
331 | + } | |
332 | + | |
333 | + return function(url, protocols) { | |
334 | + return new $WebSocket(url, protocols); | |
335 | + }; | |
336 | + } | |
337 | + | |
338 | + // $WebSocketBackendProvider.$inject = ['$window', '$log']; | |
339 | + function $WebSocketBackendProvider($window, $log) { | |
340 | + this.create = function create(url, protocols) { | |
341 | + var match = /wss?:\/\//.exec(url); | |
342 | + var Socket, ws; | |
343 | + if (!match) { | |
344 | + throw new Error('Invalid url provided'); | |
345 | + } | |
346 | + | |
347 | + // CommonJS | |
348 | + if (typeof exports === 'object' && require) { | |
349 | + try { | |
350 | + ws = require('ws'); | |
351 | + Socket = (ws.Client || ws.client || ws); | |
352 | + } catch(e) {} | |
353 | + } | |
354 | + | |
355 | + // Browser | |
356 | + Socket = Socket || $window.WebSocket || $window.MozWebSocket; | |
357 | + | |
358 | + if (protocols) { | |
359 | + return new Socket(url, protocols); | |
360 | + } | |
361 | + | |
362 | + return new Socket(url); | |
363 | + }; | |
364 | + this.createWebSocketBackend = function createWebSocketBackend(url, protocols) { | |
365 | + $log.warn('Deprecated: Please use .create(url, protocols)'); | |
366 | + return this.create(url, protocols); | |
367 | + }; | |
368 | + } | |
369 | + | |
370 | + angular.module('ngWebSocket', []) | |
371 | + .factory('$websocket', ['$rootScope', '$q', '$timeout', '$websocketBackend', $WebSocketProvider]) | |
372 | + .factory('WebSocket', ['$rootScope', '$q', '$timeout', 'WebsocketBackend', $WebSocketProvider]) | |
373 | + .service('$websocketBackend', ['$window', '$log', $WebSocketBackendProvider]) | |
374 | + .service('WebSocketBackend', ['$window', '$log', $WebSocketBackendProvider]); | |
375 | + | |
376 | + | |
377 | + angular.module('angular-websocket', ['ngWebSocket']); | |
378 | + | |
379 | + if (typeof module === 'object' && typeof define !== 'function') { | |
380 | + module.exports = angular.module('ngWebSocket'); | |
381 | + } | |
382 | +}()); |
test.png
View file @
f60d569
396 Bytes
ws_debug.html
View file @
f60d569
1 | -<!DOCTYPE html> | |
2 | -<html> | |
3 | -<head lang="en"> | |
4 | - <meta charset="UTF-8"> | |
5 | - <title></title> | |
6 | -</head> | |
7 | -<body> | |
8 | -<ul> | |
9 | - | |
10 | -</ul> | |
11 | -</body> | |
12 | -<script src="//code.jquery.com/jquery-2.1.4.min.js"></script> | |
13 | -<script> | |
14 | - var loc = window.location, new_uri; | |
15 | - if (loc.protocol === "https:") { | |
16 | - new_uri = "wss:"; | |
17 | - } else { | |
18 | - new_uri = "ws:"; | |
19 | - } | |
20 | - new_uri += "//" + loc.host; | |
21 | - var ws = new WebSocket(new_uri + '/ws/deck/496?subscribe-user'); | |
22 | - ws.onopen = function () { | |
23 | - console.log("websocket connected"); | |
24 | - }; | |
25 | - ws.onmessage = function (e) { | |
26 | - $('ul').append('<li>' + e.data + '</li>'); | |
27 | - }; | |
28 | - ws.onerror = function (e) { | |
29 | - console.error(e); | |
30 | - }; | |
31 | - ws.onclose = function (e) { | |
32 | - console.log("connection closed"); | |
33 | - }; | |
34 | - function send_message(msg) {1 | |
35 | - ws.send(msg); | |
36 | - } | |
37 | - var ws = new WebSocket(new_uri + '/ws/feed/496?subscribe-broadcast'); | |
38 | - ws.onopen = function () { | |
39 | - console.log("websocket connected"); | |
40 | - }; | |
41 | - ws.onmessage = function (e) { | |
42 | - $('ul').append('<li>' + e.data + '</li>'); | |
43 | - }; | |
44 | - ws.onerror = function (e) { | |
45 | - console.error(e); | |
46 | - }; | |
47 | - ws.onclose = function (e) { | |
48 | - console.log("connection closed"); | |
49 | - }; | |
50 | - function send_message(msg) { | |
51 | - ws.send(msg); | |
52 | - } | |
53 | -</script> | |
54 | -</html> |