PluginProbe
Comments Extra Fields For Post,Pages and CPT / 4.4
Comments Extra Fields For Post,Pages and CPT v4.4
trunk 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 2.2 2.3 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 5.0 5.1 5.2
wp-comment-fields / js / wpcomment-modal.js

wpcomment-modal.js in Comments Extra Fields For Post,Pages and CPT 4.4, at js/wpcomment-modal.js

794 lines 19.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*
2 * Remodal - v1.1.1
3 * Responsive, lightweight, fast, synchronized with CSS animations, fully customizable modal window plugin with declarative configuration and hash tracking.
4 * http://vodkabears.github.io/remodal/
5 *
6 * Made by Ilya Makarov
7 * Under MIT License
8 */
9
10 !(function(root, factory) {
11 if (typeof define === 'function' && define.amd) {
12 define(['jquery'], function($) {
13 return factory(root, $);
14 });
15 }
16 else if (typeof exports === 'object') {
17 factory(root, require('jquery'));
18 }
19 else {
20 factory(root, root.jQuery || root.Zepto);
21 }
22 })(this, function(global, $) {
23
24 'use strict';
25
26 /**
27 * Name of the plugin
28 * @private
29 * @const
30 * @type {String}
31 */
32 var PLUGIN_NAME = 'wpcomment_modal';
33
34 /**
35 * Namespace for CSS and events
36 * @private
37 * @const
38 * @type {String}
39 */
40 var NAMESPACE = global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME;
41
42 /**
43 * Animationstart event with vendor prefixes
44 * @private
45 * @const
46 * @type {String}
47 */
48 var ANIMATIONSTART_EVENTS = $.map(
49 ['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'],
50
51 function(eventName) {
52 return eventName + '.' + NAMESPACE;
53 }
54
55 ).join(' ');
56
57 /**
58 * Animationend event with vendor prefixes
59 * @private
60 * @const
61 * @type {String}
62 */
63 var ANIMATIONEND_EVENTS = $.map(
64 ['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'],
65
66 function(eventName) {
67 return eventName + '.' + NAMESPACE;
68 }
69
70 ).join(' ');
71
72 /**
73 * Default settings
74 * @private
75 * @const
76 * @type {Object}
77 */
78 var DEFAULTS = $.extend({
79 hashTracking: true,
80 closeOnConfirm: true,
81 closeOnCancel: true,
82 closeOnEscape: true,
83 closeOnOutsideClick: true,
84 modifier: '',
85 appendTo: ''
86 }, global.REMODAL_GLOBALS && global.REMODAL_GLOBALS.DEFAULTS);
87
88 /**
89 * States of the Remodal
90 * @private
91 * @const
92 * @enum {String}
93 */
94 var STATES = {
95 CLOSING: 'closing',
96 CLOSED: 'closed',
97 OPENING: 'opening',
98 OPENED: 'opened'
99 };
100
101 /**
102 * Reasons of the state change.
103 * @private
104 * @const
105 * @enum {String}
106 */
107 var STATE_CHANGE_REASONS = {
108 CONFIRMATION: 'confirmation',
109 CANCELLATION: 'cancellation'
110 };
111
112 /**
113 * Is animation supported?
114 * @private
115 * @const
116 * @type {Boolean}
117 */
118 var IS_ANIMATION = (function() {
119 var style = document.createElement('div').style;
120
121 return style.animationName !== undefined ||
122 style.WebkitAnimationName !== undefined ||
123 style.MozAnimationName !== undefined ||
124 style.msAnimationName !== undefined ||
125 style.OAnimationName !== undefined;
126 })();
127
128 /**
129 * Is iOS?
130 * @private
131 * @const
132 * @type {Boolean}
133 */
134 var IS_IOS = /iPad|iPhone|iPod/.test(navigator.platform);
135
136 /**
137 * Current modal
138 * @private
139 * @type {Remodal}
140 */
141 var current;
142
143 /**
144 * Scrollbar position
145 * @private
146 * @type {Number}
147 */
148 var scrollTop;
149
150 /**
151 * Returns an animation duration
152 * @private
153 * @param {jQuery} $elem
154 * @returns {Number}
155 */
156 function getAnimationDuration($elem) {
157 if (
158 IS_ANIMATION &&
159 $elem.css('animation-name') === 'none' &&
160 $elem.css('-webkit-animation-name') === 'none' &&
161 $elem.css('-moz-animation-name') === 'none' &&
162 $elem.css('-o-animation-name') === 'none' &&
163 $elem.css('-ms-animation-name') === 'none'
164 ) {
165 return 0;
166 }
167
168 var duration = $elem.css('animation-duration') ||
169 $elem.css('-webkit-animation-duration') ||
170 $elem.css('-moz-animation-duration') ||
171 $elem.css('-o-animation-duration') ||
172 $elem.css('-ms-animation-duration') ||
173 '0s';
174
175 var delay = $elem.css('animation-delay') ||
176 $elem.css('-webkit-animation-delay') ||
177 $elem.css('-moz-animation-delay') ||
178 $elem.css('-o-animation-delay') ||
179 $elem.css('-ms-animation-delay') ||
180 '0s';
181
182 var iterationCount = $elem.css('animation-iteration-count') ||
183 $elem.css('-webkit-animation-iteration-count') ||
184 $elem.css('-moz-animation-iteration-count') ||
185 $elem.css('-o-animation-iteration-count') ||
186 $elem.css('-ms-animation-iteration-count') ||
187 '1';
188
189 var max;
190 var len;
191 var num;
192 var i;
193
194 duration = duration.split(', ');
195 delay = delay.split(', ');
196 iterationCount = iterationCount.split(', ');
197
198 // The 'duration' size is the same as the 'delay' size
199 for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) {
200 num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]);
201
202 if (num > max) {
203 max = num;
204 }
205 }
206
207 return max;
208 }
209
210 /**
211 * Returns a scrollbar width
212 * @private
213 * @returns {Number}
214 */
215 function getScrollbarWidth() {
216 if ($(document).height() <= $(window).height()) {
217 return 0;
218 }
219
220 var outer = document.createElement('div');
221 var inner = document.createElement('div');
222 var widthNoScroll;
223 var widthWithScroll;
224
225 outer.style.visibility = 'hidden';
226 outer.style.width = '100px';
227 document.body.appendChild(outer);
228
229 widthNoScroll = outer.offsetWidth;
230
231 // Force scrollbars
232 outer.style.overflow = 'scroll';
233
234 // Add inner div
235 inner.style.width = '100%';
236 outer.appendChild(inner);
237
238 widthWithScroll = inner.offsetWidth;
239
240 // Remove divs
241 outer.parentNode.removeChild(outer);
242
243 return widthNoScroll - widthWithScroll;
244 }
245
246 /**
247 * Locks the screen
248 * @private
249 */
250 function lockScreen() {
251 if (IS_IOS) {
252 return;
253 }
254
255 var $html = $('html');
256 var lockedClass = namespacify('is-locked');
257 var paddingRight;
258 var $body;
259
260 if (!$html.hasClass(lockedClass)) {
261 $body = $(document.body);
262
263 // Zepto does not support '-=', '+=' in the `css` method
264 paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth();
265
266 $body.css('padding-right', paddingRight + 'px');
267 $html.addClass(lockedClass);
268 }
269 }
270
271 /**
272 * Unlocks the screen
273 * @private
274 */
275 function unlockScreen() {
276 if (IS_IOS) {
277 return;
278 }
279
280 var $html = $('html');
281 var lockedClass = namespacify('is-locked');
282 var paddingRight;
283 var $body;
284
285 if ($html.hasClass(lockedClass)) {
286 $body = $(document.body);
287
288 // Zepto does not support '-=', '+=' in the `css` method
289 paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth();
290
291 $body.css('padding-right', paddingRight + 'px');
292 $html.removeClass(lockedClass);
293 }
294 }
295
296 /**
297 * Sets a state for an instance
298 * @private
299 * @param {Remodal} instance
300 * @param {STATES} state
301 * @param {Boolean} isSilent If true, Remodal does not trigger events
302 * @param {String} Reason of a state change.
303 */
304 function setState(instance, state, isSilent, reason) {
305
306 var newState = namespacify('is', state);
307 var allStates = [namespacify('is', STATES.CLOSING),
308 namespacify('is', STATES.OPENING),
309 namespacify('is', STATES.CLOSED),
310 namespacify('is', STATES.OPENED)
311 ].join(' ');
312
313 instance.$bg
314 .removeClass(allStates)
315 .addClass(newState);
316
317 instance.$overlay
318 .removeClass(allStates)
319 .addClass(newState);
320
321 instance.$wrapper
322 .removeClass(allStates)
323 .addClass(newState);
324
325 instance.$modal
326 .removeClass(allStates)
327 .addClass(newState);
328
329 instance.state = state;
330 !isSilent && instance.$modal.trigger({
331 type: state,
332 reason: reason
333 }, [{ reason: reason }]);
334 }
335
336 /**
337 * Synchronizes with the animation
338 * @param {Function} doBeforeAnimation
339 * @param {Function} doAfterAnimation
340 * @param {Remodal} instance
341 */
342 function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) {
343 var runningAnimationsCount = 0;
344
345 var handleAnimationStart = function(e) {
346 if (e.target !== this) {
347 return;
348 }
349
350 runningAnimationsCount++;
351 };
352
353 var handleAnimationEnd = function(e) {
354 if (e.target !== this) {
355 return;
356 }
357
358 if (--runningAnimationsCount === 0) {
359
360 // Remove event listeners
361 $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
362 instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
363 });
364
365 doAfterAnimation();
366 }
367 };
368
369 $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
370 instance[elemName]
371 .on(ANIMATIONSTART_EVENTS, handleAnimationStart)
372 .on(ANIMATIONEND_EVENTS, handleAnimationEnd);
373 });
374
375 doBeforeAnimation();
376
377 // If the animation is not supported by a browser or its duration is 0
378 if (
379 getAnimationDuration(instance.$bg) === 0 &&
380 getAnimationDuration(instance.$overlay) === 0 &&
381 getAnimationDuration(instance.$wrapper) === 0 &&
382 getAnimationDuration(instance.$modal) === 0
383 ) {
384
385 // Remove event listeners
386 $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
387 instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
388 });
389
390 doAfterAnimation();
391 }
392 }
393
394 /**
395 * Closes immediately
396 * @private
397 * @param {Remodal} instance
398 */
399 function halt(instance) {
400 if (instance.state === STATES.CLOSED) {
401 return;
402 }
403
404 $.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
405 instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
406 });
407
408 instance.$bg.removeClass(instance.settings.modifier);
409 instance.$overlay.removeClass(instance.settings.modifier).hide();
410 instance.$wrapper.hide();
411 unlockScreen();
412 setState(instance, STATES.CLOSED, true);
413 }
414
415 /**
416 * Parses a string with options
417 * @private
418 * @param str
419 * @returns {Object}
420 */
421 function parseOptions(str) {
422 var obj = {};
423 var arr;
424 var len;
425 var val;
426 var i;
427
428 // Remove spaces before and after delimiters
429 str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',');
430
431 // Parse a string
432 arr = str.split(',');
433 for (i = 0, len = arr.length; i < len; i++) {
434 arr[i] = arr[i].split(':');
435 val = arr[i][1];
436
437 // Convert a string value if it is like a boolean
438 if (typeof val === 'string' || val instanceof String) {
439 val = val === 'true' || (val === 'false' ? false : val);
440 }
441
442 // Convert a string value if it is like a number
443 if (typeof val === 'string' || val instanceof String) {
444 val = !isNaN(val) ? +val : val;
445 }
446
447 obj[arr[i][0]] = val;
448 }
449
450 return obj;
451 }
452
453 /**
454 * Generates a string separated by dashes and prefixed with NAMESPACE
455 * @private
456 * @param {...String}
457 * @returns {String}
458 */
459 function namespacify() {
460 var result = NAMESPACE;
461
462 for (var i = 0; i < arguments.length; ++i) {
463 result += '-' + arguments[i];
464 }
465
466 return result;
467 }
468
469 /**
470 * Handles the hashchange event
471 * @private
472 * @listens hashchange
473 */
474 function handleHashChangeEvent() {
475 var id = location.hash.replace('#', '');
476 var instance;
477 var $elem;
478
479 if (!id) {
480
481 // Check if we have currently opened modal and animation was completed
482 if (current && current.state === STATES.OPENED && current.settings.hashTracking) {
483 current.close();
484 }
485 }
486 else {
487
488 // Catch syntax error if your hash is bad
489 try {
490 $elem = $(
491 '[data-' + PLUGIN_NAME + '-id="' + id + '"]'
492 );
493 }
494 catch (err) {}
495
496 if ($elem && $elem.length) {
497 instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
498
499 if (instance && instance.settings.hashTracking) {
500 instance.open();
501 }
502 }
503
504 }
505 }
506
507 /**
508 * Remodal constructor
509 * @constructor
510 * @param {jQuery} $modal
511 * @param {Object} options
512 */
513 function Remodal($modal, options) {
514 // var $body = $(document.body);
515 var $body = $modal.parent();
516 var $appendTo = $body;
517 var remodal = this;
518
519 remodal.settings = $.extend({}, DEFAULTS, options);
520 remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1;
521 remodal.state = STATES.CLOSED;
522
523 remodal.$overlay = $('.' + namespacify('overlay'));
524
525 if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) {
526 $appendTo = $(remodal.settings.appendTo);
527 }
528
529 if (!remodal.$overlay.length) {
530 remodal.$overlay = $('<div>').addClass(namespacify('overlay') + ' ' + namespacify('is', STATES.CLOSED)).hide();
531 $appendTo.append(remodal.$overlay);
532 }
533
534 remodal.$bg = $('.' + namespacify('bg')).addClass(namespacify('is', STATES.CLOSED));
535
536 remodal.$modal = $modal
537 .addClass(
538 NAMESPACE + ' ' +
539 namespacify('is-initialized') + ' ' +
540 remodal.settings.modifier + ' ' +
541 namespacify('is', STATES.CLOSED))
542 .attr('tabindex', '-1');
543
544 remodal.$wrapper = $('<div>')
545 .addClass(
546 namespacify('wrapper') + ' ' +
547 remodal.settings.modifier + ' ' +
548 namespacify('is', STATES.CLOSED))
549 .hide()
550 .append(remodal.$modal);
551 $appendTo.append(remodal.$wrapper);
552
553 // Add the event listener for the close button
554 remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) {
555 e.preventDefault();
556
557 remodal.close();
558 });
559
560 // Add the event listener for the cancel button
561 remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) {
562 e.preventDefault();
563
564 remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION);
565
566 if (remodal.settings.closeOnCancel) {
567 remodal.close(STATE_CHANGE_REASONS.CANCELLATION);
568 }
569 });
570
571 // Add the event listener for the confirm button
572 remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) {
573 e.preventDefault();
574
575 remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION);
576
577 if (remodal.settings.closeOnConfirm) {
578 remodal.close(STATE_CHANGE_REASONS.CONFIRMATION);
579 }
580 });
581
582 // Add the event listener for the overlay
583 remodal.$wrapper.on('click.' + NAMESPACE, function(e) {
584 var $target = $(e.target);
585
586 if (!$target.hasClass(namespacify('wrapper'))) {
587 return;
588 }
589
590 if (remodal.settings.closeOnOutsideClick) {
591 remodal.close();
592 }
593 });
594 }
595
596 /**
597 * Opens a modal window
598 * @public
599 */
600 Remodal.prototype.open = function() {
601 var remodal = this;
602 var id;
603
604 // Check if the animation was completed
605 if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) {
606 return;
607 }
608
609 id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id');
610
611 if (id && remodal.settings.hashTracking) {
612 scrollTop = $(window).scrollTop();
613 location.hash = id;
614 }
615
616 if (current && current !== remodal) {
617 halt(current);
618 }
619
620 current = remodal;
621 lockScreen();
622 remodal.$bg.addClass(remodal.settings.modifier);
623 remodal.$overlay.addClass(remodal.settings.modifier).show();
624 remodal.$wrapper.show().scrollTop(0);
625 remodal.$modal.focus();
626
627 syncWithAnimation(
628 function() {
629 setState(remodal, STATES.OPENING);
630 },
631
632 function() {
633 setState(remodal, STATES.OPENED);
634 },
635
636 remodal);
637 };
638
639 /**
640 * Closes a modal window
641 * @public
642 * @param {String} reason
643 */
644 Remodal.prototype.close = function(reason) {
645 var remodal = this;
646
647 // Check if the animation was completed
648 if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) {
649 return;
650 }
651
652 if (
653 remodal.settings.hashTracking &&
654 remodal.$modal.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
655 ) {
656 location.hash = '';
657 $(window).scrollTop(scrollTop);
658 }
659
660 syncWithAnimation(
661 function() {
662 setState(remodal, STATES.CLOSING, false, reason);
663 },
664
665 function() {
666 remodal.$bg.removeClass(remodal.settings.modifier);
667 remodal.$overlay.removeClass(remodal.settings.modifier).hide();
668 remodal.$wrapper.hide();
669 unlockScreen();
670
671 setState(remodal, STATES.CLOSED, false, reason);
672 },
673
674 remodal);
675 };
676
677 /**
678 * Returns a current state of a modal
679 * @public
680 * @returns {STATES}
681 */
682 Remodal.prototype.getState = function() {
683 return this.state;
684 };
685
686 /**
687 * Destroys a modal
688 * @public
689 */
690 Remodal.prototype.destroy = function() {
691 var lookup = $[PLUGIN_NAME].lookup;
692 var instanceCount;
693
694 halt(this);
695 this.$wrapper.remove();
696
697 delete lookup[this.index];
698 instanceCount = $.grep(lookup, function(instance) {
699 return !!instance;
700 }).length;
701
702 if (instanceCount === 0) {
703 this.$overlay.remove();
704 this.$bg.removeClass(
705 namespacify('is', STATES.CLOSING) + ' ' +
706 namespacify('is', STATES.OPENING) + ' ' +
707 namespacify('is', STATES.CLOSED) + ' ' +
708 namespacify('is', STATES.OPENED));
709 }
710 };
711
712 /**
713 * Special plugin object for instances
714 * @public
715 * @type {Object}
716 */
717 $[PLUGIN_NAME] = {
718 lookup: []
719 };
720
721 /**
722 * Plugin constructor
723 * @constructor
724 * @param {Object} options
725 * @returns {JQuery}
726 */
727 $.fn[PLUGIN_NAME] = function(opts) {
728 var instance;
729 var $elem;
730
731 this.each(function(index, elem) {
732 $elem = $(elem);
733
734 if ($elem.data(PLUGIN_NAME) == null) {
735 instance = new Remodal($elem, opts);
736 $elem.data(PLUGIN_NAME, instance.index);
737
738 if (
739 instance.settings.hashTracking &&
740 $elem.attr('data-' + PLUGIN_NAME + '-id') === location.hash.substr(1)
741 ) {
742 instance.open();
743 }
744 }
745 else {
746 instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
747 }
748 });
749
750 return instance;
751 };
752
753 $(document).ready(function() {
754
755 // data-remodal-target opens a modal window with the special Id
756 $(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) {
757 e.preventDefault();
758
759 var elem = e.currentTarget;
760 var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target');
761 var $target = $('[data-' + PLUGIN_NAME + '-id="' + id + '"]');
762
763 $[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open();
764 });
765
766 // Auto initialization of modal windows
767 // They should have the 'remodal' class attribute
768 // Also you can write the `data-remodal-options` attribute to pass params into the modal
769 $(document).find('.' + NAMESPACE).each(function(i, container) {
770 var $container = $(container);
771 var options = $container.data(PLUGIN_NAME + '-options');
772
773 if (!options) {
774 options = {};
775 }
776 else if (typeof options === 'string' || options instanceof String) {
777 options = parseOptions(options);
778 }
779
780 $container[PLUGIN_NAME](options);
781 });
782
783 // Handles the keydown event
784 $(document).on('keydown.' + NAMESPACE, function(e) {
785 if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) {
786 current.close();
787 }
788 });
789
790 // Handles the hashchange event
791 $(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent);
792 });
793 });
794