Skip to content
Snippets Groups Projects
redactor-plugins.js 73.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • if (!RedactorPlugins) var RedactorPlugins = {};
    
    
        $.Redactor.prototype.definedlinks = function()
        {
            return {
                init: function()
                {
                    if (!this.opts.definedLinks) return;
    
                    this.modal.addCallback('link', $.proxy(this.definedlinks.load, this));
    
                },
                load: function()
                {
                    var $select = $('<select id="redactor-defined-links" />');
                    $('#redactor-modal-link-insert').prepend($select);
    
                    this.definedlinks.storage = {};
    
                    $.getJSON(this.opts.definedLinks, $.proxy(function(data)
                    {
                        $.each(data, $.proxy(function(key, val)
                        {
                            this.definedlinks.storage[key] = val;
                            $select.append($('<option>').val(key).html(val.name));
    
                        }, this));
    
                        $select.on('change', $.proxy(this.definedlinks.select, this));
    
                    }, this));
    
                },
                select: function(e)
                {
                    var key = $(e.target).val();
                    var name = '', url = '';
                    if (key !== 0)
                    {
                        name = this.definedlinks.storage[key].name;
                        url = this.definedlinks.storage[key].url;
                    }
    
                    $('#redactor-link-url').val(url);
    
                    var $el = $('#redactor-link-url-text');
                    if ($el.val() === '') $el.val(name);
                }
            };
        };
    })(jQuery);
    
    
    RedactorPlugins.fontcolor = function()
    {
    	return {
    		init: function()
    		{
    			var colors = [
    				'#ffffff', '#000000', '#eeece1', '#1f497d', '#4f81bd', '#c0504d', '#9bbb59', '#8064a2', '#4bacc6', '#f79646', '#ffff00',
    				'#f2f2f2', '#7f7f7f', '#ddd9c3', '#c6d9f0', '#dbe5f1', '#f2dcdb', '#ebf1dd', '#e5e0ec', '#dbeef3', '#fdeada', '#fff2ca',
    				'#d8d8d8', '#595959', '#c4bd97', '#8db3e2', '#b8cce4', '#e5b9b7', '#d7e3bc', '#ccc1d9', '#b7dde8', '#fbd5b5', '#ffe694',
    				'#bfbfbf', '#3f3f3f', '#938953', '#548dd4', '#95b3d7', '#d99694', '#c3d69b', '#b2a2c7', '#b7dde8', '#fac08f', '#f2c314',
    				'#a5a5a5', '#262626', '#494429', '#17365d', '#366092', '#953734', '#76923c', '#5f497a', '#92cddc', '#e36c09', '#c09100',
    				'#7f7f7f', '#0c0c0c', '#1d1b10', '#0f243e', '#244061', '#632423', '#4f6128', '#3f3151', '#31859b',  '#974806', '#7f6000'
    			];
    
    			var buttons = ['fontcolor', 'backcolor'];
    
    			for (var i = 0; i < 2; i++)
    			{
    				var name = buttons[i];
    
    				var button = this.button.addBefore('deleted', name, this.lang.get(name));
    				var $dropdown = this.button.addDropdown(button);
    
    				$dropdown.width(242);
    				this.fontcolor.buildPicker($dropdown, name, colors);
    
    			}
    		},
    		buildPicker: function($dropdown, name, colors)
    		{
    			var rule = (name == 'backcolor') ? 'background-color' : 'color';
    
    			var len = colors.length;
    			var self = this;
    			var func = function(e)
    			{
    				e.preventDefault();
    
    				self.fontcolor.set(rule, $this.attr('rel'));
    
                $dropdown.on('click', 'a.redactor.color-swatch', func);
    
    
                var template = $('<a class="redactor color-swatch" href="#"></a>');
    
    
    			for (var z = 0; z < len; z++)
    			{
    				var color = colors[z];
    
    				var $swatch = template.clone().attr('rel', color);
    
    				$swatch.css('background-color', color);
    				$dropdown.append($swatch);
    			}
    
    
    			var $elNone = $('<a href="#" style="redactor uncolor"></a>').html(this.lang.get('none'));
    
    			$elNone.on('click', $.proxy(function(e)
    			{
    				e.preventDefault();
    				this.fontcolor.remove(rule);
    
    			}, this));
    
    			$dropdown.append($elNone);
    		},
    		set: function(rule, type)
    		{
    			this.inline.format('span', 'style', rule + ': ' + type + ';');
    		},
    		remove: function(rule)
    		{
    			this.inline.removeStyleRule(rule);
    		}
    	};
    };
    
    RedactorPlugins.fontfamily = function()
    {
    	return {
    		init: function ()
    		{
    			var fonts = [ 'Arial', 'Helvetica', 'Georgia', 'Times New Roman', 'Monospace' ];
    			var that = this;
    			var dropdown = {};
    
    			$.each(fonts, function(i, s)
    			{
    				dropdown['s' + i] = { title: '<span style="font-family:' + s.toLowerCase() + ';">' +
                        s + '</span>', func: function() { that.fontfamily.set(s); }};
    			});
    
    			dropdown.remove = { title: __('Remove Font Family'), func: that.fontfamily.reset };
    
    			var button = this.button.addBefore('bold', 'fontfamily', __('Change Font Family'));
    			this.button.addDropdown(button, dropdown);
    
    		},
    		set: function (value)
    		{
    			this.inline.format('span', 'style', 'font-family:' + value + ';');
    		},
    		reset: function()
    		{
    			this.inline.removeStyleRule('font-family');
    		}
    	};
    };
    
    RedactorPlugins.fullscreen = function()
    {
    	return {
    		init: function()
    		{
    			this.fullscreen.isOpen = false;
    
    			var button = this.button.add('fullscreen', 'Fullscreen');
    			this.button.addCallback(button, this.fullscreen.toggle);
    
    			if (this.opts.fullscreen) this.fullscreen.toggle();
    		},
    		enable: function()
    		{
    			this.button.changeIcon('fullscreen', 'normalscreen');
    			this.button.setActive('fullscreen');
    			this.fullscreen.isOpen = true;
    
    			if (this.opts.toolbarExternal)
    			{
    				this.fullscreen.toolcss = {};
    				this.fullscreen.boxcss = {};
    				this.fullscreen.toolcss.width = this.$toolbar.css('width');
    				this.fullscreen.toolcss.top = this.$toolbar.css('top');
    				this.fullscreen.toolcss.position = this.$toolbar.css('position');
    				this.fullscreen.boxcss.top = this.$box.css('top');
    			}
    
    			this.fullscreen.height = this.$editor.height();
    
    			if (this.opts.maxHeight) this.$editor.css('max-height', '');
    			if (this.opts.minHeight) this.$editor.css('min-height', '');
    
    			if (!this.$fullscreenPlaceholder) this.$fullscreenPlaceholder = $('<div/>');
    			this.$fullscreenPlaceholder.insertAfter(this.$box);
    
    			this.$box.appendTo(document.body);
    
    			this.$box.addClass('redactor-box-fullscreen');
    			$('body, html').css('overflow', 'hidden');
    
    			this.fullscreen.resize();
    			$(window).on('resize.redactor.fullscreen', $.proxy(this.fullscreen.resize, this));
    			$(document).scrollTop(0, 0);
    
    			this.$editor.focus();
    			this.observe.load();
    		},
    		disable: function()
    		{
    			this.button.removeIcon('fullscreen', 'normalscreen');
    			this.button.setInactive('fullscreen');
    			this.fullscreen.isOpen = false;
    
    			$(window).off('resize.redactor.fullscreen');
    			$('body, html').css('overflow', '');
    
    			this.$box.insertBefore(this.$fullscreenPlaceholder);
    			this.$fullscreenPlaceholder.remove();
    
    			this.$box.removeClass('redactor-box-fullscreen').css({ width: 'auto', height: 'auto' });
    
    			this.code.sync();
    
    			if (this.opts.toolbarExternal)
    			{
    				this.$box.css('top', this.fullscreen.boxcss.top);
    				this.$toolbar.css({
    					'width': this.fullscreen.toolcss.width,
    					'top': this.fullscreen.toolcss.top,
    					'position': this.fullscreen.toolcss.position
    				});
    			}
    
    			if (this.opts.minHeight) this.$editor.css('minHeight', this.opts.minHeight);
    			if (this.opts.maxHeight) this.$editor.css('maxHeight', this.opts.maxHeight);
    
    			this.$editor.css('height', 'auto');
    			this.$editor.focus();
    			this.observe.load();
    		},
    		toggle: function()
    		{
    			if (this.fullscreen.isOpen)
    			{
    				this.fullscreen.disable();
    			}
    			else
    			{
    				this.fullscreen.enable();
    			}
    		},
    		resize: function()
    		{
    			if (!this.fullscreen.isOpen) return;
    
    			var toolbarHeight = this.$toolbar.height();
    
    			var height = $(window).height() - toolbarHeight;
    			this.$box.width($(window).width() - 2).height(height + toolbarHeight);
    
    			if (this.opts.toolbarExternal)
    			{
    				this.$toolbar.css({
    					'top': '0px',
    					'position': 'absolute',
    					'width': '100%'
    				});
    
    				this.$box.css('top', toolbarHeight + 'px');
    			}
    
    			this.$editor.height(height - 14);
    		}
    	};
    };
    
    
        $.Redactor.prototype.imagemanager = function()
        {
            return {
                init: function()
                {
                    if (!this.opts.imageManagerJson) return;
    
                    this.modal.addCallback('image', this.imagemanager.load);
                },
                load: function()
                {
                    var $modal = this.modal.getModal();
    
                    this.modal.createTabber($modal);
                    this.modal.addTab(1, 'Upload', 'active');
                    this.modal.addTab(2, 'Choose');
    
                    $('#redactor-modal-image-droparea').addClass('redactor-tab redactor-tab1');
    
                    var $box = $('<div id="redactor-image-manager-box" style="overflow: auto; height: 300px;" class="redactor-tab redactor-tab2">').hide();
                    $modal.append($box);
    
                    $.ajax({
                      dataType: "json",
                      cache: false,
                      url: this.opts.imageManagerJson,
                      success: $.proxy(function(data)
                        {
                            $.each(data, $.proxy(function(key, val)
                            {
                                // title
                                var thumbtitle = '';
                                if (typeof val.title !== 'undefined') thumbtitle = val.title;
    
                                var img = $('<img src="' + val.thumb + '" rel="' + val.image + '" title="' + thumbtitle + '" style="width: 100px; height: 75px; cursor: pointer;" />');
                                $('#redactor-image-manager-box').append(img);
                                $(img).click($.proxy(this.imagemanager.insert, this));
    
                            }, this));
    
    
                        }, this)
                    });
    
    
                },
                insert: function(e)
                {
                    this.image.insert('<img src="' + $(e.target).attr('rel') + '" alt="' + $(e.target).attr('title') + '">');
                }
            };
        };
    })(jQuery);
    
    (function($)
    
        $.Redactor.prototype.table = function()
        {
            return {
                getTemplate: function()
                {
                    return String()
                    + '<section id="redactor-modal-table-insert">'
                        + '<label>' + this.lang.get('rows') + '</label>'
                        + '<input type="text" size="5" value="2" id="redactor-table-rows" />'
                        + '<label>' + this.lang.get('columns') + '</label>'
                        + '<input type="text" size="5" value="3" id="redactor-table-columns" />'
                    + '</section>';
                },
                init: function()
                {
                    var dropdown = {};
    
                    dropdown.insert_table = {
                                        title: this.lang.get('insert_table'),
                                        func: this.table.show,
                                        observe: {
                                            element: 'table',
    
                                            'in': {
    
    358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.insert_row_above = {
                                        title: this.lang.get('insert_row_above'),
                                        func: this.table.addRowAbove,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.insert_row_below = {
                                        title: this.lang.get('insert_row_below'),
                                        func: this.table.addRowBelow,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.insert_row_below = {
                                        title: this.lang.get('insert_row_below'),
                                        func: this.table.addRowBelow,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.insert_column_left = {
                                        title: this.lang.get('insert_column_left'),
                                        func: this.table.addColumnLeft,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.insert_column_right = {
                                        title: this.lang.get('insert_column_right'),
                                        func: this.table.addColumnRight,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.add_head = {
                                        title: this.lang.get('add_head'),
                                        func: this.table.addHead,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.delete_head = {
                                        title: this.lang.get('delete_head'),
                                        func: this.table.deleteHead,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.delete_column = {
                                        title: this.lang.get('delete_column'),
                                        func: this.table.deleteColumn,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.delete_row = {
                                        title: this.lang.get('delete_row'),
                                        func: this.table.deleteRow,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    dropdown.delete_row = {
                                        title: this.lang.get('delete_table'),
                                        func: this.table.deleteTable,
                                        observe: {
                                            element: 'table',
                                            out: {
                                                attr: {
                                                    'class': 'redactor-dropdown-link-inactive',
                                                    'aria-disabled': true,
                                                }
                                            }
                                        }
                                    };
    
                    this.observe.addButton('td', 'table');
                    this.observe.addButton('th', 'table');
    
                    var button = this.button.addBefore('link', 'table', this.lang.get('table'));
                    this.button.addDropdown(button, dropdown);
                },
                show: function()
                {
                    this.modal.addTemplate('table', this.table.getTemplate());
    
                    this.modal.load('table', this.lang.get('insert_table'), 300);
                    this.modal.createCancelButton();
    
                    var button = this.modal.createActionButton(this.lang.get('insert'));
                    button.on('click', this.table.insert);
    
                    this.selection.save();
                    this.modal.show();
    
                    $('#redactor-table-rows').focus();
    
                },
                insert: function()
                {
                    this.placeholder.remove();
    
                    var rows = $('#redactor-table-rows').val(),
                        columns = $('#redactor-table-columns').val(),
                        $tableBox = $('<div>'),
                        tableId = Math.floor(Math.random() * 99999),
                        $table = $('<table id="table' + tableId + '"><tbody></tbody></table>'),
                        i, $row, z, $column;
    
                    for (i = 0; i < rows; i++)
                    {
                        $row = $('<tr>');
    
                        for (z = 0; z < columns; z++)
                        {
                            $column = $('<td>' + this.opts.invisibleSpace + '</td>');
    
                            // set the focus to the first td
                            if (i === 0 && z === 0)
                            {
                                $column.append(this.selection.getMarker());
                            }
    
                            $($row).append($column);
                        }
    
                        $table.append($row);
                    }
    
                    $tableBox.append($table);
                    var html = $tableBox.html();
    
                    this.modal.close();
                    this.selection.restore();
    
                    if (this.table.getTable()) return;
    
                    this.buffer.set();
    
                    var current = this.selection.getBlock() || this.selection.getCurrent();
                    if (current && current.tagName != 'BODY')
                    {
                        if (current.tagName == 'LI') current = $(current).closest('ul, ol');
                        $(current).after(html);
                    }
                    else
                    {
                        this.insert.html(html, false);
                    }
    
                    this.selection.restore();
    
                    var table = this.$editor.find('#table' + tableId);
    
                    var p = table.prev("p");
    
                    if (p.length > 0 && this.utils.isEmpty(p.html()))
                    {
                        p.remove();
                    }
    
                    if (!this.opts.linebreaks && (this.utils.browser('mozilla') || this.utils.browser('msie')))
                    {
                        var $next = table.next();
                        if ($next.length === 0)
                        {
                             table.after(this.opts.emptyHtml);
                        }
                    }
    
                    this.observe.buttons();
    
                    table.find('span.redactor-selection-marker').remove();
                    table.removeAttr('id');
    
                    this.code.sync();
                    this.core.setCallback('insertedTable', table);
                },
                getTable: function()
                {
                    var $table = $(this.selection.getParent()).closest('table');
    
                    if (!this.utils.isRedactorParent($table)) return false;
                    if ($table.size() === 0) return false;
    
                    return $table;
                },
                restoreAfterDelete: function($table)
                {
                    this.selection.restore();
                    $table.find('span.redactor-selection-marker').remove();
                    this.code.sync();
                },
                deleteTable: function()
                {
                    var $table = this.table.getTable();
                    if (!$table) return;
    
                    this.buffer.set();
    
    
                    var $next = $table.next();
                    if (!this.opts.linebreaks && $next.length !== 0)
                    {
                        this.caret.setStart($next);
                    }
                    else
                    {
                        this.caret.setAfter($table);
                    }
    
    
                    $table.remove();
    
                    this.code.sync();
                },
                deleteRow: function()
                {
                var $table = this.table.getTable();
                if (!$table) return;
    
                var $current = $(this.selection.getCurrent());
    
                this.buffer.set();
    
                var $current_tr = $current.closest('tr');
                var $focus_tr = $current_tr.prev().length ? $current_tr.prev() : $current_tr.next();
                if ($focus_tr.length)
                {
                    var $focus_td = $focus_tr.children('td, th').first();
                    if ($focus_td.length) $focus_td.prepend(this.selection.getMarker());
                }
    
                $current_tr.remove();
                this.table.restoreAfterDelete($table);
            },
                deleteColumn: function()
                {
                var $table = this.table.getTable();
                if (!$table) return;
    
                this.buffer.set();
    
                var $current = $(this.selection.getCurrent());
                var $current_td = $current.closest('td, th');
                var index = $current_td[0].cellIndex;
    
                $table.find('tr').each($.proxy(function(i, elem)
                {
                    var $elem = $(elem);
                    var focusIndex = index - 1 < 0 ? index + 1 : index - 1;
                    if (i === 0) $elem.find('td, th').eq(focusIndex).prepend(this.selection.getMarker());
    
                    $elem.find('td, th').eq(index).remove();
    
                }, this));
    
                this.table.restoreAfterDelete($table);
            },
                addHead: function()
                {
                    var $table = this.table.getTable();
                    if (!$table) return;
    
                    this.buffer.set();
    
                    if ($table.find('thead').size() !== 0)
                    {
                        this.table.deleteHead();
                        return;
                    }
    
                    var tr = $table.find('tr').first().clone();
                    tr.find('td').replaceWith($.proxy(function()
                    {
                        return $('<th>').html(this.opts.invisibleSpace);
                    }, this));
    
                    $thead = $('<thead></thead>').append(tr);
                    $table.prepend($thead);
    
                    this.code.sync();
    
                },
                deleteHead: function()
                {
                    var $table = this.table.getTable();
                    if (!$table) return;
    
                    var $thead = $table.find('thead');
                    if ($thead.size() === 0) return;
    
                    this.buffer.set();
    
                    $thead.remove();
                    this.code.sync();
                },
                addRowAbove: function()
                {
                    this.table.addRow('before');
                },
                addRowBelow: function()
                {
                    this.table.addRow('after');
                },
                addColumnLeft: function()
                {
                    this.table.addColumn('before');
                },
                addColumnRight: function()
                {
                    this.table.addColumn('after');
                },
                addRow: function(type)
                {
                    var $table = this.table.getTable();
                    if (!$table) return;
    
                    this.buffer.set();
    
                    var $current = $(this.selection.getCurrent());
                    var $current_tr = $current.closest('tr');
                    var new_tr = $current_tr.clone();
    
                    new_tr.find('th').replaceWith(function()
                    {
                        var $td = $('<td>');
                        $td[0].attributes = this.attributes;
    
                        return $td.append($(this).contents());
                    });
    
                    new_tr.find('td').html(this.opts.invisibleSpace);
    
                    if (type == 'after')
                    {
                        $current_tr.after(new_tr);
                    }
                    else
                    {
                        $current_tr.before(new_tr);
                    }
    
                    this.code.sync();
                },
                addColumn: function (type)
                {
                    var $table = this.table.getTable();
                    if (!$table) return;
    
                    var index = 0;
                    var current = $(this.selection.getCurrent());
    
                    this.buffer.set();
    
                    var $current_tr = current.closest('tr');
                    var $current_td = current.closest('td, th');
    
                    $current_tr.find('td, th').each($.proxy(function(i, elem)
                    {
                        if ($(elem)[0] === $current_td[0]) index = i;
    
                    }, this));
    
                    $table.find('tr').each($.proxy(function(i, elem)
                    {
                        var $current = $(elem).find('td, th').eq(index);
    
                        var td = $current.clone();
                        td.html(this.opts.invisibleSpace);
    
                        if (type == 'after')
                        {
                            $current.after(td);
                        }
                        else
                        {
                            $current.before(td);
                        }
    
                    }, this));
    
                    this.code.sync();
                }
            };
        };
    })(jQuery);
    
    
    RedactorPlugins.textdirection = function() {
      return {
        init: function()
        {
            var that = this;
            var dropdown = {};
    
            dropdown.ltr = { title: __('Left to Right'), callback: this.setLtr };
            dropdown.rtl = { title: __('Right to Left'), callback: this.setRtl };
    
            var button = this.button.add('textdirection', __('Change Text Direction'),
                false, dropdown);
    
            if (this.opts.direction == 'rtl')
                this.setRtl();
        },
        setRtl: function()
        {
            var c = this.getCurrent(), s = this.getSelection();
    
            this.buffer.set();
    
            if (s.type == 'Range' && s.focusNode.nodeName != 'div') {
                this.linebreakHack(s);
            }
            else if (!c) {
                var repl = '<div dir="rtl">' + this.get() + '</div>';
                this.set(repl, false);
            }
            $(this.getCurrent()).attr('dir', 'rtl');
            this.sync();
        },
        setLtr: function()
        {
            var c = this.getCurrent(), s = this.getSelection();
    
            this.buffer.set();
    
            if (s.type == 'Range' && s.focusNode.nodeName != 'div') {
                this.linebreakHack(s);
            }
            else if (!c) {
                var repl = '<div dir="ltr">' + this.get() + '</div>';
                this.set(repl, false);
            }
            $(this.getCurrent()).attr('dir', 'ltr');
            this.sync();
        },
        linebreakHack: function(sel) {
            var range = sel.getRangeAt(0);
            var wrapper = document.createElement('div');
            wrapper.appendChild(range.extractContents());
            range.insertNode(wrapper);
            this.selectionElement(wrapper);
        }
      };
    };
    
    
        $.Redactor.prototype.video = function()
        {
            return {
                reUrlYoutube: /https?:\/\/(?:[0-9A-Z-]+\.)?(?:youtu\.be\/|youtube\.com\S*[^\w\-\s])([\w\-]{11})(?=[^\w\-]|$)(?![?=&+%\w.-]*(?:['"][^<>]*>|<\/a>))[?=&+%\w.-]*/ig,
                reUrlVimeo: /https?:\/\/(www\.)?vimeo.com\/(\d+)($|\/)/,
                getTemplate: function()
                {
                    return String()
                    + '<section id="redactor-modal-video-insert">'
                        + '<label>' + this.lang.get('video_html_code') + '</label>'
                        + '<textarea id="redactor-insert-video-area" style="height: 160px;"></textarea>'
                    + '</section>';
                },
                init: function()
                {
                    var button = this.button.addAfter('image', 'video', this.lang.get('video'));
                    this.button.addCallback(button, this.video.show);
                },
                show: function()
                {
                    this.modal.addTemplate('video', this.video.getTemplate());
    
                    this.modal.load('video', this.lang.get('video'), 700);
                    this.modal.createCancelButton();
    
                    var button = this.modal.createActionButton(this.lang.get('insert'));
                    button.on('click', this.video.insert);
    
                    this.selection.save();
                    this.modal.show();
    
                    $('#redactor-insert-video-area').focus();
    
                },
                insert: function()
                {
                    var data = $('#redactor-insert-video-area').val();
    
                    if (!data.match(/<iframe|<video/gi))
                    {
                        data = this.clean.stripTags(data);
    
                        // parse if it is link on youtube & vimeo
                        var iframeStart = '<iframe style="width: 500px; height: 281px;" src="',
                            iframeEnd = '" frameborder="0" allowfullscreen></iframe>';
    
                        if (data.match(this.video.reUrlYoutube))
                        {
                            data = data.replace(this.video.reUrlYoutube, iframeStart + '//www.youtube.com/embed/$1' + iframeEnd);
                        }
                        else if (data.match(this.video.reUrlVimeo))
                        {
                            data = data.replace(this.video.reUrlVimeo, iframeStart + '//player.vimeo.com/video/$2' + iframeEnd);
                        }
                    }
    
                    this.selection.restore();
                    this.modal.close();
    
                    var current = this.selection.getBlock() || this.selection.getCurrent();
    
                    if (current) $(current).after(data);
                    else
                    {
                        this.insert.html(data);
                    }
    
                    this.code.sync();
                }
    
            };
        };
    })(jQuery);
    
    
    RedactorPlugins.imagepaste = function() {
      return {
        init: function() {
          if (this.utils.browser('webkit') && navigator.userAgent.indexOf('Chrome') === -1)
          {
            var arr = this.utils.browser('version').split('.');
            if (arr[0] < 536)
              return true;
          }
    
          // paste except opera (not webkit)
          if (this.utils.browser('opera'))
              return true;
    
          this.$editor.on('paste.imagepaste', $.proxy(this.imagepaste.buildEventPaste, this));
    
    
          // Capture the selection position every so often as Redactor seems to
          // drop it when attempting an image paste before `paste` browser event
          // fires
          var that = this,
              plugin = this.imagepaste;
          setInterval(function() {
            if (plugin.inpaste)
              return;
    
            that.selection.get();
            var coords = that.range.getClientRects();
            if (!coords.length)
                return;
            coords = coords[0];
            var proxy = {
              clientX: (Math.max(coords.left, 0) || 0) + 10,
              clientY: (coords.top || 0) + 10,
            };
            if (coords.left < 0)
                return;
            plugin.offset = proxy; //that.caret.getOffset() || plugin.offset;
    
        offset: 0,
        inpaste: false,
    
        buildEventPaste: function(e)
        {
          var event = e.originalEvent || e,
              fileUpload = false,
              files = [],
              i, file,
    
              plugin = this.imagepaste,
              cd = event.clipboardData,
              self = this, node,
              bail = function() {
                plugin.inpaste = false;
              };