Load TinyMCE's non-minified "*src.js" plugin files when SCRIPT_DEBUG is defined, part props ericlewis, fixes #20055
git-svn-id: http://svn.automattic.com/wordpress/trunk@19945 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
parent
fe96801c4b
commit
4c10d04190
|
@ -517,13 +517,15 @@ final class _WP_Editors {
|
|||
'language' => self::$mce_locale
|
||||
);
|
||||
|
||||
$suffix = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '_src' : '';
|
||||
|
||||
do_action('before_wp_tiny_mce', self::$mce_settings);
|
||||
?>
|
||||
|
||||
<script type="text/javascript">
|
||||
tinyMCEPreInit = {
|
||||
base : "<?php echo self::$baseurl; ?>",
|
||||
suffix : "",
|
||||
suffix : "<?php echo $suffix; ?>",
|
||||
query : "<?php echo $version; ?>",
|
||||
mceInit : <?php echo $mceInit; ?>,
|
||||
qtInit : <?php echo $qtInit; ?>,
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
(function(){
|
||||
if ( 'undefined' == tinyMCEPreInit )
|
||||
return;
|
||||
|
||||
var baseurl = tinyMCEPreInit.base, sl = tinymce.ScriptLoader, lang = tinyMCEPreInit.ref.language,
|
||||
theme = tinyMCEPreInit.ref.theme, plugins = tinyMCEPreInit.ref.plugins, suffix = tinyMCEPreInit.suffix;
|
||||
|
||||
sl.markDone( baseurl+'/langs/'+lang+'.js' );
|
||||
sl.markDone( baseurl+'/themes/'+theme+'/editor_template'+suffix+'.js' );
|
||||
sl.markDone( baseurl+'/themes/'+theme+'/langs/'+lang+'.js' );
|
||||
sl.markDone( baseurl+'/themes/'+theme+'/langs/'+lang+'_dlg.js' );
|
||||
|
||||
tinymce.each( plugins.split(','), function(plugin){
|
||||
if ( plugin && plugin.charAt(0) != '-' ) {
|
||||
sl.markDone( baseurl+'/plugins/'+plugin+'/editor_plugin'+suffix+'.js' );
|
||||
sl.markDone( baseurl+'/plugins/'+plugin+'/langs/'+lang+'.js' );
|
||||
sl.markDone( baseurl+'/plugins/'+plugin+'/langs/'+lang+'_dlg.js' )
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
tinymce.create('tinymce.plugins.Directionality', {
|
||||
init : function(ed, url) {
|
||||
var t = this;
|
||||
|
||||
t.editor = ed;
|
||||
|
||||
ed.addCommand('mceDirectionLTR', function() {
|
||||
var e = ed.dom.getParent(ed.selection.getNode(), ed.dom.isBlock);
|
||||
|
||||
if (e) {
|
||||
if (ed.dom.getAttrib(e, "dir") != "ltr")
|
||||
ed.dom.setAttrib(e, "dir", "ltr");
|
||||
else
|
||||
ed.dom.setAttrib(e, "dir", "");
|
||||
}
|
||||
|
||||
ed.nodeChanged();
|
||||
});
|
||||
|
||||
ed.addCommand('mceDirectionRTL', function() {
|
||||
var e = ed.dom.getParent(ed.selection.getNode(), ed.dom.isBlock);
|
||||
|
||||
if (e) {
|
||||
if (ed.dom.getAttrib(e, "dir") != "rtl")
|
||||
ed.dom.setAttrib(e, "dir", "rtl");
|
||||
else
|
||||
ed.dom.setAttrib(e, "dir", "");
|
||||
}
|
||||
|
||||
ed.nodeChanged();
|
||||
});
|
||||
|
||||
ed.addButton('ltr', {title : 'directionality.ltr_desc', cmd : 'mceDirectionLTR'});
|
||||
ed.addButton('rtl', {title : 'directionality.rtl_desc', cmd : 'mceDirectionRTL'});
|
||||
|
||||
ed.onNodeChange.add(t._nodeChange, t);
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Directionality',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
},
|
||||
|
||||
// Private methods
|
||||
|
||||
_nodeChange : function(ed, cm, n) {
|
||||
var dom = ed.dom, dir;
|
||||
|
||||
n = dom.getParent(n, dom.isBlock);
|
||||
if (!n) {
|
||||
cm.setDisabled('ltr', 1);
|
||||
cm.setDisabled('rtl', 1);
|
||||
return;
|
||||
}
|
||||
|
||||
dir = dom.getAttrib(n, 'dir');
|
||||
cm.setActive('ltr', dir == "ltr");
|
||||
cm.setDisabled('ltr', 0);
|
||||
cm.setActive('rtl', dir == "rtl");
|
||||
cm.setDisabled('rtl', 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('directionality', tinymce.plugins.Directionality);
|
||||
})();
|
|
@ -0,0 +1,159 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var DOM = tinymce.DOM;
|
||||
|
||||
tinymce.create('tinymce.plugins.FullScreenPlugin', {
|
||||
init : function(ed, url) {
|
||||
var t = this, s = {}, vp, posCss;
|
||||
|
||||
t.editor = ed;
|
||||
|
||||
// Register commands
|
||||
ed.addCommand('mceFullScreen', function() {
|
||||
var win, de = DOM.doc.documentElement;
|
||||
|
||||
if (ed.getParam('fullscreen_is_enabled')) {
|
||||
if (ed.getParam('fullscreen_new_window'))
|
||||
closeFullscreen(); // Call to close in new window
|
||||
else {
|
||||
DOM.win.setTimeout(function() {
|
||||
tinymce.dom.Event.remove(DOM.win, 'resize', t.resizeFunc);
|
||||
tinyMCE.get(ed.getParam('fullscreen_editor_id')).setContent(ed.getContent());
|
||||
tinyMCE.remove(ed);
|
||||
DOM.remove('mce_fullscreen_container');
|
||||
de.style.overflow = ed.getParam('fullscreen_html_overflow');
|
||||
DOM.setStyle(DOM.doc.body, 'overflow', ed.getParam('fullscreen_overflow'));
|
||||
DOM.win.scrollTo(ed.getParam('fullscreen_scrollx'), ed.getParam('fullscreen_scrolly'));
|
||||
tinyMCE.settings = tinyMCE.oldSettings; // Restore old settings
|
||||
}, 10);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (ed.getParam('fullscreen_new_window')) {
|
||||
win = DOM.win.open(url + "/fullscreen.htm", "mceFullScreenPopup", "fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width=" + screen.availWidth + ",height=" + screen.availHeight);
|
||||
try {
|
||||
win.resizeTo(screen.availWidth, screen.availHeight);
|
||||
} catch (e) {
|
||||
// Ignore
|
||||
}
|
||||
} else {
|
||||
tinyMCE.oldSettings = tinyMCE.settings; // Store old settings
|
||||
s.fullscreen_overflow = DOM.getStyle(DOM.doc.body, 'overflow', 1) || 'auto';
|
||||
s.fullscreen_html_overflow = DOM.getStyle(de, 'overflow', 1);
|
||||
vp = DOM.getViewPort();
|
||||
s.fullscreen_scrollx = vp.x;
|
||||
s.fullscreen_scrolly = vp.y;
|
||||
|
||||
// Fixes an Opera bug where the scrollbars doesn't reappear
|
||||
if (tinymce.isOpera && s.fullscreen_overflow == 'visible')
|
||||
s.fullscreen_overflow = 'auto';
|
||||
|
||||
// Fixes an IE bug where horizontal scrollbars would appear
|
||||
if (tinymce.isIE && s.fullscreen_overflow == 'scroll')
|
||||
s.fullscreen_overflow = 'auto';
|
||||
|
||||
// Fixes an IE bug where the scrollbars doesn't reappear
|
||||
if (tinymce.isIE && (s.fullscreen_html_overflow == 'visible' || s.fullscreen_html_overflow == 'scroll'))
|
||||
s.fullscreen_html_overflow = 'auto';
|
||||
|
||||
if (s.fullscreen_overflow == '0px')
|
||||
s.fullscreen_overflow = '';
|
||||
|
||||
DOM.setStyle(DOM.doc.body, 'overflow', 'hidden');
|
||||
de.style.overflow = 'hidden'; //Fix for IE6/7
|
||||
vp = DOM.getViewPort();
|
||||
DOM.win.scrollTo(0, 0);
|
||||
|
||||
if (tinymce.isIE)
|
||||
vp.h -= 1;
|
||||
|
||||
// Use fixed position if it exists
|
||||
if (tinymce.isIE6)
|
||||
posCss = 'absolute;top:' + vp.y;
|
||||
else
|
||||
posCss = 'fixed;top:0';
|
||||
|
||||
n = DOM.add(DOM.doc.body, 'div', {
|
||||
id : 'mce_fullscreen_container',
|
||||
style : 'position:' + posCss + ';left:0;width:' + vp.w + 'px;height:' + vp.h + 'px;z-index:200000;'});
|
||||
DOM.add(n, 'div', {id : 'mce_fullscreen'});
|
||||
|
||||
tinymce.each(ed.settings, function(v, n) {
|
||||
s[n] = v;
|
||||
});
|
||||
|
||||
s.id = 'mce_fullscreen';
|
||||
s.width = n.clientWidth;
|
||||
s.height = n.clientHeight - 15;
|
||||
s.fullscreen_is_enabled = true;
|
||||
s.fullscreen_editor_id = ed.id;
|
||||
s.theme_advanced_resizing = false;
|
||||
s.save_onsavecallback = function() {
|
||||
ed.setContent(tinyMCE.get(s.id).getContent());
|
||||
ed.execCommand('mceSave');
|
||||
};
|
||||
|
||||
tinymce.each(ed.getParam('fullscreen_settings'), function(v, k) {
|
||||
s[k] = v;
|
||||
});
|
||||
|
||||
if (s.theme_advanced_toolbar_location === 'external')
|
||||
s.theme_advanced_toolbar_location = 'top';
|
||||
|
||||
t.fullscreenEditor = new tinymce.Editor('mce_fullscreen', s);
|
||||
t.fullscreenEditor.onInit.add(function() {
|
||||
t.fullscreenEditor.setContent(ed.getContent());
|
||||
t.fullscreenEditor.focus();
|
||||
});
|
||||
|
||||
t.fullscreenEditor.render();
|
||||
|
||||
t.fullscreenElement = new tinymce.dom.Element('mce_fullscreen_container');
|
||||
t.fullscreenElement.update();
|
||||
//document.body.overflow = 'hidden';
|
||||
|
||||
t.resizeFunc = tinymce.dom.Event.add(DOM.win, 'resize', function() {
|
||||
var vp = tinymce.DOM.getViewPort(), fed = t.fullscreenEditor, outerSize, innerSize;
|
||||
|
||||
// Get outer/inner size to get a delta size that can be used to calc the new iframe size
|
||||
outerSize = fed.dom.getSize(fed.getContainer().firstChild);
|
||||
innerSize = fed.dom.getSize(fed.getContainer().getElementsByTagName('iframe')[0]);
|
||||
|
||||
fed.theme.resizeTo(vp.w - outerSize.w + innerSize.w, vp.h - outerSize.h + innerSize.h);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Register buttons
|
||||
ed.addButton('fullscreen', {title : 'fullscreen.desc', cmd : 'mceFullScreen'});
|
||||
|
||||
ed.onNodeChange.add(function(ed, cm) {
|
||||
cm.setActive('fullscreen', ed.getParam('fullscreen_is_enabled'));
|
||||
});
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Fullscreen',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('fullscreen', tinymce.plugins.FullScreenPlugin);
|
||||
})();
|
|
@ -0,0 +1,699 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var DOM = tinymce.DOM, Element = tinymce.dom.Element, Event = tinymce.dom.Event, each = tinymce.each, is = tinymce.is;
|
||||
|
||||
tinymce.create('tinymce.plugins.InlinePopups', {
|
||||
init : function(ed, url) {
|
||||
// Replace window manager
|
||||
ed.onBeforeRenderUI.add(function() {
|
||||
ed.windowManager = new tinymce.InlineWindowManager(ed);
|
||||
DOM.loadCSS(url + '/skins/' + (ed.settings.inlinepopups_skin || 'clearlooks2') + "/window.css");
|
||||
});
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'InlinePopups',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/inlinepopups',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
tinymce.create('tinymce.InlineWindowManager:tinymce.WindowManager', {
|
||||
InlineWindowManager : function(ed) {
|
||||
var t = this;
|
||||
|
||||
t.parent(ed);
|
||||
t.zIndex = 300000;
|
||||
t.count = 0;
|
||||
t.windows = {};
|
||||
},
|
||||
|
||||
open : function(f, p) {
|
||||
var t = this, id, opt = '', ed = t.editor, dw = 0, dh = 0, vp, po, mdf, clf, we, w, u, parentWindow;
|
||||
|
||||
f = f || {};
|
||||
p = p || {};
|
||||
|
||||
// Run native windows
|
||||
if (!f.inline)
|
||||
return t.parent(f, p);
|
||||
|
||||
parentWindow = t._frontWindow();
|
||||
if (parentWindow && DOM.get(parentWindow.id + '_ifr')) {
|
||||
parentWindow.focussedElement = DOM.get(parentWindow.id + '_ifr').contentWindow.document.activeElement;
|
||||
}
|
||||
|
||||
// Only store selection if the type is a normal window
|
||||
if (!f.type)
|
||||
t.bookmark = ed.selection.getBookmark(1);
|
||||
|
||||
id = DOM.uniqueId();
|
||||
vp = DOM.getViewPort();
|
||||
f.width = parseInt(f.width || 320);
|
||||
f.height = parseInt(f.height || 240) + (tinymce.isIE ? 8 : 0);
|
||||
f.min_width = parseInt(f.min_width || 150);
|
||||
f.min_height = parseInt(f.min_height || 100);
|
||||
f.max_width = parseInt(f.max_width || 2000);
|
||||
f.max_height = parseInt(f.max_height || 2000);
|
||||
f.left = f.left || Math.round(Math.max(vp.x, vp.x + (vp.w / 2.0) - (f.width / 2.0)));
|
||||
f.top = f.top || Math.round(Math.max(vp.y, vp.y + (vp.h / 2.0) - (f.height / 2.0)));
|
||||
f.movable = f.resizable = true;
|
||||
p.mce_width = f.width;
|
||||
p.mce_height = f.height;
|
||||
p.mce_inline = true;
|
||||
p.mce_window_id = id;
|
||||
p.mce_auto_focus = f.auto_focus;
|
||||
|
||||
// Transpose
|
||||
// po = DOM.getPos(ed.getContainer());
|
||||
// f.left -= po.x;
|
||||
// f.top -= po.y;
|
||||
|
||||
t.features = f;
|
||||
t.params = p;
|
||||
t.onOpen.dispatch(t, f, p);
|
||||
|
||||
if (f.type) {
|
||||
opt += ' mceModal';
|
||||
|
||||
if (f.type)
|
||||
opt += ' mce' + f.type.substring(0, 1).toUpperCase() + f.type.substring(1);
|
||||
|
||||
f.resizable = false;
|
||||
}
|
||||
|
||||
if (f.statusbar)
|
||||
opt += ' mceStatusbar';
|
||||
|
||||
if (f.resizable)
|
||||
opt += ' mceResizable';
|
||||
|
||||
if (f.minimizable)
|
||||
opt += ' mceMinimizable';
|
||||
|
||||
if (f.maximizable)
|
||||
opt += ' mceMaximizable';
|
||||
|
||||
if (f.movable)
|
||||
opt += ' mceMovable';
|
||||
|
||||
// Create DOM objects
|
||||
t._addAll(DOM.doc.body,
|
||||
['div', {id : id, role : 'dialog', 'aria-labelledby': f.type ? id + '_content' : id + '_title', 'class' : (ed.settings.inlinepopups_skin || 'clearlooks2') + (tinymce.isIE && window.getSelection ? ' ie9' : ''), style : 'width:100px;height:100px'},
|
||||
['div', {id : id + '_wrapper', 'class' : 'mceWrapper' + opt},
|
||||
['div', {id : id + '_top', 'class' : 'mceTop'},
|
||||
['div', {'class' : 'mceLeft'}],
|
||||
['div', {'class' : 'mceCenter'}],
|
||||
['div', {'class' : 'mceRight'}],
|
||||
['span', {id : id + '_title'}, f.title || '']
|
||||
],
|
||||
|
||||
['div', {id : id + '_middle', 'class' : 'mceMiddle'},
|
||||
['div', {id : id + '_left', 'class' : 'mceLeft', tabindex : '0'}],
|
||||
['span', {id : id + '_content'}],
|
||||
['div', {id : id + '_right', 'class' : 'mceRight', tabindex : '0'}]
|
||||
],
|
||||
|
||||
['div', {id : id + '_bottom', 'class' : 'mceBottom'},
|
||||
['div', {'class' : 'mceLeft'}],
|
||||
['div', {'class' : 'mceCenter'}],
|
||||
['div', {'class' : 'mceRight'}],
|
||||
['span', {id : id + '_status'}, 'Content']
|
||||
],
|
||||
|
||||
['a', {'class' : 'mceMove', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {'class' : 'mceMin', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
|
||||
['a', {'class' : 'mceMax', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
|
||||
['a', {'class' : 'mceMed', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
|
||||
['a', {'class' : 'mceClose', tabindex : '-1', href : 'javascript:;', onmousedown : 'return false;'}],
|
||||
['a', {id : id + '_resize_n', 'class' : 'mceResize mceResizeN', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_s', 'class' : 'mceResize mceResizeS', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_w', 'class' : 'mceResize mceResizeW', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_e', 'class' : 'mceResize mceResizeE', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_nw', 'class' : 'mceResize mceResizeNW', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_ne', 'class' : 'mceResize mceResizeNE', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_sw', 'class' : 'mceResize mceResizeSW', tabindex : '-1', href : 'javascript:;'}],
|
||||
['a', {id : id + '_resize_se', 'class' : 'mceResize mceResizeSE', tabindex : '-1', href : 'javascript:;'}]
|
||||
]
|
||||
]
|
||||
);
|
||||
|
||||
DOM.setStyles(id, {top : -10000, left : -10000});
|
||||
|
||||
// Fix gecko rendering bug, where the editors iframe messed with window contents
|
||||
if (tinymce.isGecko)
|
||||
DOM.setStyle(id, 'overflow', 'auto');
|
||||
|
||||
// Measure borders
|
||||
if (!f.type) {
|
||||
dw += DOM.get(id + '_left').clientWidth;
|
||||
dw += DOM.get(id + '_right').clientWidth;
|
||||
dh += DOM.get(id + '_top').clientHeight;
|
||||
dh += DOM.get(id + '_bottom').clientHeight;
|
||||
}
|
||||
|
||||
// Resize window
|
||||
DOM.setStyles(id, {top : f.top, left : f.left, width : f.width + dw, height : f.height + dh});
|
||||
|
||||
u = f.url || f.file;
|
||||
if (u) {
|
||||
if (tinymce.relaxedDomain)
|
||||
u += (u.indexOf('?') == -1 ? '?' : '&') + 'mce_rdomain=' + tinymce.relaxedDomain;
|
||||
|
||||
u = tinymce._addVer(u);
|
||||
}
|
||||
|
||||
if (!f.type) {
|
||||
DOM.add(id + '_content', 'iframe', {id : id + '_ifr', src : 'javascript:""', frameBorder : 0, style : 'border:0;width:10px;height:10px'});
|
||||
DOM.setStyles(id + '_ifr', {width : f.width, height : f.height});
|
||||
DOM.setAttrib(id + '_ifr', 'src', u);
|
||||
} else {
|
||||
DOM.add(id + '_wrapper', 'a', {id : id + '_ok', 'class' : 'mceButton mceOk', href : 'javascript:;', onmousedown : 'return false;'}, 'Ok');
|
||||
|
||||
if (f.type == 'confirm')
|
||||
DOM.add(id + '_wrapper', 'a', {'class' : 'mceButton mceCancel', href : 'javascript:;', onmousedown : 'return false;'}, 'Cancel');
|
||||
|
||||
DOM.add(id + '_middle', 'div', {'class' : 'mceIcon'});
|
||||
DOM.setHTML(id + '_content', f.content.replace('\n', '<br />'));
|
||||
|
||||
Event.add(id, 'keyup', function(evt) {
|
||||
var VK_ESCAPE = 27;
|
||||
if (evt.keyCode === VK_ESCAPE) {
|
||||
f.button_func(false);
|
||||
return Event.cancel(evt);
|
||||
}
|
||||
});
|
||||
|
||||
Event.add(id, 'keydown', function(evt) {
|
||||
var cancelButton, VK_TAB = 9;
|
||||
if (evt.keyCode === VK_TAB) {
|
||||
cancelButton = DOM.select('a.mceCancel', id + '_wrapper')[0];
|
||||
if (cancelButton && cancelButton !== evt.target) {
|
||||
cancelButton.focus();
|
||||
} else {
|
||||
DOM.get(id + '_ok').focus();
|
||||
}
|
||||
return Event.cancel(evt);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Register events
|
||||
mdf = Event.add(id, 'mousedown', function(e) {
|
||||
var n = e.target, w, vp;
|
||||
|
||||
w = t.windows[id];
|
||||
t.focus(id);
|
||||
|
||||
if (n.nodeName == 'A' || n.nodeName == 'a') {
|
||||
if (n.className == 'mceClose') {
|
||||
t.close(null, id);
|
||||
return Event.cancel(e);
|
||||
} else if (n.className == 'mceMax') {
|
||||
w.oldPos = w.element.getXY();
|
||||
w.oldSize = w.element.getSize();
|
||||
|
||||
vp = DOM.getViewPort();
|
||||
|
||||
// Reduce viewport size to avoid scrollbars
|
||||
vp.w -= 2;
|
||||
vp.h -= 2;
|
||||
|
||||
w.element.moveTo(vp.x, vp.y);
|
||||
w.element.resizeTo(vp.w, vp.h);
|
||||
DOM.setStyles(id + '_ifr', {width : vp.w - w.deltaWidth, height : vp.h - w.deltaHeight});
|
||||
DOM.addClass(id + '_wrapper', 'mceMaximized');
|
||||
} else if (n.className == 'mceMed') {
|
||||
// Reset to old size
|
||||
w.element.moveTo(w.oldPos.x, w.oldPos.y);
|
||||
w.element.resizeTo(w.oldSize.w, w.oldSize.h);
|
||||
w.iframeElement.resizeTo(w.oldSize.w - w.deltaWidth, w.oldSize.h - w.deltaHeight);
|
||||
|
||||
DOM.removeClass(id + '_wrapper', 'mceMaximized');
|
||||
} else if (n.className == 'mceMove')
|
||||
return t._startDrag(id, e, n.className);
|
||||
else if (DOM.hasClass(n, 'mceResize'))
|
||||
return t._startDrag(id, e, n.className.substring(13));
|
||||
}
|
||||
});
|
||||
|
||||
clf = Event.add(id, 'click', function(e) {
|
||||
var n = e.target;
|
||||
|
||||
t.focus(id);
|
||||
|
||||
if (n.nodeName == 'A' || n.nodeName == 'a') {
|
||||
switch (n.className) {
|
||||
case 'mceClose':
|
||||
t.close(null, id);
|
||||
return Event.cancel(e);
|
||||
|
||||
case 'mceButton mceOk':
|
||||
case 'mceButton mceCancel':
|
||||
f.button_func(n.className == 'mceButton mceOk');
|
||||
return Event.cancel(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Make sure the tab order loops within the dialog.
|
||||
Event.add([id + '_left', id + '_right'], 'focus', function(evt) {
|
||||
var iframe = DOM.get(id + '_ifr');
|
||||
if (iframe) {
|
||||
var body = iframe.contentWindow.document.body;
|
||||
var focusable = DOM.select(':input:enabled,*[tabindex=0]', body);
|
||||
if (evt.target.id === (id + '_left')) {
|
||||
focusable[focusable.length - 1].focus();
|
||||
} else {
|
||||
focusable[0].focus();
|
||||
}
|
||||
} else {
|
||||
DOM.get(id + '_ok').focus();
|
||||
}
|
||||
});
|
||||
|
||||
// Add window
|
||||
w = t.windows[id] = {
|
||||
id : id,
|
||||
mousedown_func : mdf,
|
||||
click_func : clf,
|
||||
element : new Element(id, {blocker : 1, container : ed.getContainer()}),
|
||||
iframeElement : new Element(id + '_ifr'),
|
||||
features : f,
|
||||
deltaWidth : dw,
|
||||
deltaHeight : dh
|
||||
};
|
||||
|
||||
w.iframeElement.on('focus', function() {
|
||||
t.focus(id);
|
||||
});
|
||||
|
||||
// Setup blocker
|
||||
if (t.count == 0 && t.editor.getParam('dialog_type', 'modal') == 'modal') {
|
||||
DOM.add(DOM.doc.body, 'div', {
|
||||
id : 'mceModalBlocker',
|
||||
'class' : (t.editor.settings.inlinepopups_skin || 'clearlooks2') + '_modalBlocker',
|
||||
style : {zIndex : t.zIndex - 1}
|
||||
});
|
||||
|
||||
DOM.show('mceModalBlocker'); // Reduces flicker in IE
|
||||
DOM.setAttrib(DOM.doc.body, 'aria-hidden', 'true');
|
||||
} else
|
||||
DOM.setStyle('mceModalBlocker', 'z-index', t.zIndex - 1);
|
||||
|
||||
if (tinymce.isIE6 || /Firefox\/2\./.test(navigator.userAgent) || (tinymce.isIE && !DOM.boxModel))
|
||||
DOM.setStyles('mceModalBlocker', {position : 'absolute', left : vp.x, top : vp.y, width : vp.w - 2, height : vp.h - 2});
|
||||
|
||||
DOM.setAttrib(id, 'aria-hidden', 'false');
|
||||
t.focus(id);
|
||||
t._fixIELayout(id, 1);
|
||||
|
||||
// Focus ok button
|
||||
if (DOM.get(id + '_ok'))
|
||||
DOM.get(id + '_ok').focus();
|
||||
t.count++;
|
||||
|
||||
return w;
|
||||
},
|
||||
|
||||
focus : function(id) {
|
||||
var t = this, w;
|
||||
|
||||
if (w = t.windows[id]) {
|
||||
w.zIndex = this.zIndex++;
|
||||
w.element.setStyle('zIndex', w.zIndex);
|
||||
w.element.update();
|
||||
|
||||
id = id + '_wrapper';
|
||||
DOM.removeClass(t.lastId, 'mceFocus');
|
||||
DOM.addClass(id, 'mceFocus');
|
||||
t.lastId = id;
|
||||
|
||||
if (w.focussedElement) {
|
||||
w.focussedElement.focus();
|
||||
} else if (DOM.get(id + '_ok')) {
|
||||
DOM.get(w.id + '_ok').focus();
|
||||
} else if (DOM.get(w.id + '_ifr')) {
|
||||
DOM.get(w.id + '_ifr').focus();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_addAll : function(te, ne) {
|
||||
var i, n, t = this, dom = tinymce.DOM;
|
||||
|
||||
if (is(ne, 'string'))
|
||||
te.appendChild(dom.doc.createTextNode(ne));
|
||||
else if (ne.length) {
|
||||
te = te.appendChild(dom.create(ne[0], ne[1]));
|
||||
|
||||
for (i=2; i<ne.length; i++)
|
||||
t._addAll(te, ne[i]);
|
||||
}
|
||||
},
|
||||
|
||||
_startDrag : function(id, se, ac) {
|
||||
var t = this, mu, mm, d = DOM.doc, eb, w = t.windows[id], we = w.element, sp = we.getXY(), p, sz, ph, cp, vp, sx, sy, sex, sey, dx, dy, dw, dh;
|
||||
|
||||
// Get positons and sizes
|
||||
// cp = DOM.getPos(t.editor.getContainer());
|
||||
cp = {x : 0, y : 0};
|
||||
vp = DOM.getViewPort();
|
||||
|
||||
// Reduce viewport size to avoid scrollbars while dragging
|
||||
vp.w -= 2;
|
||||
vp.h -= 2;
|
||||
|
||||
sex = se.screenX;
|
||||
sey = se.screenY;
|
||||
dx = dy = dw = dh = 0;
|
||||
|
||||
// Handle mouse up
|
||||
mu = Event.add(d, 'mouseup', function(e) {
|
||||
Event.remove(d, 'mouseup', mu);
|
||||
Event.remove(d, 'mousemove', mm);
|
||||
|
||||
if (eb)
|
||||
eb.remove();
|
||||
|
||||
we.moveBy(dx, dy);
|
||||
we.resizeBy(dw, dh);
|
||||
sz = we.getSize();
|
||||
DOM.setStyles(id + '_ifr', {width : sz.w - w.deltaWidth, height : sz.h - w.deltaHeight});
|
||||
t._fixIELayout(id, 1);
|
||||
|
||||
return Event.cancel(e);
|
||||
});
|
||||
|
||||
if (ac != 'Move')
|
||||
startMove();
|
||||
|
||||
function startMove() {
|
||||
if (eb)
|
||||
return;
|
||||
|
||||
t._fixIELayout(id, 0);
|
||||
|
||||
// Setup event blocker
|
||||
DOM.add(d.body, 'div', {
|
||||
id : 'mceEventBlocker',
|
||||
'class' : 'mceEventBlocker ' + (t.editor.settings.inlinepopups_skin || 'clearlooks2'),
|
||||
style : {zIndex : t.zIndex + 1}
|
||||
});
|
||||
|
||||
if (tinymce.isIE6 || (tinymce.isIE && !DOM.boxModel))
|
||||
DOM.setStyles('mceEventBlocker', {position : 'absolute', left : vp.x, top : vp.y, width : vp.w - 2, height : vp.h - 2});
|
||||
|
||||
eb = new Element('mceEventBlocker');
|
||||
eb.update();
|
||||
|
||||
// Setup placeholder
|
||||
p = we.getXY();
|
||||
sz = we.getSize();
|
||||
sx = cp.x + p.x - vp.x;
|
||||
sy = cp.y + p.y - vp.y;
|
||||
DOM.add(eb.get(), 'div', {id : 'mcePlaceHolder', 'class' : 'mcePlaceHolder', style : {left : sx, top : sy, width : sz.w, height : sz.h}});
|
||||
ph = new Element('mcePlaceHolder');
|
||||
};
|
||||
|
||||
// Handle mouse move/drag
|
||||
mm = Event.add(d, 'mousemove', function(e) {
|
||||
var x, y, v;
|
||||
|
||||
startMove();
|
||||
|
||||
x = e.screenX - sex;
|
||||
y = e.screenY - sey;
|
||||
|
||||
switch (ac) {
|
||||
case 'ResizeW':
|
||||
dx = x;
|
||||
dw = 0 - x;
|
||||
break;
|
||||
|
||||
case 'ResizeE':
|
||||
dw = x;
|
||||
break;
|
||||
|
||||
case 'ResizeN':
|
||||
case 'ResizeNW':
|
||||
case 'ResizeNE':
|
||||
if (ac == "ResizeNW") {
|
||||
dx = x;
|
||||
dw = 0 - x;
|
||||
} else if (ac == "ResizeNE")
|
||||
dw = x;
|
||||
|
||||
dy = y;
|
||||
dh = 0 - y;
|
||||
break;
|
||||
|
||||
case 'ResizeS':
|
||||
case 'ResizeSW':
|
||||
case 'ResizeSE':
|
||||
if (ac == "ResizeSW") {
|
||||
dx = x;
|
||||
dw = 0 - x;
|
||||
} else if (ac == "ResizeSE")
|
||||
dw = x;
|
||||
|
||||
dh = y;
|
||||
break;
|
||||
|
||||
case 'mceMove':
|
||||
dx = x;
|
||||
dy = y;
|
||||
break;
|
||||
}
|
||||
|
||||
// Boundary check
|
||||
if (dw < (v = w.features.min_width - sz.w)) {
|
||||
if (dx !== 0)
|
||||
dx += dw - v;
|
||||
|
||||
dw = v;
|
||||
}
|
||||
|
||||
if (dh < (v = w.features.min_height - sz.h)) {
|
||||
if (dy !== 0)
|
||||
dy += dh - v;
|
||||
|
||||
dh = v;
|
||||
}
|
||||
|
||||
dw = Math.min(dw, w.features.max_width - sz.w);
|
||||
dh = Math.min(dh, w.features.max_height - sz.h);
|
||||
dx = Math.max(dx, vp.x - (sx + vp.x));
|
||||
dy = Math.max(dy, vp.y - (sy + vp.y));
|
||||
dx = Math.min(dx, (vp.w + vp.x) - (sx + sz.w + vp.x));
|
||||
dy = Math.min(dy, (vp.h + vp.y) - (sy + sz.h + vp.y));
|
||||
|
||||
// Move if needed
|
||||
if (dx + dy !== 0) {
|
||||
if (sx + dx < 0)
|
||||
dx = 0;
|
||||
|
||||
if (sy + dy < 0)
|
||||
dy = 0;
|
||||
|
||||
ph.moveTo(sx + dx, sy + dy);
|
||||
}
|
||||
|
||||
// Resize if needed
|
||||
if (dw + dh !== 0)
|
||||
ph.resizeTo(sz.w + dw, sz.h + dh);
|
||||
|
||||
return Event.cancel(e);
|
||||
});
|
||||
|
||||
return Event.cancel(se);
|
||||
},
|
||||
|
||||
resizeBy : function(dw, dh, id) {
|
||||
var w = this.windows[id];
|
||||
|
||||
if (w) {
|
||||
w.element.resizeBy(dw, dh);
|
||||
w.iframeElement.resizeBy(dw, dh);
|
||||
}
|
||||
},
|
||||
|
||||
close : function(win, id) {
|
||||
var t = this, w, d = DOM.doc, fw, id;
|
||||
|
||||
id = t._findId(id || win);
|
||||
|
||||
// Probably not inline
|
||||
if (!t.windows[id]) {
|
||||
t.parent(win);
|
||||
return;
|
||||
}
|
||||
|
||||
t.count--;
|
||||
|
||||
if (t.count == 0) {
|
||||
DOM.remove('mceModalBlocker');
|
||||
DOM.setAttrib(DOM.doc.body, 'aria-hidden', 'false');
|
||||
t.editor.focus();
|
||||
}
|
||||
|
||||
if (w = t.windows[id]) {
|
||||
t.onClose.dispatch(t);
|
||||
Event.remove(d, 'mousedown', w.mousedownFunc);
|
||||
Event.remove(d, 'click', w.clickFunc);
|
||||
Event.clear(id);
|
||||
Event.clear(id + '_ifr');
|
||||
|
||||
DOM.setAttrib(id + '_ifr', 'src', 'javascript:""'); // Prevent leak
|
||||
w.element.remove();
|
||||
delete t.windows[id];
|
||||
|
||||
fw = t._frontWindow();
|
||||
|
||||
if (fw)
|
||||
t.focus(fw.id);
|
||||
}
|
||||
},
|
||||
|
||||
// Find front most window
|
||||
_frontWindow : function() {
|
||||
var fw, ix = 0;
|
||||
// Find front most window and focus that
|
||||
each (this.windows, function(w) {
|
||||
if (w.zIndex > ix) {
|
||||
fw = w;
|
||||
ix = w.zIndex;
|
||||
}
|
||||
});
|
||||
return fw;
|
||||
},
|
||||
|
||||
setTitle : function(w, ti) {
|
||||
var e;
|
||||
|
||||
w = this._findId(w);
|
||||
|
||||
if (e = DOM.get(w + '_title'))
|
||||
e.innerHTML = DOM.encode(ti);
|
||||
},
|
||||
|
||||
alert : function(txt, cb, s) {
|
||||
var t = this, w;
|
||||
|
||||
w = t.open({
|
||||
title : t,
|
||||
type : 'alert',
|
||||
button_func : function(s) {
|
||||
if (cb)
|
||||
cb.call(s || t, s);
|
||||
|
||||
t.close(null, w.id);
|
||||
},
|
||||
content : DOM.encode(t.editor.getLang(txt, txt)),
|
||||
inline : 1,
|
||||
width : 400,
|
||||
height : 130
|
||||
});
|
||||
},
|
||||
|
||||
confirm : function(txt, cb, s) {
|
||||
var t = this, w;
|
||||
|
||||
w = t.open({
|
||||
title : t,
|
||||
type : 'confirm',
|
||||
button_func : function(s) {
|
||||
if (cb)
|
||||
cb.call(s || t, s);
|
||||
|
||||
t.close(null, w.id);
|
||||
},
|
||||
content : DOM.encode(t.editor.getLang(txt, txt)),
|
||||
inline : 1,
|
||||
width : 400,
|
||||
height : 130
|
||||
});
|
||||
},
|
||||
|
||||
// Internal functions
|
||||
|
||||
_findId : function(w) {
|
||||
var t = this;
|
||||
|
||||
if (typeof(w) == 'string')
|
||||
return w;
|
||||
|
||||
each(t.windows, function(wo) {
|
||||
var ifr = DOM.get(wo.id + '_ifr');
|
||||
|
||||
if (ifr && w == ifr.contentWindow) {
|
||||
w = wo.id;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
return w;
|
||||
},
|
||||
|
||||
_fixIELayout : function(id, s) {
|
||||
var w, img;
|
||||
|
||||
if (!tinymce.isIE6)
|
||||
return;
|
||||
|
||||
// Fixes the bug where hover flickers and does odd things in IE6
|
||||
each(['n','s','w','e','nw','ne','sw','se'], function(v) {
|
||||
var e = DOM.get(id + '_resize_' + v);
|
||||
|
||||
DOM.setStyles(e, {
|
||||
width : s ? e.clientWidth : '',
|
||||
height : s ? e.clientHeight : '',
|
||||
cursor : DOM.getStyle(e, 'cursor', 1)
|
||||
});
|
||||
|
||||
DOM.setStyle(id + "_bottom", 'bottom', '-1px');
|
||||
|
||||
e = 0;
|
||||
});
|
||||
|
||||
// Fixes graphics glitch
|
||||
if (w = this.windows[id]) {
|
||||
// Fixes rendering bug after resize
|
||||
w.element.hide();
|
||||
w.element.show();
|
||||
|
||||
// Forced a repaint of the window
|
||||
//DOM.get(id).style.filter = '';
|
||||
|
||||
// IE has a bug where images used in CSS won't get loaded
|
||||
// sometimes when the cache in the browser is disabled
|
||||
// This fix tries to solve it by loading the images using the image object
|
||||
each(DOM.select('div,a', id), function(e, i) {
|
||||
if (e.currentStyle.backgroundImage != 'none') {
|
||||
img = new Image();
|
||||
img.src = e.currentStyle.backgroundImage.replace(/url\(\"(.+)\"\)/, '$1');
|
||||
}
|
||||
});
|
||||
|
||||
DOM.get(id).style.filter = '';
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('inlinepopups', tinymce.plugins.InlinePopups);
|
||||
})();
|
||||
|
|
@ -0,0 +1,890 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var rootAttributes = tinymce.explode('id,name,width,height,style,align,class,hspace,vspace,bgcolor,type'), excludedAttrs = tinymce.makeMap(rootAttributes.join(',')), Node = tinymce.html.Node,
|
||||
mediaTypes, scriptRegExp, JSON = tinymce.util.JSON, mimeTypes;
|
||||
|
||||
// Media types supported by this plugin
|
||||
mediaTypes = [
|
||||
// Type, clsid:s, mime types, codebase
|
||||
["Flash", "d27cdb6e-ae6d-11cf-96b8-444553540000", "application/x-shockwave-flash", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
|
||||
["ShockWave", "166b1bca-3f9c-11cf-8075-444553540000", "application/x-director", "http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],
|
||||
["WindowsMedia", "6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a", "application/x-mplayer2", "http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],
|
||||
["QuickTime", "02bf25d5-8c17-4b23-bc80-d3488abddc6b", "video/quicktime", "http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],
|
||||
["RealMedia", "cfcdaa03-8be4-11cf-b84b-0020afbbccfa", "audio/x-pn-realaudio-plugin", "http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],
|
||||
["Java", "8ad9c840-044e-11d1-b3e9-00805f499d93", "application/x-java-applet", "http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],
|
||||
["Silverlight", "dfeaf541-f3e1-4c24-acac-99c30715084a", "application/x-silverlight-2"],
|
||||
["Iframe"],
|
||||
["Video"],
|
||||
["EmbeddedAudio"],
|
||||
["Audio"]
|
||||
];
|
||||
|
||||
function toArray(obj) {
|
||||
var undef, out, i;
|
||||
|
||||
if (obj && !obj.splice) {
|
||||
out = [];
|
||||
|
||||
for (i = 0; true; i++) {
|
||||
if (obj[i])
|
||||
out[i] = obj[i];
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
tinymce.create('tinymce.plugins.MediaPlugin', {
|
||||
init : function(ed, url) {
|
||||
var self = this, lookup = {}, i, y, item, name;
|
||||
|
||||
function isMediaImg(node) {
|
||||
return node && node.nodeName === 'IMG' && ed.dom.hasClass(node, 'mceItemMedia');
|
||||
};
|
||||
|
||||
self.editor = ed;
|
||||
self.url = url;
|
||||
|
||||
// Parse media types into a lookup table
|
||||
scriptRegExp = '';
|
||||
for (i = 0; i < mediaTypes.length; i++) {
|
||||
name = mediaTypes[i][0];
|
||||
|
||||
item = {
|
||||
name : name,
|
||||
clsids : tinymce.explode(mediaTypes[i][1] || ''),
|
||||
mimes : tinymce.explode(mediaTypes[i][2] || ''),
|
||||
codebase : mediaTypes[i][3]
|
||||
};
|
||||
|
||||
for (y = 0; y < item.clsids.length; y++)
|
||||
lookup['clsid:' + item.clsids[y]] = item;
|
||||
|
||||
for (y = 0; y < item.mimes.length; y++)
|
||||
lookup[item.mimes[y]] = item;
|
||||
|
||||
lookup['mceItem' + name] = item;
|
||||
lookup[name.toLowerCase()] = item;
|
||||
|
||||
scriptRegExp += (scriptRegExp ? '|' : '') + name;
|
||||
}
|
||||
|
||||
// Handle the media_types setting
|
||||
tinymce.each(ed.getParam("media_types",
|
||||
"video=mp4,m4v,ogv,webm;" +
|
||||
"silverlight=xap;" +
|
||||
"flash=swf,flv;" +
|
||||
"shockwave=dcr;" +
|
||||
"quicktime=mov,qt,mpg,mpeg;" +
|
||||
"shockwave=dcr;" +
|
||||
"windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;" +
|
||||
"realmedia=rm,ra,ram;" +
|
||||
"java=jar;" +
|
||||
"audio=mp3,ogg"
|
||||
).split(';'), function(item) {
|
||||
var i, extensions, type;
|
||||
|
||||
item = item.split(/=/);
|
||||
extensions = tinymce.explode(item[1].toLowerCase());
|
||||
for (i = 0; i < extensions.length; i++) {
|
||||
type = lookup[item[0].toLowerCase()];
|
||||
|
||||
if (type)
|
||||
lookup[extensions[i]] = type;
|
||||
}
|
||||
});
|
||||
|
||||
scriptRegExp = new RegExp('write(' + scriptRegExp + ')\\(([^)]+)\\)');
|
||||
self.lookup = lookup;
|
||||
|
||||
ed.onPreInit.add(function() {
|
||||
// Allow video elements
|
||||
ed.schema.addValidElements('object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]');
|
||||
|
||||
// Convert video elements to image placeholder
|
||||
ed.parser.addNodeFilter('object,embed,video,audio,script,iframe', function(nodes) {
|
||||
var i = nodes.length;
|
||||
|
||||
while (i--)
|
||||
self.objectToImg(nodes[i]);
|
||||
});
|
||||
|
||||
// Convert image placeholders to video elements
|
||||
ed.serializer.addNodeFilter('img', function(nodes, name, args) {
|
||||
var i = nodes.length, node;
|
||||
|
||||
while (i--) {
|
||||
node = nodes[i];
|
||||
if ((node.attr('class') || '').indexOf('mceItemMedia') !== -1)
|
||||
self.imgToObject(node, args);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
ed.onInit.add(function() {
|
||||
// Display "media" instead of "img" in element path
|
||||
if (ed.theme && ed.theme.onResolveName) {
|
||||
ed.theme.onResolveName.add(function(theme, path_object) {
|
||||
if (path_object.name === 'img' && ed.dom.hasClass(path_object.node, 'mceItemMedia'))
|
||||
path_object.name = 'media';
|
||||
});
|
||||
}
|
||||
|
||||
// Add contect menu if it's loaded
|
||||
if (ed && ed.plugins.contextmenu) {
|
||||
ed.plugins.contextmenu.onContextMenu.add(function(plugin, menu, element) {
|
||||
if (element.nodeName === 'IMG' && element.className.indexOf('mceItemMedia') !== -1)
|
||||
menu.add({title : 'media.edit', icon : 'media', cmd : 'mceMedia'});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Register commands
|
||||
ed.addCommand('mceMedia', function() {
|
||||
var data, img;
|
||||
|
||||
img = ed.selection.getNode();
|
||||
if (isMediaImg(img)) {
|
||||
data = ed.dom.getAttrib(img, 'data-mce-json');
|
||||
if (data) {
|
||||
data = JSON.parse(data);
|
||||
|
||||
// Add some extra properties to the data object
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
var value = ed.dom.getAttrib(img, name);
|
||||
|
||||
if (value)
|
||||
data[name] = value;
|
||||
});
|
||||
|
||||
data.type = self.getType(img.className).name.toLowerCase();
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
data = {
|
||||
type : 'flash',
|
||||
video: {sources:[]},
|
||||
params: {}
|
||||
};
|
||||
}
|
||||
|
||||
ed.windowManager.open({
|
||||
file : url + '/media.htm',
|
||||
width : 430 + parseInt(ed.getLang('media.delta_width', 0)),
|
||||
height : 500 + parseInt(ed.getLang('media.delta_height', 0)),
|
||||
inline : 1
|
||||
}, {
|
||||
plugin_url : url,
|
||||
data : data
|
||||
});
|
||||
});
|
||||
|
||||
// Register buttons
|
||||
ed.addButton('media', {title : 'media.desc', cmd : 'mceMedia'});
|
||||
|
||||
// Update media selection status
|
||||
ed.onNodeChange.add(function(ed, cm, node) {
|
||||
cm.setActive('media', isMediaImg(node));
|
||||
});
|
||||
},
|
||||
|
||||
convertUrl : function(url, force_absolute) {
|
||||
var self = this, editor = self.editor, settings = editor.settings,
|
||||
urlConverter = settings.url_converter,
|
||||
urlConverterScope = settings.url_converter_scope || self;
|
||||
|
||||
if (!url)
|
||||
return url;
|
||||
|
||||
if (force_absolute)
|
||||
return editor.documentBaseURI.toAbsolute(url);
|
||||
|
||||
return urlConverter.call(urlConverterScope, url, 'src', 'object');
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Media',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the JSON data object to an img node.
|
||||
*/
|
||||
dataToImg : function(data, force_absolute) {
|
||||
var self = this, editor = self.editor, baseUri = editor.documentBaseURI, sources, attrs, img, i;
|
||||
|
||||
data.params.src = self.convertUrl(data.params.src, force_absolute);
|
||||
|
||||
attrs = data.video.attrs;
|
||||
if (attrs)
|
||||
attrs.src = self.convertUrl(attrs.src, force_absolute);
|
||||
|
||||
if (attrs)
|
||||
attrs.poster = self.convertUrl(attrs.poster, force_absolute);
|
||||
|
||||
sources = toArray(data.video.sources);
|
||||
if (sources) {
|
||||
for (i = 0; i < sources.length; i++)
|
||||
sources[i].src = self.convertUrl(sources[i].src, force_absolute);
|
||||
}
|
||||
|
||||
img = self.editor.dom.create('img', {
|
||||
id : data.id,
|
||||
style : data.style,
|
||||
align : data.align,
|
||||
hspace : data.hspace,
|
||||
vspace : data.vspace,
|
||||
src : self.editor.theme.url + '/img/trans.gif',
|
||||
'class' : 'mceItemMedia mceItem' + self.getType(data.type).name,
|
||||
'data-mce-json' : JSON.serialize(data, "'")
|
||||
});
|
||||
|
||||
img.width = data.width || (data.type == 'audio' ? "300" : "320");
|
||||
img.height = data.height || (data.type == 'audio' ? "32" : "240");
|
||||
|
||||
return img;
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the JSON data object to a HTML string.
|
||||
*/
|
||||
dataToHtml : function(data, force_absolute) {
|
||||
return this.editor.serializer.serialize(this.dataToImg(data, force_absolute), {forced_root_block : '', force_absolute : force_absolute});
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the JSON data object to a HTML string.
|
||||
*/
|
||||
htmlToData : function(html) {
|
||||
var fragment, img, data;
|
||||
|
||||
data = {
|
||||
type : 'flash',
|
||||
video: {sources:[]},
|
||||
params: {}
|
||||
};
|
||||
|
||||
fragment = this.editor.parser.parse(html);
|
||||
img = fragment.getAll('img')[0];
|
||||
|
||||
if (img) {
|
||||
data = JSON.parse(img.attr('data-mce-json'));
|
||||
data.type = this.getType(img.attr('class')).name.toLowerCase();
|
||||
|
||||
// Add some extra properties to the data object
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
var value = img.attr(name);
|
||||
|
||||
if (value)
|
||||
data[name] = value;
|
||||
});
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get type item by extension, class, clsid or mime type.
|
||||
*
|
||||
* @method getType
|
||||
* @param {String} value Value to get type item by.
|
||||
* @return {Object} Type item object or undefined.
|
||||
*/
|
||||
getType : function(value) {
|
||||
var i, values, typeItem;
|
||||
|
||||
// Find type by checking the classes
|
||||
values = tinymce.explode(value, ' ');
|
||||
for (i = 0; i < values.length; i++) {
|
||||
typeItem = this.lookup[values[i]];
|
||||
|
||||
if (typeItem)
|
||||
return typeItem;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a tinymce.html.Node image element to video/object/embed.
|
||||
*/
|
||||
imgToObject : function(node, args) {
|
||||
var self = this, editor = self.editor, video, object, embed, iframe, name, value, data,
|
||||
source, sources, params, param, typeItem, i, item, mp4Source, replacement,
|
||||
posterSrc, style, audio;
|
||||
|
||||
// Adds the flash player
|
||||
function addPlayer(video_src, poster_src) {
|
||||
var baseUri, flashVars, flashVarsOutput, params, flashPlayer;
|
||||
|
||||
flashPlayer = editor.getParam('flash_video_player_url', self.convertUrl(self.url + '/moxieplayer.swf'));
|
||||
if (flashPlayer) {
|
||||
baseUri = editor.documentBaseURI;
|
||||
data.params.src = flashPlayer;
|
||||
|
||||
// Convert the movie url to absolute urls
|
||||
if (editor.getParam('flash_video_player_absvideourl', true)) {
|
||||
video_src = baseUri.toAbsolute(video_src || '', true);
|
||||
poster_src = baseUri.toAbsolute(poster_src || '', true);
|
||||
}
|
||||
|
||||
// Generate flash vars
|
||||
flashVarsOutput = '';
|
||||
flashVars = editor.getParam('flash_video_player_flashvars', {url : '$url', poster : '$poster'});
|
||||
tinymce.each(flashVars, function(value, name) {
|
||||
// Replace $url and $poster variables in flashvars value
|
||||
value = value.replace(/\$url/, video_src || '');
|
||||
value = value.replace(/\$poster/, poster_src || '');
|
||||
|
||||
if (value.length > 0)
|
||||
flashVarsOutput += (flashVarsOutput ? '&' : '') + name + '=' + escape(value);
|
||||
});
|
||||
|
||||
if (flashVarsOutput.length)
|
||||
data.params.flashvars = flashVarsOutput;
|
||||
|
||||
params = editor.getParam('flash_video_player_params', {
|
||||
allowfullscreen: true,
|
||||
allowscriptaccess: true
|
||||
});
|
||||
|
||||
tinymce.each(params, function(value, name) {
|
||||
data.params[name] = "" + value;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
data = node.attr('data-mce-json');
|
||||
if (!data)
|
||||
return;
|
||||
|
||||
data = JSON.parse(data);
|
||||
typeItem = this.getType(node.attr('class'));
|
||||
|
||||
style = node.attr('data-mce-style')
|
||||
if (!style) {
|
||||
style = node.attr('style');
|
||||
|
||||
if (style)
|
||||
style = editor.dom.serializeStyle(editor.dom.parseStyle(style, 'img'));
|
||||
}
|
||||
|
||||
// Handle iframe
|
||||
if (typeItem.name === 'Iframe') {
|
||||
replacement = new Node('iframe', 1);
|
||||
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
var value = node.attr(name);
|
||||
|
||||
if (name == 'class' && value)
|
||||
value = value.replace(/mceItem.+ ?/g, '');
|
||||
|
||||
if (value && value.length > 0)
|
||||
replacement.attr(name, value);
|
||||
});
|
||||
|
||||
for (name in data.params)
|
||||
replacement.attr(name, data.params[name]);
|
||||
|
||||
replacement.attr({
|
||||
style: style,
|
||||
src: data.params.src
|
||||
});
|
||||
|
||||
node.replace(replacement);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle scripts
|
||||
if (this.editor.settings.media_use_script) {
|
||||
replacement = new Node('script', 1).attr('type', 'text/javascript');
|
||||
|
||||
value = new Node('#text', 3);
|
||||
value.value = 'write' + typeItem.name + '(' + JSON.serialize(tinymce.extend(data.params, {
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height')
|
||||
})) + ');';
|
||||
|
||||
replacement.append(value);
|
||||
node.replace(replacement);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Add HTML5 video element
|
||||
if (typeItem.name === 'Video' && data.video.sources[0]) {
|
||||
// Create new object element
|
||||
video = new Node('video', 1).attr(tinymce.extend({
|
||||
id : node.attr('id'),
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height'),
|
||||
style : style
|
||||
}, data.video.attrs));
|
||||
|
||||
// Get poster source and use that for flash fallback
|
||||
if (data.video.attrs)
|
||||
posterSrc = data.video.attrs.poster;
|
||||
|
||||
sources = data.video.sources = toArray(data.video.sources);
|
||||
for (i = 0; i < sources.length; i++) {
|
||||
if (/\.mp4$/.test(sources[i].src))
|
||||
mp4Source = sources[i].src;
|
||||
}
|
||||
|
||||
if (!sources[0].type) {
|
||||
video.attr('src', sources[0].src);
|
||||
sources.splice(0, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < sources.length; i++) {
|
||||
source = new Node('source', 1).attr(sources[i]);
|
||||
source.shortEnded = true;
|
||||
video.append(source);
|
||||
}
|
||||
|
||||
// Create flash fallback for video if we have a mp4 source
|
||||
if (mp4Source) {
|
||||
addPlayer(mp4Source, posterSrc);
|
||||
typeItem = self.getType('flash');
|
||||
} else
|
||||
data.params.src = '';
|
||||
}
|
||||
|
||||
// Add HTML5 audio element
|
||||
if (typeItem.name === 'Audio' && data.video.sources[0]) {
|
||||
// Create new object element
|
||||
audio = new Node('audio', 1).attr(tinymce.extend({
|
||||
id : node.attr('id'),
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height'),
|
||||
style : style
|
||||
}, data.video.attrs));
|
||||
|
||||
// Get poster source and use that for flash fallback
|
||||
if (data.video.attrs)
|
||||
posterSrc = data.video.attrs.poster;
|
||||
|
||||
sources = data.video.sources = toArray(data.video.sources);
|
||||
if (!sources[0].type) {
|
||||
audio.attr('src', sources[0].src);
|
||||
sources.splice(0, 1);
|
||||
}
|
||||
|
||||
for (i = 0; i < sources.length; i++) {
|
||||
source = new Node('source', 1).attr(sources[i]);
|
||||
source.shortEnded = true;
|
||||
audio.append(source);
|
||||
}
|
||||
|
||||
data.params.src = '';
|
||||
}
|
||||
|
||||
if (typeItem.name === 'EmbeddedAudio') {
|
||||
embed = new Node('embed', 1);
|
||||
embed.shortEnded = true;
|
||||
embed.attr({
|
||||
id: node.attr('id'),
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height'),
|
||||
style : style,
|
||||
type: node.attr('type')
|
||||
});
|
||||
|
||||
for (name in data.params)
|
||||
embed.attr(name, data.params[name]);
|
||||
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
if (data[name] && name != 'type')
|
||||
embed.attr(name, data[name]);
|
||||
});
|
||||
|
||||
data.params.src = '';
|
||||
}
|
||||
|
||||
// Do we have a params src then we can generate object
|
||||
if (data.params.src) {
|
||||
// Is flv movie add player for it
|
||||
if (/\.flv$/i.test(data.params.src))
|
||||
addPlayer(data.params.src, '');
|
||||
|
||||
if (args && args.force_absolute)
|
||||
data.params.src = editor.documentBaseURI.toAbsolute(data.params.src);
|
||||
|
||||
// Create new object element
|
||||
object = new Node('object', 1).attr({
|
||||
id : node.attr('id'),
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height'),
|
||||
style : style
|
||||
});
|
||||
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
var value = data[name];
|
||||
|
||||
if (name == 'class' && value)
|
||||
value = value.replace(/mceItem.+ ?/g, '');
|
||||
|
||||
if (value && name != 'type')
|
||||
object.attr(name, value);
|
||||
});
|
||||
|
||||
// Add params
|
||||
for (name in data.params) {
|
||||
param = new Node('param', 1);
|
||||
param.shortEnded = true;
|
||||
value = data.params[name];
|
||||
|
||||
// Windows media needs to use url instead of src for the media URL
|
||||
if (name === 'src' && typeItem.name === 'WindowsMedia')
|
||||
name = 'url';
|
||||
|
||||
param.attr({name: name, value: value});
|
||||
object.append(param);
|
||||
}
|
||||
|
||||
// Setup add type and classid if strict is disabled
|
||||
if (this.editor.getParam('media_strict', true)) {
|
||||
object.attr({
|
||||
data: data.params.src,
|
||||
type: typeItem.mimes[0]
|
||||
});
|
||||
} else {
|
||||
object.attr({
|
||||
classid: "clsid:" + typeItem.clsids[0],
|
||||
codebase: typeItem.codebase
|
||||
});
|
||||
|
||||
embed = new Node('embed', 1);
|
||||
embed.shortEnded = true;
|
||||
embed.attr({
|
||||
id: node.attr('id'),
|
||||
width: node.attr('width'),
|
||||
height: node.attr('height'),
|
||||
style : style,
|
||||
type: typeItem.mimes[0]
|
||||
});
|
||||
|
||||
for (name in data.params)
|
||||
embed.attr(name, data.params[name]);
|
||||
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
if (data[name] && name != 'type')
|
||||
embed.attr(name, data[name]);
|
||||
});
|
||||
|
||||
object.append(embed);
|
||||
}
|
||||
|
||||
// Insert raw HTML
|
||||
if (data.object_html) {
|
||||
value = new Node('#text', 3);
|
||||
value.raw = true;
|
||||
value.value = data.object_html;
|
||||
object.append(value);
|
||||
}
|
||||
|
||||
// Append object to video element if it exists
|
||||
if (video)
|
||||
video.append(object);
|
||||
}
|
||||
|
||||
if (video) {
|
||||
// Insert raw HTML
|
||||
if (data.video_html) {
|
||||
value = new Node('#text', 3);
|
||||
value.raw = true;
|
||||
value.value = data.video_html;
|
||||
video.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
if (audio) {
|
||||
// Insert raw HTML
|
||||
if (data.video_html) {
|
||||
value = new Node('#text', 3);
|
||||
value.raw = true;
|
||||
value.value = data.video_html;
|
||||
audio.append(value);
|
||||
}
|
||||
}
|
||||
|
||||
var n = video || audio || object || embed;
|
||||
if (n)
|
||||
node.replace(n);
|
||||
else
|
||||
node.remove();
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts a tinymce.html.Node video/object/embed to an img element.
|
||||
*
|
||||
* The video/object/embed will be converted into an image placeholder with a JSON data attribute like this:
|
||||
* <img class="mceItemMedia mceItemFlash" width="100" height="100" data-mce-json="{..}" />
|
||||
*
|
||||
* The JSON structure will be like this:
|
||||
* {'params':{'flashvars':'something','quality':'high','src':'someurl'}, 'video':{'sources':[{src: 'someurl', type: 'video/mp4'}]}}
|
||||
*/
|
||||
objectToImg : function(node) {
|
||||
var object, embed, video, iframe, img, name, id, width, height, style, i, html,
|
||||
param, params, source, sources, data, type, lookup = this.lookup,
|
||||
matches, attrs, urlConverter = this.editor.settings.url_converter,
|
||||
urlConverterScope = this.editor.settings.url_converter_scope,
|
||||
hspace, vspace, align, bgcolor;
|
||||
|
||||
function getInnerHTML(node) {
|
||||
return new tinymce.html.Serializer({
|
||||
inner: true,
|
||||
validate: false
|
||||
}).serialize(node);
|
||||
};
|
||||
|
||||
function lookupAttribute(o, attr) {
|
||||
return lookup[(o.attr(attr) || '').toLowerCase()];
|
||||
}
|
||||
|
||||
function lookupExtension(src) {
|
||||
var ext = src.replace(/^.*\.([^.]+)$/, '$1');
|
||||
return lookup[ext.toLowerCase() || ''];
|
||||
}
|
||||
|
||||
// If node isn't in document
|
||||
if (!node.parent)
|
||||
return;
|
||||
|
||||
// Handle media scripts
|
||||
if (node.name === 'script') {
|
||||
if (node.firstChild)
|
||||
matches = scriptRegExp.exec(node.firstChild.value);
|
||||
|
||||
if (!matches)
|
||||
return;
|
||||
|
||||
type = matches[1];
|
||||
data = {video : {}, params : JSON.parse(matches[2])};
|
||||
width = data.params.width;
|
||||
height = data.params.height;
|
||||
}
|
||||
|
||||
// Setup data objects
|
||||
data = data || {
|
||||
video : {},
|
||||
params : {}
|
||||
};
|
||||
|
||||
// Setup new image object
|
||||
img = new Node('img', 1);
|
||||
img.attr({
|
||||
src : this.editor.theme.url + '/img/trans.gif'
|
||||
});
|
||||
|
||||
// Video element
|
||||
name = node.name;
|
||||
if (name === 'video' || name == 'audio') {
|
||||
video = node;
|
||||
object = node.getAll('object')[0];
|
||||
embed = node.getAll('embed')[0];
|
||||
width = video.attr('width');
|
||||
height = video.attr('height');
|
||||
id = video.attr('id');
|
||||
data.video = {attrs : {}, sources : []};
|
||||
|
||||
// Get all video attributes
|
||||
attrs = data.video.attrs;
|
||||
for (name in video.attributes.map)
|
||||
attrs[name] = video.attributes.map[name];
|
||||
|
||||
source = node.attr('src');
|
||||
if (source)
|
||||
data.video.sources.push({src : urlConverter.call(urlConverterScope, source, 'src', node.name)});
|
||||
|
||||
// Get all sources
|
||||
sources = video.getAll("source");
|
||||
for (i = 0; i < sources.length; i++) {
|
||||
source = sources[i].remove();
|
||||
|
||||
data.video.sources.push({
|
||||
src: urlConverter.call(urlConverterScope, source.attr('src'), 'src', 'source'),
|
||||
type: source.attr('type'),
|
||||
media: source.attr('media')
|
||||
});
|
||||
}
|
||||
|
||||
// Convert the poster URL
|
||||
if (attrs.poster)
|
||||
attrs.poster = urlConverter.call(urlConverterScope, attrs.poster, 'poster', node.name);
|
||||
}
|
||||
|
||||
// Object element
|
||||
if (node.name === 'object') {
|
||||
object = node;
|
||||
embed = node.getAll('embed')[0];
|
||||
}
|
||||
|
||||
// Embed element
|
||||
if (node.name === 'embed')
|
||||
embed = node;
|
||||
|
||||
// Iframe element
|
||||
if (node.name === 'iframe') {
|
||||
iframe = node;
|
||||
type = 'Iframe';
|
||||
}
|
||||
|
||||
if (object) {
|
||||
// Get width/height
|
||||
width = width || object.attr('width');
|
||||
height = height || object.attr('height');
|
||||
style = style || object.attr('style');
|
||||
id = id || object.attr('id');
|
||||
hspace = hspace || object.attr('hspace');
|
||||
vspace = vspace || object.attr('vspace');
|
||||
align = align || object.attr('align');
|
||||
bgcolor = bgcolor || object.attr('bgcolor');
|
||||
data.name = object.attr('name');
|
||||
|
||||
// Get all object params
|
||||
params = object.getAll("param");
|
||||
for (i = 0; i < params.length; i++) {
|
||||
param = params[i];
|
||||
name = param.remove().attr('name');
|
||||
|
||||
if (!excludedAttrs[name])
|
||||
data.params[name] = param.attr('value');
|
||||
}
|
||||
|
||||
data.params.src = data.params.src || object.attr('data');
|
||||
}
|
||||
|
||||
if (embed) {
|
||||
// Get width/height
|
||||
width = width || embed.attr('width');
|
||||
height = height || embed.attr('height');
|
||||
style = style || embed.attr('style');
|
||||
id = id || embed.attr('id');
|
||||
hspace = hspace || embed.attr('hspace');
|
||||
vspace = vspace || embed.attr('vspace');
|
||||
align = align || embed.attr('align');
|
||||
bgcolor = bgcolor || embed.attr('bgcolor');
|
||||
|
||||
// Get all embed attributes
|
||||
for (name in embed.attributes.map) {
|
||||
if (!excludedAttrs[name] && !data.params[name])
|
||||
data.params[name] = embed.attributes.map[name];
|
||||
}
|
||||
}
|
||||
|
||||
if (iframe) {
|
||||
// Get width/height
|
||||
width = iframe.attr('width');
|
||||
height = iframe.attr('height');
|
||||
style = style || iframe.attr('style');
|
||||
id = iframe.attr('id');
|
||||
hspace = iframe.attr('hspace');
|
||||
vspace = iframe.attr('vspace');
|
||||
align = iframe.attr('align');
|
||||
bgcolor = iframe.attr('bgcolor');
|
||||
|
||||
tinymce.each(rootAttributes, function(name) {
|
||||
img.attr(name, iframe.attr(name));
|
||||
});
|
||||
|
||||
// Get all iframe attributes
|
||||
for (name in iframe.attributes.map) {
|
||||
if (!excludedAttrs[name] && !data.params[name])
|
||||
data.params[name] = iframe.attributes.map[name];
|
||||
}
|
||||
}
|
||||
|
||||
// Use src not movie
|
||||
if (data.params.movie) {
|
||||
data.params.src = data.params.src || data.params.movie;
|
||||
delete data.params.movie;
|
||||
}
|
||||
|
||||
// Convert the URL to relative/absolute depending on configuration
|
||||
if (data.params.src)
|
||||
data.params.src = urlConverter.call(urlConverterScope, data.params.src, 'src', 'object');
|
||||
|
||||
if (video) {
|
||||
if (node.name === 'video')
|
||||
type = lookup.video.name;
|
||||
else if (node.name === 'audio')
|
||||
type = lookup.audio.name;
|
||||
}
|
||||
|
||||
if (object && !type)
|
||||
type = (lookupAttribute(object, 'clsid') || lookupAttribute(object, 'classid') || lookupAttribute(object, 'type') || {}).name;
|
||||
|
||||
if (embed && !type)
|
||||
type = (lookupAttribute(embed, 'type') || lookupExtension(data.params.src) || {}).name;
|
||||
|
||||
// for embedded audio we preserve the original specified type
|
||||
if (embed && type == 'EmbeddedAudio') {
|
||||
data.params.type = embed.attr('type');
|
||||
}
|
||||
|
||||
// Replace the video/object/embed element with a placeholder image containing the data
|
||||
node.replace(img);
|
||||
|
||||
// Remove embed
|
||||
if (embed)
|
||||
embed.remove();
|
||||
|
||||
// Serialize the inner HTML of the object element
|
||||
if (object) {
|
||||
html = getInnerHTML(object.remove());
|
||||
|
||||
if (html)
|
||||
data.object_html = html;
|
||||
}
|
||||
|
||||
// Serialize the inner HTML of the video element
|
||||
if (video) {
|
||||
html = getInnerHTML(video.remove());
|
||||
|
||||
if (html)
|
||||
data.video_html = html;
|
||||
}
|
||||
|
||||
data.hspace = hspace;
|
||||
data.vspace = vspace;
|
||||
data.align = align;
|
||||
data.bgcolor = bgcolor;
|
||||
|
||||
// Set width/height of placeholder
|
||||
img.attr({
|
||||
id : id,
|
||||
'class' : 'mceItemMedia mceItem' + (type || 'Flash'),
|
||||
style : style,
|
||||
width : width || (node.name == 'audio' ? "300" : "320"),
|
||||
height : height || (node.name == 'audio' ? "32" : "240"),
|
||||
hspace : hspace,
|
||||
vspace : vspace,
|
||||
align : align,
|
||||
bgcolor : bgcolor,
|
||||
"data-mce-json" : JSON.serialize(data, "'")
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('media', tinymce.plugins.MediaPlugin);
|
||||
})();
|
|
@ -0,0 +1,871 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var each = tinymce.each,
|
||||
defs = {
|
||||
paste_auto_cleanup_on_paste : true,
|
||||
paste_enable_default_filters : true,
|
||||
paste_block_drop : false,
|
||||
paste_retain_style_properties : "none",
|
||||
paste_strip_class_attributes : "mso",
|
||||
paste_remove_spans : false,
|
||||
paste_remove_styles : false,
|
||||
paste_remove_styles_if_webkit : true,
|
||||
paste_convert_middot_lists : true,
|
||||
paste_convert_headers_to_strong : false,
|
||||
paste_dialog_width : "450",
|
||||
paste_dialog_height : "400",
|
||||
paste_text_use_dialog : false,
|
||||
paste_text_sticky : false,
|
||||
paste_text_sticky_default : false,
|
||||
paste_text_notifyalways : false,
|
||||
paste_text_linebreaktype : "combined",
|
||||
paste_text_replacements : [
|
||||
[/\u2026/g, "..."],
|
||||
[/[\x93\x94\u201c\u201d]/g, '"'],
|
||||
[/[\x60\x91\x92\u2018\u2019]/g, "'"]
|
||||
]
|
||||
};
|
||||
|
||||
function getParam(ed, name) {
|
||||
return ed.getParam(name, defs[name]);
|
||||
}
|
||||
|
||||
tinymce.create('tinymce.plugins.PastePlugin', {
|
||||
init : function(ed, url) {
|
||||
var t = this;
|
||||
|
||||
t.editor = ed;
|
||||
t.url = url;
|
||||
|
||||
// Setup plugin events
|
||||
t.onPreProcess = new tinymce.util.Dispatcher(t);
|
||||
t.onPostProcess = new tinymce.util.Dispatcher(t);
|
||||
|
||||
// Register default handlers
|
||||
t.onPreProcess.add(t._preProcess);
|
||||
t.onPostProcess.add(t._postProcess);
|
||||
|
||||
// Register optional preprocess handler
|
||||
t.onPreProcess.add(function(pl, o) {
|
||||
ed.execCallback('paste_preprocess', pl, o);
|
||||
});
|
||||
|
||||
// Register optional postprocess
|
||||
t.onPostProcess.add(function(pl, o) {
|
||||
ed.execCallback('paste_postprocess', pl, o);
|
||||
});
|
||||
|
||||
ed.onKeyDown.addToTop(function(ed, e) {
|
||||
// Block ctrl+v from adding an undo level since the default logic in tinymce.Editor will add that
|
||||
if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
|
||||
return false; // Stop other listeners
|
||||
});
|
||||
|
||||
// Initialize plain text flag
|
||||
ed.pasteAsPlainText = getParam(ed, 'paste_text_sticky_default');
|
||||
|
||||
// This function executes the process handlers and inserts the contents
|
||||
// force_rich overrides plain text mode set by user, important for pasting with execCommand
|
||||
function process(o, force_rich) {
|
||||
var dom = ed.dom, rng;
|
||||
|
||||
// Execute pre process handlers
|
||||
t.onPreProcess.dispatch(t, o);
|
||||
|
||||
// Create DOM structure
|
||||
o.node = dom.create('div', 0, o.content);
|
||||
|
||||
// If pasting inside the same element and the contents is only one block
|
||||
// remove the block and keep the text since Firefox will copy parts of pre and h1-h6 as a pre element
|
||||
if (tinymce.isGecko) {
|
||||
rng = ed.selection.getRng(true);
|
||||
if (rng.startContainer == rng.endContainer && rng.startContainer.nodeType == 3) {
|
||||
// Is only one block node and it doesn't contain word stuff
|
||||
if (o.node.childNodes.length === 1 && /^(p|h[1-6]|pre)$/i.test(o.node.firstChild.nodeName) && o.content.indexOf('__MCE_ITEM__') === -1)
|
||||
dom.remove(o.node.firstChild, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Execute post process handlers
|
||||
t.onPostProcess.dispatch(t, o);
|
||||
|
||||
// Serialize content
|
||||
o.content = ed.serializer.serialize(o.node, {getInner : 1, forced_root_block : ''});
|
||||
|
||||
// Plain text option active?
|
||||
if ((!force_rich) && (ed.pasteAsPlainText)) {
|
||||
t._insertPlainText(o.content);
|
||||
|
||||
if (!getParam(ed, "paste_text_sticky")) {
|
||||
ed.pasteAsPlainText = false;
|
||||
ed.controlManager.setActive("pastetext", false);
|
||||
}
|
||||
} else {
|
||||
t._insert(o.content);
|
||||
}
|
||||
}
|
||||
|
||||
// Add command for external usage
|
||||
ed.addCommand('mceInsertClipboardContent', function(u, o) {
|
||||
process(o, true);
|
||||
});
|
||||
|
||||
if (!getParam(ed, "paste_text_use_dialog")) {
|
||||
ed.addCommand('mcePasteText', function(u, v) {
|
||||
var cookie = tinymce.util.Cookie;
|
||||
|
||||
ed.pasteAsPlainText = !ed.pasteAsPlainText;
|
||||
ed.controlManager.setActive('pastetext', ed.pasteAsPlainText);
|
||||
|
||||
if ((ed.pasteAsPlainText) && (!cookie.get("tinymcePasteText"))) {
|
||||
if (getParam(ed, "paste_text_sticky")) {
|
||||
ed.windowManager.alert(ed.translate('paste.plaintext_mode_sticky'));
|
||||
} else {
|
||||
ed.windowManager.alert(ed.translate('paste.plaintext_mode'));
|
||||
}
|
||||
|
||||
if (!getParam(ed, "paste_text_notifyalways")) {
|
||||
cookie.set("tinymcePasteText", "1", new Date(new Date().getFullYear() + 1, 12, 31))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
ed.addButton('pastetext', {title: 'paste.paste_text_desc', cmd: 'mcePasteText'});
|
||||
ed.addButton('selectall', {title: 'paste.selectall_desc', cmd: 'selectall'});
|
||||
|
||||
// This function grabs the contents from the clipboard by adding a
|
||||
// hidden div and placing the caret inside it and after the browser paste
|
||||
// is done it grabs that contents and processes that
|
||||
function grabContent(e) {
|
||||
var n, or, rng, oldRng, sel = ed.selection, dom = ed.dom, body = ed.getBody(), posY, textContent;
|
||||
|
||||
// Check if browser supports direct plaintext access
|
||||
if (e.clipboardData || dom.doc.dataTransfer) {
|
||||
textContent = (e.clipboardData || dom.doc.dataTransfer).getData('Text');
|
||||
|
||||
if (ed.pasteAsPlainText) {
|
||||
e.preventDefault();
|
||||
process({content : dom.encode(textContent).replace(/\r?\n/g, '<br />')});
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (dom.get('_mcePaste'))
|
||||
return;
|
||||
|
||||
// Create container to paste into
|
||||
n = dom.add(body, 'div', {id : '_mcePaste', 'class' : 'mcePaste', 'data-mce-bogus' : '1'}, '\uFEFF\uFEFF');
|
||||
|
||||
// If contentEditable mode we need to find out the position of the closest element
|
||||
if (body != ed.getDoc().body)
|
||||
posY = dom.getPos(ed.selection.getStart(), body).y;
|
||||
else
|
||||
posY = body.scrollTop + dom.getViewPort(ed.getWin()).y;
|
||||
|
||||
// Styles needs to be applied after the element is added to the document since WebKit will otherwise remove all styles
|
||||
// If also needs to be in view on IE or the paste would fail
|
||||
dom.setStyles(n, {
|
||||
position : 'absolute',
|
||||
left : tinymce.isGecko ? -40 : 0, // Need to move it out of site on Gecko since it will othewise display a ghost resize rect for the div
|
||||
top : posY - 25,
|
||||
width : 1,
|
||||
height : 1,
|
||||
overflow : 'hidden'
|
||||
});
|
||||
|
||||
if (tinymce.isIE) {
|
||||
// Store away the old range
|
||||
oldRng = sel.getRng();
|
||||
|
||||
// Select the container
|
||||
rng = dom.doc.body.createTextRange();
|
||||
rng.moveToElementText(n);
|
||||
rng.execCommand('Paste');
|
||||
|
||||
// Remove container
|
||||
dom.remove(n);
|
||||
|
||||
// Check if the contents was changed, if it wasn't then clipboard extraction failed probably due
|
||||
// to IE security settings so we pass the junk though better than nothing right
|
||||
if (n.innerHTML === '\uFEFF\uFEFF') {
|
||||
ed.execCommand('mcePasteWord');
|
||||
e.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Restore the old range and clear the contents before pasting
|
||||
sel.setRng(oldRng);
|
||||
sel.setContent('');
|
||||
|
||||
// For some odd reason we need to detach the the mceInsertContent call from the paste event
|
||||
// It's like IE has a reference to the parent element that you paste in and the selection gets messed up
|
||||
// when it tries to restore the selection
|
||||
setTimeout(function() {
|
||||
// Process contents
|
||||
process({content : n.innerHTML});
|
||||
}, 0);
|
||||
|
||||
// Block the real paste event
|
||||
return tinymce.dom.Event.cancel(e);
|
||||
} else {
|
||||
function block(e) {
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
// Block mousedown and click to prevent selection change
|
||||
dom.bind(ed.getDoc(), 'mousedown', block);
|
||||
dom.bind(ed.getDoc(), 'keydown', block);
|
||||
|
||||
or = ed.selection.getRng();
|
||||
|
||||
// Move select contents inside DIV
|
||||
n = n.firstChild;
|
||||
rng = ed.getDoc().createRange();
|
||||
rng.setStart(n, 0);
|
||||
rng.setEnd(n, 2);
|
||||
sel.setRng(rng);
|
||||
|
||||
// Wait a while and grab the pasted contents
|
||||
window.setTimeout(function() {
|
||||
var h = '', nl;
|
||||
|
||||
// Paste divs duplicated in paste divs seems to happen when you paste plain text so lets first look for that broken behavior in WebKit
|
||||
if (!dom.select('div.mcePaste > div.mcePaste').length) {
|
||||
nl = dom.select('div.mcePaste');
|
||||
|
||||
// WebKit will split the div into multiple ones so this will loop through then all and join them to get the whole HTML string
|
||||
each(nl, function(n) {
|
||||
var child = n.firstChild;
|
||||
|
||||
// WebKit inserts a DIV container with lots of odd styles
|
||||
if (child && child.nodeName == 'DIV' && child.style.marginTop && child.style.backgroundColor) {
|
||||
dom.remove(child, 1);
|
||||
}
|
||||
|
||||
// Remove apply style spans
|
||||
each(dom.select('span.Apple-style-span', n), function(n) {
|
||||
dom.remove(n, 1);
|
||||
});
|
||||
|
||||
// Remove bogus br elements
|
||||
each(dom.select('br[data-mce-bogus]', n), function(n) {
|
||||
dom.remove(n);
|
||||
});
|
||||
|
||||
// WebKit will make a copy of the DIV for each line of plain text pasted and insert them into the DIV
|
||||
if (n.parentNode.className != 'mcePaste')
|
||||
h += n.innerHTML;
|
||||
});
|
||||
} else {
|
||||
// Found WebKit weirdness so force the content into paragraphs this seems to happen when you paste plain text from Nodepad etc
|
||||
// So this logic will replace double enter with paragraphs and single enter with br so it kind of looks the same
|
||||
h = '<p>' + dom.encode(textContent).replace(/\r?\n\r?\n/g, '</p><p>').replace(/\r?\n/g, '<br />') + '</p>';
|
||||
}
|
||||
|
||||
// Remove the nodes
|
||||
each(dom.select('div.mcePaste'), function(n) {
|
||||
dom.remove(n);
|
||||
});
|
||||
|
||||
// Restore the old selection
|
||||
if (or)
|
||||
sel.setRng(or);
|
||||
|
||||
process({content : h});
|
||||
|
||||
// Unblock events ones we got the contents
|
||||
dom.unbind(ed.getDoc(), 'mousedown', block);
|
||||
dom.unbind(ed.getDoc(), 'keydown', block);
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we should use the new auto process method
|
||||
if (getParam(ed, "paste_auto_cleanup_on_paste")) {
|
||||
// Is it's Opera or older FF use key handler
|
||||
if (tinymce.isOpera || /Firefox\/2/.test(navigator.userAgent)) {
|
||||
ed.onKeyDown.addToTop(function(ed, e) {
|
||||
if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
|
||||
grabContent(e);
|
||||
});
|
||||
} else {
|
||||
// Grab contents on paste event on Gecko and WebKit
|
||||
ed.onPaste.addToTop(function(ed, e) {
|
||||
return grabContent(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ed.onInit.add(function() {
|
||||
ed.controlManager.setActive("pastetext", ed.pasteAsPlainText);
|
||||
|
||||
// Block all drag/drop events
|
||||
if (getParam(ed, "paste_block_drop")) {
|
||||
ed.dom.bind(ed.getBody(), ['dragend', 'dragover', 'draggesture', 'dragdrop', 'drop', 'drag'], function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Add legacy support
|
||||
t._legacySupport();
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Paste text/word',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
},
|
||||
|
||||
_preProcess : function(pl, o) {
|
||||
var ed = this.editor,
|
||||
h = o.content,
|
||||
grep = tinymce.grep,
|
||||
explode = tinymce.explode,
|
||||
trim = tinymce.trim,
|
||||
len, stripClass;
|
||||
|
||||
//console.log('Before preprocess:' + o.content);
|
||||
|
||||
function process(items) {
|
||||
each(items, function(v) {
|
||||
// Remove or replace
|
||||
if (v.constructor == RegExp)
|
||||
h = h.replace(v, '');
|
||||
else
|
||||
h = h.replace(v[0], v[1]);
|
||||
});
|
||||
}
|
||||
|
||||
if (ed.settings.paste_enable_default_filters == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
|
||||
if (tinymce.isIE && document.documentMode >= 9) {
|
||||
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
|
||||
process([[/(?:<br> [\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br> [\s\r\n]+|<br>)*/g, '$1']]);
|
||||
|
||||
// IE9 also adds an extra BR element for each soft-linefeed and it also adds a BR for each word wrap break
|
||||
process([
|
||||
[/<br><br>/g, '<BR><BR>'], // Replace multiple BR elements with uppercase BR to keep them intact
|
||||
[/<br>/g, ' '], // Replace single br elements with space since they are word wrap BR:s
|
||||
[/<BR><BR>/g, '<br>'] // Replace back the double brs but into a single BR
|
||||
]);
|
||||
}
|
||||
|
||||
// Detect Word content and process it more aggressive
|
||||
if (/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(h) || o.wordContent) {
|
||||
o.wordContent = true; // Mark the pasted contents as word specific content
|
||||
//console.log('Word contents detected.');
|
||||
|
||||
// Process away some basic content
|
||||
process([
|
||||
/^\s*( )+/gi, // entities at the start of contents
|
||||
/( |<br[^>]*>)+\s*$/gi // entities at the end of contents
|
||||
]);
|
||||
|
||||
if (getParam(ed, "paste_convert_headers_to_strong")) {
|
||||
h = h.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi, "<p><strong>$1</strong></p>");
|
||||
}
|
||||
|
||||
if (getParam(ed, "paste_convert_middot_lists")) {
|
||||
process([
|
||||
[/<!--\[if !supportLists\]-->/gi, '$&__MCE_ITEM__'], // Convert supportLists to a list item marker
|
||||
[/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi, '$1__MCE_ITEM__'], // Convert mso-list and symbol spans to item markers
|
||||
[/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi, '$1__MCE_ITEM__'] // Convert mso-list and symbol paragraphs to item markers (FF)
|
||||
]);
|
||||
}
|
||||
|
||||
process([
|
||||
// Word comments like conditional comments etc
|
||||
/<!--[\s\S]+?-->/gi,
|
||||
|
||||
// Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, MS Office namespaced tags, and a few other tags
|
||||
/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,
|
||||
|
||||
// Convert <s> into <strike> for line-though
|
||||
[/<(\/?)s>/gi, "<$1strike>"],
|
||||
|
||||
// Replace nsbp entites to char since it's easier to handle
|
||||
[/ /gi, "\u00a0"]
|
||||
]);
|
||||
|
||||
// Remove bad attributes, with or without quotes, ensuring that attribute text is really inside a tag.
|
||||
// If JavaScript had a RegExp look-behind, we could have integrated this with the last process() array and got rid of the loop. But alas, it does not, so we cannot.
|
||||
do {
|
||||
len = h.length;
|
||||
h = h.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi, "$1");
|
||||
} while (len != h.length);
|
||||
|
||||
// Remove all spans if no styles is to be retained
|
||||
if (getParam(ed, "paste_retain_style_properties").replace(/^none$/i, "").length == 0) {
|
||||
h = h.replace(/<\/?span[^>]*>/gi, "");
|
||||
} else {
|
||||
// We're keeping styles, so at least clean them up.
|
||||
// CSS Reference: http://msdn.microsoft.com/en-us/library/aa155477.aspx
|
||||
|
||||
process([
|
||||
// Convert <span style="mso-spacerun:yes">___</span> to string of alternating breaking/non-breaking spaces of same length
|
||||
[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,
|
||||
function(str, spaces) {
|
||||
return (spaces.length > 0)? spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : "";
|
||||
}
|
||||
],
|
||||
|
||||
// Examine all styles: delete junk, transform some, and keep the rest
|
||||
[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,
|
||||
function(str, tag, style) {
|
||||
var n = [],
|
||||
i = 0,
|
||||
s = explode(trim(style).replace(/"/gi, "'"), ";");
|
||||
|
||||
// Examine each style definition within the tag's style attribute
|
||||
each(s, function(v) {
|
||||
var name, value,
|
||||
parts = explode(v, ":");
|
||||
|
||||
function ensureUnits(v) {
|
||||
return v + ((v !== "0") && (/\d$/.test(v)))? "px" : "";
|
||||
}
|
||||
|
||||
if (parts.length == 2) {
|
||||
name = parts[0].toLowerCase();
|
||||
value = parts[1].toLowerCase();
|
||||
|
||||
// Translate certain MS Office styles into their CSS equivalents
|
||||
switch (name) {
|
||||
case "mso-padding-alt":
|
||||
case "mso-padding-top-alt":
|
||||
case "mso-padding-right-alt":
|
||||
case "mso-padding-bottom-alt":
|
||||
case "mso-padding-left-alt":
|
||||
case "mso-margin-alt":
|
||||
case "mso-margin-top-alt":
|
||||
case "mso-margin-right-alt":
|
||||
case "mso-margin-bottom-alt":
|
||||
case "mso-margin-left-alt":
|
||||
case "mso-table-layout-alt":
|
||||
case "mso-height":
|
||||
case "mso-width":
|
||||
case "mso-vertical-align-alt":
|
||||
n[i++] = name.replace(/^mso-|-alt$/g, "") + ":" + ensureUnits(value);
|
||||
return;
|
||||
|
||||
case "horiz-align":
|
||||
n[i++] = "text-align:" + value;
|
||||
return;
|
||||
|
||||
case "vert-align":
|
||||
n[i++] = "vertical-align:" + value;
|
||||
return;
|
||||
|
||||
case "font-color":
|
||||
case "mso-foreground":
|
||||
n[i++] = "color:" + value;
|
||||
return;
|
||||
|
||||
case "mso-background":
|
||||
case "mso-highlight":
|
||||
n[i++] = "background:" + value;
|
||||
return;
|
||||
|
||||
case "mso-default-height":
|
||||
n[i++] = "min-height:" + ensureUnits(value);
|
||||
return;
|
||||
|
||||
case "mso-default-width":
|
||||
n[i++] = "min-width:" + ensureUnits(value);
|
||||
return;
|
||||
|
||||
case "mso-padding-between-alt":
|
||||
n[i++] = "border-collapse:separate;border-spacing:" + ensureUnits(value);
|
||||
return;
|
||||
|
||||
case "text-line-through":
|
||||
if ((value == "single") || (value == "double")) {
|
||||
n[i++] = "text-decoration:line-through";
|
||||
}
|
||||
return;
|
||||
|
||||
case "mso-zero-height":
|
||||
if (value == "yes") {
|
||||
n[i++] = "display:none";
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Eliminate all MS Office style definitions that have no CSS equivalent by examining the first characters in the name
|
||||
if (/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(name)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If it reached this point, it must be a valid CSS style
|
||||
n[i++] = name + ":" + parts[1]; // Lower-case name, but keep value case
|
||||
}
|
||||
});
|
||||
|
||||
// If style attribute contained any valid styles the re-write it; otherwise delete style attribute.
|
||||
if (i > 0) {
|
||||
return tag + ' style="' + n.join(';') + '"';
|
||||
} else {
|
||||
return tag;
|
||||
}
|
||||
}
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Replace headers with <strong>
|
||||
if (getParam(ed, "paste_convert_headers_to_strong")) {
|
||||
process([
|
||||
[/<h[1-6][^>]*>/gi, "<p><strong>"],
|
||||
[/<\/h[1-6][^>]*>/gi, "</strong></p>"]
|
||||
]);
|
||||
}
|
||||
|
||||
process([
|
||||
// Copy paste from Java like Open Office will produce this junk on FF
|
||||
[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi, '']
|
||||
]);
|
||||
|
||||
// Class attribute options are: leave all as-is ("none"), remove all ("all"), or remove only those starting with mso ("mso").
|
||||
// Note:- paste_strip_class_attributes: "none", verify_css_classes: true is also a good variation.
|
||||
stripClass = getParam(ed, "paste_strip_class_attributes");
|
||||
|
||||
if (stripClass !== "none") {
|
||||
function removeClasses(match, g1) {
|
||||
if (stripClass === "all")
|
||||
return '';
|
||||
|
||||
var cls = grep(explode(g1.replace(/^(["'])(.*)\1$/, "$2"), " "),
|
||||
function(v) {
|
||||
return (/^(?!mso)/i.test(v));
|
||||
}
|
||||
);
|
||||
|
||||
return cls.length ? ' class="' + cls.join(" ") + '"' : '';
|
||||
};
|
||||
|
||||
h = h.replace(/ class="([^"]+)"/gi, removeClasses);
|
||||
h = h.replace(/ class=([\-\w]+)/gi, removeClasses);
|
||||
}
|
||||
|
||||
// Remove spans option
|
||||
if (getParam(ed, "paste_remove_spans")) {
|
||||
h = h.replace(/<\/?span[^>]*>/gi, "");
|
||||
}
|
||||
|
||||
//console.log('After preprocess:' + h);
|
||||
|
||||
o.content = h;
|
||||
},
|
||||
|
||||
/**
|
||||
* Various post process items.
|
||||
*/
|
||||
_postProcess : function(pl, o) {
|
||||
var t = this, ed = t.editor, dom = ed.dom, styleProps;
|
||||
|
||||
if (ed.settings.paste_enable_default_filters == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (o.wordContent) {
|
||||
// Remove named anchors or TOC links
|
||||
each(dom.select('a', o.node), function(a) {
|
||||
if (!a.href || a.href.indexOf('#_Toc') != -1)
|
||||
dom.remove(a, 1);
|
||||
});
|
||||
|
||||
if (getParam(ed, "paste_convert_middot_lists")) {
|
||||
t._convertLists(pl, o);
|
||||
}
|
||||
|
||||
// Process styles
|
||||
styleProps = getParam(ed, "paste_retain_style_properties"); // retained properties
|
||||
|
||||
// Process only if a string was specified and not equal to "all" or "*"
|
||||
if ((tinymce.is(styleProps, "string")) && (styleProps !== "all") && (styleProps !== "*")) {
|
||||
styleProps = tinymce.explode(styleProps.replace(/^none$/i, ""));
|
||||
|
||||
// Retains some style properties
|
||||
each(dom.select('*', o.node), function(el) {
|
||||
var newStyle = {}, npc = 0, i, sp, sv;
|
||||
|
||||
// Store a subset of the existing styles
|
||||
if (styleProps) {
|
||||
for (i = 0; i < styleProps.length; i++) {
|
||||
sp = styleProps[i];
|
||||
sv = dom.getStyle(el, sp);
|
||||
|
||||
if (sv) {
|
||||
newStyle[sp] = sv;
|
||||
npc++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all of the existing styles
|
||||
dom.setAttrib(el, 'style', '');
|
||||
|
||||
if (styleProps && npc > 0)
|
||||
dom.setStyles(el, newStyle); // Add back the stored subset of styles
|
||||
else // Remove empty span tags that do not have class attributes
|
||||
if (el.nodeName == 'SPAN' && !el.className)
|
||||
dom.remove(el, true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Remove all style information or only specifically on WebKit to avoid the style bug on that browser
|
||||
if (getParam(ed, "paste_remove_styles") || (getParam(ed, "paste_remove_styles_if_webkit") && tinymce.isWebKit)) {
|
||||
each(dom.select('*[style]', o.node), function(el) {
|
||||
el.removeAttribute('style');
|
||||
el.removeAttribute('data-mce-style');
|
||||
});
|
||||
} else {
|
||||
if (tinymce.isWebKit) {
|
||||
// We need to compress the styles on WebKit since if you paste <img border="0" /> it will become <img border="0" style="... lots of junk ..." />
|
||||
// Removing the mce_style that contains the real value will force the Serializer engine to compress the styles
|
||||
each(dom.select('*', o.node), function(el) {
|
||||
el.removeAttribute('data-mce-style');
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Converts the most common bullet and number formats in Office into a real semantic UL/LI list.
|
||||
*/
|
||||
_convertLists : function(pl, o) {
|
||||
var dom = pl.editor.dom, listElm, li, lastMargin = -1, margin, levels = [], lastType, html;
|
||||
|
||||
// Convert middot lists into real semantic lists
|
||||
each(dom.select('p', o.node), function(p) {
|
||||
var sib, val = '', type, html, idx, parents;
|
||||
|
||||
// Get text node value at beginning of paragraph
|
||||
for (sib = p.firstChild; sib && sib.nodeType == 3; sib = sib.nextSibling)
|
||||
val += sib.nodeValue;
|
||||
|
||||
val = p.innerHTML.replace(/<\/?\w+[^>]*>/gi, '').replace(/ /g, '\u00a0');
|
||||
|
||||
// Detect unordered lists look for bullets
|
||||
if (/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(val))
|
||||
type = 'ul';
|
||||
|
||||
// Detect ordered lists 1., a. or ixv.
|
||||
if (/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(val))
|
||||
type = 'ol';
|
||||
|
||||
// Check if node value matches the list pattern: o
|
||||
if (type) {
|
||||
margin = parseFloat(p.style.marginLeft || 0);
|
||||
|
||||
if (margin > lastMargin)
|
||||
levels.push(margin);
|
||||
|
||||
if (!listElm || type != lastType) {
|
||||
listElm = dom.create(type);
|
||||
dom.insertAfter(listElm, p);
|
||||
} else {
|
||||
// Nested list element
|
||||
if (margin > lastMargin) {
|
||||
listElm = li.appendChild(dom.create(type));
|
||||
} else if (margin < lastMargin) {
|
||||
// Find parent level based on margin value
|
||||
idx = tinymce.inArray(levels, margin);
|
||||
parents = dom.getParents(listElm.parentNode, type);
|
||||
listElm = parents[parents.length - 1 - idx] || listElm;
|
||||
}
|
||||
}
|
||||
|
||||
// Remove middot or number spans if they exists
|
||||
each(dom.select('span', p), function(span) {
|
||||
var html = span.innerHTML.replace(/<\/?\w+[^>]*>/gi, '');
|
||||
|
||||
// Remove span with the middot or the number
|
||||
if (type == 'ul' && /^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(html))
|
||||
dom.remove(span);
|
||||
else if (/^__MCE_ITEM__[\s\S]*\w+\.( |\u00a0)*\s*/.test(html))
|
||||
dom.remove(span);
|
||||
});
|
||||
|
||||
html = p.innerHTML;
|
||||
|
||||
// Remove middot/list items
|
||||
if (type == 'ul')
|
||||
html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*( |\u00a0)+\s*/, '');
|
||||
else
|
||||
html = p.innerHTML.replace(/__MCE_ITEM__/g, '').replace(/^\s*\w+\.( |\u00a0)+\s*/, '');
|
||||
|
||||
// Create li and add paragraph data into the new li
|
||||
li = listElm.appendChild(dom.create('li', 0, html));
|
||||
dom.remove(p);
|
||||
|
||||
lastMargin = margin;
|
||||
lastType = type;
|
||||
} else
|
||||
listElm = lastMargin = 0; // End list element
|
||||
});
|
||||
|
||||
// Remove any left over makers
|
||||
html = o.node.innerHTML;
|
||||
if (html.indexOf('__MCE_ITEM__') != -1)
|
||||
o.node.innerHTML = html.replace(/__MCE_ITEM__/g, '');
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts the specified contents at the caret position.
|
||||
*/
|
||||
_insert : function(h, skip_undo) {
|
||||
var ed = this.editor, r = ed.selection.getRng();
|
||||
|
||||
// First delete the contents seems to work better on WebKit when the selection spans multiple list items or multiple table cells.
|
||||
if (!ed.selection.isCollapsed() && r.startContainer != r.endContainer)
|
||||
ed.getDoc().execCommand('Delete', false, null);
|
||||
|
||||
ed.execCommand('mceInsertContent', false, h, {skip_undo : skip_undo});
|
||||
},
|
||||
|
||||
/**
|
||||
* Instead of the old plain text method which tried to re-create a paste operation, the
|
||||
* new approach adds a plain text mode toggle switch that changes the behavior of paste.
|
||||
* This function is passed the same input that the regular paste plugin produces.
|
||||
* It performs additional scrubbing and produces (and inserts) the plain text.
|
||||
* This approach leverages all of the great existing functionality in the paste
|
||||
* plugin, and requires minimal changes to add the new functionality.
|
||||
* Speednet - June 2009
|
||||
*/
|
||||
_insertPlainText : function(content) {
|
||||
var ed = this.editor,
|
||||
linebr = getParam(ed, "paste_text_linebreaktype"),
|
||||
rl = getParam(ed, "paste_text_replacements"),
|
||||
is = tinymce.is;
|
||||
|
||||
function process(items) {
|
||||
each(items, function(v) {
|
||||
if (v.constructor == RegExp)
|
||||
content = content.replace(v, "");
|
||||
else
|
||||
content = content.replace(v[0], v[1]);
|
||||
});
|
||||
};
|
||||
|
||||
if ((typeof(content) === "string") && (content.length > 0)) {
|
||||
// If HTML content with line-breaking tags, then remove all cr/lf chars because only tags will break a line
|
||||
if (/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(content)) {
|
||||
process([
|
||||
/[\n\r]+/g
|
||||
]);
|
||||
} else {
|
||||
// Otherwise just get rid of carriage returns (only need linefeeds)
|
||||
process([
|
||||
/\r+/g
|
||||
]);
|
||||
}
|
||||
|
||||
process([
|
||||
[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi, "\n\n"], // Block tags get a blank line after them
|
||||
[/<br[^>]*>|<\/tr>/gi, "\n"], // Single linebreak for <br /> tags and table rows
|
||||
[/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"], // Table cells get tabs betweem them
|
||||
/<[a-z!\/?][^>]*>/gi, // Delete all remaining tags
|
||||
[/ /gi, " "], // Convert non-break spaces to regular spaces (remember, *plain text*)
|
||||
[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"],// Cool little RegExp deletes whitespace around linebreak chars.
|
||||
[/\n{3,}/g, "\n\n"] // Max. 2 consecutive linebreaks
|
||||
]);
|
||||
|
||||
content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content));
|
||||
|
||||
// Perform default or custom replacements
|
||||
if (is(rl, "array")) {
|
||||
process(rl);
|
||||
} else if (is(rl, "string")) {
|
||||
process(new RegExp(rl, "gi"));
|
||||
}
|
||||
|
||||
// Treat paragraphs as specified in the config
|
||||
if (linebr == "none") {
|
||||
// Convert all line breaks to space
|
||||
process([
|
||||
[/\n+/g, " "]
|
||||
]);
|
||||
} else if (linebr == "br") {
|
||||
// Convert all line breaks to <br />
|
||||
process([
|
||||
[/\n/g, "<br />"]
|
||||
]);
|
||||
} else if (linebr == "p") {
|
||||
// Convert all line breaks to <p>...</p>
|
||||
process([
|
||||
[/\n+/g, "</p><p>"],
|
||||
[/^(.*<\/p>)(<p>)$/, '<p>$1']
|
||||
]);
|
||||
} else {
|
||||
// defaults to "combined"
|
||||
// Convert single line breaks to <br /> and double line breaks to <p>...</p>
|
||||
process([
|
||||
[/\n\n/g, "</p><p>"],
|
||||
[/^(.*<\/p>)(<p>)$/, '<p>$1'],
|
||||
[/\n/g, "<br />"]
|
||||
]);
|
||||
}
|
||||
|
||||
ed.execCommand('mceInsertContent', false, content);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* This method will open the old style paste dialogs. Some users might want the old behavior but still use the new cleanup engine.
|
||||
*/
|
||||
_legacySupport : function() {
|
||||
var t = this, ed = t.editor;
|
||||
|
||||
// Register command(s) for backwards compatibility
|
||||
ed.addCommand("mcePasteWord", function() {
|
||||
ed.windowManager.open({
|
||||
file: t.url + "/pasteword.htm",
|
||||
width: parseInt(getParam(ed, "paste_dialog_width")),
|
||||
height: parseInt(getParam(ed, "paste_dialog_height")),
|
||||
inline: 1
|
||||
});
|
||||
});
|
||||
|
||||
if (getParam(ed, "paste_text_use_dialog")) {
|
||||
ed.addCommand("mcePasteText", function() {
|
||||
ed.windowManager.open({
|
||||
file : t.url + "/pastetext.htm",
|
||||
width: parseInt(getParam(ed, "paste_dialog_width")),
|
||||
height: parseInt(getParam(ed, "paste_dialog_height")),
|
||||
inline : 1
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Register button for backwards compatibility
|
||||
ed.addButton("pasteword", {title : "paste.paste_word_desc", cmd : "mcePasteWord"});
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add("paste", tinymce.plugins.PastePlugin);
|
||||
})();
|
|
@ -0,0 +1,436 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var JSONRequest = tinymce.util.JSONRequest, each = tinymce.each, DOM = tinymce.DOM;
|
||||
|
||||
tinymce.create('tinymce.plugins.SpellcheckerPlugin', {
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Spellchecker',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
},
|
||||
|
||||
init : function(ed, url) {
|
||||
var t = this, cm;
|
||||
|
||||
t.url = url;
|
||||
t.editor = ed;
|
||||
t.rpcUrl = ed.getParam("spellchecker_rpc_url", "{backend}");
|
||||
|
||||
if (t.rpcUrl == '{backend}') {
|
||||
// Sniff if the browser supports native spellchecking (Don't know of a better way)
|
||||
if (tinymce.isIE)
|
||||
return;
|
||||
|
||||
t.hasSupport = true;
|
||||
|
||||
// Disable the context menu when spellchecking is active
|
||||
ed.onContextMenu.addToTop(function(ed, e) {
|
||||
if (t.active)
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
// Register commands
|
||||
ed.addCommand('mceSpellCheck', function() {
|
||||
if (t.rpcUrl == '{backend}') {
|
||||
// Enable/disable native spellchecker
|
||||
t.editor.getBody().spellcheck = t.active = !t.active;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!t.active) {
|
||||
ed.setProgressState(1);
|
||||
t._sendRPC('checkWords', [t.selectedLang, t._getWords()], function(r) {
|
||||
if (r.length > 0) {
|
||||
t.active = 1;
|
||||
t._markWords(r);
|
||||
ed.setProgressState(0);
|
||||
ed.nodeChanged();
|
||||
} else {
|
||||
ed.setProgressState(0);
|
||||
|
||||
if (ed.getParam('spellchecker_report_no_misspellings', true))
|
||||
ed.windowManager.alert('spellchecker.no_mpell');
|
||||
}
|
||||
});
|
||||
} else
|
||||
t._done();
|
||||
});
|
||||
|
||||
if (ed.settings.content_css !== false)
|
||||
ed.contentCSS.push(url + '/css/content.css');
|
||||
|
||||
ed.onClick.add(t._showMenu, t);
|
||||
ed.onContextMenu.add(t._showMenu, t);
|
||||
ed.onBeforeGetContent.add(function() {
|
||||
if (t.active)
|
||||
t._removeWords();
|
||||
});
|
||||
|
||||
ed.onNodeChange.add(function(ed, cm) {
|
||||
cm.setActive('spellchecker', t.active);
|
||||
});
|
||||
|
||||
ed.onSetContent.add(function() {
|
||||
t._done();
|
||||
});
|
||||
|
||||
ed.onBeforeGetContent.add(function() {
|
||||
t._done();
|
||||
});
|
||||
|
||||
ed.onBeforeExecCommand.add(function(ed, cmd) {
|
||||
if (cmd == 'mceFullScreen')
|
||||
t._done();
|
||||
});
|
||||
|
||||
// Find selected language
|
||||
t.languages = {};
|
||||
each(ed.getParam('spellchecker_languages', '+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv', 'hash'), function(v, k) {
|
||||
if (k.indexOf('+') === 0) {
|
||||
k = k.substring(1);
|
||||
t.selectedLang = v;
|
||||
}
|
||||
|
||||
t.languages[k] = v;
|
||||
});
|
||||
},
|
||||
|
||||
createControl : function(n, cm) {
|
||||
var t = this, c, ed = t.editor;
|
||||
|
||||
if (n == 'spellchecker') {
|
||||
// Use basic button if we use the native spellchecker
|
||||
if (t.rpcUrl == '{backend}') {
|
||||
// Create simple toggle button if we have native support
|
||||
if (t.hasSupport)
|
||||
c = cm.createButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
c = cm.createSplitButton(n, {title : 'spellchecker.desc', cmd : 'mceSpellCheck', scope : t});
|
||||
|
||||
c.onRenderMenu.add(function(c, m) {
|
||||
m.add({title : 'spellchecker.langs', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
|
||||
each(t.languages, function(v, k) {
|
||||
var o = {icon : 1}, mi;
|
||||
|
||||
o.onclick = function() {
|
||||
if (v == t.selectedLang) {
|
||||
return;
|
||||
}
|
||||
mi.setSelected(1);
|
||||
t.selectedItem.setSelected(0);
|
||||
t.selectedItem = mi;
|
||||
t.selectedLang = v;
|
||||
};
|
||||
|
||||
o.title = k;
|
||||
mi = m.add(o);
|
||||
mi.setSelected(v == t.selectedLang);
|
||||
|
||||
if (v == t.selectedLang)
|
||||
t.selectedItem = mi;
|
||||
})
|
||||
});
|
||||
|
||||
return c;
|
||||
}
|
||||
},
|
||||
|
||||
// Internal functions
|
||||
|
||||
_walk : function(n, f) {
|
||||
var d = this.editor.getDoc(), w;
|
||||
|
||||
if (d.createTreeWalker) {
|
||||
w = d.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, false);
|
||||
|
||||
while ((n = w.nextNode()) != null)
|
||||
f.call(this, n);
|
||||
} else
|
||||
tinymce.walk(n, f, 'childNodes');
|
||||
},
|
||||
|
||||
_getSeparators : function() {
|
||||
var re = '', i, str = this.editor.getParam('spellchecker_word_separator_chars', '\\s!"#$%&()*+,-./:;<=>?@[\]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');
|
||||
|
||||
// Build word separator regexp
|
||||
for (i=0; i<str.length; i++)
|
||||
re += '\\' + str.charAt(i);
|
||||
|
||||
return re;
|
||||
},
|
||||
|
||||
_getWords : function() {
|
||||
var ed = this.editor, wl = [], tx = '', lo = {}, rawWords = [];
|
||||
|
||||
// Get area text
|
||||
this._walk(ed.getBody(), function(n) {
|
||||
if (n.nodeType == 3)
|
||||
tx += n.nodeValue + ' ';
|
||||
});
|
||||
|
||||
// split the text up into individual words
|
||||
if (ed.getParam('spellchecker_word_pattern')) {
|
||||
// look for words that match the pattern
|
||||
rawWords = tx.match('(' + ed.getParam('spellchecker_word_pattern') + ')', 'gi');
|
||||
} else {
|
||||
// Split words by separator
|
||||
tx = tx.replace(new RegExp('([0-9]|[' + this._getSeparators() + '])', 'g'), ' ');
|
||||
tx = tinymce.trim(tx.replace(/(\s+)/g, ' '));
|
||||
rawWords = tx.split(' ');
|
||||
}
|
||||
|
||||
// Build word array and remove duplicates
|
||||
each(rawWords, function(v) {
|
||||
if (!lo[v]) {
|
||||
wl.push(v);
|
||||
lo[v] = 1;
|
||||
}
|
||||
});
|
||||
|
||||
return wl;
|
||||
},
|
||||
|
||||
_removeWords : function(w) {
|
||||
var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
|
||||
|
||||
each(dom.select('span').reverse(), function(n) {
|
||||
if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
|
||||
if (!w || dom.decode(n.innerHTML) == w)
|
||||
dom.remove(n, 1);
|
||||
}
|
||||
});
|
||||
|
||||
se.moveToBookmark(b);
|
||||
},
|
||||
|
||||
_markWords : function(wl) {
|
||||
var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [],
|
||||
w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g');
|
||||
|
||||
// Collect all text nodes
|
||||
this._walk(ed.getBody(), function(n) {
|
||||
if (n.nodeType == 3) {
|
||||
nl.push(n);
|
||||
}
|
||||
});
|
||||
|
||||
// Wrap incorrect words in spans
|
||||
each(nl, function(n) {
|
||||
var node, elem, txt, pos, v = n.nodeValue;
|
||||
|
||||
if (rx.test(v)) {
|
||||
// Encode the content
|
||||
v = dom.encode(v);
|
||||
// Create container element
|
||||
elem = dom.create('span', {'class' : 'mceItemHidden'});
|
||||
|
||||
// Following code fixes IE issues by creating text nodes
|
||||
// using DOM methods instead of innerHTML.
|
||||
// Bug #3124: <PRE> elements content is broken after spellchecking.
|
||||
// Bug #1408: Preceding whitespace characters are removed
|
||||
// @TODO: I'm not sure that both are still issues on IE9.
|
||||
if (tinymce.isIE) {
|
||||
// Enclose mispelled words with temporal tag
|
||||
v = v.replace(rx, '$1<mcespell>$2</mcespell>');
|
||||
// Loop over the content finding mispelled words
|
||||
while ((pos = v.indexOf('<mcespell>')) != -1) {
|
||||
// Add text node for the content before the word
|
||||
txt = v.substring(0, pos);
|
||||
if (txt.length) {
|
||||
node = doc.createTextNode(dom.decode(txt));
|
||||
elem.appendChild(node);
|
||||
}
|
||||
v = v.substring(pos+10);
|
||||
pos = v.indexOf('</mcespell>');
|
||||
txt = v.substring(0, pos);
|
||||
v = v.substring(pos+11);
|
||||
// Add span element for the word
|
||||
elem.appendChild(dom.create('span', {'class' : 'mceItemHiddenSpellWord'}, txt));
|
||||
}
|
||||
// Add text node for the rest of the content
|
||||
if (v.length) {
|
||||
node = doc.createTextNode(dom.decode(v));
|
||||
elem.appendChild(node);
|
||||
}
|
||||
} else {
|
||||
// Other browsers preserve whitespace characters on innerHTML usage
|
||||
elem.innerHTML = v.replace(rx, '$1<span class="mceItemHiddenSpellWord">$2</span>');
|
||||
}
|
||||
|
||||
// Finally, replace the node with the container
|
||||
dom.replace(elem, n);
|
||||
}
|
||||
});
|
||||
|
||||
se.moveToBookmark(b);
|
||||
},
|
||||
|
||||
_showMenu : function(ed, e) {
|
||||
var t = this, ed = t.editor, m = t._menu, p1, dom = ed.dom, vp = dom.getViewPort(ed.getWin()), wordSpan = e.target;
|
||||
|
||||
e = 0; // Fixes IE memory leak
|
||||
|
||||
if (!m) {
|
||||
m = ed.controlManager.createDropMenu('spellcheckermenu', {'class' : 'mceNoIcons'});
|
||||
t._menu = m;
|
||||
}
|
||||
|
||||
if (dom.hasClass(wordSpan, 'mceItemHiddenSpellWord')) {
|
||||
m.removeAll();
|
||||
m.add({title : 'spellchecker.wait', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
|
||||
|
||||
t._sendRPC('getSuggestions', [t.selectedLang, dom.decode(wordSpan.innerHTML)], function(r) {
|
||||
var ignoreRpc;
|
||||
|
||||
m.removeAll();
|
||||
|
||||
if (r.length > 0) {
|
||||
m.add({title : 'spellchecker.sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
|
||||
each(r, function(v) {
|
||||
m.add({title : v, onclick : function() {
|
||||
dom.replace(ed.getDoc().createTextNode(v), wordSpan);
|
||||
t._checkDone();
|
||||
}});
|
||||
});
|
||||
|
||||
m.addSeparator();
|
||||
} else
|
||||
m.add({title : 'spellchecker.no_sug', 'class' : 'mceMenuItemTitle'}).setDisabled(1);
|
||||
|
||||
if (ed.getParam('show_ignore_words', true)) {
|
||||
ignoreRpc = t.editor.getParam("spellchecker_enable_ignore_rpc", '');
|
||||
m.add({
|
||||
title : 'spellchecker.ignore_word',
|
||||
onclick : function() {
|
||||
var word = wordSpan.innerHTML;
|
||||
|
||||
dom.remove(wordSpan, 1);
|
||||
t._checkDone();
|
||||
|
||||
// tell the server if we need to
|
||||
if (ignoreRpc) {
|
||||
ed.setProgressState(1);
|
||||
t._sendRPC('ignoreWord', [t.selectedLang, word], function(r) {
|
||||
ed.setProgressState(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
m.add({
|
||||
title : 'spellchecker.ignore_words',
|
||||
onclick : function() {
|
||||
var word = wordSpan.innerHTML;
|
||||
|
||||
t._removeWords(dom.decode(word));
|
||||
t._checkDone();
|
||||
|
||||
// tell the server if we need to
|
||||
if (ignoreRpc) {
|
||||
ed.setProgressState(1);
|
||||
t._sendRPC('ignoreWords', [t.selectedLang, word], function(r) {
|
||||
ed.setProgressState(0);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (t.editor.getParam("spellchecker_enable_learn_rpc")) {
|
||||
m.add({
|
||||
title : 'spellchecker.learn_word',
|
||||
onclick : function() {
|
||||
var word = wordSpan.innerHTML;
|
||||
|
||||
dom.remove(wordSpan, 1);
|
||||
t._checkDone();
|
||||
|
||||
ed.setProgressState(1);
|
||||
t._sendRPC('learnWord', [t.selectedLang, word], function(r) {
|
||||
ed.setProgressState(0);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
m.update();
|
||||
});
|
||||
|
||||
p1 = DOM.getPos(ed.getContentAreaContainer());
|
||||
m.settings.offset_x = p1.x;
|
||||
m.settings.offset_y = p1.y;
|
||||
|
||||
ed.selection.select(wordSpan);
|
||||
p1 = dom.getPos(wordSpan);
|
||||
m.showMenu(p1.x, p1.y + wordSpan.offsetHeight - vp.y);
|
||||
|
||||
return tinymce.dom.Event.cancel(e);
|
||||
} else
|
||||
m.hideMenu();
|
||||
},
|
||||
|
||||
_checkDone : function() {
|
||||
var t = this, ed = t.editor, dom = ed.dom, o;
|
||||
|
||||
each(dom.select('span'), function(n) {
|
||||
if (n && dom.hasClass(n, 'mceItemHiddenSpellWord')) {
|
||||
o = true;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
if (!o)
|
||||
t._done();
|
||||
},
|
||||
|
||||
_done : function() {
|
||||
var t = this, la = t.active;
|
||||
|
||||
if (t.active) {
|
||||
t.active = 0;
|
||||
t._removeWords();
|
||||
|
||||
if (t._menu)
|
||||
t._menu.hideMenu();
|
||||
|
||||
if (la)
|
||||
t.editor.nodeChanged();
|
||||
}
|
||||
},
|
||||
|
||||
_sendRPC : function(m, p, cb) {
|
||||
var t = this;
|
||||
|
||||
JSONRequest.sendRPC({
|
||||
url : t.rpcUrl,
|
||||
method : m,
|
||||
params : p,
|
||||
success : cb,
|
||||
error : function(e, x) {
|
||||
t.editor.setProgressState(0);
|
||||
t.editor.windowManager.alert(e.errstr || ('Error response: ' + x.responseText));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('spellchecker', tinymce.plugins.SpellcheckerPlugin);
|
||||
})();
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
* editor_plugin_src.js
|
||||
*
|
||||
* Copyright 2009, Moxiecode Systems AB
|
||||
* Released under LGPL License.
|
||||
*
|
||||
* License: http://tinymce.moxiecode.com/license
|
||||
* Contributing: http://tinymce.moxiecode.com/contributing
|
||||
*/
|
||||
|
||||
(function() {
|
||||
var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, explode = tinymce.explode;
|
||||
|
||||
tinymce.create('tinymce.plugins.TabFocusPlugin', {
|
||||
init : function(ed, url) {
|
||||
function tabCancel(ed, e) {
|
||||
if (e.keyCode === 9)
|
||||
return Event.cancel(e);
|
||||
}
|
||||
|
||||
function tabHandler(ed, e) {
|
||||
var x, i, f, el, v;
|
||||
|
||||
function find(d) {
|
||||
el = DOM.select(':input:enabled,*[tabindex]');
|
||||
|
||||
function canSelectRecursive(e) {
|
||||
return e.nodeName==="BODY" || (e.type != 'hidden' &&
|
||||
!(e.style.display == "none") &&
|
||||
!(e.style.visibility == "hidden") && canSelectRecursive(e.parentNode));
|
||||
}
|
||||
function canSelectInOldIe(el) {
|
||||
return el.attributes["tabIndex"].specified || el.nodeName == "INPUT" || el.nodeName == "TEXTAREA";
|
||||
}
|
||||
function isOldIe() {
|
||||
return tinymce.isIE6 || tinymce.isIE7;
|
||||
}
|
||||
function canSelect(el) {
|
||||
return ((!isOldIe() || canSelectInOldIe(el))) && el.getAttribute("tabindex") != '-1' && canSelectRecursive(el);
|
||||
}
|
||||
|
||||
each(el, function(e, i) {
|
||||
if (e.id == ed.id) {
|
||||
x = i;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (d > 0) {
|
||||
for (i = x + 1; i < el.length; i++) {
|
||||
if (canSelect(el[i]))
|
||||
return el[i];
|
||||
}
|
||||
} else {
|
||||
for (i = x - 1; i >= 0; i--) {
|
||||
if (canSelect(el[i]))
|
||||
return el[i];
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
if (e.keyCode === 9) {
|
||||
v = explode(ed.getParam('tab_focus', ed.getParam('tabfocus_elements', ':prev,:next')));
|
||||
|
||||
if (v.length == 1) {
|
||||
v[1] = v[0];
|
||||
v[0] = ':prev';
|
||||
}
|
||||
|
||||
// Find element to focus
|
||||
if (e.shiftKey) {
|
||||
if (v[0] == ':prev')
|
||||
el = find(-1);
|
||||
else
|
||||
el = DOM.get(v[0]);
|
||||
} else {
|
||||
if (v[1] == ':next')
|
||||
el = find(1);
|
||||
else
|
||||
el = DOM.get(v[1]);
|
||||
}
|
||||
|
||||
if (el) {
|
||||
if (el.id && (ed = tinymce.get(el.id || el.name)))
|
||||
ed.focus();
|
||||
else
|
||||
window.setTimeout(function() {
|
||||
if (!tinymce.isWebKit)
|
||||
window.focus();
|
||||
el.focus();
|
||||
}, 10);
|
||||
|
||||
return Event.cancel(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ed.onKeyUp.add(tabCancel);
|
||||
|
||||
if (tinymce.isGecko) {
|
||||
ed.onKeyPress.add(tabHandler);
|
||||
ed.onKeyDown.add(tabCancel);
|
||||
} else
|
||||
ed.onKeyDown.add(tabHandler);
|
||||
|
||||
},
|
||||
|
||||
getInfo : function() {
|
||||
return {
|
||||
longname : 'Tabfocus',
|
||||
author : 'Moxiecode Systems AB',
|
||||
authorurl : 'http://tinymce.moxiecode.com',
|
||||
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus',
|
||||
version : tinymce.majorVersion + "." + tinymce.minorVersion
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Register plugin
|
||||
tinymce.PluginManager.add('tabfocus', tinymce.plugins.TabFocusPlugin);
|
||||
})();
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue