]> scripts.mit.edu Git - autoinstalls/wordpress.git/blobdiff - wp-includes/js/tinymce/plugins/media/plugin.js
WordPress 4.5.1-scripts
[autoinstalls/wordpress.git] / wp-includes / js / tinymce / plugins / media / plugin.js
index a97d5a067aaabf3a11735a7016f8ccf86e6a8249..21a2c8cf56a37a9cbde47c777f8f1fcf3453648b 100644 (file)
@@ -1,8 +1,8 @@
 /**
  * plugin.js
  *
- * Copyright, Moxiecode Systems AB
  * Released under LGPL License.
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
  *
  * License: http://www.tinymce.com/license
  * Contributing: http://www.tinymce.com/contributing
 
 tinymce.PluginManager.add('media', function(editor, url) {
        var urlPatterns = [
-               {regex: /youtu\.be\/([\w\-.]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$1'},
-               {regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 425, h: 350, url: '//www.youtube.com/embed/$2'},
-               {regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: '//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc'},
-               {regex: /maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/, type: 'iframe', w: 425, h: 350, url: '//maps.google.com/maps/ms?msid=$2&output=embed"'}
+               {regex: /youtu\.be\/([\w\-.]+)/, type: 'iframe', w: 560, h: 314, url: '//www.youtube.com/embed/$1', allowFullscreen: true},
+               {regex: /youtube\.com(.+)v=([^&]+)/, type: 'iframe', w: 560, h: 314, url: '//www.youtube.com/embed/$2', allowFullscreen: true},
+               {regex: /youtube.com\/embed\/([a-z0-9\-_]+)/i, type: 'iframe', w: 560, h: 314, url: '//www.youtube.com/embed/$1', allowFullscreen: true},
+               {regex: /vimeo\.com\/([0-9]+)/, type: 'iframe', w: 425, h: 350, url: '//player.vimeo.com/video/$1?title=0&byline=0&portrait=0&color=8dc7dc', allowfullscreen: true},
+               {regex: /vimeo\.com\/(.*)\/([0-9]+)/, type: "iframe", w: 425, h: 350, url: "//player.vimeo.com/video/$2?title=0&byline=0", allowfullscreen: true},
+               {regex: /maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/, type: 'iframe', w: 425, h: 350, url: '//maps.google.com/maps/ms?msid=$2&output=embed"', allowFullscreen: false},
+               {regex: /dailymotion\.com\/video\/([^_]+)/, type: 'iframe', w: 480, h: 270, url: '//www.dailymotion.com/embed/video/$1', allowFullscreen: true}
        ];
 
+       var embedChange = (tinymce.Env.ie && tinymce.Env.ie <= 8) ? 'onChange' : 'onInput';
+
        function guessMime(url) {
+               url = url.toLowerCase();
+
                if (url.indexOf('.mp3') != -1) {
                        return 'audio/mpeg';
                }
@@ -62,8 +69,21 @@ tinymce.PluginManager.add('media', function(editor, url) {
 
        function showDialog() {
                var win, width, height, data;
+
                var generalFormItems = [
-                       {name: 'source1', type: 'filepicker', filetype: 'media', size: 40, autofocus: true, label: 'Source'}
+                       {
+                               name: 'source1',
+                               type: 'filepicker',
+                               filetype: 'media',
+                               size: 40,
+                               autofocus: true,
+                               label: 'Source',
+                               onchange: function(e) {
+                                       tinymce.each(e.meta, function(value, key) {
+                                               win.find('#' + key).value(value);
+                                       });
+                               }
+                       }
                ];
 
                function recalcSize(e) {
@@ -78,10 +98,16 @@ tinymce.PluginManager.add('media', function(editor, url) {
                        if (win.find('#constrain')[0].checked() && width && height && newWidth && newHeight) {
                                if (e.control == widthCtrl) {
                                        newHeight = Math.round((newWidth / width) * newHeight);
-                                       heightCtrl.value(newHeight);
+
+                                       if (!isNaN(newHeight)) {
+                                               heightCtrl.value(newHeight);
+                                       }
                                } else {
                                        newWidth = Math.round((newHeight / height) * newWidth);
-                                       widthCtrl.value(newWidth);
+
+                                       if (!isNaN(newWidth)) {
+                                               widthCtrl.value(newWidth);
+                                       }
                                }
                        }
 
@@ -105,9 +131,9 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                align: 'center',
                                spacing: 5,
                                items: [
-                                       {name: 'width', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
+                                       {name: 'width', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Width'},
                                        {type: 'label', text: 'x'},
-                                       {name: 'height', type: 'textbox', maxLength: 3, size: 3, onchange: recalcSize},
+                                       {name: 'height', type: 'textbox', maxLength: 5, size: 3, onchange: recalcSize, ariaLabel: 'Height'},
                                        {name: 'constrain', type: 'checkbox', checked: true, text: 'Constrain proportions'}
                                ]
                        });
@@ -117,6 +143,23 @@ tinymce.PluginManager.add('media', function(editor, url) {
                width = data.width;
                height = data.height;
 
+               var embedTextBox = {
+                       id: 'mcemediasource',
+                       type: 'textbox',
+                       flex: 1,
+                       name: 'embed',
+                       value: getSource(),
+                       multiline: true,
+                       label: 'Source'
+               };
+
+               function updateValueOnChange() {
+                       data = htmlToData(this.value());
+                       this.parent().parent().fromJSON(data);
+               }
+
+               embedTextBox[embedChange] = updateValueOnChange;
+
                win = editor.windowManager.open({
                        title: 'Insert/edit video',
                        data: data,
@@ -134,7 +177,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
 
                                {
                                        title: 'Embed',
-                                       type: "panel",
+                                       type: "container",
                                        layout: 'flex',
                                        direction: 'column',
                                        align: 'stretch',
@@ -149,20 +192,28 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                                        text: 'Paste your embed code below:',
                                                        forId: 'mcemediasource'
                                                },
-                                               {
-                                                       id: 'mcemediasource',
-                                                       type: 'textbox',
-                                                       flex: 1,
-                                                       name: 'embed',
-                                                       value: getSource(),
-                                                       multiline: true,
-                                                       label: 'Source'
-                                               }
+                                               embedTextBox
                                        ]
                                }
                        ],
                        onSubmit: function() {
+                               var beforeObjects, afterObjects, i, y;
+
+                               beforeObjects = editor.dom.select('img[data-mce-object]');
                                editor.insertContent(dataToHtml(this.toJSON()));
+                               afterObjects = editor.dom.select('img[data-mce-object]');
+
+                               // Find new image placeholder so we can select it
+                               for (i = 0; i < beforeObjects.length; i++) {
+                                       for (y = afterObjects.length - 1; y >= 0; y--) {
+                                               if (beforeObjects[i] == afterObjects[y]) {
+                                                       afterObjects.splice(y, 1);
+                                               }
+                                       }
+                               }
+
+                               editor.selection.select(afterObjects[0]);
+                               editor.nodeChanged();
                        }
                });
        }
@@ -200,30 +251,31 @@ tinymce.PluginManager.add('media', function(editor, url) {
                data.poster = editor.convertURL(data.poster, "poster");
                data.flashPlayerUrl = editor.convertURL(url + '/moxieplayer.swf', "movie");
 
-               if (data.embed) {
-                       html = updateHtml(data.embed, data, true);
-               } else {
-                       tinymce.each(urlPatterns, function(pattern) {
-                               var match, i, url;
+               tinymce.each(urlPatterns, function(pattern) {
+                       var match, i, url;
 
-                               if ((match = pattern.regex.exec(data.source1))) {
-                                       url = pattern.url;
+                       if ((match = pattern.regex.exec(data.source1))) {
+                               url = pattern.url;
 
-                                       for (i = 0; match[i]; i++) {
-                                               /*jshint loopfunc:true*/
-                                               /*eslint no-loop-func:0 */
-                                               url = url.replace('$' + i, function() {
-                                                       return match[i];
-                                               });
-                                       }
-
-                                       data.source1 = url;
-                                       data.type = pattern.type;
-                                       data.width = data.width || pattern.w;
-                                       data.height = data.height || pattern.h;
+                               for (i = 0; match[i]; i++) {
+                                       /*jshint loopfunc:true*/
+                                       /*eslint no-loop-func:0 */
+                                       url = url.replace('$' + i, function() {
+                                               return match[i];
+                                       });
                                }
-                       });
 
+                               data.source1 = url;
+                               data.type = pattern.type;
+                               data.allowFullscreen = pattern.allowFullscreen;
+                               data.width = data.width || pattern.w;
+                               data.height = data.height || pattern.h;
+                       }
+               });
+
+               if (data.embed) {
+                       html = updateHtml(data.embed, data, true);
+               } else {
                        var videoScript = getVideoScriptMatch(data.source1);
                        if (videoScript) {
                                data.type = 'script';
@@ -239,7 +291,8 @@ tinymce.PluginManager.add('media', function(editor, url) {
                        });
 
                        if (data.type == "iframe") {
-                               html += '<iframe src="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '"></iframe>';
+                               var allowFullscreen = data.allowFullscreen ? ' allowFullscreen="1"' : '';
+                               html += '<iframe src="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '"' + allowFullscreen + '></iframe>';
                        } else if (data.source1mime == "application/x-shockwave-flash") {
                                html += '<object data="' + data.source1 + '" width="' + data.width + '" height="' + data.height + '" type="application/x-shockwave-flash">';
 
@@ -340,6 +393,63 @@ tinymce.PluginManager.add('media', function(editor, url) {
                return {};
        }
 
+       function sanitize(html) {
+               if (editor.settings.media_filter_html === false) {
+                       return html;
+               }
+
+               var writer = new tinymce.html.Writer(), blocked;
+
+               new tinymce.html.SaxParser({
+                       validate: false,
+                       allow_conditional_comments: false,
+                       special: 'script,noscript',
+
+                       comment: function(text) {
+                               writer.comment(text);
+                       },
+
+                       cdata: function(text) {
+                               writer.cdata(text);
+                       },
+
+                       text: function(text, raw) {
+                               writer.text(text, raw);
+                       },
+
+                       start: function(name, attrs, empty) {
+                               blocked = true;
+
+                               if (name == 'script' || name == 'noscript') {
+                                       return;
+                               }
+
+                               for (var i = 0; i < attrs.length; i++) {
+                                       if (attrs[i].name.indexOf('on') === 0) {
+                                               return;
+                                       }
+
+                                       if (attrs[i].name == 'style') {
+                                               attrs[i].value = editor.dom.serializeStyle(editor.dom.parseStyle(attrs[i].value), name);
+                                       }
+                               }
+
+                               writer.start(name, attrs, empty);
+                               blocked = false;
+                       },
+
+                       end: function(name) {
+                               if (blocked) {
+                                       return;
+                               }
+
+                               writer.end(name);
+                       }
+               }, new tinymce.html.Schema({})).parse(html);
+
+               return writer.getContent();
+       }
+
        function updateHtml(html, data, updateAll) {
                var writer = new tinymce.html.Writer();
                var sourceCount = 0, hasImage;
@@ -404,7 +514,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                                        width: data.width,
                                                        height: data.height
                                                });
-                                       break;
+                                               break;
                                }
 
                                if (updateAll) {
@@ -420,13 +530,13 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                                                        src: ""
                                                                });
                                                        }
-                                               break;
+                                                       break;
 
                                                case "iframe":
                                                        setAttributes(attrs, {
                                                                src: data.source1
                                                        });
-                                               break;
+                                                       break;
 
                                                case "source":
                                                        sourceCount++;
@@ -441,7 +551,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                                                        return;
                                                                }
                                                        }
-                                               break;
+                                                       break;
 
                                                case "img":
                                                        if (!data.poster) {
@@ -503,15 +613,95 @@ tinymce.PluginManager.add('media', function(editor, url) {
                }
        });
 
+       function retainAttributesAndInnerHtml(sourceNode, targetNode) {
+               var attrName, attrValue, attribs, ai, innerHtml;
+
+               // Prefix all attributes except width, height and style since we
+               // will add these to the placeholder
+               attribs = sourceNode.attributes;
+               ai = attribs.length;
+               while (ai--) {
+                       attrName = attribs[ai].name;
+                       attrValue = attribs[ai].value;
+
+                       if (attrName !== "width" && attrName !== "height" && attrName !== "style") {
+                               if (attrName == "data" || attrName == "src") {
+                                       attrValue = editor.convertURL(attrValue, attrName);
+                               }
+
+                               targetNode.attr('data-mce-p-' + attrName, attrValue);
+                       }
+               }
+
+               // Place the inner HTML contents inside an escaped attribute
+               // This enables us to copy/paste the fake object
+               innerHtml = sourceNode.firstChild && sourceNode.firstChild.value;
+               if (innerHtml) {
+                       targetNode.attr("data-mce-html", escape(innerHtml));
+                       targetNode.firstChild = null;
+               }
+       }
+
+       function createPlaceholderNode(node) {
+               var placeHolder, name = node.name;
+
+               placeHolder = new tinymce.html.Node('img', 1);
+               placeHolder.shortEnded = true;
+
+               retainAttributesAndInnerHtml(node, placeHolder);
+
+               placeHolder.attr({
+                       width: node.attr('width') || "300",
+                       height: node.attr('height') || (name == "audio" ? "30" : "150"),
+                       style: node.attr('style'),
+                       src: tinymce.Env.transparentSrc,
+                       "data-mce-object": name,
+                       "class": "mce-object mce-object-" + name
+               });
+
+               return placeHolder;
+       }
+
+       function createPreviewNode(node) {
+               var previewWrapper, previewNode, shimNode, name = node.name;
+
+               previewWrapper = new tinymce.html.Node('span', 1);
+               previewWrapper.attr({
+                       contentEditable: 'false',
+                       style: node.attr('style'),
+                       "data-mce-object": name,
+                       "class": "mce-preview-object mce-object-" + name
+               });
+
+               retainAttributesAndInnerHtml(node, previewWrapper);
+
+               previewNode = new tinymce.html.Node(name, 1);
+               previewNode.attr({
+                       src: node.attr('src'),
+                       allowfullscreen: node.attr('allowfullscreen'),
+                       width: node.attr('width') || "300",
+                       height: node.attr('height') || (name == "audio" ? "30" : "150"),
+                       frameborder: '0'
+               });
+
+               shimNode = new tinymce.html.Node('span', 1);
+               shimNode.attr('class', 'mce-shim');
+
+               previewWrapper.append(previewNode);
+               previewWrapper.append(shimNode);
+
+               return previewWrapper;
+       }
+
        editor.on('preInit', function() {
                // Make sure that any messy HTML is retained inside these
                var specialElements = editor.schema.getSpecialElements();
                tinymce.each('video audio iframe object'.split(' '), function(name) {
-                       specialElements[name] = new RegExp('<\/' + name + '[^>]*>','gi');
+                       specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
                });
 
                // Allow elements
-               editor.schema.addValidElements('object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]');
+               //editor.schema.addValidElements('object[id|style|width|height|classid|codebase|*],embed[id|style|width|height|type|src|*],video[*],audio[*]');
 
                // Set allowFullscreen attribs as boolean
                var boolAttrs = editor.schema.getBoolAttrs();
@@ -520,12 +710,18 @@ tinymce.PluginManager.add('media', function(editor, url) {
                });
 
                // Converts iframe, video etc into placeholder images
-               editor.parser.addNodeFilter('iframe,video,audio,object,embed,script', function(nodes, name) {
-                       var i = nodes.length, ai, node, placeHolder, attrName, attrValue, attribs, innerHtml;
-                       var videoScript;
+               editor.parser.addNodeFilter('iframe,video,audio,object,embed,script', function(nodes) {
+                       var i = nodes.length, node, placeHolder, videoScript;
 
                        while (i--) {
                                node = nodes[i];
+                               if (!node.parent) {
+                                       continue;
+                               }
+
+                               if (node.parent.attr('data-mce-object')) {
+                                       continue;
+                               }
 
                                if (node.name == 'script') {
                                        videoScript = getVideoScriptMatch(node.attr('src'));
@@ -534,9 +730,6 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                        }
                                }
 
-                               placeHolder = new tinymce.html.Node('img', 1);
-                               placeHolder.shortEnded = true;
-
                                if (videoScript) {
                                        if (videoScript.width) {
                                                node.attr('width', videoScript.width.toString());
@@ -547,59 +740,43 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                        }
                                }
 
-                               // Prefix all attributes except width, height and style since we
-                               // will add these to the placeholder
-                               attribs = node.attributes;
-                               ai = attribs.length;
-                               while (ai--) {
-                                       attrName = attribs[ai].name;
-                                       attrValue = attribs[ai].value;
-
-                                       if (attrName !== "width" && attrName !== "height" && attrName !== "style") {
-                                               if (attrName == "data" || attrName == "src") {
-                                                       attrValue = editor.convertURL(attrValue, attrName);
-                                               }
-
-                                               placeHolder.attr('data-mce-p-' + attrName, attrValue);
-                                       }
-                               }
-
-                               // Place the inner HTML contents inside an escaped attribute
-                               // This enables us to copy/paste the fake object
-                               innerHtml = node.firstChild && node.firstChild.value;
-                               if (innerHtml) {
-                                       placeHolder.attr("data-mce-html", escape(innerHtml));
-                                       placeHolder.firstChild = null;
+                               if (node.name == 'iframe' && editor.settings.media_live_embeds !== false && tinymce.Env.ceFalse) {
+                                       placeHolder = createPreviewNode(node);
+                               } else {
+                                       placeHolder = createPlaceholderNode(node);
                                }
 
-                               placeHolder.attr({
-                                       width: node.attr('width') || "300",
-                                       height: node.attr('height') || (name == "audio" ? "30" : "150"),
-                                       style: node.attr('style'),
-                                       src: tinymce.Env.transparentSrc,
-                                       "data-mce-object": name,
-                                       "class": "mce-object mce-object-" + name
-                               });
-
                                node.replace(placeHolder);
                        }
                });
 
                // Replaces placeholder images with real elements for video, object, iframe etc
                editor.serializer.addAttributeFilter('data-mce-object', function(nodes, name) {
-                       var i = nodes.length, node, realElm, ai, attribs, innerHtml, innerNode, realElmName;
+                       var i = nodes.length, node, realElm, ai, attribs, innerHtml, innerNode, realElmName, className;
 
                        while (i--) {
                                node = nodes[i];
+                               if (!node.parent) {
+                                       continue;
+                               }
+
                                realElmName = node.attr(name);
                                realElm = new tinymce.html.Node(realElmName, 1);
 
                                // Add width/height to everything but audio
                                if (realElmName != "audio" && realElmName != "script") {
-                                       realElm.attr({
-                                               width: node.attr('width'),
-                                               height: node.attr('height')
-                                       });
+                                       className = node.attr('class');
+                                       if (className && className.indexOf('mce-preview-object') !== -1) {
+                                               realElm.attr({
+                                                       width: node.firstChild.attr('width'),
+                                                       height: node.firstChild.attr('height')
+                                               });
+                                       } else {
+                                               realElm.attr({
+                                                       width: node.attr('width'),
+                                                       height: node.attr('height')
+                                               });
+                                       }
                                }
 
                                realElm.attr({
@@ -626,7 +803,7 @@ tinymce.PluginManager.add('media', function(editor, url) {
                                if (innerHtml) {
                                        innerNode = new tinymce.html.Node('#text', 3);
                                        innerNode.raw = true;
-                                       innerNode.value = unescape(innerHtml);
+                                       innerNode.value = sanitize(unescape(innerHtml));
                                        realElm.append(innerNode);
                                }
 
@@ -635,6 +812,16 @@ tinymce.PluginManager.add('media', function(editor, url) {
                });
        });
 
+       editor.on('click keyup', function() {
+               var selectedNode = editor.selection.getNode();
+
+               if (selectedNode && editor.dom.hasClass(selectedNode, 'mce-preview-object')) {
+                       if (editor.dom.getAttrib(selectedNode, 'data-mce-selected')) {
+                               selectedNode.setAttribute('data-mce-selected', '2');
+                       }
+               }
+       });
+
        editor.on('ObjectSelected', function(e) {
                var objectType = e.target.getAttribute('data-mce-object');
 
@@ -663,14 +850,30 @@ tinymce.PluginManager.add('media', function(editor, url) {
        editor.addButton('media', {
                tooltip: 'Insert/edit video',
                onclick: showDialog,
-               stateSelector: ['img[data-mce-object=video]', 'img[data-mce-object=iframe]']
+               stateSelector: ['img[data-mce-object]', 'span[data-mce-object]']
        });
 
        editor.addMenuItem('media', {
                icon: 'media',
-               text: 'Insert video',
+               text: 'Insert/edit video',
                onclick: showDialog,
                context: 'insert',
                prependToContext: true
        });
+
+       editor.on('setContent', function() {
+               // TODO: This shouldn't be needed there should be a way to mark bogus
+               // elements so they are never removed except external save
+               editor.$('span.mce-preview-object').each(function(index, elm) {
+                       var $elm = editor.$(elm);
+
+                       if ($elm.find('span.mce-shim', elm).length === 0) {
+                               $elm.append('<span class="mce-shim"></span>');
+                       }
+               });
+       });
+
+       editor.addCommand('mceMedia', showDialog);
+
+       this.showDialog = showDialog;
 });