Total Complexity | 302 |
Complexity/F | 2.88 |
Lines of Code | 1476 |
Function Count | 105 |
Duplicated Lines | 1476 |
Ratio | 100 % |
Changes | 0 |
Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.
Common duplication problems, and corresponding solutions are:
Complex classes like public/lib/semantic/components/popup.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
1 | /*! |
||
11 | View Code Duplication | ;(function ($, window, document, undefined) { |
|
|
|||
12 | |||
13 | "use strict"; |
||
14 | |||
15 | window = (typeof window != 'undefined' && window.Math == Math) |
||
16 | ? window |
||
17 | : (typeof self != 'undefined' && self.Math == Math) |
||
18 | ? self |
||
19 | : Function('return this')() |
||
20 | ; |
||
21 | |||
22 | $.fn.popup = function(parameters) { |
||
23 | var |
||
24 | $allModules = $(this), |
||
25 | $document = $(document), |
||
26 | $window = $(window), |
||
27 | $body = $('body'), |
||
28 | |||
29 | moduleSelector = $allModules.selector || '', |
||
30 | |||
31 | hasTouch = (true), |
||
32 | time = new Date().getTime(), |
||
33 | performance = [], |
||
34 | |||
35 | query = arguments[0], |
||
36 | methodInvoked = (typeof query == 'string'), |
||
37 | queryArguments = [].slice.call(arguments, 1), |
||
38 | |||
39 | returnedValue |
||
40 | ; |
||
41 | $allModules |
||
42 | .each(function() { |
||
43 | var |
||
44 | settings = ( $.isPlainObject(parameters) ) |
||
45 | ? $.extend(true, {}, $.fn.popup.settings, parameters) |
||
46 | : $.extend({}, $.fn.popup.settings), |
||
47 | |||
48 | selector = settings.selector, |
||
49 | className = settings.className, |
||
50 | error = settings.error, |
||
51 | metadata = settings.metadata, |
||
52 | namespace = settings.namespace, |
||
53 | |||
54 | eventNamespace = '.' + settings.namespace, |
||
55 | moduleNamespace = 'module-' + namespace, |
||
56 | |||
57 | $module = $(this), |
||
58 | $context = $(settings.context), |
||
59 | $scrollContext = $(settings.scrollContext), |
||
60 | $boundary = $(settings.boundary), |
||
61 | $target = (settings.target) |
||
62 | ? $(settings.target) |
||
63 | : $module, |
||
64 | |||
65 | $popup, |
||
66 | $offsetParent, |
||
67 | |||
68 | searchDepth = 0, |
||
69 | triedPositions = false, |
||
70 | openedWithTouch = false, |
||
71 | |||
72 | element = this, |
||
73 | instance = $module.data(moduleNamespace), |
||
74 | |||
75 | documentObserver, |
||
76 | elementNamespace, |
||
77 | id, |
||
78 | module |
||
79 | ; |
||
80 | |||
81 | module = { |
||
82 | |||
83 | // binds events |
||
84 | initialize: function() { |
||
85 | module.debug('Initializing', $module); |
||
86 | module.createID(); |
||
87 | module.bind.events(); |
||
88 | if(!module.exists() && settings.preserve) { |
||
89 | module.create(); |
||
90 | } |
||
91 | if(settings.observeChanges) { |
||
92 | module.observeChanges(); |
||
93 | } |
||
94 | module.instantiate(); |
||
95 | }, |
||
96 | |||
97 | instantiate: function() { |
||
98 | module.verbose('Storing instance', module); |
||
99 | instance = module; |
||
100 | $module |
||
101 | .data(moduleNamespace, instance) |
||
102 | ; |
||
103 | }, |
||
104 | |||
105 | observeChanges: function() { |
||
106 | if('MutationObserver' in window) { |
||
107 | documentObserver = new MutationObserver(module.event.documentChanged); |
||
108 | documentObserver.observe(document, { |
||
109 | childList : true, |
||
110 | subtree : true |
||
111 | }); |
||
112 | module.debug('Setting up mutation observer', documentObserver); |
||
113 | } |
||
114 | }, |
||
115 | |||
116 | refresh: function() { |
||
117 | if(settings.popup) { |
||
118 | $popup = $(settings.popup).eq(0); |
||
119 | } |
||
120 | else { |
||
121 | if(settings.inline) { |
||
122 | $popup = $target.nextAll(selector.popup).eq(0); |
||
123 | settings.popup = $popup; |
||
124 | } |
||
125 | } |
||
126 | if(settings.popup) { |
||
127 | $popup.addClass(className.loading); |
||
128 | $offsetParent = module.get.offsetParent($target); |
||
129 | $popup.removeClass(className.loading); |
||
130 | if(settings.movePopup && module.has.popup() && module.get.offsetParent($popup)[0] !== $offsetParent[0]) { |
||
131 | module.debug('Moving popup to the same offset parent as target'); |
||
132 | $popup |
||
133 | .detach() |
||
134 | .appendTo($offsetParent) |
||
135 | ; |
||
136 | } |
||
137 | } |
||
138 | else { |
||
139 | $offsetParent = (settings.inline) |
||
140 | ? module.get.offsetParent($target) |
||
141 | : module.has.popup() |
||
142 | ? module.get.offsetParent($target) |
||
143 | : $body |
||
144 | ; |
||
145 | } |
||
146 | if( $offsetParent.is('html') && $offsetParent[0] !== $body[0] ) { |
||
147 | module.debug('Setting page as offset parent'); |
||
148 | $offsetParent = $body; |
||
149 | } |
||
150 | if( module.get.variation() ) { |
||
151 | module.set.variation(); |
||
152 | } |
||
153 | }, |
||
154 | |||
155 | reposition: function() { |
||
156 | module.refresh(); |
||
157 | module.set.position(); |
||
158 | }, |
||
159 | |||
160 | destroy: function() { |
||
161 | module.debug('Destroying previous module'); |
||
162 | if(documentObserver) { |
||
163 | documentObserver.disconnect(); |
||
164 | } |
||
165 | // remove element only if was created dynamically |
||
166 | if($popup && !settings.preserve) { |
||
167 | module.removePopup(); |
||
168 | } |
||
169 | // clear all timeouts |
||
170 | clearTimeout(module.hideTimer); |
||
171 | clearTimeout(module.showTimer); |
||
172 | // remove events |
||
173 | module.unbind.close(); |
||
174 | module.unbind.events(); |
||
175 | $module |
||
176 | .removeData(moduleNamespace) |
||
177 | ; |
||
178 | }, |
||
179 | |||
180 | event: { |
||
181 | start: function(event) { |
||
182 | var |
||
183 | delay = ($.isPlainObject(settings.delay)) |
||
184 | ? settings.delay.show |
||
185 | : settings.delay |
||
186 | ; |
||
187 | clearTimeout(module.hideTimer); |
||
188 | if(!openedWithTouch) { |
||
189 | module.showTimer = setTimeout(module.show, delay); |
||
190 | } |
||
191 | }, |
||
192 | end: function() { |
||
193 | var |
||
194 | delay = ($.isPlainObject(settings.delay)) |
||
195 | ? settings.delay.hide |
||
196 | : settings.delay |
||
197 | ; |
||
198 | clearTimeout(module.showTimer); |
||
199 | module.hideTimer = setTimeout(module.hide, delay); |
||
200 | }, |
||
201 | touchstart: function(event) { |
||
202 | openedWithTouch = true; |
||
203 | module.show(); |
||
204 | }, |
||
205 | resize: function() { |
||
206 | if( module.is.visible() ) { |
||
207 | module.set.position(); |
||
208 | } |
||
209 | }, |
||
210 | documentChanged: function(mutations) { |
||
211 | [].forEach.call(mutations, function(mutation) { |
||
212 | if(mutation.removedNodes) { |
||
213 | [].forEach.call(mutation.removedNodes, function(node) { |
||
214 | if(node == element || $(node).find(element).length > 0) { |
||
215 | module.debug('Element removed from DOM, tearing down events'); |
||
216 | module.destroy(); |
||
217 | } |
||
218 | }); |
||
219 | } |
||
220 | }); |
||
221 | }, |
||
222 | hideGracefully: function(event) { |
||
223 | var |
||
224 | $target = $(event.target), |
||
225 | isInDOM = $.contains(document.documentElement, event.target), |
||
226 | inPopup = ($target.closest(selector.popup).length > 0) |
||
227 | ; |
||
228 | // don't close on clicks inside popup |
||
229 | if(event && !inPopup && isInDOM) { |
||
230 | module.debug('Click occurred outside popup hiding popup'); |
||
231 | module.hide(); |
||
232 | } |
||
233 | else { |
||
234 | module.debug('Click was inside popup, keeping popup open'); |
||
235 | } |
||
236 | } |
||
237 | }, |
||
238 | |||
239 | // generates popup html from metadata |
||
240 | create: function() { |
||
241 | var |
||
242 | html = module.get.html(), |
||
243 | title = module.get.title(), |
||
244 | content = module.get.content() |
||
245 | ; |
||
246 | |||
247 | if(html || content || title) { |
||
248 | module.debug('Creating pop-up html'); |
||
249 | if(!html) { |
||
250 | html = settings.templates.popup({ |
||
251 | title : title, |
||
252 | content : content |
||
253 | }); |
||
254 | } |
||
255 | $popup = $('<div/>') |
||
256 | .addClass(className.popup) |
||
257 | .data(metadata.activator, $module) |
||
258 | .html(html) |
||
259 | ; |
||
260 | if(settings.inline) { |
||
261 | module.verbose('Inserting popup element inline', $popup); |
||
262 | $popup |
||
263 | .insertAfter($module) |
||
264 | ; |
||
265 | } |
||
266 | else { |
||
267 | module.verbose('Appending popup element to body', $popup); |
||
268 | $popup |
||
269 | .appendTo( $context ) |
||
270 | ; |
||
271 | } |
||
272 | module.refresh(); |
||
273 | module.set.variation(); |
||
274 | |||
275 | if(settings.hoverable) { |
||
276 | module.bind.popup(); |
||
277 | } |
||
278 | settings.onCreate.call($popup, element); |
||
279 | } |
||
280 | else if($target.next(selector.popup).length !== 0) { |
||
281 | module.verbose('Pre-existing popup found'); |
||
282 | settings.inline = true; |
||
283 | settings.popup = $target.next(selector.popup).data(metadata.activator, $module); |
||
284 | module.refresh(); |
||
285 | if(settings.hoverable) { |
||
286 | module.bind.popup(); |
||
287 | } |
||
288 | } |
||
289 | else if(settings.popup) { |
||
290 | $(settings.popup).data(metadata.activator, $module); |
||
291 | module.verbose('Used popup specified in settings'); |
||
292 | module.refresh(); |
||
293 | if(settings.hoverable) { |
||
294 | module.bind.popup(); |
||
295 | } |
||
296 | } |
||
297 | else { |
||
298 | module.debug('No content specified skipping display', element); |
||
299 | } |
||
300 | }, |
||
301 | |||
302 | createID: function() { |
||
303 | id = (Math.random().toString(16) + '000000000').substr(2, 8); |
||
304 | elementNamespace = '.' + id; |
||
305 | module.verbose('Creating unique id for element', id); |
||
306 | }, |
||
307 | |||
308 | // determines popup state |
||
309 | toggle: function() { |
||
310 | module.debug('Toggling pop-up'); |
||
311 | if( module.is.hidden() ) { |
||
312 | module.debug('Popup is hidden, showing pop-up'); |
||
313 | module.unbind.close(); |
||
314 | module.show(); |
||
315 | } |
||
316 | else { |
||
317 | module.debug('Popup is visible, hiding pop-up'); |
||
318 | module.hide(); |
||
319 | } |
||
320 | }, |
||
321 | |||
322 | show: function(callback) { |
||
323 | callback = callback || function(){}; |
||
324 | module.debug('Showing pop-up', settings.transition); |
||
325 | if(module.is.hidden() && !( module.is.active() && module.is.dropdown()) ) { |
||
326 | if( !module.exists() ) { |
||
327 | module.create(); |
||
328 | } |
||
329 | if(settings.onShow.call($popup, element) === false) { |
||
330 | module.debug('onShow callback returned false, cancelling popup animation'); |
||
331 | return; |
||
332 | } |
||
333 | else if(!settings.preserve && !settings.popup) { |
||
334 | module.refresh(); |
||
335 | } |
||
336 | if( $popup && module.set.position() ) { |
||
337 | module.save.conditions(); |
||
338 | if(settings.exclusive) { |
||
339 | module.hideAll(); |
||
340 | } |
||
341 | module.animate.show(callback); |
||
342 | } |
||
343 | } |
||
344 | }, |
||
345 | |||
346 | |||
347 | hide: function(callback) { |
||
348 | callback = callback || function(){}; |
||
349 | if( module.is.visible() || module.is.animating() ) { |
||
350 | if(settings.onHide.call($popup, element) === false) { |
||
351 | module.debug('onHide callback returned false, cancelling popup animation'); |
||
352 | return; |
||
353 | } |
||
354 | module.remove.visible(); |
||
355 | module.unbind.close(); |
||
356 | module.restore.conditions(); |
||
357 | module.animate.hide(callback); |
||
358 | } |
||
359 | }, |
||
360 | |||
361 | hideAll: function() { |
||
362 | $(selector.popup) |
||
363 | .filter('.' + className.popupVisible) |
||
364 | .each(function() { |
||
365 | $(this) |
||
366 | .data(metadata.activator) |
||
367 | .popup('hide') |
||
368 | ; |
||
369 | }) |
||
370 | ; |
||
371 | }, |
||
372 | exists: function() { |
||
373 | if(!$popup) { |
||
374 | return false; |
||
375 | } |
||
376 | if(settings.inline || settings.popup) { |
||
377 | return ( module.has.popup() ); |
||
378 | } |
||
379 | else { |
||
380 | return ( $popup.closest($context).length >= 1 ) |
||
381 | ? true |
||
382 | : false |
||
383 | ; |
||
384 | } |
||
385 | }, |
||
386 | |||
387 | removePopup: function() { |
||
388 | if( module.has.popup() && !settings.popup) { |
||
389 | module.debug('Removing popup', $popup); |
||
390 | $popup.remove(); |
||
391 | $popup = undefined; |
||
392 | settings.onRemove.call($popup, element); |
||
393 | } |
||
394 | }, |
||
395 | |||
396 | save: { |
||
397 | conditions: function() { |
||
398 | module.cache = { |
||
399 | title: $module.attr('title') |
||
400 | }; |
||
401 | if (module.cache.title) { |
||
402 | $module.removeAttr('title'); |
||
403 | } |
||
404 | module.verbose('Saving original attributes', module.cache.title); |
||
405 | } |
||
406 | }, |
||
407 | restore: { |
||
408 | conditions: function() { |
||
409 | if(module.cache && module.cache.title) { |
||
410 | $module.attr('title', module.cache.title); |
||
411 | module.verbose('Restoring original attributes', module.cache.title); |
||
412 | } |
||
413 | return true; |
||
414 | } |
||
415 | }, |
||
416 | supports: { |
||
417 | svg: function() { |
||
418 | return (typeof SVGGraphicsElement === 'undefined'); |
||
419 | } |
||
420 | }, |
||
421 | animate: { |
||
422 | show: function(callback) { |
||
423 | callback = $.isFunction(callback) ? callback : function(){}; |
||
424 | if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) { |
||
425 | module.set.visible(); |
||
426 | $popup |
||
427 | .transition({ |
||
428 | animation : settings.transition + ' in', |
||
429 | queue : false, |
||
430 | debug : settings.debug, |
||
431 | verbose : settings.verbose, |
||
432 | duration : settings.duration, |
||
433 | onComplete : function() { |
||
434 | module.bind.close(); |
||
435 | callback.call($popup, element); |
||
436 | settings.onVisible.call($popup, element); |
||
437 | } |
||
438 | }) |
||
439 | ; |
||
440 | } |
||
441 | else { |
||
442 | module.error(error.noTransition); |
||
443 | } |
||
444 | }, |
||
445 | hide: function(callback) { |
||
446 | callback = $.isFunction(callback) ? callback : function(){}; |
||
447 | module.debug('Hiding pop-up'); |
||
448 | if(settings.onHide.call($popup, element) === false) { |
||
449 | module.debug('onHide callback returned false, cancelling popup animation'); |
||
450 | return; |
||
451 | } |
||
452 | if(settings.transition && $.fn.transition !== undefined && $module.transition('is supported')) { |
||
453 | $popup |
||
454 | .transition({ |
||
455 | animation : settings.transition + ' out', |
||
456 | queue : false, |
||
457 | duration : settings.duration, |
||
458 | debug : settings.debug, |
||
459 | verbose : settings.verbose, |
||
460 | onComplete : function() { |
||
461 | module.reset(); |
||
462 | callback.call($popup, element); |
||
463 | settings.onHidden.call($popup, element); |
||
464 | } |
||
465 | }) |
||
466 | ; |
||
467 | } |
||
468 | else { |
||
469 | module.error(error.noTransition); |
||
470 | } |
||
471 | } |
||
472 | }, |
||
473 | |||
474 | change: { |
||
475 | content: function(html) { |
||
476 | $popup.html(html); |
||
477 | } |
||
478 | }, |
||
479 | |||
480 | get: { |
||
481 | html: function() { |
||
482 | $module.removeData(metadata.html); |
||
483 | return $module.data(metadata.html) || settings.html; |
||
484 | }, |
||
485 | title: function() { |
||
486 | $module.removeData(metadata.title); |
||
487 | return $module.data(metadata.title) || settings.title; |
||
488 | }, |
||
489 | content: function() { |
||
490 | $module.removeData(metadata.content); |
||
491 | return $module.data(metadata.content) || $module.attr('title') || settings.content; |
||
492 | }, |
||
493 | variation: function() { |
||
494 | $module.removeData(metadata.variation); |
||
495 | return $module.data(metadata.variation) || settings.variation; |
||
496 | }, |
||
497 | popup: function() { |
||
498 | return $popup; |
||
499 | }, |
||
500 | popupOffset: function() { |
||
501 | return $popup.offset(); |
||
502 | }, |
||
503 | calculations: function() { |
||
504 | var |
||
505 | targetElement = $target[0], |
||
506 | isWindow = ($boundary[0] == window), |
||
507 | targetPosition = (settings.inline || (settings.popup && settings.movePopup)) |
||
508 | ? $target.position() |
||
509 | : $target.offset(), |
||
510 | screenPosition = (isWindow) |
||
511 | ? { top: 0, left: 0 } |
||
512 | : $boundary.offset(), |
||
513 | calculations = {}, |
||
514 | scroll = (isWindow) |
||
515 | ? { top: $window.scrollTop(), left: $window.scrollLeft() } |
||
516 | : { top: 0, left: 0}, |
||
517 | screen |
||
518 | ; |
||
519 | calculations = { |
||
520 | // element which is launching popup |
||
521 | target : { |
||
522 | element : $target[0], |
||
523 | width : $target.outerWidth(), |
||
524 | height : $target.outerHeight(), |
||
525 | top : targetPosition.top, |
||
526 | left : targetPosition.left, |
||
527 | margin : {} |
||
528 | }, |
||
529 | // popup itself |
||
530 | popup : { |
||
531 | width : $popup.outerWidth(), |
||
532 | height : $popup.outerHeight() |
||
533 | }, |
||
534 | // offset container (or 3d context) |
||
535 | parent : { |
||
536 | width : $offsetParent.outerWidth(), |
||
537 | height : $offsetParent.outerHeight() |
||
538 | }, |
||
539 | // screen boundaries |
||
540 | screen : { |
||
541 | top : screenPosition.top, |
||
542 | left : screenPosition.left, |
||
543 | scroll: { |
||
544 | top : scroll.top, |
||
545 | left : scroll.left |
||
546 | }, |
||
547 | width : $boundary.width(), |
||
548 | height : $boundary.height() |
||
549 | } |
||
550 | }; |
||
551 | |||
552 | // add in container calcs if fluid |
||
553 | if( settings.setFluidWidth && module.is.fluid() ) { |
||
554 | calculations.container = { |
||
555 | width: $popup.parent().outerWidth() |
||
556 | }; |
||
557 | calculations.popup.width = calculations.container.width; |
||
558 | } |
||
559 | |||
560 | // add in margins if inline |
||
561 | calculations.target.margin.top = (settings.inline) |
||
562 | ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-top'), 10) |
||
563 | : 0 |
||
564 | ; |
||
565 | calculations.target.margin.left = (settings.inline) |
||
566 | ? module.is.rtl() |
||
567 | ? parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-right'), 10) |
||
568 | : parseInt( window.getComputedStyle(targetElement).getPropertyValue('margin-left'), 10) |
||
569 | : 0 |
||
570 | ; |
||
571 | // calculate screen boundaries |
||
572 | screen = calculations.screen; |
||
573 | calculations.boundary = { |
||
574 | top : screen.top + screen.scroll.top, |
||
575 | bottom : screen.top + screen.scroll.top + screen.height, |
||
576 | left : screen.left + screen.scroll.left, |
||
577 | right : screen.left + screen.scroll.left + screen.width |
||
578 | }; |
||
579 | return calculations; |
||
580 | }, |
||
581 | id: function() { |
||
582 | return id; |
||
583 | }, |
||
584 | startEvent: function() { |
||
585 | if(settings.on == 'hover') { |
||
586 | return 'mouseenter'; |
||
587 | } |
||
588 | else if(settings.on == 'focus') { |
||
589 | return 'focus'; |
||
590 | } |
||
591 | return false; |
||
592 | }, |
||
593 | scrollEvent: function() { |
||
594 | return 'scroll'; |
||
595 | }, |
||
596 | endEvent: function() { |
||
597 | if(settings.on == 'hover') { |
||
598 | return 'mouseleave'; |
||
599 | } |
||
600 | else if(settings.on == 'focus') { |
||
601 | return 'blur'; |
||
602 | } |
||
603 | return false; |
||
604 | }, |
||
605 | distanceFromBoundary: function(offset, calculations) { |
||
606 | var |
||
607 | distanceFromBoundary = {}, |
||
608 | popup, |
||
609 | boundary |
||
610 | ; |
||
611 | calculations = calculations || module.get.calculations(); |
||
612 | |||
613 | // shorthand |
||
614 | popup = calculations.popup; |
||
615 | boundary = calculations.boundary; |
||
616 | |||
617 | if(offset) { |
||
618 | distanceFromBoundary = { |
||
619 | top : (offset.top - boundary.top), |
||
620 | left : (offset.left - boundary.left), |
||
621 | right : (boundary.right - (offset.left + popup.width) ), |
||
622 | bottom : (boundary.bottom - (offset.top + popup.height) ) |
||
623 | }; |
||
624 | module.verbose('Distance from boundaries determined', offset, distanceFromBoundary); |
||
625 | } |
||
626 | return distanceFromBoundary; |
||
627 | }, |
||
628 | offsetParent: function($target) { |
||
629 | var |
||
630 | element = ($target !== undefined) |
||
631 | ? $target[0] |
||
632 | : $module[0], |
||
633 | parentNode = element.parentNode, |
||
634 | $node = $(parentNode) |
||
635 | ; |
||
636 | if(parentNode) { |
||
637 | var |
||
638 | is2D = ($node.css('transform') === 'none'), |
||
639 | isStatic = ($node.css('position') === 'static'), |
||
640 | isHTML = $node.is('html') |
||
641 | ; |
||
642 | while(parentNode && !isHTML && isStatic && is2D) { |
||
643 | parentNode = parentNode.parentNode; |
||
644 | $node = $(parentNode); |
||
645 | is2D = ($node.css('transform') === 'none'); |
||
646 | isStatic = ($node.css('position') === 'static'); |
||
647 | isHTML = $node.is('html'); |
||
648 | } |
||
649 | } |
||
650 | return ($node && $node.length > 0) |
||
651 | ? $node |
||
652 | : $() |
||
653 | ; |
||
654 | }, |
||
655 | positions: function() { |
||
656 | return { |
||
657 | 'top left' : false, |
||
658 | 'top center' : false, |
||
659 | 'top right' : false, |
||
660 | 'bottom left' : false, |
||
661 | 'bottom center' : false, |
||
662 | 'bottom right' : false, |
||
663 | 'left center' : false, |
||
664 | 'right center' : false |
||
665 | }; |
||
666 | }, |
||
667 | nextPosition: function(position) { |
||
668 | var |
||
669 | positions = position.split(' '), |
||
670 | verticalPosition = positions[0], |
||
671 | horizontalPosition = positions[1], |
||
672 | opposite = { |
||
673 | top : 'bottom', |
||
674 | bottom : 'top', |
||
675 | left : 'right', |
||
676 | right : 'left' |
||
677 | }, |
||
678 | adjacent = { |
||
679 | left : 'center', |
||
680 | center : 'right', |
||
681 | right : 'left' |
||
682 | }, |
||
683 | backup = { |
||
684 | 'top left' : 'top center', |
||
685 | 'top center' : 'top right', |
||
686 | 'top right' : 'right center', |
||
687 | 'right center' : 'bottom right', |
||
688 | 'bottom right' : 'bottom center', |
||
689 | 'bottom center' : 'bottom left', |
||
690 | 'bottom left' : 'left center', |
||
691 | 'left center' : 'top left' |
||
692 | }, |
||
693 | adjacentsAvailable = (verticalPosition == 'top' || verticalPosition == 'bottom'), |
||
694 | oppositeTried = false, |
||
695 | adjacentTried = false, |
||
696 | nextPosition = false |
||
697 | ; |
||
698 | if(!triedPositions) { |
||
699 | module.verbose('All available positions available'); |
||
700 | triedPositions = module.get.positions(); |
||
701 | } |
||
702 | |||
703 | module.debug('Recording last position tried', position); |
||
704 | triedPositions[position] = true; |
||
705 | |||
706 | if(settings.prefer === 'opposite') { |
||
707 | nextPosition = [opposite[verticalPosition], horizontalPosition]; |
||
708 | nextPosition = nextPosition.join(' '); |
||
709 | oppositeTried = (triedPositions[nextPosition] === true); |
||
710 | module.debug('Trying opposite strategy', nextPosition); |
||
711 | } |
||
712 | if((settings.prefer === 'adjacent') && adjacentsAvailable ) { |
||
713 | nextPosition = [verticalPosition, adjacent[horizontalPosition]]; |
||
714 | nextPosition = nextPosition.join(' '); |
||
715 | adjacentTried = (triedPositions[nextPosition] === true); |
||
716 | module.debug('Trying adjacent strategy', nextPosition); |
||
717 | } |
||
718 | if(adjacentTried || oppositeTried) { |
||
719 | module.debug('Using backup position', nextPosition); |
||
720 | nextPosition = backup[position]; |
||
721 | } |
||
722 | return nextPosition; |
||
723 | } |
||
724 | }, |
||
725 | |||
726 | set: { |
||
727 | position: function(position, calculations) { |
||
728 | |||
729 | // exit conditions |
||
730 | if($target.length === 0 || $popup.length === 0) { |
||
731 | module.error(error.notFound); |
||
732 | return; |
||
733 | } |
||
734 | var |
||
735 | offset, |
||
736 | distanceAway, |
||
737 | target, |
||
738 | popup, |
||
739 | parent, |
||
740 | positioning, |
||
741 | popupOffset, |
||
742 | distanceFromBoundary |
||
743 | ; |
||
744 | |||
745 | calculations = calculations || module.get.calculations(); |
||
746 | position = position || $module.data(metadata.position) || settings.position; |
||
747 | |||
748 | offset = $module.data(metadata.offset) || settings.offset; |
||
749 | distanceAway = settings.distanceAway; |
||
750 | |||
751 | // shorthand |
||
752 | target = calculations.target; |
||
753 | popup = calculations.popup; |
||
754 | parent = calculations.parent; |
||
755 | |||
756 | if(target.width === 0 && target.height === 0 && !module.is.svg(target.element)) { |
||
757 | module.debug('Popup target is hidden, no action taken'); |
||
758 | return false; |
||
759 | } |
||
760 | |||
761 | if(settings.inline) { |
||
762 | module.debug('Adding margin to calculation', target.margin); |
||
763 | if(position == 'left center' || position == 'right center') { |
||
764 | offset += target.margin.top; |
||
765 | distanceAway += -target.margin.left; |
||
766 | } |
||
767 | else if (position == 'top left' || position == 'top center' || position == 'top right') { |
||
768 | offset += target.margin.left; |
||
769 | distanceAway -= target.margin.top; |
||
770 | } |
||
771 | else { |
||
772 | offset += target.margin.left; |
||
773 | distanceAway += target.margin.top; |
||
774 | } |
||
775 | } |
||
776 | |||
777 | module.debug('Determining popup position from calculations', position, calculations); |
||
778 | |||
779 | if (module.is.rtl()) { |
||
780 | position = position.replace(/left|right/g, function (match) { |
||
781 | return (match == 'left') |
||
782 | ? 'right' |
||
783 | : 'left' |
||
784 | ; |
||
785 | }); |
||
786 | module.debug('RTL: Popup position updated', position); |
||
787 | } |
||
788 | |||
789 | // if last attempt use specified last resort position |
||
790 | if(searchDepth == settings.maxSearchDepth && typeof settings.lastResort === 'string') { |
||
791 | position = settings.lastResort; |
||
792 | } |
||
793 | |||
794 | switch (position) { |
||
795 | case 'top left': |
||
796 | positioning = { |
||
797 | top : 'auto', |
||
798 | bottom : parent.height - target.top + distanceAway, |
||
799 | left : target.left + offset, |
||
800 | right : 'auto' |
||
801 | }; |
||
802 | break; |
||
803 | case 'top center': |
||
804 | positioning = { |
||
805 | bottom : parent.height - target.top + distanceAway, |
||
806 | left : target.left + (target.width / 2) - (popup.width / 2) + offset, |
||
807 | top : 'auto', |
||
808 | right : 'auto' |
||
809 | }; |
||
810 | break; |
||
811 | case 'top right': |
||
812 | positioning = { |
||
813 | bottom : parent.height - target.top + distanceAway, |
||
814 | right : parent.width - target.left - target.width - offset, |
||
815 | top : 'auto', |
||
816 | left : 'auto' |
||
817 | }; |
||
818 | break; |
||
819 | case 'left center': |
||
820 | positioning = { |
||
821 | top : target.top + (target.height / 2) - (popup.height / 2) + offset, |
||
822 | right : parent.width - target.left + distanceAway, |
||
823 | left : 'auto', |
||
824 | bottom : 'auto' |
||
825 | }; |
||
826 | break; |
||
827 | case 'right center': |
||
828 | positioning = { |
||
829 | top : target.top + (target.height / 2) - (popup.height / 2) + offset, |
||
830 | left : target.left + target.width + distanceAway, |
||
831 | bottom : 'auto', |
||
832 | right : 'auto' |
||
833 | }; |
||
834 | break; |
||
835 | case 'bottom left': |
||
836 | positioning = { |
||
837 | top : target.top + target.height + distanceAway, |
||
838 | left : target.left + offset, |
||
839 | bottom : 'auto', |
||
840 | right : 'auto' |
||
841 | }; |
||
842 | break; |
||
843 | case 'bottom center': |
||
844 | positioning = { |
||
845 | top : target.top + target.height + distanceAway, |
||
846 | left : target.left + (target.width / 2) - (popup.width / 2) + offset, |
||
847 | bottom : 'auto', |
||
848 | right : 'auto' |
||
849 | }; |
||
850 | break; |
||
851 | case 'bottom right': |
||
852 | positioning = { |
||
853 | top : target.top + target.height + distanceAway, |
||
854 | right : parent.width - target.left - target.width - offset, |
||
855 | left : 'auto', |
||
856 | bottom : 'auto' |
||
857 | }; |
||
858 | break; |
||
859 | } |
||
860 | if(positioning === undefined) { |
||
861 | module.error(error.invalidPosition, position); |
||
862 | } |
||
863 | |||
864 | module.debug('Calculated popup positioning values', positioning); |
||
865 | |||
866 | // tentatively place on stage |
||
867 | $popup |
||
868 | .css(positioning) |
||
869 | .removeClass(className.position) |
||
870 | .addClass(position) |
||
871 | .addClass(className.loading) |
||
872 | ; |
||
873 | |||
874 | popupOffset = module.get.popupOffset(); |
||
875 | |||
876 | // see if any boundaries are surpassed with this tentative position |
||
877 | distanceFromBoundary = module.get.distanceFromBoundary(popupOffset, calculations); |
||
878 | |||
879 | if( module.is.offstage(distanceFromBoundary, position) ) { |
||
880 | module.debug('Position is outside viewport', position); |
||
881 | if(searchDepth < settings.maxSearchDepth) { |
||
882 | searchDepth++; |
||
883 | position = module.get.nextPosition(position); |
||
884 | module.debug('Trying new position', position); |
||
885 | return ($popup) |
||
886 | ? module.set.position(position, calculations) |
||
887 | : false |
||
888 | ; |
||
889 | } |
||
890 | else { |
||
891 | if(settings.lastResort) { |
||
892 | module.debug('No position found, showing with last position'); |
||
893 | } |
||
894 | else { |
||
895 | module.debug('Popup could not find a position to display', $popup); |
||
896 | module.error(error.cannotPlace, element); |
||
897 | module.remove.attempts(); |
||
898 | module.remove.loading(); |
||
899 | module.reset(); |
||
900 | settings.onUnplaceable.call($popup, element); |
||
901 | return false; |
||
902 | } |
||
903 | } |
||
904 | } |
||
905 | module.debug('Position is on stage', position); |
||
906 | module.remove.attempts(); |
||
907 | module.remove.loading(); |
||
908 | if( settings.setFluidWidth && module.is.fluid() ) { |
||
909 | module.set.fluidWidth(calculations); |
||
910 | } |
||
911 | return true; |
||
912 | }, |
||
913 | |||
914 | fluidWidth: function(calculations) { |
||
915 | calculations = calculations || module.get.calculations(); |
||
916 | module.debug('Automatically setting element width to parent width', calculations.parent.width); |
||
917 | $popup.css('width', calculations.container.width); |
||
918 | }, |
||
919 | |||
920 | variation: function(variation) { |
||
921 | variation = variation || module.get.variation(); |
||
922 | if(variation && module.has.popup() ) { |
||
923 | module.verbose('Adding variation to popup', variation); |
||
924 | $popup.addClass(variation); |
||
925 | } |
||
926 | }, |
||
927 | |||
928 | visible: function() { |
||
929 | $module.addClass(className.visible); |
||
930 | } |
||
931 | }, |
||
932 | |||
933 | remove: { |
||
934 | loading: function() { |
||
935 | $popup.removeClass(className.loading); |
||
936 | }, |
||
937 | variation: function(variation) { |
||
938 | variation = variation || module.get.variation(); |
||
939 | if(variation) { |
||
940 | module.verbose('Removing variation', variation); |
||
941 | $popup.removeClass(variation); |
||
942 | } |
||
943 | }, |
||
944 | visible: function() { |
||
945 | $module.removeClass(className.visible); |
||
946 | }, |
||
947 | attempts: function() { |
||
948 | module.verbose('Resetting all searched positions'); |
||
949 | searchDepth = 0; |
||
950 | triedPositions = false; |
||
951 | } |
||
952 | }, |
||
953 | |||
954 | bind: { |
||
955 | events: function() { |
||
956 | module.debug('Binding popup events to module'); |
||
957 | if(settings.on == 'click') { |
||
958 | $module |
||
959 | .on('click' + eventNamespace, module.toggle) |
||
960 | ; |
||
961 | } |
||
962 | if(settings.on == 'hover' && hasTouch) { |
||
963 | $module |
||
964 | .on('touchstart' + eventNamespace, module.event.touchstart) |
||
965 | ; |
||
966 | } |
||
967 | if( module.get.startEvent() ) { |
||
968 | $module |
||
969 | .on(module.get.startEvent() + eventNamespace, module.event.start) |
||
970 | .on(module.get.endEvent() + eventNamespace, module.event.end) |
||
971 | ; |
||
972 | } |
||
973 | if(settings.target) { |
||
974 | module.debug('Target set to element', $target); |
||
975 | } |
||
976 | $window.on('resize' + elementNamespace, module.event.resize); |
||
977 | }, |
||
978 | popup: function() { |
||
979 | module.verbose('Allowing hover events on popup to prevent closing'); |
||
980 | if( $popup && module.has.popup() ) { |
||
981 | $popup |
||
982 | .on('mouseenter' + eventNamespace, module.event.start) |
||
983 | .on('mouseleave' + eventNamespace, module.event.end) |
||
984 | ; |
||
985 | } |
||
986 | }, |
||
987 | close: function() { |
||
988 | if(settings.hideOnScroll === true || (settings.hideOnScroll == 'auto' && settings.on != 'click')) { |
||
989 | module.bind.closeOnScroll(); |
||
990 | } |
||
991 | if(settings.on == 'hover' && openedWithTouch) { |
||
992 | module.bind.touchClose(); |
||
993 | } |
||
994 | if(settings.on == 'click' && settings.closable) { |
||
995 | module.bind.clickaway(); |
||
996 | } |
||
997 | }, |
||
998 | closeOnScroll: function() { |
||
999 | module.verbose('Binding scroll close event to document'); |
||
1000 | $scrollContext |
||
1001 | .one(module.get.scrollEvent() + elementNamespace, module.event.hideGracefully) |
||
1002 | ; |
||
1003 | }, |
||
1004 | touchClose: function() { |
||
1005 | module.verbose('Binding popup touchclose event to document'); |
||
1006 | $document |
||
1007 | .on('touchstart' + elementNamespace, function(event) { |
||
1008 | module.verbose('Touched away from popup'); |
||
1009 | module.event.hideGracefully.call(element, event); |
||
1010 | }) |
||
1011 | ; |
||
1012 | }, |
||
1013 | clickaway: function() { |
||
1014 | module.verbose('Binding popup close event to document'); |
||
1015 | $document |
||
1016 | .on('click' + elementNamespace, function(event) { |
||
1017 | module.verbose('Clicked away from popup'); |
||
1018 | module.event.hideGracefully.call(element, event); |
||
1019 | }) |
||
1020 | ; |
||
1021 | } |
||
1022 | }, |
||
1023 | |||
1024 | unbind: { |
||
1025 | events: function() { |
||
1026 | $window |
||
1027 | .off(elementNamespace) |
||
1028 | ; |
||
1029 | $module |
||
1030 | .off(eventNamespace) |
||
1031 | ; |
||
1032 | }, |
||
1033 | close: function() { |
||
1034 | $document |
||
1035 | .off(elementNamespace) |
||
1036 | ; |
||
1037 | $scrollContext |
||
1038 | .off(elementNamespace) |
||
1039 | ; |
||
1040 | }, |
||
1041 | }, |
||
1042 | |||
1043 | has: { |
||
1044 | popup: function() { |
||
1045 | return ($popup && $popup.length > 0); |
||
1046 | } |
||
1047 | }, |
||
1048 | |||
1049 | is: { |
||
1050 | offstage: function(distanceFromBoundary, position) { |
||
1051 | var |
||
1052 | offstage = [] |
||
1053 | ; |
||
1054 | // return boundaries that have been surpassed |
||
1055 | $.each(distanceFromBoundary, function(direction, distance) { |
||
1056 | if(distance < -settings.jitter) { |
||
1057 | module.debug('Position exceeds allowable distance from edge', direction, distance, position); |
||
1058 | offstage.push(direction); |
||
1059 | } |
||
1060 | }); |
||
1061 | if(offstage.length > 0) { |
||
1062 | return true; |
||
1063 | } |
||
1064 | else { |
||
1065 | return false; |
||
1066 | } |
||
1067 | }, |
||
1068 | svg: function(element) { |
||
1069 | return module.supports.svg() && (element instanceof SVGGraphicsElement); |
||
1070 | }, |
||
1071 | active: function() { |
||
1072 | return $module.hasClass(className.active); |
||
1073 | }, |
||
1074 | animating: function() { |
||
1075 | return ($popup !== undefined && $popup.hasClass(className.animating) ); |
||
1076 | }, |
||
1077 | fluid: function() { |
||
1078 | return ($popup !== undefined && $popup.hasClass(className.fluid)); |
||
1079 | }, |
||
1080 | visible: function() { |
||
1081 | return ($popup !== undefined && $popup.hasClass(className.popupVisible)); |
||
1082 | }, |
||
1083 | dropdown: function() { |
||
1084 | return $module.hasClass(className.dropdown); |
||
1085 | }, |
||
1086 | hidden: function() { |
||
1087 | return !module.is.visible(); |
||
1088 | }, |
||
1089 | rtl: function () { |
||
1090 | return $module.css('direction') == 'rtl'; |
||
1091 | } |
||
1092 | }, |
||
1093 | |||
1094 | reset: function() { |
||
1095 | module.remove.visible(); |
||
1096 | if(settings.preserve) { |
||
1097 | if($.fn.transition !== undefined) { |
||
1098 | $popup |
||
1099 | .transition('remove transition') |
||
1100 | ; |
||
1101 | } |
||
1102 | } |
||
1103 | else { |
||
1104 | module.removePopup(); |
||
1105 | } |
||
1106 | }, |
||
1107 | |||
1108 | setting: function(name, value) { |
||
1109 | if( $.isPlainObject(name) ) { |
||
1110 | $.extend(true, settings, name); |
||
1111 | } |
||
1112 | else if(value !== undefined) { |
||
1113 | settings[name] = value; |
||
1114 | } |
||
1115 | else { |
||
1116 | return settings[name]; |
||
1117 | } |
||
1118 | }, |
||
1119 | internal: function(name, value) { |
||
1120 | if( $.isPlainObject(name) ) { |
||
1121 | $.extend(true, module, name); |
||
1122 | } |
||
1123 | else if(value !== undefined) { |
||
1124 | module[name] = value; |
||
1125 | } |
||
1126 | else { |
||
1127 | return module[name]; |
||
1128 | } |
||
1129 | }, |
||
1130 | debug: function() { |
||
1131 | if(!settings.silent && settings.debug) { |
||
1132 | if(settings.performance) { |
||
1133 | module.performance.log(arguments); |
||
1134 | } |
||
1135 | else { |
||
1136 | module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':'); |
||
1137 | module.debug.apply(console, arguments); |
||
1138 | } |
||
1139 | } |
||
1140 | }, |
||
1141 | verbose: function() { |
||
1142 | if(!settings.silent && settings.verbose && settings.debug) { |
||
1143 | if(settings.performance) { |
||
1144 | module.performance.log(arguments); |
||
1145 | } |
||
1146 | else { |
||
1147 | module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':'); |
||
1148 | module.verbose.apply(console, arguments); |
||
1149 | } |
||
1150 | } |
||
1151 | }, |
||
1152 | error: function() { |
||
1153 | if(!settings.silent) { |
||
1154 | module.error = Function.prototype.bind.call(console.error, console, settings.name + ':'); |
||
1155 | module.error.apply(console, arguments); |
||
1156 | } |
||
1157 | }, |
||
1158 | performance: { |
||
1159 | log: function(message) { |
||
1160 | var |
||
1161 | currentTime, |
||
1162 | executionTime, |
||
1163 | previousTime |
||
1164 | ; |
||
1165 | if(settings.performance) { |
||
1166 | currentTime = new Date().getTime(); |
||
1167 | previousTime = time || currentTime; |
||
1168 | executionTime = currentTime - previousTime; |
||
1169 | time = currentTime; |
||
1170 | performance.push({ |
||
1171 | 'Name' : message[0], |
||
1172 | 'Arguments' : [].slice.call(message, 1) || '', |
||
1173 | 'Element' : element, |
||
1174 | 'Execution Time' : executionTime |
||
1175 | }); |
||
1176 | } |
||
1177 | clearTimeout(module.performance.timer); |
||
1178 | module.performance.timer = setTimeout(module.performance.display, 500); |
||
1179 | }, |
||
1180 | display: function() { |
||
1181 | var |
||
1182 | title = settings.name + ':', |
||
1183 | totalTime = 0 |
||
1184 | ; |
||
1185 | time = false; |
||
1186 | clearTimeout(module.performance.timer); |
||
1187 | $.each(performance, function(index, data) { |
||
1188 | totalTime += data['Execution Time']; |
||
1189 | }); |
||
1190 | title += ' ' + totalTime + 'ms'; |
||
1191 | if(moduleSelector) { |
||
1192 | title += ' \'' + moduleSelector + '\''; |
||
1193 | } |
||
1194 | if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) { |
||
1195 | console.groupCollapsed(title); |
||
1196 | if(console.table) { |
||
1197 | console.table(performance); |
||
1198 | } |
||
1199 | else { |
||
1200 | $.each(performance, function(index, data) { |
||
1201 | console.log(data['Name'] + ': ' + data['Execution Time']+'ms'); |
||
1202 | }); |
||
1203 | } |
||
1204 | console.groupEnd(); |
||
1205 | } |
||
1206 | performance = []; |
||
1207 | } |
||
1208 | }, |
||
1209 | invoke: function(query, passedArguments, context) { |
||
1210 | var |
||
1211 | object = instance, |
||
1212 | maxDepth, |
||
1213 | found, |
||
1214 | response |
||
1215 | ; |
||
1216 | passedArguments = passedArguments || queryArguments; |
||
1217 | context = element || context; |
||
1218 | if(typeof query == 'string' && object !== undefined) { |
||
1219 | query = query.split(/[\. ]/); |
||
1220 | maxDepth = query.length - 1; |
||
1221 | $.each(query, function(depth, value) { |
||
1222 | var camelCaseValue = (depth != maxDepth) |
||
1223 | ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1) |
||
1224 | : query |
||
1225 | ; |
||
1226 | if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) { |
||
1227 | object = object[camelCaseValue]; |
||
1228 | } |
||
1229 | else if( object[camelCaseValue] !== undefined ) { |
||
1230 | found = object[camelCaseValue]; |
||
1231 | return false; |
||
1232 | } |
||
1233 | else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) { |
||
1234 | object = object[value]; |
||
1235 | } |
||
1236 | else if( object[value] !== undefined ) { |
||
1237 | found = object[value]; |
||
1238 | return false; |
||
1239 | } |
||
1240 | else { |
||
1241 | return false; |
||
1242 | } |
||
1243 | }); |
||
1244 | } |
||
1245 | if ( $.isFunction( found ) ) { |
||
1246 | response = found.apply(context, passedArguments); |
||
1247 | } |
||
1248 | else if(found !== undefined) { |
||
1249 | response = found; |
||
1250 | } |
||
1251 | if($.isArray(returnedValue)) { |
||
1252 | returnedValue.push(response); |
||
1253 | } |
||
1254 | else if(returnedValue !== undefined) { |
||
1255 | returnedValue = [returnedValue, response]; |
||
1256 | } |
||
1257 | else if(response !== undefined) { |
||
1258 | returnedValue = response; |
||
1259 | } |
||
1260 | return found; |
||
1261 | } |
||
1262 | }; |
||
1263 | |||
1264 | if(methodInvoked) { |
||
1265 | if(instance === undefined) { |
||
1266 | module.initialize(); |
||
1267 | } |
||
1268 | module.invoke(query); |
||
1269 | } |
||
1270 | else { |
||
1271 | if(instance !== undefined) { |
||
1272 | instance.invoke('destroy'); |
||
1273 | } |
||
1274 | module.initialize(); |
||
1275 | } |
||
1276 | }) |
||
1277 | ; |
||
1278 | |||
1279 | return (returnedValue !== undefined) |
||
1280 | ? returnedValue |
||
1281 | : this |
||
1282 | ; |
||
1283 | }; |
||
1284 | |||
1285 | $.fn.popup.settings = { |
||
1286 | |||
1287 | name : 'Popup', |
||
1288 | |||
1289 | // module settings |
||
1290 | silent : false, |
||
1291 | debug : false, |
||
1292 | verbose : false, |
||
1293 | performance : true, |
||
1294 | namespace : 'popup', |
||
1295 | |||
1296 | // whether it should use dom mutation observers |
||
1297 | observeChanges : true, |
||
1298 | |||
1299 | // callback only when element added to dom |
||
1300 | onCreate : function(){}, |
||
1301 | |||
1302 | // callback before element removed from dom |
||
1303 | onRemove : function(){}, |
||
1304 | |||
1305 | // callback before show animation |
||
1306 | onShow : function(){}, |
||
1307 | |||
1308 | // callback after show animation |
||
1309 | onVisible : function(){}, |
||
1310 | |||
1311 | // callback before hide animation |
||
1312 | onHide : function(){}, |
||
1313 | |||
1314 | // callback when popup cannot be positioned in visible screen |
||
1315 | onUnplaceable : function(){}, |
||
1316 | |||
1317 | // callback after hide animation |
||
1318 | onHidden : function(){}, |
||
1319 | |||
1320 | // when to show popup |
||
1321 | on : 'hover', |
||
1322 | |||
1323 | // element to use to determine if popup is out of boundary |
||
1324 | boundary : window, |
||
1325 | |||
1326 | // whether to add touchstart events when using hover |
||
1327 | addTouchEvents : true, |
||
1328 | |||
1329 | // default position relative to element |
||
1330 | position : 'top left', |
||
1331 | |||
1332 | // name of variation to use |
||
1333 | variation : '', |
||
1334 | |||
1335 | // whether popup should be moved to context |
||
1336 | movePopup : true, |
||
1337 | |||
1338 | // element which popup should be relative to |
||
1339 | target : false, |
||
1340 | |||
1341 | // jq selector or element that should be used as popup |
||
1342 | popup : false, |
||
1343 | |||
1344 | // popup should remain inline next to activator |
||
1345 | inline : false, |
||
1346 | |||
1347 | // popup should be removed from page on hide |
||
1348 | preserve : false, |
||
1349 | |||
1350 | // popup should not close when being hovered on |
||
1351 | hoverable : false, |
||
1352 | |||
1353 | // explicitly set content |
||
1354 | content : false, |
||
1355 | |||
1356 | // explicitly set html |
||
1357 | html : false, |
||
1358 | |||
1359 | // explicitly set title |
||
1360 | title : false, |
||
1361 | |||
1362 | // whether automatically close on clickaway when on click |
||
1363 | closable : true, |
||
1364 | |||
1365 | // automatically hide on scroll |
||
1366 | hideOnScroll : 'auto', |
||
1367 | |||
1368 | // hide other popups on show |
||
1369 | exclusive : false, |
||
1370 | |||
1371 | // context to attach popups |
||
1372 | context : 'body', |
||
1373 | |||
1374 | // context for binding scroll events |
||
1375 | scrollContext : window, |
||
1376 | |||
1377 | // position to prefer when calculating new position |
||
1378 | prefer : 'opposite', |
||
1379 | |||
1380 | // specify position to appear even if it doesn't fit |
||
1381 | lastResort : false, |
||
1382 | |||
1383 | // delay used to prevent accidental refiring of animations due to user error |
||
1384 | delay : { |
||
1385 | show : 50, |
||
1386 | hide : 70 |
||
1387 | }, |
||
1388 | |||
1389 | // whether fluid variation should assign width explicitly |
||
1390 | setFluidWidth : true, |
||
1391 | |||
1392 | // transition settings |
||
1393 | duration : 200, |
||
1394 | transition : 'scale', |
||
1395 | |||
1396 | // distance away from activating element in px |
||
1397 | distanceAway : 0, |
||
1398 | |||
1399 | // number of pixels an element is allowed to be "offstage" for a position to be chosen (allows for rounding) |
||
1400 | jitter : 2, |
||
1401 | |||
1402 | // offset on aligning axis from calculated position |
||
1403 | offset : 0, |
||
1404 | |||
1405 | // maximum times to look for a position before failing (9 positions total) |
||
1406 | maxSearchDepth : 15, |
||
1407 | |||
1408 | error: { |
||
1409 | invalidPosition : 'The position you specified is not a valid position', |
||
1410 | cannotPlace : 'Popup does not fit within the boundaries of the viewport', |
||
1411 | method : 'The method you called is not defined.', |
||
1412 | noTransition : 'This module requires ui transitions <https://github.com/Semantic-Org/UI-Transition>', |
||
1413 | notFound : 'The target or popup you specified does not exist on the page' |
||
1414 | }, |
||
1415 | |||
1416 | metadata: { |
||
1417 | activator : 'activator', |
||
1418 | content : 'content', |
||
1419 | html : 'html', |
||
1420 | offset : 'offset', |
||
1421 | position : 'position', |
||
1422 | title : 'title', |
||
1423 | variation : 'variation' |
||
1424 | }, |
||
1425 | |||
1426 | className : { |
||
1427 | active : 'active', |
||
1428 | animating : 'animating', |
||
1429 | dropdown : 'dropdown', |
||
1430 | fluid : 'fluid', |
||
1431 | loading : 'loading', |
||
1432 | popup : 'ui popup', |
||
1433 | position : 'top left center bottom right', |
||
1434 | visible : 'visible', |
||
1435 | popupVisible : 'visible' |
||
1436 | }, |
||
1437 | |||
1438 | selector : { |
||
1439 | popup : '.ui.popup' |
||
1440 | }, |
||
1441 | |||
1442 | templates: { |
||
1443 | escape: function(string) { |
||
1444 | var |
||
1445 | badChars = /[&<>"'`]/g, |
||
1446 | shouldEscape = /[&<>"'`]/, |
||
1447 | escape = { |
||
1448 | "&": "&", |
||
1449 | "<": "<", |
||
1450 | ">": ">", |
||
1451 | '"': """, |
||
1452 | "'": "'", |
||
1453 | "`": "`" |
||
1454 | }, |
||
1455 | escapedChar = function(chr) { |
||
1456 | return escape[chr]; |
||
1457 | } |
||
1458 | ; |
||
1459 | if(shouldEscape.test(string)) { |
||
1460 | return string.replace(badChars, escapedChar); |
||
1461 | } |
||
1462 | return string; |
||
1463 | }, |
||
1464 | popup: function(text) { |
||
1465 | var |
||
1466 | html = '', |
||
1467 | escape = $.fn.popup.settings.templates.escape |
||
1468 | ; |
||
1469 | if(typeof text !== undefined) { |
||
1470 | if(typeof text.title !== undefined && text.title) { |
||
1471 | text.title = escape(text.title); |
||
1472 | html += '<div class="header">' + text.title + '</div>'; |
||
1473 | } |
||
1474 | if(typeof text.content !== undefined && text.content) { |
||
1475 | text.content = escape(text.content); |
||
1476 | html += '<div class="content">' + text.content + '</div>'; |
||
1477 | } |
||
1478 | } |
||
1479 | return html; |
||
1480 | } |
||
1481 | } |
||
1482 | |||
1483 | }; |
||
1484 | |||
1485 | |||
1486 | })( jQuery, window, document ); |
||
1487 |