uppdated modules

This commit is contained in:
Bachir Soussi Chiadmi
2017-01-22 16:19:36 +01:00
parent 03be8f82aa
commit 8416e3eea1
748 changed files with 32317 additions and 20900 deletions
View File
+4 -2
View File
@@ -1,4 +1,3 @@
This is a highly flexible and easy extendable filter module to embed any type
of video in your site using a simple tag. Other modules can add video
sites/formats (called codecs) using an easy plug-in architecture.
@@ -22,6 +21,9 @@ processed after that filter.
To enable WYSIWYG support, go to the WYSIWYG settings for each input format and
enable the Video Filter button.
To enable CKEditor (standalone) support, please see plugin instructions in:
editors/ckeditor/README.txt
========= Usage =========
Single video: [video:url]
@@ -74,7 +76,7 @@ function MODULE_youtube($video) {
// $video contains the video URL in source, the codec (as above) and also
// [code][matches] with the result of the regexp and [codec][delta] with the
// key of the matched regexp.
$video['source'] = 'http://www.youtube.com/v/' . $video['codec']['matches'][1] . ($video['autoplay'] ? '&autoplay=1' : '');
$video['source'] = '//www.youtube.com/v/' . $video['codec']['matches'][1] . ($video['autoplay'] ? '&autoplay=1' : '');
// Outputs a general <object...> for embedding flash players. Needs width,
// height, source and optionally align (left or right) and params (a list of
+8 -28
View File
@@ -1,35 +1,15 @@
##############################################
## ONLY if you use ckeditor WITHOUT wysiwyg ##
##############################################
############################################################
## ONLY if with the CKEditor module, *NOT* WYSIWYG module ##
############################################################
Installation:
Do the following steps to add video_filter button to the CKEditor toolbar:
1. Open ckeditor.config.js (in the ckeditor module root)
1. Go to Configuration -> CKEditor (admin/config/content/ckeditor)
Click "Edit" on the profile you what to use with the video filter.
2. Scroll down to the end of the file, right before "};" insert:
2. Expand "Editor appearance" and go to "Toolbar". Drag the new video_filter
button from the "All Buttons" toolbar to the "Used Buttons" toolbar.
// Video_filter plugin.
config.extraPlugins += (config.extraPlugins ? ',video_filter' : 'video_filter' );
CKEDITOR.plugins.addExternal('video_filter', Drupal.settings.basePath + Drupal.settings.video_filter.modulepath + '/editors/ckeditor/');
3. Add button to the toolbar.
3.1 Go to Configuration -> CKEditor (admin/config/content/ckeditor)
Click "Edit" on the profile you what to use with Linkit.
3.2 Expand "Editor appearance" and go to "Toolbar".
The button name is: video_filter
For example if you have a toolbar with an array of buttons defined as
follows:
['Bold','Italic']
simply add the button somewhere in the array:
['Bold','Italic','video_filter']
(remember the single quotes).
3. Go to "Plugins" on the same page. Enable the video filter plugin.
+114 -10
View File
@@ -7,25 +7,126 @@
requires : [],
init: function(editor) {
// Add Button
editor.ui.addButton('video_filter', {
label: 'Video filter',
command: 'video_filter',
icon: this.path + 'video_filter.png'
});
// Add Command
editor.addCommand('video_filter', {
exec : function () {
var path = (Drupal.settings.video_filter.url.wysiwyg_ckeditor) ? Drupal.settings.video_filter.url.wysiwyg_ckeditor : Drupal.settings.video_filter.url.ckeditor
var media = window.showModalDialog(path, { 'opener' : window, 'editorname' : editor.name }, "dialogWidth:580px; dialogHeight:480px; center:yes; resizable:yes; help:no;");
}
});
// Register an extra fucntion, this will be used in the popup.
editor._.video_filterFnNum = CKEDITOR.tools.addFunction(insert, editor);
if(typeof window.showModalDialog !== 'undefined') {
editor.addCommand('video_filter', {
exec : function () {
var path = (Drupal.settings.video_filter.url.wysiwyg_ckeditor) ? Drupal.settings.video_filter.url.wysiwyg_ckeditor : Drupal.settings.video_filter.url.ckeditor
var media = window.showModalDialog(path, { 'opener' : window, 'editorname' : editor.name }, "dialogWidth:580px; dialogHeight:480px; center:yes; resizable:yes; help:no;");
}
});
// Register an extra function, this will be used in the popup.
editor._.video_filterFnNum = CKEDITOR.tools.addFunction(insert, editor);
}
else {
editor.addCommand('video_filter', new CKEDITOR.dialogCommand('video_filterDialog'));
}
}
});
CKEDITOR.dialog.add('video_filterDialog', function( editor ) {
var instructions_path = Drupal.settings.video_filter.instructions_url;
return {
title : 'Add Video',
minWidth : 600,
minHeight : 180,
contents : [{
id : 'general',
label : 'Settings',
elements : [
{
type : 'text',
id : 'file_url',
label : 'URL',
validate : CKEDITOR.dialog.validate.notEmpty( 'The link must have a URL.' ),
required : true,
commit : function( data )
{
data.file_url = this.getValue();
}
},
{
type : 'text',
id : 'width',
label : 'Width',
commit : function( data )
{
data.width = this.getValue();
}
},
{
type : 'text',
id : 'height',
label : 'Height',
commit : function( data )
{
data.height = this.getValue();
}
},
{
type : 'select',
id : 'align',
label : 'Align',
'default': 'none',
items: [
['None', ''],
['Left', 'left'],
['Right', 'right'],
['Center', 'center']
],
commit : function( data )
{
data.align = this.getValue();
}
},
{
type : 'checkbox',
id : 'autoplay',
label : 'Autoplay',
'default': '',
commit : function( data )
{
data.autoplay = this.getValue() ? 1 : 0;
}
},
{
type: 'html',
html: '<iframe src="' + instructions_path + '" style="width:100%; height: 200px;"></iframe>',
},
]
}],
onOk : function()
{
var dialog = this,
data = {},
link = editor.document.createElement( 'p' );
this.commitContent( data );
var str = '[video:' + data.file_url;
if (data.width) {
str += ' width:' + data.width;
}
if (data.height) {
str += ' height:' + data.height;
}
if (data.align) {
str += ' align:' + data.align;
}
if (data.autoplay) {
str += ' autoplay:' + data.autoplay;
}
str += ']';
link.setHtml( str );
editor.insertElement( link );
}
};
});
function insert(params, editor) {
@@ -49,6 +150,9 @@
if (params.autoplay) {
str += ' autoplay:' + params.autoplay;
}
else {
str += ' autoplay:' + '0';
}
str += ']';
for (var i = 0, len = ranges.length; i < len; i++) {

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 800 B

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 800 B

@@ -43,6 +43,9 @@ function Ok() {
if ($('#edit-autoplay').is(':checked')) {
str += ' autoplay:' + $('#edit-autoplay').val();
}
else {
str += ' autoplay:' + '0';
}
str += ']';
oEditor.FCKUndo.SaveUndoStep();

Before

Width:  |  Height:  |  Size: 800 B

After

Width:  |  Height:  |  Size: 800 B

@@ -0,0 +1,53 @@
/**
* @file
* Video Filter plugin for TinyMCE 4.x
*/
var video_filter_dialog = {};
(function ($) {
video_filter_dialog = {
insert : function() {
var ed = top.tinymce.activeEditor, e;
var file_url = $('#edit-file-url').val();
if (file_url == "") {
// File url is empty, we have nothing to insert, close the window
top.tinymce.activeEditor.windowManager.close();
}
else {
var str = '[video:' + file_url;
// If field is present (ie. not unset by the admin theme) and if value is not empty: insert value.
if (typeof $('#edit-width').val() != 'undefined' && $('#edit-width').val() !== '') {
str += ' width:' + $('#edit-width').val();
}
if (typeof $('#edit-height').val() != 'undefined' && $('#edit-height').val() !== '') {
str += ' height:' + $('#edit-height').val();
}
if (typeof $('#edit-align').val() != 'undefined' && $('#edit-align').val() !== 'none') {
str += ' align:' + $('#edit-align').val();
}
if ($('#edit-autoplay').is(':checked')) {
str += ' autoplay:' + $('#edit-autoplay').val();
}
str += ']';
ed.execCommand('mceInsertContent', false, str);
top.tinymce.activeEditor.windowManager.close();
}
}
};
Drupal.behaviors.video_filter_tinymce = {
attach: function(context, settings) {
$('#edit-insert').click(function() {
video_filter_dialog.insert();
});
$('#edit-cancel').click(function() {
top.tinymce.activeEditor.windowManager.close();
});
}
}
})(jQuery);
@@ -34,6 +34,9 @@ video_filter_dialog = {
if ($('#edit-autoplay').is(':checked')) {
str += ' autoplay:' + $('#edit-autoplay').val();
}
else {
str += ' autoplay:' + '0';
}
str += ']';
ed.execCommand('mceInsertContent', false, str);
@@ -0,0 +1,70 @@
<?php
/**
* @file
* Hooks provided by the Video Filter module.
*/
/**
* Defines video codecs and callbacks.
*
* @return array
* A video codec described as an associative array that may contain the
* following key-value pairs:
* - name: Required. A name for the codec.
* - sample_url: Required. An example of a URL the codec can handle.
* - callback: The function to call to generate embed code for the codec.
* Either this or html5_callback must be specified.
* - html5_callback: The function to call to generate device agnostic, HTML5
* embed code for the codec. Either this or callback must be specified.
* - instructions: Instructions for using the codec, to be displayed on the
* "Compse tips" page at filter/tips.
* - regexp: Required. A regular expression describing URLs the codec can
* handle. Multiple regular expressions may be supplied as an array.
* $video['codec']['delta'] will be set to the key of the match.
* - ratio: Required. A ratio for resizing the video within the dimensions
* optionally supplied in the token, expressed as height / width.
* - control_bar_height: The pixel height of the video player control bar, if
* applicable.
*/
function hook_codec_info() {
$codecs = array();
$codecs['minimal_example'] = array(
'name' => t('Minimal Example'),
'sample_url' => 'http://minimal.example.com/uN1qUeId',
'callback' => 'MODULE_minimal_example',
'regexp' => '/minimal\.example\.com\/([a-z0-9\-_]+)/i',
'ratio' => 4 / 3,
);
$codecs['complete_example'] = array(
'name' => t('Complete Example'),
'sample_url' => 'http://complete.example.com/username/uN1qUeId',
'callback' => 'MODULE_complete_example',
'html5_callback' => 'MODULE_complete_example_html5',
'instructions' => t('Your Complete Example username can be the first URL argument or a sub-subdomain.'),
'regexp' => array(
'/complete\.example\.com\/([a-z0-9\-_]+)\/([a-z0-9\-_]+)/i',
'/([a-z0-9\-_]+)\.complete\.example\.com\/([a-z0-9\-_]+)/i',
),
'ratio' => 4 / 3,
'control_bar_height' => 25,
);
return $codecs;
}
/**
* Alters the codecs available to Video Filter.
*
* @param array $codecs
*/
function hook_video_filter_codec_info_alter(&$codecs) {}
/**
* Alters a video's attributes previous to rendering.
*
* @param array $video
*/
function hook_video_filter_video_alter(&$video) {}
+525 -32
View File
@@ -16,7 +16,7 @@ function video_filter_codec_info() {
'sample_url' => 'http://www.archive.org/details/DrupalconBoston2008-TheStateOfDrupal',
'callback' => 'video_filter_archive',
'html5_callback' => 'video_filter_archive',
'regexp' => '/archive\.org\/details\/([\w-_]+)/i',
'regexp' => '/archive\.org\/details\/([\w-_\.]+)/i',
'ratio' => 4 / 3,
);
@@ -34,6 +34,15 @@ function video_filter_codec_info() {
'control_bar_height' => 30,
);
$codecs['candidcareer'] = array(
'name' => t('Candid Career'),
'sample_url' => 'https://www.candidcareer.com/embed.php?vkey=ed5fdd900a274930252f&shared=CandidCareer&uid=30',
'callback' => 'video_filter_candidcareer',
'regexp' => '/candidcareer\.com\/embed\.php\?vkey=([a-zA-Z0-9\-_&;]+)shared=([a-zA-Z0-9\-_&;]+)uid=([a-zA-Z0-9\-_&;]+)/',
'ratio' => 16 / 9,
'control_bar_height' => 20,
);
$codecs['capped'] = array(
'name' => t('Capped'),
'sample_url' => 'http://capped.tv/playeralt.php?vid=some-title',
@@ -51,15 +60,43 @@ function video_filter_codec_info() {
'control_bar_height' => 0,
);
$codecs['coub'] = array(
'name' => t('Coub'),
'sample_url' => 'http://coub.com/view/b7ghv',
'callback' => 'video_filter_coub',
'html5_callback' => 'video_filter_coub',
'regexp' => '/coub\.com\/view\/([a-z0-9]+)/i',
'ratio' => 4 / 3,
);
$codecs['dailymotion'] = array(
'name' => t('DailyMotion'),
'sample_url' => 'http://www.dailymotion.com/video/some_video_title',
'callback' => 'video_filter_dailymotion',
'html5_callback' => 'video_filter_dailymotion_html5',
'regexp' => '/dailymotion\.com\/video\/([a-z0-9\-_]+)/i',
'ratio' => 4 / 3,
'control_bar_height' => 20,
);
$codecs['democracynow_fullshow'] = array(
'name' => t('DemocracyNow Fullshow'),
'sample_url' => 'http://www.democracynow.org/shows/2015/3/20',
'callback' => 'video_filter_democracynow_fullshow',
'regexp' => '/democracynow\.org\/shows\/([0-9]+)\/([0-9]+)\/([0-9]+)/',
'ratio' => 16 / 9,
'control_bar_height' => 0,
);
$codecs['democracynow_story'] = array(
'name' => t('DemocracyNow Story'),
'sample_url' => 'http://www.democracynow.org/2015/3/23/yemen_in_crisis_us_closes_key',
'callback' => 'video_filter_democracynow_story',
'regexp' => '/democracynow\.org\/([0-9]+)\/([0-9]+)\/([0-9]+)\/([a-zA-Z0-9\-_]+)/',
'ratio' => 16 / 9,
'control_bar_height' => 0,
);
$codecs['flickr_slideshows'] = array(
'name' => t('Flickr Slideshows'),
'sample_url' => 'http://www.flickr.com/photos/username/sets/1234567890/show/',
@@ -78,6 +115,16 @@ function video_filter_codec_info() {
'control_bar_height' => 0,
);
$codecs['foxnews'] = array(
'name' => t('Fox News'),
'sample_url' => 'http://video.foxnews.com/v/123456/the-title/',
'callback' => 'video_filter_foxnews',
'html5_callback' => 'video_filter_foxnews',
'regexp' => '/video\.foxnews\.com\/v\/([0-9]+)\/([a-zA-Z0-9\-]+)/i',
'ratio' => 466 / 263,
'control_bar_height' => 0,
);
$codecs['gametrailers'] = array(
'name' => t('Game Trailers'),
'sample_url' => 'http://www.gametrailers.com/video/some-title/12345',
@@ -97,6 +144,15 @@ function video_filter_codec_info() {
'ratio' => 500 / 319,
);
$codecs['giphy'] = array(
'name' => t('Giphy'),
'sample_url' => 'http://giphy.com/gifs/disney-kids-peter-pan-[gif-id]',
'callback' => 'video_filter_giphy',
'html5_callback' => 'video_filter_giphy',
'regexp' => '/giphy\.com\/gifs\/(([a-zA-Z0-9\-]+)\-|)([a-zA-Z0-9]+)/i',
'ratio' => 16 / 9,
);
$codecs['godtube'] = array(
'name' => t('GodTube'),
'sample_url' => 'http://www.godtube.com/watch/?v=123abc',
@@ -114,6 +170,17 @@ function video_filter_codec_info() {
'ratio' => 400 / 326,
);
$codecs['instagram'] = array(
'name' => t('Instagram'),
'callback' => 'video_filter_instagram',
'sample_url' => 'http://instagram.com/p/uN1qUeId',
'regexp' => array(
'/instagram\.com\/p\/([a-z0-9\-_]+)/i',
'/instagr.am\/p\/([a-z0-9\-_]+)/i',
),
'ratio' => 612 / 710,
);
$codecs['metacafe'] = array(
'name' => t('Meta Cafe'),
'sample_url' => 'http://www.metacafe.com/watch/1234567890/some_title/',
@@ -123,6 +190,16 @@ function video_filter_codec_info() {
'control_bar_height' => 32,
);
$codecs['mailru'] = array(
'name' => t('Mail.Ru'),
'sample_url' => 'https://my.mail.ru/v/semenikhin_denis/video/_groupvideo/[video-id].html',
'callback' => 'video_filter_mailru',
'html5_callback' => 'video_filter_mailru',
'regexp' => '/my\.mail\.ru\/v\/(.*)\/([0-9]+)\.html/i',
'ratio' => 16 / 9,
'control_bar_height' => 0,
);
$codecs['myspace'] = array(
'name' => t('MySpace'),
'sample_url' => 'http://myspace.com/video/vid/1234567890',
@@ -137,6 +214,17 @@ function video_filter_codec_info() {
'control_bar_height' => 40,
);
$codecs['myvideo'] = array(
'name' => t('MyVideo'),
'sample_url' => 'http://www.myvideo.de/filme/story-title-1234567890',
'html5_callback' => 'video_filter_myvideo',
'callback' => 'video_filter_myvideo',
'regexp' => array(
'/myvideo\.de\/(.+)\-([0-9]+)/i',
),
'ratio' => 400 / 283,
);
$codecs['picasa_slideshows'] = array(
'name' => t('Picasa Slideshows'),
'sample_url' => 'http://picasaweb.google.com/data/feed/base/user/USER_NAME/albumid/5568104935784209834?alt=rss&amp;kind=photo&amp;hl=en_US',
@@ -153,6 +241,17 @@ function video_filter_codec_info() {
'ratio' => 800 / 600,
);
$codecs['rutube'] = array(
'name' => t('Rutube'),
'sample_url' => 'http://rutube.ru/video/c80617086143e80ee08f760a2e9cbf43/?pl_type=source&pl_id=8188',
'html5_callback' => 'video_filter_rutube',
'callback' => 'video_filter_rutube',
'regexp' => array(
'/rutube\.ru\/(.*)/i',
),
'ratio' => 16 / 9,
);
$codecs['slideshare'] = array(
'name' => t('Slideshare'),
'sample_url' => 'http://slideshare.net/1759622',
@@ -181,6 +280,41 @@ function video_filter_codec_info() {
'ratio' => 16 / 9,
);
$codecs['ted'] = array(
'name' => t('TED'),
'sample_url' => 'https://www.ted.com/talks/[story-title]',
'instructions' => t('Click in Embed and copy the "Link to this talk" link and paste here.'),
'callback' => 'video_filter_ted',
'html5_callback' => 'video_filter_ted',
'regexp' => '/ted\.com\/talks\/lang\/([a-zA-Z]+)\/([a-zA-Z0-9\-_]+)(\.html)?/',
'ratio' => 4 / 3,
);
$codecs['twitch'] = array(
'name' => t('Twitch'),
'sample_url' => 'http://www.twitch.tv/uN1qUe-I_d',
'callback' => 'video_filter_twitch',
'regexp' => '/twitch\.tv\/([a-z0-9\-_]+)/i',
'ratio' => 16 / 9,
);
$codecs['ustream'] = array(
'name' => t('Ustream'),
'sample_url' => 'http://www.ustream.tv/recorded/111212121212',
'html5_callback' => 'video_filter_ustream',
'callback' => 'video_filter_ustream',
'regexp' => '/ustream\.tv\/recorded\/([0-9]+)/i',
'ratio' => 16 / 9,
);
$codecs['vbox'] = array(
'name' => t('Vbox7'),
'sample_url' => 'http://vbox7.com/play:b4a7291f3d',
'callback' => 'video_filter_vbox',
'regexp' => '/vbox7\.com\/play\:([a-z0-9]+)/i',
'ratio' => 400 / 345,
);
$codecs['vimeo'] = array(
'name' => t('Vimeo'),
'sample_url' => 'http://www.vimeo.com/123456',
@@ -191,12 +325,43 @@ function video_filter_codec_info() {
'control_bar_height' => 0,
);
$codecs['vine'] = array(
'name' => t('Vine'),
'sample_url' => 'https://www.vine.co/v/uN1qUeId',
'callback' => 'video_filter_vine',
'regexp' => '/vine\.co\/v\/([0-9a-z]+)/i',
'ratio' => 4 / 3,
);
$codecs['whatchado'] = array(
'name' => t('whatchado'),
'sample_url' => 'https://www.whatchado.com/de/some-title',
'callback' => 'video_filter_whatchado_whatchado',
'regexp' => array(
'/whatchado\.com\/[a-z]{2}\/([\w-_]+)/i',
),
'ratio' => 960 / 540,
);
$codecs['wistia'] = array(
'name' => t('Wistia'),
'sample_url' => 'http://wistia.com/medias/9pj9n6ftlk',
'callback' => 'video_filter_wistia_html5',
'html5_callback' => 'video_filter_wistia_html5',
'regexp' => '@https?://(.+\.)?(wistia\.com|wi\.st)/((m|medias|projects)|embed/(iframe|playlists))/([a-zA-Z0-9]+)@',
'regexp' => '@https?://(.+\.)?(wistia\.(com|net)|wi\.st)/((m|medias|projects)|embed/(iframe|playlists))/([a-zA-Z0-9]+)@',
);
$codecs['youku'] = array(
'name' => t('YouKu'),
'sample_url' => 'http://v.youku.com/v_show/id_XNjgzNDM4MzIw.html',
'callback' => 'video_filter_youku_html5',
'html5_callback' => 'video_filter_youku_html5',
'regexp' => array(
'/youku\.com\/v_show\/id_([a-z0-9\-_=]+)\.html/i',
'/youku\.com\/player\.php\/sid\/([a-z0-9\-_=]+)/i',
),
'ratio' => 16 / 9,
'control_bar_height' => 50,
);
$codecs['youtube'] = array(
@@ -205,12 +370,13 @@ function video_filter_codec_info() {
'callback' => 'video_filter_youtube',
'html5_callback' => 'video_filter_youtube_html5',
'regexp' => array(
'/youtube\.com\/watch\?v=([a-z0-9\-_]+)/i',
'/youtube\.com\/watch\?.*?v=([a-z0-9\-_]+)/i',
'/youtu.be\/([a-z0-9\-_]+)/i',
'/youtube\.com\/v\/([a-z0-9\-_]+)/i',
'/youtube\.com\/embed\/([a-z0-9\-_]+)/i',
),
'ratio' => 16 / 9,
'control_bar_height' => 25,
'control_bar_height' => 0,
);
$codecs['youtube_playlist'] = array(
@@ -221,7 +387,7 @@ function video_filter_codec_info() {
'/youtube\.com\/playlist\?list=([a-z0-9\-_]+)/i',
),
'ratio' => 16 / 9,
'control_bar_height' => 25,
'control_bar_height' => 0,
);
return $codecs;
@@ -233,7 +399,7 @@ function video_filter_codec_info() {
* @see video_filter_codec_info()
*/
function video_filter_archive($video) {
$video['source'] = 'http://www.archive.org/embed/' . $video['codec']['matches'][1];
$video['source'] = '//www.archive.org/embed/' . $video['codec']['matches'][1];
return video_filter_iframe($video);
}
@@ -290,7 +456,7 @@ function video_filter_bliptv($video) {
}
}
$video['source'] = 'http://blip.tv/play/' . $id;
$video['source'] = '//blip.tv/play/' . $id;
$params = array(
'allowscriptaccess' => 'always',
);
@@ -298,13 +464,27 @@ function video_filter_bliptv($video) {
return video_filter_flash($video, $params);
}
/**
* Callback for Candid Career codec.
*
* @see video_filter_codec_info()
*/
function video_filter_candidcareer($video) {
// Their urls contain & symbols which Drupal is encoding, so decode those.
$decoded = decode_entities($video['codec']['matches'][0]);
$video['source'] = '//' . $decoded;
$video['attributes']['marginwidth'] = 0;
$video['attributes']['marginheight'] = 0;
return video_filter_iframe($video);
}
/**
* Callback for Capped codec.
*
* @see video_filter_codec_info()
*/
function video_filter_capped($video) {
$video['source'] = 'http://capped.micksam7.com/playeralt.swf?vid=' . $video['codec']['matches'][1];
$video['source'] = '//capped.micksam7.com/playeralt.swf?vid=' . $video['codec']['matches'][1];
return video_filter_flash($video);
}
@@ -315,22 +495,56 @@ function video_filter_capped($video) {
* @see video_filter_codec_info()
*/
function video_filter_collegehumor($video) {
$video['source'] = 'http://www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&amp;fullscreen=1';
$video['source'] = '//www.collegehumor.com/moogaloop/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&amp;fullscreen=1';
return video_filter_flash($video);
}
/**
* HTML5 callback for Coub codec.
*
* @see video_filter_codec_info()
*/
function video_filter_coub($video) {
$attributes = array(
'autostart' => !empty($video['autoplay']) ? 'autoplay=true' : 'autoplay=false',
'originalSize' => !empty($video['originalSize']) ? 'originalSize=true' : 'originalSize=false',
'startWithHD' => !empty($video['startWithHD']) ? 'startWithHD=true' : 'startWithHD=false',
'muted' => !empty($video['muted']) ? 'muted=true' : 'muted=false',
);
$video['source'] = '//coub.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&', $attributes);
return video_filter_iframe($video);
}
/**
* Callback for DailyMotion codec.
*
* @see video_filter_codec_info()
*/
function video_filter_dailymotion($video) {
$video['source'] = 'http://www.dailymotion.com/swf/' . $video['codec']['matches'][1];
$attributes = array(
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
);
$video['source'] = '//www.dailymotion.com/swf/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
return video_filter_flash($video);
}
/**
* HTML5 callback for DailyMotion codec.
*
* @see video_filter_codec_info()
*/
function video_filter_dailymotion_html5($video) {
$attributes = array(
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
);
$video['source'] = '//www.dailymotion.com/embed/video/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
return video_filter_iframe($video);
}
/**
* Callback for Flickr Slideshows codec.
*
@@ -348,19 +562,74 @@ function video_filter_flickr_slideshows($video) {
return video_filter_flash($video, $params);
}
/**
* Callback for DemocracyNow Fullshow codec.
*
* @see video_filter_codec_info()
*/
function video_filter_democracynow_fullshow($video) {
$video['source'] = 'http://www.democracynow.org/embed/show/' . $video['codec']['matches'][0];
// The above is pulling in the url part of the regex, so we need to do a
// search and replace to remove it.
$toomuch = array("http://www.democracynow.org/embed/show/democracynow.org/shows/");
$justright = array("http://www.democracynow.org/embed/show/");
$replaced = str_replace($toomuch, $justright, $video);
$video = $replaced;
return video_filter_iframe($video);
}
/**
* Callback for DemocracyNow story codec.
*
* @see video_filter_codec_info()
*/
function video_filter_democracynow_story($video) {
$video['source'] = 'http://www.democracynow.org/embed/story/' . $video['codec']['matches'][0];
// The above is pulling in the url part of the regex, so we need to do a
// search and replace to remove it.
$toomuch = array("http://www.democracynow.org/embed/story/democracynow.org/");
$justright = array("http://www.democracynow.org/embed/story/");
$replaced = str_replace($toomuch, $justright, $video);
$video = $replaced;
return video_filter_iframe($video);
}
/**
* Callback for Ted.com codec.
*
* @see video_filter_codec_info()
*/
function video_filter_ted($video) {
$video['source'] = '//embed.ted.com/talks/' . $video['codec']['matches'][3] . '.html';
return video_filter_iframe($video);
}
/**
* Callback for Flickr Video codec.
*
* @see video_filter_codec_info()
*/
function video_filter_flickr_video($video) {
$video['source'] = 'http://www.flickr.com/apps/video/stewart.swf?v=1.161';
$video['source'] = '//www.flickr.com/apps/video/stewart.swf?v=1.161';
$params['flashvars'] = '&amp;photo_id=' . $video['codec']['matches'][2] . '&amp;flickr_show_info_box=true';
return video_filter_flash($video, $params);
}
/**
* Callback for Fox News codec.
*
* @see video_filter_codec_info()
*/
function video_filter_foxnews($video) {
$video_id = $video['codec']['matches'][1];
$html = '<script type="text/javascript" src="http://video.foxnews.com/v/embed.js?id=' . $video_id . '&w=' . $video['width'] . '&h=' . $video['height'] . '"></script>';
return $html;
}
/**
* Callback for Game Trailers codec.
*
@@ -373,7 +642,7 @@ function video_filter_gametrailers($video) {
elseif (is_numeric($video['codec']['matches'][2])) {
$match = $video['codec']['matches'][2];
}
$video['source'] = 'http://media.mtvnservices.com/embed/mgid:moses:video:gametrailers.com:' . $match;
$video['source'] = '//media.mtvnservices.com/embed/mgid:moses:video:gametrailers.com:' . $match;
return video_filter_iframe($video);
}
@@ -384,18 +653,29 @@ function video_filter_gametrailers($video) {
* @see video_filter_codec_info()
*/
function video_filter_gamevideos($video) {
$video['source'] = 'http://gamevideos.1up.com/swf/gamevideos12.swf?embedded=1&amp;fullscreen=1&amp;autoplay=0&amp;src=http://gamevideos.1up.com/do/videoListXML%3Fid%3D' . $video['codec']['matches'][1];
$video['source'] = '//gamevideos.1up.com/swf/gamevideos12.swf?embedded=1&amp;fullscreen=1&amp;autoplay=0&amp;src=http://gamevideos.1up.com/do/videoListXML%3Fid%3D' . $video['codec']['matches'][1];
return video_filter_flash($video);
}
/**
* Callback for Giphy codec.
*
* @see video_filter_codec_info()
*/
function video_filter_giphy($video) {
$video['source'] = '//giphy.com/embed/' . $video['codec']['matches'][3];
return video_filter_iframe($video);
}
/**
* Callback for GodTube codec.
*
* @see video_filter_codec_info()
*/
function video_filter_godtube($video) {
$video['source'] = 'http://www.godtube.com/embed/watch/' . $video['codec']['matches'][1];
$video['source'] = '//www.godtube.com/embed/watch/' . $video['codec']['matches'][1];
return video_filter_iframe($video);
}
@@ -406,18 +686,56 @@ function video_filter_godtube($video) {
* @see video_filter_codec_info()
*/
function video_filter_google($video) {
$video['source'] = 'http://video.google.com/googleplayer.swf?docId=' . $video['codec']['matches'][1];
$video['source'] = '//video.google.com/googleplayer.swf?docId=' . $video['codec']['matches'][1];
return video_filter_flash($video);
}
/**
* Callback for Instagram codec.
*
* @see video_filter_codec_info()
*/
function video_filter_instagram($video) {
$html = &drupal_static(__FUNCTION__);
$id = $video['codec']['matches'][1];
if ($cache = cache_get('video_filter_instagram:' . $id)) {
$html = $cache->data;
}
else {
$endpoint = 'https://api.instagram.com/oembed';
$options = array(
'url' => 'http://instagr.am/p/' . $id,
);
$data = video_filter_oembed_request($endpoint, $options);
if (!empty($data['html'])) {
$html = $data['html'];
}
cache_set('video_filter_instagram:' . $id, $html, 'cache');
}
return $html;
}
/**
* Callback for Mail.Ru codec.
*
* @see video_filter_codec_info()
*/
function video_filter_mailru($video) {
$attributes = array(
'autoplay' => !empty($video['autoplay']) ? 'autoplay=' . (int) $video['autoplay'] : '',
);
$video['source'] = 'https://videoapi.my.mail.ru/videos/embed/v/' . $video['codec']['matches'][1] . '/' . $video['codec']['matches'][2] . '.html?' . implode('&', $attributes);
return video_filter_iframe($video);
}
/**
* Callback for Meta Cafe codec.
*
* @see video_filter_codec_info()
*/
function video_filter_metacafe($video) {
$video['source'] = 'http://metacafe.com/fplayer/' . $video['codec']['matches'][1] . '/' . $video['codec']['matches'][2] . '.swf';
$video['source'] = '//metacafe.com/fplayer/' . $video['codec']['matches'][1] . '/' . $video['codec']['matches'][2] . '.swf';
return video_filter_flash($video);
}
@@ -430,18 +748,28 @@ function video_filter_metacafe($video) {
function video_filter_myspace($video) {
// The last match is the ID we need.
$last = count($video['codec']['matches']);
$video['source'] = 'http://mediaservices.myspace.com/services/media/embed.aspx/m=' . $video['codec']['matches'][$last - 1];
$video['source'] = '//mediaservices.myspace.com/services/media/embed.aspx/m=' . $video['codec']['matches'][$last - 1];
return video_filter_flash($video, $params);
}
/**
* Callback for MyVideo codec.
*
* @see video_filter_codec_info()
*/
function video_filter_myvideo($video) {
$video['source'] = 'http://www.myvideo.de/embedded/public/' . $video['codec']['matches'][2];
return video_filter_iframe($video);
}
/**
* Callback for Picasa Slideshows codec.
*
* @see video_filter_codec_info()
*/
function video_filter_picasa_slideshows($video) {
$video['source'] = 'http://picasaweb.google.com/s/c/bin/slideshow.swf';
$video['source'] = '//picasaweb.google.com/s/c/bin/slideshow.swf';
$user_name = $video['codec']['matches'][1];
$set_id = $video['codec']['matches'][2];
@@ -451,13 +779,39 @@ function video_filter_picasa_slideshows($video) {
return video_filter_flash($video, $params);
}
/**
* Callback for Rutube codec.
*
* @see video_filter_codec_info()
*/
function video_filter_rutube($video) {
$attributes = array(
'skinColor' => (isset($video['skinColor']) && !empty($video['standardColor'])) ? 'skinColor=' . (string) $video['skinColor'] : '',
'sTitle' => (isset($video['sTitle']) && $video['sTitle'] == 1) ? 'sTitle=true' : 'sTitle=false',
'sAuthor' => (isset($video['sAuthor']) && $video['sAuthor'] == 1) ? 'sAuthor=true' : 'sAuthor=false',
'bmstart' => (isset($video['bmstart']) && $video['bmstart'] > 1) ? 'bmstart=' . (int) $video['bmstart'] : 'bmstart=false',
);
$endpoint = 'http://rutube.ru/api/oembed';
$options = array(
'url' => $video['source'],
'format' => 'json',
);
$data = video_filter_oembed_request($endpoint, $options);
if (!empty($data['html'])) {
if (preg_match('/src="([^"]+)"/', $data['html'], $match)) {
$video['source'] = $match[1] . '?' . implode('&', $attributes);
return video_filter_iframe($video);
}
}
}
/**
* Callback for Slideshare codec.
*
* @see video_filter_codec_info()
*/
function video_filter_slideshare($video) {
$video['source'] = 'http://www.slideshare.net/slideshow/embed_code/' . $video['codec']['matches'][1];
$video['source'] = '//www.slideshare.net/slideshow/embed_code/' . $video['codec']['matches'][1];
return video_filter_iframe($video);
}
@@ -468,7 +822,7 @@ function video_filter_slideshare($video) {
* @see video_filter_codec_info()
*/
function video_filter_streamhoster($video) {
$video['source'] = 'http://public.streamhoster.com/Resources/Flash/JWFLVMediaPlayer/mediaplayer.swf';
$video['source'] = '//public.streamhoster.com/Resources/Flash/JWFLVMediaPlayer/mediaplayer.swf';
$params = array('allowscriptaccess' => 'always');
$protocol = $video['codec']['matches'][1];
@@ -490,26 +844,74 @@ function video_filter_streamhoster($video) {
return video_filter_flash($video, $params);
}
/**
* Callback for Twitch codec.
*
* @see video_filter_codec_info()
*/
function video_filter_twitch($video) {
$video['source'] = '//player.twitch.tv/?channel=' . $video['codec']['matches'][1] ;
return video_filter_iframe($video);
}
/**
* Callback for Teachertube codec.
*
* @see video_filter_codec_info()
*/
function video_filter_teachertube($video) {
$video['source'] = 'http://www.teachertube.com/embed/player.swf';
$video['source'] = '//www.teachertube.com/embed/player.swf';
$params['flashvars'] = 'file=http://www.teachertube.com/embedFLV.php?pg=video_' . $video['codec']['matches'][1] . '&amp;menu=false&amp;frontcolor=ffffff&amp;lightcolor=FF0000&amp;logo=http://www.teachertube.com/www3/images/greylogo.swf&amp;skin=http://www.teachertube.com/embed/overlay.swf&amp;volume=80&amp;controlbar=over&amp;displayclick=link&amp;viral.link=http://www.teachertube.com/viewVideo.php?video_id=' . $video['codec']['matches'][1] . '&amp;stretching=exactfit&amp;plugins=viral-2&amp;viral.callout=none&amp;viral.onpause=false';
return video_filter_flash($video, $params);
}
/**
* Callback for Ustream codec.
*
* @see video_filter_codec_info()
*/
function video_filter_ustream($video) {
$attributes = array(
'html5ui' => 'html5ui',
'autoplay' => isset($video['autoplay']) ? 'autoplay=' . (int) $video['autoplay'] : 'autoplay=0',
);
$video['source'] = 'http://www.ustream.tv/embed/recorded/' . $video['codec']['matches'][1] . '?' . implode('&', $attributes);
return video_filter_iframe($video);
}
/**
* Callback for VBox7 codec.
*
* @see video_filter_codec_info()
*/
function video_filter_vbox($video) {
$video['source'] = '//vbox7.com/emb/external.php?vid=' . $video['codec']['matches'][1];
return video_filter_flash($video);
}
/**
* Callback for Vimeo codec.
*
* @see video_filter_codec_info()
*/
function video_filter_vimeo($video) {
$video['source'] = 'http://www.vimeo.com/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&amp;server=www.vimeo.com&amp;fullscreen=1&amp;show_title=1&amp;show_byline=1&amp;show_portrait=0&amp;color=&amp;autoplay=' . $video['autoplay'];
$attributes = array(
'autopause' => isset($video['autopause']) ? 'autopause=' . (int) $video['autopause'] : 'autopause=1',
'autoplay' => isset($video['autoplay']) ? 'autoplay=' . (int) $video['autoplay'] : 'autoplay=0',
'badge' => isset($video['badge']) ? 'badge=' . (int) $video['badge'] : 'badge=1',
'byline' => isset($video['byline']) ? 'byline=' . (int) $video['byline'] : 'byline=1',
'loop' => isset($video['loop']) ? 'loop=' . (int) $video['loop'] : 'loop=0',
'portrait' => isset($video['portrait']) ? 'portrait=' . (int) $video['portrait'] : 'portrait=1',
'title' => isset($video['title']) ? 'autopause=' . (int) $video['title'] : 'autopause=1',
'fullscreen' => isset($video['fullscreen']) ? 'fullscreen=' . (int) $video['fullscreen'] : 'fullscreen=1',
);
if (!empty($video['color'])) {
$attributes['color'] = (string) $video['color'];
}
$video['source'] = '//www.vimeo.com/moogaloop.swf?clip_id=' . $video['codec']['matches'][1] . '&amp;server=www.vimeo.com&amp;' . implode('&amp;', $attributes);
return video_filter_flash($video);
}
@@ -520,7 +922,43 @@ function video_filter_vimeo($video) {
* @see video_filter_codec_info()
*/
function video_filter_vimeo_html5($video) {
$video['source'] = 'http://player.vimeo.com/video/' . $video['codec']['matches'][1] . ($video['autoplay'] ? '?autoplay=1' : '');
$attributes = array(
'autopause' => isset($video['autopause']) ? 'autopause=' . (int) $video['autopause'] : 'autopause=1',
'autoplay' => isset($video['autoplay']) ? 'autoplay=' . (int) $video['autoplay'] : 'autoplay=0',
'badge' => isset($video['badge']) ? 'badge=' . (int) $video['badge'] : 'badge=1',
'byline' => isset($video['byline']) ? 'byline=' . (int) $video['byline'] : 'byline=1',
'loop' => isset($video['loop']) ? 'loop=' . (int) $video['loop'] : 'loop=0',
'portrait' => isset($video['portrait']) ? 'portrait=' . (int) $video['portrait'] : 'portrait=1',
'title' => isset($video['title']) ? 'autopause=' . (int) $video['title'] : 'autopause=1',
'fullscreen' => isset($video['fullscreen']) ? 'fullscreen=' . (int) $video['fullscreen'] : 'fullscreen=1',
);
if (!empty($video['color'])) {
$attributes['color'] = (string) $video['color'];
}
$video['source'] = '//player.vimeo.com/video/' . $video['codec']['matches'][1] . '?' . implode('&', $attributes);
return video_filter_iframe($video);
}
/**
* Callback for Whatchadoo codec.
*
* @see video_filter_codec_info()
*/
function video_filter_whatchado($video) {
$video['source'] = '//www.whatchado.com/embed/player/' . $video['codec']['matches'][1];
return video_filter_iframe($video);
}
/**
* HTML5 callback for YouKu codec.
*
* @see video_filter_codec_info()
*/
function video_filter_youku_html5($video) {
$attributes = array();
$video['source'] = 'http://player.youku.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
return video_filter_iframe($video);
}
@@ -532,18 +970,50 @@ function video_filter_vimeo_html5($video) {
*/
function video_filter_youtube($video) {
$attributes = array(
'rel' => $video['related'] ? 'rel=1' : 'rel=0',
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
'modestbranding' => !empty($video['modestbranding']) ? 'modestbranding=1' : 'modestbranding=0',
'rel' => !empty($video['related']) ? 'rel=1' : 'rel=0',
'autoplay' => !empty($video['autoplay']) ? 'autoplay=1' : 'autoplay=0',
'fs' => 'fs=1',
'loop' => !empty($video['loop']) ? 'loop=1' : 'loop=0',
'controls' => !empty($video['controls']) ? 'controls=1' : (!isset($video['controls']) ? 'controls=1' : 'controls=0'),
'autohide' => !empty($video['autohide']) ? 'autohide=1' : 'autohide=0',
'showinfo' => !empty($video['showinfo']) ? 'showinfo=1' : 'showinfo=0',
'theme' => !empty($video['theme']) ? 'theme=' . $video['theme'] : 'theme=dark',
'color' => !empty($video['color']) ? 'color=' . $video['color'] : 'color=red',
'enablejsapi' => !empty($video['enablejsapi']) ? 'enablejsapi=' . (int) $video['enablejsapi'] : 'enablejsapi=0',
);
$video['source'] = 'http://www.youtube.com/v/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
if (!empty($video['loop'])) {
$attributes['playlist'] = 'playlist=' . $video['codec']['matches'][1];
}
if (preg_match('/t=((\d+[m|s])?(\d+[s]?)?)/', $video['source'], $matches)) {
$attributes['start'] = 'start=' . (preg_replace("/[^0-9]/", "", $matches[2]) * 60 + (preg_replace("/[^0-9]/", "", $matches[3])));
}
if (!empty($video['start'])) {
if (preg_match('/((\d+[m|s])?(\d+[s]?)?)/', $video['start'], $matches)) {
$attributes['start'] = 'start=' . (preg_replace("/[^0-9]/", "", $matches[2]) * 60 + (preg_replace("/[^0-9]/", "", $matches[3])));
}
}
$video['source'] = '//www.youtube.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
$params['wmode'] = 'opaque';
return video_filter_flash($video, $params);
}
/**
* Callback for Vine codec.
*
* @see video_filter_codec_info()
*/
function video_filter_vine($video) {
$video['source'] = '//vine.co/v/' . $video['codec']['matches'][1] . '/embed/simple';
return video_filter_iframe($video);
}
/**
* HTML5 callback for YouTube codec.
*
@@ -551,11 +1021,34 @@ function video_filter_youtube($video) {
*/
function video_filter_youtube_html5($video) {
$attributes = array(
'modestbranding' => !empty($video['modestbranding']) ? 'modestbranding=1' : 'modestbranding=0',
'html5' => 'html5=1',
'rel' => $video['related'] ? 'rel=1' : 'rel=0',
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
'wmode' => 'wmode=opaque',
'loop' => !empty($video['loop']) ? 'loop=1' : 'loop=0',
'controls' => !empty($video['controls']) ? 'controls=1' : (!isset($video['controls']) ? 'controls=1' : 'controls=0'),
'autohide' => !empty($video['autohide']) ? 'autohide=1' : 'autohide=0',
'showinfo' => !empty($video['showinfo']) ? 'showinfo=1' : 'showinfo=0',
'theme' => !empty($video['theme']) ? 'theme=' . $video['theme'] : 'theme=dark',
'color' => !empty($video['color']) ? 'color=' . $video['color'] : 'color=red',
'enablejsapi' => !empty($video['enablejsapi']) ? 'enablejsapi=' . (int) $video['enablejsapi'] : 'enablejsapi=0',
);
$video['source'] = 'http://www.youtube.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
if (!empty($video['loop'])) {
$attributes['playlist'] = 'playlist=' . $video['codec']['matches'][1];
}
if (preg_match('/t=((\d+[m|s])?(\d+[s]?)?)/', $video['source'], $matches)) {
$attributes['start'] = 'start=' . (preg_replace("/[^0-9]/", "", $matches[2]) * 60 + (preg_replace("/[^0-9]/", "", $matches[3])));
}
if (!empty($video['start'])) {
if (preg_match('/((\d+[m|s])?(\d+[s]?)?)/', $video['start'], $matches)) {
$attributes['start'] = 'start=' . (preg_replace("/[^0-9]/", "", $matches[2]) * 60 + (preg_replace("/[^0-9]/", "", $matches[3])));
}
}
$video['source'] = '//www.youtube.com/embed/' . $video['codec']['matches'][1] . '?' . implode('&amp;', $attributes);
return video_filter_iframe($video);
}
@@ -571,7 +1064,7 @@ function video_filter_youtube_playlist_html5($video) {
'autoplay' => $video['autoplay'] ? 'autoplay=1' : 'autoplay=0',
'wmode' => 'wmode=opaque',
);
$video['source'] = 'http://www.youtube.com/embed/videoseries?list=' . $video['codec']['matches'][1] . '&amp;' . implode('&amp;', $attributes);
$video['source'] = '//www.youtube.com/embed/videoseries?list=' . $video['codec']['matches'][1] . '&amp;' . implode('&amp;', $attributes);
return video_filter_iframe($video);
}
@@ -584,9 +1077,9 @@ function video_filter_youtube_playlist_html5($video) {
* @see video_filter_codec_info()
*/
function video_filter_wistia_html5($video) {
$video_code = $video['codec']['matches'][6];
$video_code = $video['codec']['matches'][7];
$matches = $video['codec']['matches'];
$embed_type = ($matches[3] == 'projects' || $matches[5] == 'playlists') ? 'playlists' : 'iframe';
$embed_type = ($matches[4] == 'projects' || $matches[6] == 'playlists') ? 'playlists' : 'iframe';
// Get embed code via oEmbed.
$endpoint = 'http://fast.wistia.com/oembed';
@@ -599,12 +1092,12 @@ function video_filter_wistia_html5($video) {
$html = $data['html'];
// See if the video source is already an iframe src.
$pattern = '@https?://fast.wistia.com/embed/(iframe|playlists)/[a-zA-Z0-9]+\?+.+@';
$pattern = '@https?://fast.wistia.(com|net)/embed/(iframe|playlists)/[a-zA-Z0-9]+\?+.+@';
$matches = array();
if (preg_match($pattern, $video['source'], $matches)) {
// Replace the oEmbed iframe src with that provided in the token, in order
// to support embed builder URLs.
$pattern = '@https?://fast.wistia.com/embed/(iframe|playlists)/[a-zA-Z0-9]+\?[^"]+@';
$pattern = '@https?://fast.wistia.(com|net)/embed/(iframe|playlists)/[a-zA-Z0-9]+\?[^"]+@';
$replacement = $matches[0];
$html = preg_replace($pattern, $replacement, $html);
}
View File
+3 -3
View File
@@ -5,9 +5,9 @@ package = Input filters
stylesheets[all][] = video_filter.css
; Information added by drupal.org packaging script on 2012-11-14
version = "7.x-3.1"
; Information added by Drupal.org packaging script on 2016-06-01
version = "7.x-3.4"
core = "7.x"
project = "video_filter"
datestamp = "1352915891"
datestamp = "1464823440"
+200 -45
View File
@@ -24,6 +24,8 @@ function video_filter_filter_info() {
'video_filter_autoplay' => 1,
'video_filter_related' => 1,
'video_filter_html5' => 1,
'video_filter_codecs' => _video_filter_map_codecs_name(video_filter_get_codec_info()),
'video_filter_multiple_sources' => TRUE,
),
'tips callback' => '_video_filter_tips',
// See http://drupal.org/node/1061244.
@@ -33,7 +35,6 @@ function video_filter_filter_info() {
}
function _video_filter_settings($form, &$form_state, $filter, $format, $defaults, $filters) {
$settings['video_filter_width'] = array(
'#type' => 'textfield',
'#title' => t('Default width setting'),
@@ -78,12 +79,40 @@ function _video_filter_settings($form, &$form_state, $filter, $format, $defaults
),
);
$settings['video_filter_multiple_sources'] = array(
'#type' => 'radios',
'#title' => t('Allow multiple sources'),
'#description' => t('Allow the use of multiple sources (used source is selected at random).'),
'#default_value' => isset($filter->settings['video_filter_multiple_sources']) ? $filter->settings['video_filter_multiple_sources'] : $defaults['video_filter_multiple_sources'],
'#options' => array(
0 => t('No'),
1 => t('Yes'),
),
);
$settings['video_filter_codecs'] = array(
'#type' => 'checkboxes',
'#title' => t('Codecs'),
'#description' => t('Choose which codecs will be available.'),
'#default_value' => isset($filter->settings['video_filter_codecs']) ? $filter->settings['video_filter_codecs'] : $defaults['video_filter_codecs'],
'#options' => _video_filter_map_codecs_name(video_filter_get_codec_info()),
);
return $settings;
}
function _video_filter_map_codecs_name($codecs) {
$codecs_map = array();
foreach ($codecs as $codec_cod => $codec) {
$codecs_map[$codec_cod] = $codec['name'];
}
return $codecs_map;
}
function _video_filter_tips($filter, $format, $long = FALSE) {
if ($long) {
$codecs = video_filter_get_codec_info();
$codecs = video_filter_get_codec_enabled($filter->settings['video_filter_codecs']);
$supported = array();
$instructions = array();
foreach ($codecs as $codec) {
@@ -126,14 +155,14 @@ function _video_filter_process($text, $filter, $format, $langcode, $cache, $cach
);
// Pick random out of multiple sources separated by comma (,).
if (strstr($video['source'], ',')) {
if ($filter->settings['video_filter_multiple_sources'] && strstr($video['source'], ',')) {
$sources = explode(',', $video['source']);
$random = array_rand($sources, 1);
$video['source'] = $sources[$random];
}
// Load all codecs.
$codecs = video_filter_get_codec_info();
$codecs = video_filter_get_codec_enabled($filter->settings['video_filter_codecs']);
// Find codec.
foreach ($codecs as $codec_name => $codec) {
@@ -171,17 +200,32 @@ function _video_filter_process($text, $filter, $format, $langcode, $cache, $cach
$ratio = $tratio[1] / $tratio[2];
}
elseif (isset($video['codec']['ratio'])) {
$ratio = $video['codec']['ratio'];
if (is_float($video['codec']['ratio']) || is_int($video['codec']['ratio'])) {
$ratio = $video['codec']['ratio'];
}
elseif (preg_match('/(\d+)\s*\/\s*(\d+)/', $video['codec']['ratio'], $cratio)) {
$ratio = $cratio[1] / $cratio[2];
}
}
// Sets video width & height after any user input has been parsed.
// First, check if user has set a width.
if (isset($video['width']) && !isset($video['height'])) {
$video['height'] = $filter->settings['video_filter_height'];
if ($ratio) {
$video['height'] = ceil($video['width'] / $ratio);
}
else {
$video['height'] = $filter->settings['video_filter_height'];
}
}
// Else, if user has set height.
elseif (isset($video['height']) && !isset($video['width'])) {
$video['width'] = $video['height'] * $ratio;
if ($ratio) {
$video['width'] = ceil($video['height'] * $ratio);
}
else {
$video['width'] = $filter->settings['video_filter_height'];
}
}
// Maybe both?
elseif (isset($video['height']) && isset($video['width'])) {
@@ -204,16 +248,7 @@ function _video_filter_process($text, $filter, $format, $langcode, $cache, $cach
// Respect setting provided by codec otherwise.
$control_bar_height = $video['codec']['control_bar_height'];
}
// Resize to fit within width and height repecting aspect ratio.
if ($ratio) {
$scale_factor = min(array(
($video['height'] - $control_bar_height),
$video['width'] / $ratio,
));
$video['height'] = round($scale_factor + $control_bar_height);
$video['width'] = round($scale_factor * $ratio);
}
$video['height'] += $control_bar_height;
$video['autoplay'] = (bool) $video['autoplay'];
$video['align'] = (isset($video['align']) && in_array($video['align'], array(
@@ -262,6 +297,9 @@ function video_filter_iframe($video) {
return theme('video_filter_iframe', array('video' => $video));
}
/**
* Get a list of all available video codecs.
*/
function video_filter_get_codec_info() {
static $codecs;
if (!isset($codecs)) {
@@ -271,6 +309,35 @@ function video_filter_get_codec_info() {
return $codecs;
}
/**
*
*/
function _video_filter_merge_format_codecs($filters_codecs) {
$codecs = array_pop($filters_codecs);
foreach ($filters_codecs as $format_name => $format_codecs) {
foreach ($format_codecs as $codec_name => $codec_value) {
if (!empty($codec_value) && empty($codecs[$codec_name])) {
$codecs[$codec_name] = $codec_value;
}
}
}
return $codecs;
}
/**
* Get a list of enabled video codecs.
*/
function video_filter_get_codec_enabled($video_filter_codecs) {
$codecs = array_intersect_key(
video_filter_get_codec_info(),
array_filter($video_filter_codecs)
);
return $codecs;
}
/**
* Function that outputs the <object> element.
*
@@ -284,9 +351,14 @@ function theme_video_filter_flash($variables) {
$classes = video_filter_get_classes($video);
$output .= '<object class="' . implode(' ', $classes) . '" type="application/x-shockwave-flash" ';
$attributes = '';
if (!empty($video['attributes'])) {
$attributes = drupal_attributes($video['attributes']);
}
$output .= 'width="' . $video['width'] . '" height="' . $video['height'] . '" data="' . $video['source'] . '">' . "\n";
$output .= '<div class="video-filter"><object class="' . implode(' ', $classes) . '" type="application/x-shockwave-flash" ';
$output .= 'width="' . $video['width'] . '" height="' . $video['height'] . '" data="' . $video['source'] . '" ' . $attributes . '>' . "\n";
$defaults = array(
'movie' => $video['source'],
@@ -300,7 +372,7 @@ function theme_video_filter_flash($variables) {
$output .= ' <param name="' . $name . '" value="' . $value . '" />' . "\n";
}
$output .= '</object>' . "\n";
$output .= '</object></div>' . "\n";
return $output;
}
@@ -312,10 +384,13 @@ function theme_video_filter_flash($variables) {
*/
function theme_video_filter_iframe($variables) {
$video = $variables['video'];
$classes = video_filter_get_classes($video);
$attributes = '';
if (!empty($video['attributes'])) {
$attributes = drupal_attributes($video['attributes']);
}
$output = '<iframe src="' . $video['source'] . '" width="' . $video['width'] . '" height="' . $video['height'] . '" class="' . implode(' ', $classes) . '" frameborder="0"></iframe>';
$output = '<div class="video-filter"><iframe src="' . $video['source'] . '" width="' . $video['width'] . '" height="' . $video['height'] . '" class="' . implode(' ', $classes) . '" frameborder="0" allowfullscreen="true"' . $attributes . '></iframe></div>';
return $output;
}
@@ -378,6 +453,15 @@ function video_filter_menu() {
'theme callback' => '_video_filter_dashboard_theme',
);
$items['video_filter/instructions'] = array(
'title' => 'Videofilter instructions',
'description' => 'instructions',
'page callback' => 'video_filter_instructions_page',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
'theme callback' => '_video_filter_dashboard_theme',
);
return $items;
}
@@ -414,9 +498,26 @@ function video_filter_dashboard_page($editor) {
switch ($editor) {
case 'wysiwyg_tinymce':
// Add JavaScript.
drupal_add_js(wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce/tiny_mce_popup.js');
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/tinymce/video_filter.js');
// Add JavaScript. First, we'll need to determine what version we're on.
$has_added_js = FALSE;
// Solves bug that causes tinymce.inc to not be loaded.
wysiwyg_load_includes('editors', 'editor', 'tinymce');
// Check for TinyMCE 4.x first.
if (function_exists('wysiwyg_tinymce_editor')) {
$loaded_editor = wysiwyg_tinymce_editor();
$version = wysiwyg_tinymce_version($loaded_editor['tinymce']);
if (version_compare($version, '4', '>=')) {
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/tinymce/video_filter-4.js');
$has_added_js = TRUE;
}
}
// Add JS for <= TinyMCE 3.x.
if (!$has_added_js) {
drupal_add_js(wysiwyg_get_path('tinymce') . '/jscripts/tiny_mce/tiny_mce_popup.js');
drupal_add_js(drupal_get_path('module', 'video_filter') . '/editors/tinymce/video_filter.js');
}
break;
case 'ckeditor':
@@ -432,7 +533,19 @@ function video_filter_dashboard_page($editor) {
break;
}
print theme('video_filter_dashboard', array('form' => render(drupal_get_form('_video_filter_form'))));
$form = drupal_get_form('_video_filter_form');
print theme('video_filter_dashboard', array('form' => render($form)));
exit();
}
/**
* Creates the instructions page.
*/
function video_filter_instructions_page() {
module_invoke('admin_menu', 'suppress');
$form = drupal_get_form('_video_filter_instructions_form');
print theme('video_filter_dashboard', array('form' => render($form)));
exit();
}
@@ -492,22 +605,7 @@ function _video_filter_form() {
'#weight' => 5,
);
$form['instructions'] = array(
'#type' => 'fieldset',
'#title' => t('Instructions'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#attributes' => array('class' => array('clearfix')),
'#weight' => 97,
);
$text = '<p>' . t('Insert a 3rd party video from one of the following providers.') . '</p>';
$text .= _video_filter_instructions();
$form['instructions']['text'] = array(
'#type' => 'item',
'#markup' => $text,
);
$form += _video_filter_instructions_form();
$form['cancel'] = array(
'#type' => 'button',
@@ -524,6 +622,47 @@ function _video_filter_form() {
return $form;
}
function _video_filter_instructions_form() {
$form['instructions'] = array(
'#type' => 'fieldset',
'#title' => t('Instructions'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#attributes' => array('class' => array('clearfix')),
'#weight' => 97,
);
$text = '<p>' . t('Insert a 3rd party video from one of the following providers; this list may vary depending on the text format being used.') . '</p>';
$text .= _video_filter_instructions();
$form['instructions']['text'] = array(
'#type' => 'item',
'#markup' => $text,
);
return $form;
}
/**
* Implements hook_ckeditor_plugin().
*/
function video_filter_ckeditor_plugin() {
$plugins = array();
$plugins['video_filter'] = array(
'name' => 'video_filter',
'desc' => t('Plugin to directly embed videos with the video filter module.'),
'path' => drupal_get_path('module', 'video_filter') . '/editors/ckeditor/',
'buttons' => array(
'video_filter' => array(
'label' => t('Video filter'),
'icon' => 'video_filter.png',
),
),
);
return $plugins;
}
function video_filter_wysiwyg_plugin($editor, $version) {
_video_filter_add_settings('wysiwyg_' . $editor);
@@ -582,7 +721,10 @@ function _video_filter_add_settings($editor) {
// Add popup url.
$settings = array(
'video_filter' => array('url' => array($editor => url('video_filter/dashboard/' . $editor))),
'video_filter' => array(
'url' => array($editor => url('video_filter/dashboard/' . $editor)),
'instructions_url' => url('video_filter/instructions'),
),
);
drupal_add_js($settings, 'setting');
}
@@ -604,10 +746,23 @@ function _video_filter_add_settings($editor) {
* Parses Codec into instructions for WYSIWYG popup.
*/
function _video_filter_instructions() {
$codecs = video_filter_get_codec_info();
// Get all codecs the user has permission to use in at least one text format.
global $user;
$formats = filter_formats($user);
$filters_codecs = array();
foreach ($formats as $format) {
$format_filters = filter_list_format($format->format);
if (isset($format_filters['video_filter'])) {
$filters_codecs[$format->name] = $format_filters['video_filter']->settings['video_filter_codecs'];
}
}
$video_filter_codecs = _video_filter_merge_format_codecs($filters_codecs);
$codecs = video_filter_get_codec_enabled($video_filter_codecs);
$output = '<ul>';
foreach ($codecs as $codec) {
$output .= '<li><strong>' . $codec['name'] . '</strong><br />' . $codec['sample_url'] . '</li>';
$output .= '<li><strong>' . $codec['name'] . '</strong><br />' . t('e.g.') . ' ' . $codec['sample_url'] . '</li>';
}
$output .= '</ul>';
return $output;
+38 -2
View File
@@ -1,19 +1,55 @@
Note: This file is no longer updated. See the git history or release notes instead.
Project change records will also be published for major changes.
Wysiwyg 7.x-2.x, xxxx-xx-xx
Wysiwyg 7.x-2.2, 2012-10-02
---------------------------
#356480 by TwoD, zhangtaihao, sun: Fixed initialization of editor libraries in
Ajax scenarios.
Revert "- #841794 by catch, smk-ka: Fixed wysiwyg_load_includes() performance."
#771424 by TwoD: Fixed Missing library breaks admin UI.
#967400 by skwashd, helmo, rocketeerbkw: Plugin/button label linking to project
homepage is poor usability.
#835682 by james.elliott, TwoD, Paul Lomax, JacobSingh: Fixed %t
(path_to_theme()) returns wrong theme path.
#841794 by catch, smk-ka: Fixed wysiwyg_load_includes() performance.
#614402 by jide, TwoD: Added Plugin API support for YUI Editor.
#947676 by merlinofchaos, zhangtaihao, mradcliffe: Added event for detaching
when CTools modal closes.
#356480 by Shawn_Smiley, zhangtaihao, idflood, whurleyf1: Added Lazy-load
support for editors.
#1732880 by loominade, yannickoo, sun: Added EpicEditor support.
#1691478 by aaronbauman: Fixed {wysiwyg}.settings column is not marked as
serialized.
#1679272 by marcusx: Fixed Path/statusbar location setting has no effect in
TinyMCE.
Fixed bogus variable references.
#1679980 by sun: Fixed Wysiwyg Dialog code is not ported to D7.
#614146 by jide, fearlsgroove, TwoD, aklump, sun: Added
Drupal.wysiwyg.editor.instance content methods.
#1650416 by TwoD, sun: Changed settings form callback to leverage $form_state.
#1650416 by sun: Changed arguments for settings form callback.
#1388224 by ksenzee, sun, TwoD: Fixed editors detaching on form submissions.
#746524 by fietserwin, sun, Sborsody, jastraat, kardave, pacproduct, osopolar,
amanaplan, joelcollinsdc, roderik, plach, sylus, aaronbauman, g10: Fixed No
Font Styles for CKeditor.
#1650416 by sun, fietserwin: Added 'settings form callback' to allow editor
specific changes to be made to the profile settings form.
#682160 by n_vashenko, TwoD: Fixed lists plugin support for TinyMCE.
#1414354 by Merco: Fixed none.js breaks if textarea.js is not loaded.
#1064600 by TwoD: Fixed maximized editors hidden under Drupal's toolbar.
#1405786 by logaritmisk: Fixed CKEditor being wider than parent elements.
#1531896 by Chi: Fixed strict warning for WYMeditor.
#1048300 by sreynen: Fixed external CSS not properly handled in
wysiwyg_get_css().
#1442226 by robertom: Fixed inverted list button names for WYMeditor.
#1352426 by TwoD, sun: Added install notes (CKEditor edition clarification).
#1112212 by timdiacon, TwoD: Added language direction buttons for CKEditor.
#1398560 by markwittens: Fixed TinyMCE removing the longdesc attribute.
#970452 by smk-ka, sun, TwoD, drzraf: Fixed outdated TinyMCE plugin info.
#1155678 by james.elliott, Jody Lynn, sun: Add Drupal.detachBehaviors support.
#624018 by smk-ka, quartsize, dagmar, nedjo, rickvug, catch, sun: Added Features support.
#624018 by smk-ka, quartsize, dagmar, nedjo, rickvug, catch, sun: Added Features
support.
#1238766 by Dave Reid: Fixed Missing cells in profile plugins table.
#1073106 by scottrouse: Fixed 'Input Format' should be 'Text Format'.
#1153458 by TwoD: Fixed TinyMCE 'Verify HTML' setting ignored.
View File
+9 -4
View File
@@ -1,8 +1,8 @@
-- SUMMARY --
Wysiwyg API allows to users of your site to use WYSIWYG/rich-text, and other
client-side editors for editing contents. This module depends on third-party
Wysiwyg API allows users of your site to use WYSIWYG/rich-text, and other
client-side editors for editing contents. This module depends on third-party
editor libraries, most often based on JavaScript.
For a full description of the module, visit the project page:
@@ -31,10 +31,15 @@ To submit bug reports and feature suggestions, or to track changes:
* Go to Administration » Configuration » Content authoring » Text formats, and
- either configure the Full HTML format, assign it to trusted roles, and
disable "HTML filter", "Line break converter", and (optionally) "URL filter".
disable "Limit allowed HTML tags", "Convert line breaks...", and
(optionally) "Convert URLs into links".
Note that disabling "Limit allowed HTML tags" will allow users to post
anything, including potentially malicious content. For a more configurable
alternative to "Limit allowed HTML tags" try
http://drupal.org/project/wysiwyg_filter.
- or add a new text format, assign it to trusted roles, and ensure that above
mentioned input filters are disabled.
mentioned input filters are configured as detailed.
* Setup editor profiles in Administration » Configuration » Content authoring
» Wysiwyg.
+522 -81
View File
@@ -5,6 +5,10 @@
* Editor integration functions for CKEditor.
*/
define('WYSIWYG_CKEDITOR_ACF_DISABLED', 0);
define('WYSIWYG_CKEDITOR_ACF_AUTOMATIC', 1);
define('WYSIWYG_CKEDITOR_ACF_CUSTOM', 2);
/**
* Plugin implementation of hook_editor().
*/
@@ -28,20 +32,22 @@ function wysiwyg_ckeditor_editor() {
),
),
'install note callback' => 'wysiwyg_ckeditor_install_note',
'verified version range' => array('3.0', '4.6.1.580bcaf'),
'migrate settings callback' => 'wysiwyg_ckeditor_migrate_settings',
'version callback' => 'wysiwyg_ckeditor_version',
'themes callback' => 'wysiwyg_ckeditor_themes',
'settings form callback' => 'wysiwyg_ckeditor_settings_form',
'init callback' => 'wysiwyg_ckeditor_init',
'settings callback' => 'wysiwyg_ckeditor_settings',
'plugin callback' => 'wysiwyg_ckeditor_plugins',
'plugin settings callback' => 'wysiwyg_ckeditor_plugin_settings',
'plugin callback' => '_wysiwyg_ckeditor_plugins',
'plugin meta callback' => '_wysiwyg_ckeditor_plugin_meta',
'proxy plugin' => array(
'drupal' => array(
'load' => TRUE,
'proxy' => TRUE,
),
),
'proxy plugin settings callback' => 'wysiwyg_ckeditor_proxy_plugin_settings',
'proxy plugin settings callback' => '_wysiwyg_ckeditor_proxy_plugin_settings',
'versions' => array(
'3.0.0.3665' => array(
'js files' => array('ckeditor-3.0.js'),
@@ -51,11 +57,97 @@ function wysiwyg_ckeditor_editor() {
return $editor;
}
/**
* Profile migration callback for CKEditor.
*
* Applies known changes to the editor settings as needed when the installed
* editor version is different from the one used to configure the profile.
* This fixes problems caused by settings, plugins or buttons being renamed,
* removed, or added between versions.
*
* Only changes needed up/down to and including the installed version from the
* profile version may be applied, in case the user did not install the latest
* supported version.
*
* @param $settings
* The editor settings array as it was stored in the database.
* @param $editor
* The editor definition from wysiwyg_get_editor().
* @param $profile_version
* The editor version string from when the profile was last saved.
* @param $installed_version
* The editor version currently installed on the system.
*
* @return
* An editor version string telling Wysiwyg past which version the profile
* could be migrated. If no changes were needed return TRUE.
* Returning FALSE indicates migration failed and the profile is likely
* unusable. Wysiwyg will recommend the user starts over with a new profile.
*/
function wysiwyg_ckeditor_migrate_settings(&$settings, $editor, $profile_version, $installed_version) {
$version_diff = version_compare($installed_version, $profile_version);
// Default to no changes needed.
$migrated_version = TRUE;
if ($version_diff === 1) {
// Upgrading, starting at the profile version going up.
// 3.x to 4.0.
if (version_compare($profile_version, '4.0', '<')
&& version_compare($installed_version, '4.0', '>=')) {
// The default skin changed from "kama" to "moono".
if (isset($settings['skin']) && $settings['skin'] === 'kama') {
$settings['skin'] = 'moono';
}
$migrated_version = '4.0';
}
// Version 4.6.0.
if (version_compare($profile_version, '4.6.0', '<')
&& version_compare($installed_version, '4.6.0', '>=')) {
// The default skin changed from "moono" to "moono-lisa".
if (isset($settings['skin']) && $settings['skin'] === 'moono') {
$settings['skin'] = 'moono-lisa';
}
$migrated_version = '4.6.0';
}
}
elseif ($version_diff === 0) {
// Same version. This function would never have been called.
}
// $version_diff === -1, an older version was installed.
else {
// Downgrading, starting at the profile version going down.
// 4.6.0 down to 4.x.
if (version_compare($profile_version, '4.6', '>=')
&& version_compare($installed_version, '4.6', '<')) {
if (isset($settings['skin']) && $settings['skin'] === 'moono-lisa') {
$settings['skin'] = 'moono';
}
// Going down directly to 4.0 since no changes need to run anyway.
$migrated_version = '4.6';
}
// 4.x to 3.x.
if (version_compare($profile_version, '4.0', '>=')
&& version_compare($installed_version, '4.0', '<')) {
// Change the default skin back "moono" to "kama".
if (isset($settings['skin']) && $settings['skin'] === 'moono') {
$settings['skin'] = 'kama';
}
$migrated_version = '4.0';
}
}
// Return the version which was possible to migrate to, or FALSE on fail. Must
// be within the verified range, but not necessarily match the exact version
// which is currently installed.
return $migrated_version;
}
/**
* Return an install note.
*/
function wysiwyg_ckeditor_install_note() {
return '<p class="warning">' . t('Do NOT download the "CKEditor for Drupal" edition.') . '</p>';
$output = '<p class="warning">' . t('Do NOT download the "CKEditor for Drupal" edition.') . '</br>';
$output .= t('Make sure you install the full package as not all plugins work with the standard package.') . '</p>';
return $output;
}
/**
@@ -78,7 +170,8 @@ function wysiwyg_ckeditor_version($editor) {
// version:'CKEditor 3.0 SVN',revision:'3665'
// version:'3.0 RC',revision:'3753'
// version:'3.0.1',revision:'4391'
if (preg_match('@version:\'(?:CKEditor )?([\d\.]+)(?:.+revision:\'([\d]+))?@', $line, $version)) {
// version:"4.0",revision:"769d96134b"
if (preg_match('@version:[\'"](?:CKEditor )?([\d\.]+)(?:.+revision:[\'"]([[:xdigit:]]+))?@', $line, $version)) {
fclose($library);
// Version numbers need to have three parts since 3.0.1.
$version[1] = preg_replace('/^(\d+)\.(\d+)$/', '${1}.${2}.0', $version[1]);
@@ -124,32 +217,213 @@ function wysiwyg_ckeditor_themes($editor, $profile) {
/**
* Enhances the editor profile settings form for CKEditor.
*
* Adds support for CKEditor's advanced stylesSets, which are a more advanced
* implementation and combination of block formats and font styles that allow
* to adjust the HTML element, attributes, and CSS styles at once.
*
* @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Styles
* @see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html#.stylesSet
* @see http://docs.cksource.com/ckeditor_api/symbols/CKEDITOR.config.html
*/
function wysiwyg_ckeditor_settings_form(&$form, &$form_state) {
if (version_compare($form_state['wysiwyg']['editor']['installed version'], '3.2.1', '>=')) {
// Replace CSS classes element description to explain the advanced syntax.
$form['css']['css_classes']['#description'] = t('Optionally define CSS classes for the "Font style" dropdown list.<br />Enter one class on each line in the format: !format. Example: !example<br />If left blank, CSS classes are automatically imported from loaded stylesheet(s).', array(
'!format' => '<code>[label]=[element].[class]</code>',
'!example' => '<code>Title=h1.title</code>',
));
$form['css']['css_classes']['#element_validate'][] = 'wysiwyg_ckeditor_settings_form_validate_css_classes';
$profile = $form_state['wysiwyg_profile'];
$settings = $profile->settings;
$installed_version = $form_state['wysiwyg']['editor']['installed version'];
$ckeditor_defaults = array(
'block_formats' => 'p,address,pre,h2,h3,h4,h5,h6,div',
// Custom setting.
'default_toolbar_grouping' => FALSE,
'forcePasteAsPlainText' => FALSE,
'resize_enabled' => TRUE,
'simple_source_formatting' => FALSE,
'toolbarLocation' => 'top',
'allowedContent' => TRUE,
);
if (version_compare($installed_version, '3.1.0', '>=')) {
// Enabled by default.
$ckeditor_defaults['pasteFromWordRemoveFontStyles'] = TRUE;
$ckeditor_defaults['pasteFromWordRemoveStyles'] = TRUE;
}
else {
if (version_compare($installed_version, '4.6.0', '>=')) {
// Disabled by default, deprecated.
$ckeditor_defaults['pasteFromWordRemoveFontStyles'] = FALSE;
// Dropped, no effect.
unset($ckeditor_defaults['pasteFromWordNumberedHeadingToList'],
$ckeditor_defaults['pasteFromWordRemoveStyles']);
}
if (version_compare($installed_version, '3.2.1', '>=')) {
$ckeditor_defaults['stylesSet'] = '';
}
$settings += $ckeditor_defaults;
$form['appearance']['toolbarLocation'] = array(
'#type' => 'select',
'#title' => t('Toolbar location'),
'#default_value' => $settings['toolbarLocation'],
'#options' => array('bottom' => t('Bottom'), 'top' => t('Top')),
'#description' => t('This option controls whether the editor toolbar is displayed above or below the editing area.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'toolbarLocation', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-toolbarLocation'))),
);
$form['appearance']['resize_enabled'] = array(
'#type' => 'checkbox',
'#title' => t('Enable resizing button'),
'#default_value' => $settings['resize_enabled'],
'#return_value' => 1,
'#description' => t('This option gives you the ability to enable/disable the editor resizing feature.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'resize_enabled', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-resize_enabled'))),
);
$form['output']['simple_source_formatting'] = array(
'#type' => 'checkbox',
'#title' => t('Apply simple source formatting'),
'#default_value' => $settings['simple_source_formatting'],
'#return_value' => 1,
'#description' => t('If enabled, the editor will re-format the HTML source code using a simple set of predefined rules. Disabling this option could avoid conflicts with other input filters.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'dataProcessor.write.setRules()', '@url' => url('http://docs.cksource.com/ckeditor_api/symbols/src/plugins_htmlwriter_plugin.js.html'))),
);
$form['paste'] = array(
'#type' => 'fieldset',
'#title' => t('Paste plugin'),
'#description' => t('Settings for the <a href="@url">@plugin</a> plugin.', array('@plugin' => 'paste', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-forcePasteAsPlainText'))),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'advanced',
);
$form['paste']['forcePasteAsPlainText'] = array(
'#type' => 'checkbox',
'#title' => t('Force paste as plain text'),
'#default_value' => !empty($settings['forcePasteAsPlainText']),
'#return_value' => 1,
'#description' => t('If enabled, all pasting operations insert plain text into the editor, losing any formatting information possibly available in the source text. Note: Paste from Word is not affected by this setting.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'forcePasteAsPlainText', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-forcePasteAsPlainText'))),
);
if (version_compare($installed_version, '3.1.0', '>=')) {
$form['paste']['pasteFromWord'] = array(
'#type' => 'fieldset',
'#title' => t('Paste from Word'),
);
$form['paste']['pasteFromWord']['pasteFromWordNumberedHeadingToList'] = array(
'#type' => 'checkbox',
'#title' => t('Numbered heading to list'),
'#default_value' => !empty($settings['pasteFromWordNumberedHeadingToList']),
'#return_value' => 1,
'#description' => t('If enabled, transforms MS Word outline numbered headings into lists.'),
);
$form['paste']['pasteFromWord']['pasteFromWordPromptCleanup'] = array(
'#type' => 'checkbox',
'#title' => t('Prompt on cleanup'),
'#default_value' => !empty($settings['pasteFromWordPromptCleanup']),
'#return_value' => 1,
'#description' => t('If enabled, prompts the user about the clean up of content being pasted from MS Word.'),
);
$form['paste']['pasteFromWord']['pasteFromWordRemoveFontStyles'] = array(
'#type' => 'checkbox',
'#title' => t('Remove font styles'),
'#default_value' => !empty($settings['pasteFromWordRemoveFontStyles']),
'#return_value' => 1,
'#description' => t('If enabled, removes all font related formatting styles, including font size, font family, font foreground/background color.'),
);
$form['paste']['pasteFromWord']['pasteFromWordRemoveStyles'] = array(
'#type' => 'checkbox',
'#title' => t('Remove styles'),
'#default_value' => !empty($settings['pasteFromWordRemoveStyles']),
'#return_value' => 1,
'#description' => t('If enabled, removes element styles that can not be managed with the editor, other than font specific styles.'),
);
}
if (version_compare($installed_version, '4.1.0', '>=')) {
$form['output']['acf'] = array(
'#type' => 'fieldset',
'#title' => t('Advanced Content Filter'),
'#description' => t('ACF limits and adapts input data (HTML code added in source mode or by the editor.setData method, pasted HTML code, etc.) so it matches the editor configuration in the best possible way. It may also deactivate features which generate HTML code that is not allowed by the configuration. See <a href="@url">@url</a> for details.', array('@url' => url('http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter'))),
);
$form['output']['acf']['acf_mode'] = array(
'#type' => 'select',
'#title' => t('Mode'),
'#options' => array(
WYSIWYG_CKEDITOR_ACF_AUTOMATIC => t('Automatic'),
WYSIWYG_CKEDITOR_ACF_CUSTOM => t('Custom'),
WYSIWYG_CKEDITOR_ACF_DISABLED => t('Disabled'),
),
'#default_value' => isset($profile->settings['acf_mode']) ? $profile->settings['acf_mode'] : WYSIWYG_CKEDITOR_ACF_DISABLED,
'#description' => t('If set to <em>Automatic</em> or <em>Custom</em>, the editor will strip out any content not explicitely allowed <strong>when the editor loads</strong>.'),
);
$form['output']['acf']['acf_allowed_content'] = array(
'#type' => 'textarea',
'#title' => t('Content Rules'),
'#default_value' => isset($profile->settings['acf_allowed_content']) ? $profile->settings['acf_allowed_content'] : '',
'#description' => t('Rules for whitelisting content for the advanced content filter. Both string and object formats accepted. Uses the <a href="@allowed_url">allowedContent</a> setting in <em>Custom</em> mode <strong>or</strong> the <a href="@extra_allwed_url">extraAllowedContent</a> settings in <em>Automatic</em> mode internally. See <a href="@info_url">@info_url</a> for details.', array('@info_url' => url('http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules'), '@allowed_url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-allowedContent'), '@allowed_extra_url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-extraAllowedContent'))),
'#states' => array(
'visible' => array(
':input[name="acf_mode"]' => array(
array('value' => WYSIWYG_CKEDITOR_ACF_AUTOMATIC),
array('value' => WYSIWYG_CKEDITOR_ACF_CUSTOM),
),
),
),
'#element_validate' => array('wysiwyg_ckeditor_settings_form_validate_allowed_content'),
);
}
if (version_compare($installed_version, '3.6.0', '>=')) {
$form['appearance']['default_toolbar_grouping'] = array(
'#type' => 'checkbox',
'#title' => t('Use default toolbar button grouping'),
'#default_value' => !empty($settings['default_toolbar_grouping']),
'#return_value' => 1,
'#description' => t('This option gives you the ability to enable/disable the usage of default groupings for toolbar buttons. If enabled, toolbar buttons will be placed into predetermined groups instead of all in a single group.'),
);
}
if (version_compare($installed_version, '3.2.1', '>=')) {
// Versions below 3.2.1 do not support Font styles at all.
$form['css']['css_classes']['#access'] = FALSE;
$form['css']['stylesSet'] = array(
'#type' => 'textarea',
'#title' => t('CSS classes'),
'#description' => t('Optionally define CSS classes for the "Font style" dropdown list.<br />Enter one class on each line in the format: !format. Example: !example<br />If left blank, CSS classes are automatically imported from loaded stylesheet(s).', array(
'@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.stylesSet'),
'!format' => '<code>[label]=[element].[class]</code>',
'!example' => '<code>Title=h1.title</code>',
)) . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'stylesSet', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-stylesSet'))),
'#default_value' => $settings['stylesSet'],
'#element_validate' => array('wysiwyg_ckeditor_settings_form_validate_stylessets'),
);
}
if (version_compare($installed_version, '4.6.0', '>=')) {
$form['paste']['pasteFromWord']['pasteFromWordRemoveFontStyles']['#description'] .= '<br />' . t('This setting will be deprecated in the future. Use ACF to replicate the effect of enabling it.');
unset($form['paste']['pasteFromWord']['pasteFromWordNumberedHeadingToList'],
$form['paste']['pasteFromWord']['pasteFromWordRemoveStyles']);
}
$form['css']['block_formats'] = array(
'#type' => 'textfield',
'#title' => t('Block formats'),
'#default_value' => $settings['block_formats'],
'#size' => 40,
'#maxlength' => 250,
'#description' => t('Comma separated list of HTML block formats. Possible values: <code>@format-list</code>.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,div,blockquote,address,pre,code,dt,dd and other block elements')) . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'block_formats', '@url' => url('http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-format_tags'))),
);
}
/**
* #element_validate handler for ACF Allowed Content element altered by wysiwyg_ckeditor_settings_form().
*/
function wysiwyg_ckeditor_settings_form_validate_allowed_content($element, &$form_state) {
if (_wysiwyg_ckeditor_settings_acf_is_obj($element['#value']) && json_decode($element['#value']) === NULL) {
form_error($element, t('Allowed content is not valid JSON.'));
}
}
/**
* #element_validate handler for CSS classes element altered by wysiwyg_ckeditor_settings_form().
*/
function wysiwyg_ckeditor_settings_form_validate_css_classes($element, &$form_state) {
function wysiwyg_ckeditor_settings_form_validate_stylessets($element, &$form_state) {
if (wysiwyg_ckeditor_settings_parse_styles($element['#value']) === FALSE) {
form_error($element, t('The specified CSS classes are syntactically incorrect.'));
}
@@ -199,14 +473,16 @@ EOL;
* Drupal.settings.wysiwyg.configs.{editor}
*/
function wysiwyg_ckeditor_settings($editor, $config, $theme) {
$default_skin = (version_compare($editor['installed version'], '4.0.0', '<') ? 'kama' : (version_compare($editor['installed version'], '4.6.0', '<') ? 'moono' : 'moono-lisa'));
$settings = array(
// Needed to make relative paths work in the editor.
'baseHref' => $GLOBALS['base_url'] . '/',
'width' => 'auto',
// For better compatibility with smaller textareas.
'resize_minWidth' => 450,
'height' => 420,
// @todo Do not use skins as themes and add separate skin handling.
'theme' => 'default',
'skin' => !empty($theme) ? $theme : 'kama',
'skin' => !empty($theme) ? $theme : $default_skin,
// By default, CKEditor converts most characters into HTML entities. Since
// it does not support a custom definition, but Drupal supports Unicode, we
// disable at least the additional character sets. CKEditor always converts
@@ -219,35 +495,61 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) {
// Add HTML block format settings; common block formats are already predefined
// by CKEditor.
if (isset($config['block_formats'])) {
$block_formats = explode(',', drupal_strtolower($config['block_formats']));
$block_formats = explode(',', drupal_strtolower(preg_replace('@\s+@', '', $config['block_formats'])));
$predefined_formats = array('h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre', 'address', 'div');
foreach (array_diff($block_formats, $predefined_formats) as $tag) {
$tag = trim($tag);
$settings["format_$tag"] = array('element' => $tag);
$settings["format_$tag"] = array('element' => $tag, 'name' => strtoupper(substr($tag, 0, 1)) . substr($tag, 1));
}
$settings['format_tags'] = implode(';', $block_formats);
}
if (isset($config['apply_source_formatting'])) {
$settings['apply_source_formatting'] = $config['apply_source_formatting'];
// Advanced Content Filter
// @see http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter
if (!isset($config['acf_mode']) || WYSIWYG_CKEDITOR_ACF_DISABLED == $config['acf_mode']) {
$settings['allowedContent'] = TRUE;
}
else {
if (_wysiwyg_ckeditor_settings_acf_is_obj($config['acf_allowed_content'])) {
$acf_content = json_decode($config['acf_allowed_content']);
}
else {
$acf_content = $config['acf_allowed_content'];
}
if (WYSIWYG_CKEDITOR_ACF_CUSTOM == $config['acf_mode']) {
$settings['allowedContent'] = $acf_content;
}
elseif (WYSIWYG_CKEDITOR_ACF_AUTOMATIC == $config['acf_mode']) {
$settings['extraAllowedContent'] = $acf_content;
}
}
if (isset($config['css_setting'])) {
// Versions below 3.0.1 could only handle one stylesheet.
if (version_compare($editor['installed version'], '3.0.1.4391', '<')) {
if ($config['css_setting'] == 'theme') {
$settings['contentsCss'] = reset(wysiwyg_get_css());
$css = wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : '');
$settings['contentsCss'] = reset($css);
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['contentsCss'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['contentsCss'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
}
}
else {
if ($config['css_setting'] == 'theme') {
$settings['contentsCss'] = wysiwyg_get_css();
$settings['contentsCss'] = wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : '');
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['contentsCss'] = explode(',', strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL)))));
$settings['contentsCss'] = explode(',', strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
)));
}
}
}
@@ -255,24 +557,36 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) {
// Parse and define the styles set for the Styles plugin (3.2.1+).
// @todo This should be a plugin setting, but Wysiwyg does not support
// plugin-specific settings yet.
if (!empty($config['buttons']['default']['Styles']) && version_compare($editor['installed version'], '3.2.1', '>=')) {
if ($styles = wysiwyg_ckeditor_settings_parse_styles($config['css_classes'])) {
if (!empty($config['buttons']['default']['Styles']) && version_compare($editor['installed version'], '3.2.1', '>=') && !empty($config['stylesSet'])) {
if ($styles = wysiwyg_ckeditor_settings_parse_styles($config['stylesSet'])) {
$settings['stylesSet'] = $styles;
}
}
if (isset($config['language'])) {
$settings['language'] = $config['language'];
$check_if_set = array(
'forcePasteAsPlainText',
'language',
'pasteFromWordNumberedHeadingToList',
'pasteFromWordPromptCleanup',
'pasteFromWordRemoveFontStyles',
'pasteFromWordRemoveStyles',
'simple_source_formatting',
'toolbarLocation',
);
foreach ($check_if_set as $setting_name) {
if (isset($config[$setting_name])) {
$settings[$setting_name] = $config[$setting_name];
}
}
if (isset($config['resizing'])) {
if (isset($config['resize_enabled'])) {
// CKEditor performs a type-agnostic comparison on this particular setting.
$settings['resize_enabled'] = (bool) $config['resizing'];
}
if (isset($config['toolbar_loc'])) {
$settings['toolbarLocation'] = $config['toolbar_loc'];
$settings['resize_enabled'] = (bool) $config['resize_enabled'];
}
$settings['toolbar'] = array();
$supports_groups = version_compare($editor['installed version'], '3.6.0', '>=');
$use_default_groups = $supports_groups && !empty($config['default_toolbar_grouping']);
if (!empty($config['buttons'])) {
$extra_plugins = array();
$plugins = wysiwyg_get_plugins($editor['name']);
@@ -286,7 +600,13 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) {
}
// Add buttons.
if ($type == 'buttons') {
$settings['toolbar'][] = $button;
if ($use_default_groups) {
$settings['toolbar'][_wysiwyg_ckeditor_group($button)][] = $button;
}
else {
// Use one button row for backwards compatibility.
$settings['toolbar'][] = $button;
}
}
// Add external Drupal plugins to the list of extensions.
if ($type == 'buttons' && !empty($plugins[$plugin]['proxy'])) {
@@ -315,14 +635,29 @@ function wysiwyg_ckeditor_settings($editor, $config, $theme) {
$settings['extraPlugins'] = implode(',', $extra_plugins);
}
}
// For now, all buttons are placed into one row.
$settings['toolbar'] = array($settings['toolbar']);
if ($use_default_groups) {
// Organize groups to use lables to improves accessibility.
// http://docs.ckeditor.com/#!/guide/dev_toolbar-section-3.
$groups_toolbar = array();
foreach ($settings['toolbar'] as $group => $items) {
$groups_toolbar[] = array(
'name' => $group,
'items' => $items,
);
$settings['toolbar'] = $groups_toolbar;
}
}
else {
// For now, all buttons are placed into one row.
$settings['toolbar'] = array($settings['toolbar']);
}
return $settings;
}
/**
* Parses CSS classes settings string into a stylesSet JavaScript settings array.
* Parses stylesSet settings string into a stylesSet JavaScript settings array.
*
* @param string $css_classes
* A string containing CSS class definitions to add to the Style dropdown
@@ -367,45 +702,37 @@ function wysiwyg_ckeditor_settings_parse_styles($css_classes) {
}
/**
* Build a JS settings array of native external plugins that need to be loaded separately.
* Build a JS settings array with global metadata for native external plugins.
*/
function wysiwyg_ckeditor_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
// Register all plugins that need to be loaded.
if (!empty($plugin['load'])) {
$settings[$name] = array();
// Add path for native external plugins.
if (empty($plugin['internal']) && isset($plugin['path'])) {
$settings[$name]['path'] = base_path() . $plugin['path'] . '/';
}
// Force native internal plugins to use the standard path.
else {
$settings[$name]['path'] = base_path() . $editor['library path'] . '/plugins/' . $name . '/';
}
// CKEditor defaults to 'plugin.js' on its own when filename is not set.
if (!empty($plugin['filename'])) {
$settings[$name]['fileName'] = $plugin['filename'];
}
function _wysiwyg_ckeditor_plugin_meta($editor, $plugin) {
$meta = array();
$name = $plugin['name'];
// Register all plugins that need to be loaded.
if (!empty($plugin['load'])) {
// Add path for native external plugins.
if (empty($plugin['internal']) && isset($plugin['path'])) {
$meta['path'] = base_path() . $plugin['path'] . '/';
}
// Force native internal plugins to use the standard path.
else {
$meta['path'] = base_path() . $editor['library path'] . '/plugins/' . $name . '/';
}
// CKEditor defaults to 'plugin.js' on its own when filename is not set.
if (!empty($plugin['filename'])) {
$meta['fileName'] = $plugin['filename'];
}
}
return $settings;
return $meta;
}
/**
* Build a JS settings array for Drupal plugins loaded via the proxy plugin.
*/
function wysiwyg_ckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
function _wysiwyg_ckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
// Populate required plugin settings.
$settings[$name] = $plugin['dialog settings'] + array(
'title' => $plugin['title'],
'icon' => base_path() . $plugin['icon path'] . '/' . $plugin['icon file'],
'iconTitle' => $plugin['icon title'],
// @todo These should only be set if the plugin defined them.
'css' => base_path() . $plugin['css path'] . '/' . $plugin['css file'],
);
// Just need a list of all enabled plugins for each instance.
$settings[$name] = TRUE;
}
return $settings;
}
@@ -413,22 +740,22 @@ function wysiwyg_ckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_ckeditor_plugins($editor) {
function _wysiwyg_ckeditor_plugins($editor) {
$plugins = array(
'default' => array(
'buttons' => array(
'Bold' => t('Bold'), 'Italic' => t('Italic'), 'Underline' => t('Underline'),
'Strike' => t('Strike-through'),
'JustifyLeft' => t('Align left'), 'JustifyCenter' => t('Align center'), 'JustifyRight' => t('Align right'), 'JustifyBlock' => t('Justify'),
'Strike' => t('Strike through'),
'JustifyLeft' => t('Align left'), 'JustifyCenter' => t('Center'), 'JustifyRight' => t('Align right'), 'JustifyBlock' => t('Justify'),
'BulletedList' => t('Insert/Remove Bullet list'), 'NumberedList' => t('Insert/Remove Numbered list'),
'BidiLtr' => t('Left-to-right'), 'BidiRtl' => t('Right-to-left'),
'BulletedList' => t('Bullet list'), 'NumberedList' => t('Numbered list'),
'Outdent' => t('Outdent'), 'Indent' => t('Indent'),
'Undo' => t('Undo'), 'Redo' => t('Redo'),
'Link' => t('Link'), 'Unlink' => t('Unlink'), 'Anchor' => t('Anchor'),
'Image' => t('Image'),
'TextColor' => t('Forecolor'), 'BGColor' => t('Backcolor'),
'TextColor' => t('Text color'), 'BGColor' => t('Background color'),
'Superscript' => t('Superscript'), 'Subscript' => t('Subscript'),
'Blockquote' => t('Blockquote'), 'Source' => t('Source code'),
'Blockquote' => t('Block quote'), 'Source' => t('Source code'),
'HorizontalRule' => t('Horizontal rule'),
'Cut' => t('Cut'), 'Copy' => t('Copy'), 'Paste' => t('Paste'),
'PasteText' => t('Paste Text'), 'PasteFromWord' => t('Paste from Word'),
@@ -440,10 +767,12 @@ function wysiwyg_ckeditor_plugins($editor) {
'SelectAll' => t('Select all'), 'Find' => t('Search'), 'Replace' => t('Replace'),
'Flash' => t('Flash'), 'Smiley' => t('Smiley'),
'CreateDiv' => t('Div container'),
'Iframe' => t('iFrame'),
'Iframe' => t('IFrame'),
'Maximize' => t('Maximize'),
'SpellChecker' => t('Check spelling'), 'Scayt' => t('Check spelling as you type'),
'SpellChecker' => t('Check spelling'), 'Scayt' => t('Spell check as you type'),
'About' => t('About'),
'Templates' => t('Templates'),
'CopyFormatting' => t('Copy Formatting'),
),
'internal' => TRUE,
),
@@ -459,6 +788,118 @@ function wysiwyg_ckeditor_plugins($editor) {
if (version_compare($editor['installed version'], '3.5.0.6260', '<')) {
unset($plugins['default']['buttons']['Iframe']);
}
if (version_compare($editor['installed version'], '4.6.0', '<')) {
unset($plugins['default']['CopyFormatting']);
}
return $plugins;
}
/**
* Define grouping for ckEditor buttons.
*/
function _wysiwyg_ckeditor_group($button) {
switch ($button) {
case 'Source':
$group = 'document';
break;
case 'Cut':
case 'Copy':
case 'Paste':
case 'PasteText':
case 'PasteFromWord':
case 'Undo':
case 'Redo':
$group = 'clipboard';
break;
case 'Find':
case 'Replace':
case 'SelectAll':
case 'SpellChecker':
case 'Scayt':
$group = 'editing';
break;
case 'Bold':
case 'Italic':
case 'Underline':
case 'Strike':
case 'Subscript':
case 'Superscript':
$group = 'basicstyles';
break;
case 'RemoveFormat':
case 'CopyFormatting':
$group = 'cleanup';
break;
case 'NumberedList':
case 'BulletedList':
case 'Outdent':
case 'Indent':
case 'Blockquote':
case 'CreateDiv':
case 'JustifyLeft':
case 'JustifyCenter':
case 'JustifyRight':
case 'JustifyBlock':
case 'BidiLtr':
case 'BidiRtl':
$group = 'paragraph';
break;
case 'Link':
case 'Unlink':
case 'Anchor':
$group = 'links';
break;
case 'Image':
case 'Flash':
case 'Table':
case 'HorizontalRule':
case 'Smiley':
case 'SpecialChar':
case 'Iframe':
case 'Templates':
$group = 'insert';
break;
case 'Styles':
case 'Format':
case 'Font':
case 'FontSize':
$group = 'styles';
break;
case 'TextColor':
case 'BGColor':
$group = 'colors';
break;
case 'Maximize':
case 'ShowBlocks':
case 'About':
$group = 'tools';
break;
default:
$group = 'other';
}
return $group;
}
/**
* Determine if string is supposed to be ACF obj format.
*
* @see http://docs.ckeditor.com/#!/guide/dev_allowed_content_rules
*/
function _wysiwyg_ckeditor_settings_acf_is_obj($string) {
if (strstr($string, ':') === FALSE) {
return FALSE;
}
return TRUE;
}
@@ -1,11 +0,0 @@
/**
* openWYSIWYG.
*/
table.tableTextareaEditor, table.tableTextareaEditor table {
margin: 0;
border-collapse: separate;
}
table.tableTextareaEditor td {
padding: 0;
}
@@ -1,27 +0,0 @@
/**
* TinyMCE 2.x
*/
table.mceEditor {
clear: left;
}
/**
* Align all buttons and separators in a single row, so they wrap into multiple
* rows if required.
*/
.mceToolbarTop a, .mceToolbarBottom a {
float: left;
}
.mceSeparatorLine {
float: left;
margin-top: 3px;
}
.mceSelectList {
float: left;
margin-bottom: 1px;
}
/* Place table plugin buttons into new row */
#mce_editor_0_table, #mce_editor_1_table {
clear: left;
}
View File
@@ -0,0 +1,4 @@
.mce-container.mce-toolbar > .mce-container-body > .mce-btn-group > div {
/* Hack to make buttons wrap. */
white-space: normal !important;
}
+14 -2
View File
@@ -11,8 +11,8 @@
function wysiwyg_epiceditor_editor() {
$editor['epiceditor'] = array(
'title' => 'EpicEditor',
'vendor url' => 'http://oscargodson.github.com/EpicEditor',
'download url' => 'http://oscargodson.github.com/EpicEditor/docs/downloads/EpicEditor-v0.1.1.zip',
'vendor url' => 'http://epiceditor.com',
'download url' => 'http://epiceditor.com',
'libraries' => array(
'' => array(
'title' => 'Minified',
@@ -23,8 +23,10 @@ function wysiwyg_epiceditor_editor() {
'files' => array('js/epiceditor.js'),
),
),
'verified version range' => array('0.1.1', '0.2.2'),
'version callback' => 'wysiwyg_epiceditor_version',
'themes callback' => 'wysiwyg_epiceditor_themes',
'settings form callback' => 'wysiwyg_epiceditor_settings_form',
'settings callback' => 'wysiwyg_epiceditor_settings',
'versions' => array(
'0.1.1' => array(
@@ -76,6 +78,16 @@ function wysiwyg_epiceditor_themes($editor, $profile) {
//return array('preview-dark', 'github');
}
/**
* Enhances the editor profile settings form for EpicEditor.
*
*/
function wysiwyg_epiceditor_settings_form(&$form, &$form_state) {
$form['buttons']['#access'] = FALSE;
$form['basic']['language']['#access'] = FALSE;
$form['css']['#access'] = FALSE;
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
+111 -35
View File
@@ -19,18 +19,21 @@ function wysiwyg_fckeditor_editor() {
'files' => array('fckeditor.js'),
),
),
'verified version range' => array('2.6.0', '2.6.11'),
'version callback' => 'wysiwyg_fckeditor_version',
'themes callback' => 'wysiwyg_fckeditor_themes',
'settings form callback' => 'wysiwyg_fckeditor_settings_form',
'settings callback' => 'wysiwyg_fckeditor_settings',
'plugin callback' => 'wysiwyg_fckeditor_plugins',
'plugin settings callback' => 'wysiwyg_fckeditor_plugin_settings',
'plugin callback' => '_wysiwyg_fckeditor_plugins',
'plugin meta callback' => '_wysiwyg_fckeditor_plugin_meta',
'plugin settings callback' => '_wysiwyg_fckeditor_plugin_settings',
'proxy plugin' => array(
'drupal' => array(
'load' => TRUE,
'proxy' => TRUE,
),
),
'proxy plugin settings callback' => 'wysiwyg_fckeditor_proxy_plugin_settings',
'proxy plugin settings callback' => '_wysiwyg_fckeditor_proxy_plugin_settings',
'versions' => array(
'2.6' => array(
'js files' => array('fckeditor-2.6.js'),
@@ -82,6 +85,68 @@ function wysiwyg_fckeditor_themes($editor, $profile) {
return array('default', 'office2003', 'silver');
}
/**
* Enhances the editor profile settings form for FCKeditor.
*
* @see http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options
*/
function wysiwyg_fckeditor_settings_form(&$form, &$form_state) {
$profile = $form_state['wysiwyg_profile'];
$settings = $profile->settings;
$settings += array(
'AutoDetectPasteFromWord' => TRUE,
'ForcePasteAsPlainText' => FALSE,
'FontFormats' => 'p;address;pre;h2;h3;h4;h5;h6;div',
'FormatOutput' => TRUE,
'FormatSource' => TRUE,
);
$form['output']['FormatSource'] = array(
'#type' => 'checkbox',
'#title' => t('Apply source formatting'),
'#default_value' => $settings['FormatSource'],
'#return_value' => 1,
'#description' => t('If enabled, the editor will re-format the HTML source code when switching to Source View.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'FormatSource', '@url' => url('http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options/FormatSource'))),
);
$form['output']['FormatOutput'] = array(
'#type' => 'checkbox',
'#title' => t('Apply output formatting'),
'#default_value' => $settings['FormatOutput'],
'#return_value' => 1,
'#description' => t('If enabled, the editor will re-format the HTML source code output. Disabling this option could avoid conflicts with other input filters.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'FormatOutput', '@url' => url('http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options/FormatOutput'))),
);
$form['css']['FontFormats'] = array(
'#type' => 'textfield',
'#title' => t('Block formats'),
'#default_value' => $settings['FontFormats'],
'#size' => 40,
'#maxlength' => 250,
'#description' => t('Semicolon separated list of HTML block formats. Possible values: <code>@format-list</code>.', array('@format-list' => 'p;h1;h2;h3;h4;h5;h6;div;address;pre')) . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'FontFormats', '@url' => url('http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options/FontFormats'))),
);
$form['paste'] = array(
'#type' => 'fieldset',
'#title' => t('Paste plugin'),
'#description' => t('Settings for the paste plugin.'),
'#collapsible' => TRUE,
'#collapsed' => TRUE,
'#group' => 'advanced',
);
$form['paste']['AutoDetectPasteFromWord'] = array(
'#type' => 'checkbox',
'#title' => t('Auto detect paste from Word'),
'#default_value' => $settings['AutoDetectPasteFromWord'],
'#return_value' => 1,
'#description' => t('If enabled, FCKeditor checks if pasted text comes from MS Word. If so the editor will launch the "Paste from Word" window. <strong>Only works in Internet Explorer.</strong>') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'AutoDetectPasteFromWord', '@url' => url('http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options/AutoDetectPasteFromWord'))),
);
$form['paste']['ForcePasteAsPlainText'] = array(
'#type' => 'checkbox',
'#title' => t('Force paste as plain text'),
'#default_value' => $settings['ForcePasteAsPlainText'],
'#return_value' => 1,
'#description' => t('If enabled, forces the editor to discard all formatting when pasting text. It will also disable the <strong>Paste from Word</strong> operation.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'ForcePasteAsPlainText', '@url' => url('http://docs.cksource.com/FCKeditor_2.x/Developers_Guide/Configuration/Configuration_Options/ForcePasteAsPlainText'))),
);
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
@@ -102,7 +167,6 @@ function wysiwyg_fckeditor_settings($editor, $config, $theme) {
'SkinPath' => base_path() . $editor['library path'] . '/editor/skins/' . $theme . '/',
'CustomConfigurationsPath' => base_path() . drupal_get_path('module', 'wysiwyg') . '/editors/js/fckeditor.config.js',
'Width' => '100%',
'Height' => 420,
'LinkBrowser' => FALSE,
'LinkUpload' => FALSE,
'ImageBrowser' => FALSE,
@@ -117,22 +181,31 @@ function wysiwyg_fckeditor_settings($editor, $config, $theme) {
'IncludeLatinEntities' => FALSE,
'IncludeGreekEntities' => FALSE,
);
if (isset($config['block_formats'])) {
$settings['FontFormats'] = strtr($config['block_formats'], array(',' => ';'));
if (isset($config['FontFormats'])) {
$settings['FontFormats'] = preg_replace('@\s+@', '', $config['FontFormats']);
}
if (isset($config['apply_source_formatting'])) {
$settings['FormatOutput'] = $settings['FormatSource'] = $config['apply_source_formatting'];
}
if (isset($config['paste_auto_cleanup_on_paste'])) {
$settings['AutoDetectPasteFromWord'] = $config['paste_auto_cleanup_on_paste'];
$check_if_set = array(
'AutoDetectPasteFromWord',
'ForcePasteAsPlainText',
'FormatOutput',
'FormatSource',
);
foreach ($check_if_set as $setting_name) {
if (isset($config[$setting_name])) {
$settings[$setting_name] = $config[$setting_name];
}
}
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
$settings['EditorAreaCSS'] = implode(',', wysiwyg_get_css());
$settings['EditorAreaCSS'] = implode(',', wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : ''));
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['EditorAreaCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['EditorAreaCSS'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
}
}
@@ -169,22 +242,30 @@ function wysiwyg_fckeditor_settings($editor, $config, $theme) {
}
/**
* Build a JS settings array of native external plugins that need to be loaded separately.
* Build a JS settings array with global metadata for native external plugins.
*/
function wysiwyg_fckeditor_plugin_settings($editor, $profile, $plugins) {
function _wysiwyg_fckeditor_plugin_meta($editor, $plugin) {
$meta = array();
// Add path for native external plugins; internal ones do not need a path.
if (empty($plugin['internal']) && isset($plugin['path'])) {
// All native FCKeditor plugins use the filename fckplugin.js.
$meta['path'] = base_path() . $plugin['path'] . '/';
}
if (!empty($plugin['languages'])) {
$meta['languages'] = $plugin['languages'];
}
return $meta;
}
/**
* Build a JS settings array for native external plugins.
*/
function _wysiwyg_fckeditor_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
// Register all plugins that need to be loaded.
if (!empty($plugin['load'])) {
$settings[$name] = array();
// Add path for native external plugins; internal ones do not need a path.
if (empty($plugin['internal']) && isset($plugin['path'])) {
// All native FCKeditor plugins use the filename fckplugin.js.
$settings[$name]['path'] = base_path() . $plugin['path'] . '/';
}
if (!empty($plugin['languages'])) {
$settings[$name]['languages'] = $plugin['languages'];
}
// Just need a list of all enabled plugins for each instance.
$settings[$name] = TRUE;
}
}
return $settings;
@@ -193,25 +274,20 @@ function wysiwyg_fckeditor_plugin_settings($editor, $profile, $plugins) {
/**
* Build a JS settings array for Drupal plugins loaded via the proxy plugin.
*/
function wysiwyg_fckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
function _wysiwyg_fckeditor_proxy_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
// Populate required plugin settings.
$settings[$name] = $plugin['dialog settings'] + array(
'title' => $plugin['title'],
'icon' => base_path() . $plugin['icon path'] . '/' . $plugin['icon file'],
'iconTitle' => $plugin['icon title'],
// @todo These should only be set if the plugin defined them.
'css' => base_path() . $plugin['css path'] . '/' . $plugin['css file'],
);
// Just need a list of all enabled plugins for each instance.
$settings[$name] = TRUE;
}
return $settings;
}
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_fckeditor_plugins($editor) {
function _wysiwyg_fckeditor_plugins($editor) {
$plugins = array(
'default' => array(
'buttons' => array(
+129 -78
View File
@@ -1,36 +1,64 @@
(function($) {
Drupal.wysiwyg.editor.init.ckeditor = function(settings) {
// Plugins must only be loaded once. Only the settings from the first format
// will be used but they're identical anyway.
var registeredPlugins = {};
for (var format in settings) {
if (Drupal.settings.wysiwyg.plugins[format]) {
// Register native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
for (var pluginName in Drupal.settings.wysiwyg.plugins[format]['native']) {
if (!registeredPlugins[pluginName]) {
var plugin = Drupal.settings.wysiwyg.plugins[format]['native'][pluginName];
CKEDITOR.plugins.addExternal(pluginName, plugin.path, plugin.fileName);
registeredPlugins[pluginName] = true;
}
}
// Register Drupal plugins.
for (var pluginName in Drupal.settings.wysiwyg.plugins[format].drupal) {
if (!registeredPlugins[pluginName]) {
Drupal.wysiwyg.editor.instance.ckeditor.addPlugin(pluginName, Drupal.settings.wysiwyg.plugins[format].drupal[pluginName], Drupal.settings.wysiwyg.plugins.drupal[pluginName]);
registeredPlugins[pluginName] = true;
}
}
CKEDITOR.disableAutoInline = true;
// Exclude every id starting with 'cke_' in ajax_html_ids during AJAX requests.
Drupal.wysiwyg.excludeIdSelectors.wysiwyg_ckeditor = ['[id^="cke_"]'];
// Keeps track of private instance data.
var instanceMap;
/**
* Initialize the editor library.
*
* This method is called once the first time a library is needed. If new
* WYSIWYG fieldsare added later, update() will be called instead.
*
* @param settings
* An object containing editor settings for each input format.
* @param pluginInfo
* An object containing global plugin configuration.
*/
Drupal.wysiwyg.editor.init.ckeditor = function(settings, pluginInfo) {
instanceMap = {};
// Nothing to do here other than register new plugins etc.
Drupal.wysiwyg.editor.update.ckeditor(settings, pluginInfo);
};
/**
* Update the editor library when new settings are available.
*
* This method is called instead of init() when at least one new WYSIWYG field
* has been added to the document and the library has already been initialized.
*
* $param settings
* An object containing editor settings for each input format.
* $param pluginInfo
* An object containing global plugin configuration.
*/
Drupal.wysiwyg.editor.update.ckeditor = function(settings, pluginInfo) {
// Register native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
for (var pluginId in pluginInfo['native']) {
if (pluginInfo['native'].hasOwnProperty(pluginId) && (!CKEDITOR.plugins.externals || !CKEDITOR.plugins.externals[pluginId])) {
var plugin = pluginInfo['native'][pluginId];
CKEDITOR.plugins.addExternal(pluginId, plugin.path, plugin.fileName);
}
// Register Font styles (versions 3.2.1 and above).
if (Drupal.settings.wysiwyg.configs.ckeditor[format].stylesSet) {
CKEDITOR.stylesSet.add(format, Drupal.settings.wysiwyg.configs.ckeditor[format].stylesSet);
}
// Build and register Drupal plugin wrappers.
for (var pluginId in pluginInfo.drupal) {
if (pluginInfo.drupal.hasOwnProperty(pluginId) && (!CKEDITOR.plugins.registered || !CKEDITOR.plugins.registered[pluginId])) {
Drupal.wysiwyg.editor.instance.ckeditor.addPlugin(pluginId, pluginInfo.drupal[pluginId]);
}
}
// Register Font styles (versions 3.2.1 and above).
for (var format in settings) {
if (settings[format].stylesSet && (!CKEDITOR.stylesSet || !CKEDITOR.stylesSet.registered[format])) {
CKEDITOR.stylesSet.add(format, settings[format].stylesSet);
}
}
};
/**
* Attach this editor to a target element.
*/
@@ -38,8 +66,10 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
// Apply editor instance settings.
CKEDITOR.config.customConfig = '';
var $drupalToolbar = $('#toolbar', Drupal.overlayChild ? window.parent.document : document);
var $drupalToolbars = $('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.document : document);
if (!settings.height) {
settings.height = $('#' + params.field).height();
}
settings.on = {
instanceReady: function(ev) {
var editor = ev.editor;
@@ -49,7 +79,7 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
var tags = CKEDITOR.tools.extend({}, dtd.$block, dtd.$listItem, dtd.$tableContent);
// Set source formatting rules for each listed tag except <pre>.
// Linebreaks can be inserted before or after opening and closing tags.
if (settings.apply_source_formatting) {
if (settings.simple_source_formatting) {
// Mimic FCKeditor output, by breaking lines between tags.
for (var tag in tags) {
if (tag == 'pre') {
@@ -85,16 +115,18 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
},
pluginsLoaded: function(ev) {
var wysiwygInstance = instanceMap[this.name];
var enabledPlugins = wysiwygInstance.pluginInfo.instances.drupal;
// Override the conversion methods to let Drupal plugins modify the data.
var editor = ev.editor;
if (editor.dataProcessor && Drupal.settings.wysiwyg.plugins[params.format]) {
if (editor.dataProcessor && enabledPlugins) {
editor.dataProcessor.toHtml = CKEDITOR.tools.override(editor.dataProcessor.toHtml, function(originalToHtml) {
// Convert raw data for display in WYSIWYG mode.
return function(data, fixForBody) {
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
for (var plugin in enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
data = Drupal.wysiwyg.instances[params.field].prepareContent(data);
data = Drupal.wysiwyg.plugins[plugin].attach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name);
data = wysiwygInstance.prepareContent(data);
}
}
return originalToHtml.call(this, data, fixForBody);
@@ -104,9 +136,9 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
// Convert WYSIWYG mode content to raw data.
return function(data, fixForBody) {
data = originalToDataFormat.call(this, data, fixForBody);
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
for (var plugin in enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], editor.name);
data = Drupal.wysiwyg.plugins[plugin].detach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name);
}
}
return data;
@@ -116,16 +148,15 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
},
selectionChange: function (event) {
var pluginSettings = Drupal.settings.wysiwyg.plugins[params.format];
if (pluginSettings && pluginSettings.drupal) {
$.each(pluginSettings.drupal, function (name) {
var plugin = Drupal.wysiwyg.plugins[name];
if ($.isFunction(plugin.isNode)) {
var node = event.data.selection.getSelectedElement();
var state = plugin.isNode(node ? node.$ : null) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
event.editor.getCommand(name).setState(state);
}
});
var wysiwygInstance = instanceMap[this.name];
var enabledPlugins = wysiwygInstance.pluginInfo.instances.drupal;
for (var name in enabledPlugins) {
var plugin = Drupal.wysiwyg.plugins[name];
if ($.isFunction(plugin.isNode)) {
var node = event.data.selection.getSelectedElement();
var state = plugin.isNode(node ? node.$ : null) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
event.editor.getCommand(name).setState(state);
}
}
},
@@ -139,52 +170,46 @@ Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
return;
}
if (ev.data.command.state == CKEDITOR.TRISTATE_ON) {
$drupalToolbar.hide();
$drupalToolbars.hide();
}
else {
$drupalToolbar.show();
$drupalToolbars.show();
}
},
destroy: function (event) {
// Free our reference to the private instance to not risk memory leaks.
delete instanceMap[this.name];
}
};
instanceMap[params.field] = this;
// Attach editor.
CKEDITOR.replace(params.field, settings);
var editorInstance = CKEDITOR.replace(params.field, settings);
};
/**
* Detach a single or all editors.
*
* @todo 3.x: editor.prototype.getInstances() should always return an array
* containing all instances or the passed in params.field instance, but
* always return an array to simplify all detach functions.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.ckeditor = function (context, params, trigger) {
var method = (trigger == 'serialize') ? 'updateElement' : 'destroy';
if (typeof params != 'undefined') {
var instance = CKEDITOR.instances[params.field];
if (instance) {
instance[method]();
}
}
else {
for (var instanceName in CKEDITOR.instances) {
if (CKEDITOR.instances.hasOwnProperty(instanceName)) {
CKEDITOR.instances[instanceName][method]();
}
}
var instance = CKEDITOR.instances[params.field];
if (!instance) {
return;
}
instance[method]();
};
Drupal.wysiwyg.editor.instance.ckeditor = {
addPlugin: function(pluginName, settings, pluginSettings) {
addPlugin: function (pluginName, pluginSettings) {
CKEDITOR.plugins.add(pluginName, {
// Wrap Drupal plugin in a proxy pluygin.
init: function(editor) {
if (settings.css) {
if (pluginSettings.css) {
editor.on('mode', function(ev) {
if (ev.editor.mode == 'wysiwyg') {
// Inject CSS files directly into the editing area head tag.
$('head', $('#cke_contents_' + ev.editor.name + ' iframe').eq(0).contents()).append('<link rel="stylesheet" href="' + settings.css + '" type="text/css" >');
var iframe = $('#cke_contents_' + ev.editor.name + ' iframe, #' + ev.editor.id + '_contents iframe');
$('head', iframe.eq(0).contents()).append('<link rel="stylesheet" href="' + pluginSettings.css + '" type="text/css" >');
}
});
}
@@ -199,12 +224,7 @@ Drupal.wysiwyg.editor.instance.ckeditor = {
data.node = data.node.$;
}
if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
if (CKEDITOR.env.ie) {
data.content = selection.getNative().createRange().text;
}
else {
data.content = selection.getNative().toString();
}
data.content = selection.getSelectedText();
}
else if (data.node) {
// content is supposed to contain the "outerHTML".
@@ -217,9 +237,9 @@ Drupal.wysiwyg.editor.instance.ckeditor = {
editor.addCommand(pluginName, pluginCommand);
}
editor.ui.addButton(pluginName, {
label: settings.iconTitle,
label: pluginSettings.title,
command: pluginName,
icon: settings.icon
icon: pluginSettings.icon
});
// @todo Add button state handling.
@@ -233,7 +253,33 @@ Drupal.wysiwyg.editor.instance.ckeditor = {
insert: function(content) {
content = this.prepareContent(content);
CKEDITOR.instances[this.field].insertHtml(content);
if (CKEDITOR.env.webkit || CKEDITOR.env.chrome || CKEDITOR.env.opera || CKEDITOR.env.safari) {
// Works around a WebKit bug which removes wrapper elements.
// @see https://drupal.org/node/1927968
var tmp = new CKEDITOR.dom.element('div'), children, skip = 0, item;
tmp.setHtml(content);
children = tmp.getChildren();
skip = 0;
while (children.count() > skip) {
item = children.getItem(skip);
switch(item.type) {
case 1:
CKEDITOR.instances[this.field].insertElement(item);
break;
case 3:
CKEDITOR.instances[this.field].insertText(item.getText());
skip++;
break;
case 8:
CKEDITOR.instances[this.field].insertHtml(item.getOuterHtml());
skip++;
break;
}
}
}
else {
CKEDITOR.instances[this.field].insertHtml(content);
}
},
setContent: function (content) {
@@ -242,6 +288,11 @@ Drupal.wysiwyg.editor.instance.ckeditor = {
getContent: function () {
return CKEDITOR.instances[this.field].getData();
},
isFullscreen: function () {
var cmd = CKEDITOR.instances[this.field].commands.maximize;
return !!(cmd && cmd.state == CKEDITOR.TRISTATE_ON);
}
};
+141 -12
View File
@@ -4,10 +4,15 @@
* Attach this editor to a target element.
*/
Drupal.wysiwyg.editor.attach.epiceditor = function (context, params, settings) {
var $target = $('#' + params.field);
var containerId = params.field + '-epiceditor';
var defaultContent = $target.val();
$target.hide().after('<div id="' + containerId + '" />');
var $target = $('#' + params.field),
containerId = params.field + '-epiceditor',
defaultContent = $target.val(),
$container = $('<div id="' + containerId + '" />');
$target.hide().after($container);
if (!settings.height) {
settings.height = $('#' + params.field).height();
}
$container.height(settings.height);
settings.container = containerId;
settings.file = {
@@ -16,23 +21,147 @@ Drupal.wysiwyg.editor.attach.epiceditor = function (context, params, settings) {
settings.theme = {
preview: '/themes/preview/preview-dark.css',
editor: '/themes/editor/' + settings.theme + '.css'
}
};
var editor = new EpicEditor(settings).load();
$target.data('epiceditor', editor);
};
/**
* Detach a single or all editors.
* Detach a single edtor instance.
*/
Drupal.wysiwyg.editor.detach.epiceditor = function (context, params, trigger) {
var $target = $('#' + params.field);
var $target = $('#' + params.field, context);
var editor = $target.data('epiceditor');
if (!editor) {
return;
}
// Save contents of the editor back into the textarea.
$target.val(editor.exportFile());
editor.unload(function () {
$target.show();
});
if (trigger !== 'serialize') {
// Remove editor instance and container.
editor.unload(function () {
$target.show();
$('#' + $target.attr('id') + '-epiceditor').remove();
});
$target.removeData('epiceditor');
}
};
/**
* Check if a DOM node is inside another or if they are the same.
*/
function isInside (innerNode, outerNode) {
var found = false;
if (innerNode === outerNode) {
return true;
}
$(innerNode).parents().each(function (index, parent) {
if (parent === outerNode) {
found = true;
return false;
}
});
return found;
}
/**
* Converts HTML markup to plain text.
*
* EpicEditor isn't WYSIWYG and is meant to handle plain text though it does so
* in a contentEditable element. This is taken from EpicEditor's internal
* _setText() function in version 0.2.0.
*/
function toPlainText (content) {
content = content.replace(/</g, '&lt;');
content = content.replace(/>/g, '&gt;');
content = content.replace(/\n/g, '<br>');
content = content.replace(/\s\s/g, ' &nbsp;')
return content;
}
Drupal.wysiwyg.editor.instance.epiceditor = {
insert: function (content) {
var instance = this.getInstance();
var editingArea = instance.getElement('editor').body;
// IE.
// @todo Can't test this, EpicEditor breaks in IE.
if (document.selection) {
var sel = editingArea.selection;
range = sel.createRange();
// If the caret is not in the editing area, just append the content.
if (!isInside(range.parentElement(), editingArea)) {
editingArea.innerHTML += toPlainText(content);
}
else {
// Insert content and set the caret after it.
range.pasteHTML(content);
range.select();
range.collapse(false);
}
}
else {
// The code below doesn't work in IE, but it never gets here.
var sel = editingArea.ownerDocument.getSelection();
// Convert selection to a range.
// W3C compatible.
if (sel.getRangeAt) {
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
}
// Safari.
else {
range = editingArea.ownerDocument.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, userSeletion.focusOffset);
}
// If the caret is not in the editing area, just append the content.
if (sel.rangeCount == 0 || !isInside(range.commonAncestorContainer, editingArea)) {
editingArea.innerHTML += toPlainText(content);
return;
}
var fragment = editingArea.ownerDocument.createDocumentFragment();
// Fragments don't support innerHTML.
var wrapper = editingArea.ownerDocument.createElement('div');
wrapper.innerHTML = toPlainText(content);
while (wrapper.firstChild) {
fragment.appendChild(wrapper.firstChild);
}
// Append a temporary node to control caret position.
var tn = editingArea.ownerDocument.createElement('span');
fragment.appendChild(tn);
range.deleteContents();
// Only fragment children are inserted.
range.insertNode(fragment);
// Move caret to temp node and remove it.
range.setStartBefore(tn);
range.setEndBefore(tn);
sel.removeAllRanges();
sel.addRange(range);
tn.parentNode.removeChild(tn);
}
},
setContent: function (content) {
this.getInstance().importFile(null, content);
},
getContent: function () {
return this.getInstance().exportFile();
},
isFullscreen: function () {
return this.getInstance().is('fullscreen');
},
getInstance: function () {
if (!this.editorInstance) {
this.editorInstance = $('#' + this.field).data('epiceditor');
}
return this.editorInstance;
}
}
})(jQuery);
+54 -52
View File
@@ -4,7 +4,14 @@
* Attach this editor to a target element.
*/
Drupal.wysiwyg.editor.attach.fckeditor = function(context, params, settings) {
if (!settings.Height) {
settings.Height = $('#' + params.field).height();
}
var FCKinstance = new FCKeditor(params.field, settings.Width, settings.Height, settings.ToolbarSet);
// Keep track of the settings for this instance.
this.editorSettings = settings;
// Temporarily store the private instance for use in the config file.
$('#' + params.field, context).data('wysiwygInstance', this);
// Apply editor instance settings.
FCKinstance.BasePath = settings.EditorPath;
FCKinstance.Config.wysiwygFormat = params.format;
@@ -19,52 +26,46 @@ Drupal.wysiwyg.editor.attach.fckeditor = function(context, params, settings) {
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.fckeditor = function (context, params, trigger) {
var instances = [];
if (typeof params != 'undefined' && typeof FCKeditorAPI != 'undefined') {
var instance = FCKeditorAPI.GetInstance(params.field);
if (instance) {
instances[params.field] = instance;
}
var instanceName = params.field;
var instance = FCKeditorAPI.GetInstance(instanceName);
if (!instance) {
return;
}
else {
instances = FCKeditorAPI.__Instances;
instance.UpdateLinkedField();
if (trigger == 'serialize') {
// The editor is not being removed from the DOM, so updating the linked
// field is the only action necessary.
return;
}
for (var instanceName in instances) {
var instance = instances[instanceName];
instance.UpdateLinkedField();
if (trigger == 'serialize') {
// The editor is not being removed from the DOM, so updating the linked
// field is the only action necessary.
continue;
}
// Since we already detach the editor and update the textarea, the submit
// event handler needs to be removed to prevent data loss (in IE).
// FCKeditor uses 2 nested iFrames; instance.EditingArea.Window is the
// deepest. Its parent is the iFrame containing the editor.
var instanceScope = instance.EditingArea.Window.parent;
instanceScope.FCKTools.RemoveEventListener(instance.GetParentForm(), 'submit', instance.UpdateLinkedField);
// Run cleanups before forcing an unload of the iFrames or IE crashes.
// This also deletes the instance from the FCKeditorAPI.__Instances array.
instanceScope.FCKTools.RemoveEventListener(instanceScope, 'unload', instanceScope.FCKeditorAPI_Cleanup);
instanceScope.FCKTools.RemoveEventListener(instanceScope, 'beforeunload', instanceScope.FCKeditorAPI_ConfirmCleanup);
if (jQuery.isFunction(instanceScope.FCKIECleanup_Cleanup)) {
instanceScope.FCKIECleanup_Cleanup();
}
instanceScope.FCKeditorAPI_ConfirmCleanup();
instanceScope.FCKeditorAPI_Cleanup();
// Remove the editor elements.
$('#' + instanceName + '___Config').remove();
$('#' + instanceName + '___Frame').remove();
$('#' + instanceName).show();
// Since we already detach the editor and update the textarea, the submit
// event handler needs to be removed to prevent data loss (in IE).
// FCKeditor uses 2 nested iFrames; instance.EditingArea.Window is the
// deepest. Its parent is the iFrame containing the editor.
var instanceScope = instance.EditingArea.Window.parent;
instanceScope.FCKTools.RemoveEventListener(instance.GetParentForm(), 'submit', instance.UpdateLinkedField);
// Run cleanups before forcing an unload of the iFrames or IE crashes.
// This also deletes the instance from the FCKeditorAPI.__Instances array.
instanceScope.FCKTools.RemoveEventListener(instanceScope, 'unload', instanceScope.FCKeditorAPI_Cleanup);
instanceScope.FCKTools.RemoveEventListener(instanceScope, 'beforeunload', instanceScope.FCKeditorAPI_ConfirmCleanup);
if (jQuery.isFunction(instanceScope.FCKIECleanup_Cleanup)) {
instanceScope.FCKIECleanup_Cleanup();
}
instanceScope.FCKeditorAPI_ConfirmCleanup();
instanceScope.FCKeditorAPI_Cleanup();
// Remove the editor elements.
$('#' + instanceName + '___Config').remove();
$('#' + instanceName + '___Frame').remove();
$('#' + instanceName).show();
};
Drupal.wysiwyg.editor.instance.fckeditor = {
init: function(instance) {
var wysiwygInstance = instance.wysiwygInstance;
var pluginInfo = wysiwygInstance.pluginInfo;
var enabledPlugins = pluginInfo.instances.drupal;
// Track which editor instance is active.
instance.FCK.Events.AttachEvent('OnFocus', function(editorInstance) {
Drupal.wysiwyg.activeId = editorInstance.Name;
@@ -79,12 +80,10 @@ Drupal.wysiwyg.editor.instance.fckeditor = {
// Called from SetData() with stripped comments/scripts, revert those
// manipulations and attach Drupal plugins.
var data = instance.FCKConfig.ProtectedSource.Revert(data);
if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat] && Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
for (var plugin in Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].attach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], instance.FCK.Name);
data = Drupal.wysiwyg.editor.instance.fckeditor.prepareContent(data);
}
for (var plugin in enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].attach(data, pluginInfo.global.drupal[plugin], instance.FCK.Name);
data = Drupal.wysiwyg.editor.instance.fckeditor.prepareContent(data);
}
}
// Re-protect the source and use the original data processor to convert it
@@ -97,11 +96,9 @@ Drupal.wysiwyg.editor.instance.fckeditor = {
// Called from GetData(), convert the content's DOM into a XHTML string
// using the original data processor and detach Drupal plugins.
var data = instance.FCKDataProcessor.prototype.ConvertToDataFormat.call(this, rootNode, excludeRoot, ignoreIfEmptyParagraph, format);
if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat] && Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
for (var plugin in Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal) {
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].detach(data, Drupal.settings.wysiwyg.plugins.drupal[plugin], instance.FCK.Name);
}
for (var plugin in enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].detach(data, pluginInfo.global.drupal[plugin], instance.FCK.Name);
}
}
return data;
@@ -109,13 +106,13 @@ Drupal.wysiwyg.editor.instance.fckeditor = {
instance.FCK.DataProcessor = new wysiwygDataProcessor();
},
addPlugin: function(plugin, settings, pluginSettings, instance) {
addPlugin: function(plugin, pluginSettings, instance) {
if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
return;
}
if (Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal[plugin].css) {
instance.FCKConfig.EditorAreaCSS += ',' + Drupal.settings.wysiwyg.plugins[instance.wysiwygFormat].drupal[plugin].css;
if (pluginSettings.css) {
instance.FCKConfig.EditorAreaCSS += ',' + pluginSettings.css;
}
// @see fckcommands.js, fck_othercommands.js, fckpastewordcommand.js
@@ -160,7 +157,7 @@ Drupal.wysiwyg.editor.instance.fckeditor = {
// Register the plugin button.
// Arguments: commandName, label, tooltip, style, sourceView, contextSensitive, icon.
instance.FCKToolbarItems.RegisterItem(plugin, new instance.FCKToolbarButton(plugin, settings.iconTitle, settings.iconTitle, null, false, true, settings.icon));
instance.FCKToolbarItems.RegisterItem(plugin, new instance.FCKToolbarButton(plugin, pluginSettings.title, pluginSettings.title, null, false, true, pluginSettings.icon));
},
openDialog: function(dialog, params) {
@@ -190,6 +187,11 @@ Drupal.wysiwyg.editor.instance.fckeditor = {
setContent: function (content) {
var instance = FCKeditorAPI.GetInstance(this.field);
instance.SetHTML(content);
},
isFullscreen: function () {
var cmd = FCKeditorAPI.GetInstance(this.field).Commands.LoadedCommands.FitWindow;
return !!(cmd && cmd.IsMaximized);
}
};
+15 -11
View File
@@ -8,9 +8,12 @@ Drupal = window.parent.Drupal;
* Instance settings passed to FCKinstance.Config are temporarily stored in
* FCKConfig.PageConfig.
*/
var wysiwygFormat = FCKConfig.PageConfig.wysiwygFormat;
var wysiwygSettings = Drupal.settings.wysiwyg.configs.fckeditor[wysiwygFormat];
var pluginSettings = (Drupal.settings.wysiwyg.plugins[wysiwygFormat] ? Drupal.settings.wysiwyg.plugins[wysiwygFormat] : { 'native': {}, 'drupal': {} });
// Fetch the private instance and make sure nothing can tamper with it.
var $field = window.parent.jQuery(FCK.LinkedField);
var wysiwygInstance = $field.data('wysiwygInstance');
$field.removeData('wysiwygInstance');
var wysiwygSettings = wysiwygInstance.editorSettings;
var pluginInfo = wysiwygInstance.pluginInfo;
/**
* Apply format-specific settings.
@@ -42,21 +45,21 @@ for (var setting in wysiwygSettings) {
// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
var oldFitWindowExecute = FCKFitWindow.prototype.Execute;
var $drupalToolbar = window.parent.jQuery('#toolbar', Drupal.overlayChild ? window.parent.window.parent.document : window.parent.document);
var $drupalToolbars = window.parent.jQuery('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.window.parent.document : window.parent.document);
FCKFitWindow.prototype.Execute = function() {
oldFitWindowExecute.apply(this, arguments);
if (this.IsMaximized) {
$drupalToolbar.hide();
$drupalToolbars.hide();
}
else {
$drupalToolbar.show();
$drupalToolbars.show();
}
}
/**
* Initialize this editor instance.
*/
Drupal.wysiwyg.editor.instance.fckeditor.init(window);
wysiwygInstance.init(window);
/**
* Register native plugins for this input format.
@@ -66,9 +69,9 @@ Drupal.wysiwyg.editor.instance.fckeditor.init(window);
* - Languages the plugin is available in.
* - Location of the plugin folder; <plugin_name>/fckplugin.js is appended.
*/
for (var plugin in pluginSettings['native']) {
for (var pluginId in pluginInfo.instances['native']) {
// Languages and path may be undefined for internal plugins.
FCKConfig.Plugins.Add(plugin, pluginSettings['native'][plugin].languages, pluginSettings['native'][plugin].path);
FCKConfig.Plugins.Add(pluginId, pluginInfo.global['native'][pluginId].languages, pluginInfo.global['native'][pluginId].path);
}
/**
@@ -80,7 +83,8 @@ for (var plugin in pluginSettings['native']) {
* - General plugin settings.
* - A reference to this window so the plugin setup can access FCKConfig.
*/
for (var plugin in pluginSettings.drupal) {
Drupal.wysiwyg.editor.instance.fckeditor.addPlugin(plugin, pluginSettings.drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin], window);
for (var pluginId in pluginInfo.instances.drupal) {
var plugin = pluginInfo.instances.drupal[pluginId];
Drupal.wysiwyg.editor.instance.fckeditor.addPlugin(pluginId, pluginInfo.global.drupal[pluginId], window);
}
+2 -2
View File
@@ -5,11 +5,11 @@
*/
Drupal.wysiwyg.editor.attach.jwysiwyg = function(context, params, settings) {
// Attach editor.
$('#' + params.field).wysiwyg();
$('#' + params.field).wysiwyg(settings);
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.jwysiwyg = function (context, params, trigger) {
var $field = $('#' + params.field);
+1 -6
View File
@@ -21,12 +21,7 @@ Drupal.wysiwyg.editor.detach.markitup = function (context, params, trigger) {
if (trigger == 'serialize') {
return;
}
if (typeof params != 'undefined') {
$('#' + params.field, context).markItUpRemove();
}
else {
$('.markItUpEditor', context).markItUpRemove();
}
$('#' + params.field, context).markItUpRemove();
};
Drupal.wysiwyg.editor.instance.markitup = {
+61 -38
View File
@@ -26,62 +26,70 @@ Drupal.wysiwyg.editor.attach.nicedit = function(context, params, settings) {
};
/**
* Detach a single or all editors.
*
* See Drupal.wysiwyg.editor.detach.none() for a full description of this hook.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.nicedit = function (context, params, trigger) {
if (typeof params != 'undefined') {
var instance = nicEditors.findEditor(params.field);
if (instance) {
if (trigger == 'serialize') {
instance.saveContent();
}
else {
instance.ne.removeInstance(params.field);
instance.ne.removePanel();
}
}
var instance = nicEditors.findEditor(params.field);
if (!instance) {
return;
}
if (trigger === 'serialize') {
instance.saveContent();
}
else {
for (var e in nicEditors.editors) {
// Save contents of all editors back into textareas.
var instances = nicEditors.editors[e].nicInstances;
for (var i = 0; i < instances.length; i++) {
if (trigger == 'serialize') {
instances[i].saveContent();
}
else {
instances[i].remove();
}
}
// Remove all editor instances.
if (trigger != 'serialize') {
nicEditors.editors[e].nicInstances = [];
}
}
instance.ne.removeInstance(params.field);
instance.ne.removePanel();
}
};
/**
* Check if a DOM node is inside another or if they are the same.
*/
function isInside (innerNode, outerNode) {
var found = false;
if (innerNode === outerNode) {
return true;
}
$(innerNode).parents().each(function (index, parent) {
if (parent === outerNode) {
found = true;
return false;
}
});
return found;
}
/**
* Instance methods for nicEdit.
*/
Drupal.wysiwyg.editor.instance.nicedit = {
insert: function (content) {
var instance = nicEditors.findEditor(this.field);
var editingArea = instance.getElm();
var sel = instance.getSel();
var instance = nicEditors.findEditor(this.field),
editingArea = instance.getElm(),
sel = instance.getSel(), range;
// IE.
if (document.selection) {
editingArea.focus();
sel.createRange().pasteHTML(content);
range = sel.createRange();
// If the caret is not in the editing area, just append the content.
if (!isInside(range.parentElement(), editingArea)) {
editingArea.innerHTML += content;
}
else {
// Insert content and set the caret after it.
range.pasteHTML(content);
range.select();
range.collapse(false);
}
}
else {
// The code below doesn't work in IE, but it never gets here.
// Convert selection to a range.
var range;
// W3C compatible.
if (sel.getRangeAt) {
range = sel.getRangeAt(0);
if (sel.rangeCount > 0) {
range = sel.getRangeAt(0);
}
}
// Safari.
else {
@@ -89,7 +97,13 @@ Drupal.wysiwyg.editor.instance.nicedit = {
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, userSeletion.focusOffset);
}
// The code below doesn't work in IE, but it never gets here.
// If the caret is not in the editing area, just append the content.
if (sel.rangeCount == 0 || !isInside(range.commonAncestorContainer, editingArea)) {
editingArea.innerHTML += content;
return;
}
var fragment = editingArea.ownerDocument.createDocumentFragment();
// Fragments don't support innerHTML.
var wrapper = editingArea.ownerDocument.createElement('div');
@@ -97,9 +111,18 @@ Drupal.wysiwyg.editor.instance.nicedit = {
while (wrapper.firstChild) {
fragment.appendChild(wrapper.firstChild);
}
// Append a temporary node to control caret position.
var tn = editingArea.ownerDocument.createElement('span');
fragment.appendChild(tn);
range.deleteContents();
// Only fragment children are inserted.
range.insertNode(fragment);
// Move caret to temp node and remove it.
range.setStartBefore(tn);
range.setEndBefore(tn);
sel.removeAllRanges();
sel.addRange(range);
tn.parentNode.removeChild(tn);
}
},
+11 -9
View File
@@ -15,26 +15,28 @@
*/
Drupal.wysiwyg.editor.attach.none = function(context, params, settings) {
if (params.resizable) {
var $wrapper = $('#' + params.field).parents('.form-textarea-wrapper:first');
var $wrapper = $('#' + params.field, context).parents('.form-textarea-wrapper:first');
$wrapper.addClass('resizable');
if (Drupal.behaviors.textarea) {
Drupal.behaviors.textarea.attach();
Drupal.behaviors.textarea.attach(context);
}
}
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*
* The editor syncs its contents back to the original field before its instance
* is removed.
*
* In here, 'this' is an instance of WysiwygInternalInstance.
* See Drupal.wysiwyg.editor.instance.none for more details.
*
* @param context
* A DOM element, supplied by Drupal.attachBehaviors().
* @param params
* (optional) An object containing input format parameters. If defined,
* only the editor instance in params.field should be detached. Otherwise,
* all editors should be detached and saved, so they can be submitted in
* An object containing input format parameters. Only the editor instance in
* params.field should be detached and saved, so its data can be submitted in
* AJAX/AHAH applications.
* @param trigger
* A string describing why the editor is being detached.
@@ -47,9 +49,9 @@ Drupal.wysiwyg.editor.attach.none = function(context, params, settings) {
* @see Drupal.detachBehaviors
*/
Drupal.wysiwyg.editor.detach.none = function (context, params, trigger) {
if (typeof params != 'undefined' && (trigger != 'serialize')) {
var $wrapper = $('#' + params.field).parents('.form-textarea-wrapper:first');
$wrapper.removeOnce('textarea').removeClass('.resizable-textarea')
if (trigger != 'serialize') {
var $wrapper = $('#' + params.field, context).parents('.form-textarea-wrapper:first');
$wrapper.removeOnce('textarea').removeClass('.resizable-textarea').removeClass('resizable')
.find('.grippie').remove();
}
};
@@ -1,141 +0,0 @@
// Backup $ and reset it to jQuery.
Drupal.wysiwyg._openwysiwyg = $;
$ = jQuery;
// Wrap openWYSIWYG's methods to temporarily use its version of $.
jQuery.each(WYSIWYG, function (key, value) {
if (jQuery.isFunction(value)) {
WYSIWYG[key] = function () {
var old$ = $;
$ = Drupal.wysiwyg._openwysiwyg;
var result = value.apply(this, arguments);
$ = old$;
return result;
};
}
});
// Override editor functions.
WYSIWYG.getEditor = function (n) {
return Drupal.wysiwyg._openwysiwyg("wysiwyg" + n);
};
(function($) {
// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
var oldMaximize = WYSIWYG.maximize;
WYSIWYG.maximize = function (n) {
var $drupalToolbar = $('#toolbar', Drupal.overlayChild ? window.parent.document : document);
oldMaximize.apply(this, arguments);
if (this.maximized[n]) {
$drupalToolbar.hide();
}
else {
$drupalToolbar.show();
}
}
/**
* Attach this editor to a target element.
*/
Drupal.wysiwyg.editor.attach.openwysiwyg = function(context, params, settings) {
// Initialize settings.
settings.ImagesDir = settings.path + 'images/';
settings.PopupsDir = settings.path + 'popups/';
settings.CSSFile = settings.path + 'styles/wysiwyg.css';
//settings.DropDowns = [];
var config = new WYSIWYG.Settings();
for (var setting in settings) {
config[setting] = settings[setting];
}
// Attach editor.
WYSIWYG.setSettings(params.field, config);
WYSIWYG_Core.includeCSS(WYSIWYG.config[params.field].CSSFile);
WYSIWYG._generate(params.field, config);
};
/**
* Detach a single or all editors.
*/
Drupal.wysiwyg.editor.detach.openwysiwyg = function (context, params, trigger) {
if (typeof params != 'undefined') {
var instance = WYSIWYG.config[params.field];
if (typeof instance != 'undefined') {
WYSIWYG.updateTextArea(params.field);
if (trigger != 'serialize') {
jQuery('#wysiwyg_div_' + params.field).remove();
delete instance;
}
}
if (trigger != 'serialize') {
jQuery('#' + params.field).show();
}
}
else {
jQuery.each(WYSIWYG.config, function(field) {
WYSIWYG.updateTextArea(field);
if (trigger != 'serialize') {
jQuery('#wysiwyg_div_' + field).remove();
delete this;
jQuery('#' + field).show();
}
});
}
};
/**
* Instance methods for openWYSIWYG.
*/
Drupal.wysiwyg.editor.instance.openwysiwyg = {
insert: function (content) {
// If IE has dropped focus content will be inserted at the top of the page.
$('#wysiwyg' + this.field).contents().find('body').focus();
WYSIWYG.insertHTML(content, this.field);
},
setContent: function (content) {
// Based on openWYSIWYG's _generate() method.
var doc = WYSIWYG.getEditorWindow(this.field).document;
if (WYSIWYG.config[this.field].ReplaceLineBreaks) {
content = content.replace(/\n\r|\n/ig, '<br />');
}
if (WYSIWYG.viewTextMode[this.field]) {
var html = document.createTextNode(content);
doc.body.innerHTML = '';
doc.body.appendChild(html);
}
else {
doc.open();
doc.write(content);
doc.close();
}
},
getContent: function () {
// Based on openWYSIWYG's updateTextarea() method.
var content = '';
var doc = WYSIWYG.getEditorWindow(this.field).document;
if (WYSIWYG.viewTextMode[this.field]) {
if (WYSIWYG_Core.isMSIE) {
content = doc.body.innerText;
}
else {
var range = doc.body.ownerDocument.createRange();
range.selectNodeContents(doc.body);
content = range.toString();
}
}
else {
content = doc.body.innerHTML;
}
content = WYSIWYG.stripURLPath(this.field, content);
content = WYSIWYG_Core.replaceRGBWithHexColor(content);
if (WYSIWYG.config[this.field].ReplaceLineBreaks) {
content = content.replace(/(\r\n)|(\n)/ig, '');
}
return content;
}
};
})(jQuery);
@@ -1,203 +0,0 @@
(function($) {
/**
* Initialize editor instances.
*
* This function needs to be called before the page is fully loaded, as
* calling tinyMCE.init() after the page is loaded breaks IE6.
*
* @param editorSettings
* An object containing editor settings for each input format.
*/
Drupal.wysiwyg.editor.init.tinymce = function(settings) {
// Initialize editor configurations.
for (var format in settings) {
tinyMCE.init(settings[format]);
if (Drupal.settings.wysiwyg.plugins[format]) {
// Load native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
for (var plugin in Drupal.settings.wysiwyg.plugins[format]['native']) {
tinyMCE.loadPlugin(plugin, Drupal.settings.wysiwyg.plugins[format]['native'][plugin]);
}
// Load Drupal plugins.
for (var plugin in Drupal.settings.wysiwyg.plugins[format].drupal) {
Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins[format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
}
}
}
};
/**
* Attach this editor to a target element.
*
* See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook.
*/
Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
// Configure editor settings for this input format.
for (var setting in settings) {
tinyMCE.settings[setting] = settings[setting];
}
// Remove TinyMCE's internal mceItem class, which was incorrectly added to
// submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
// for placeholder elements. If preemptively set, the class prevents (native)
// editor plugins from gaining an active state, so we have to manually remove
// it prior to attaching the editor. This is done on the client-side instead
// of the server-side, as Wysiwyg has no way to figure out where content is
// stored, and the class only affects editing.
$field = $('#' + params.field);
$field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));
// Attach editor.
tinyMCE.execCommand('mceAddControl', true, params.field);
};
/**
* Detach a single or all editors.
*
* See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
*/
Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
if (typeof params != 'undefined') {
tinyMCE.removeMCEControl(tinyMCE.getEditorId(params.field));
$('#' + params.field).removeAttr('style');
}
// else if (tinyMCE.activeEditor) {
// tinyMCE.triggerSave();
// tinyMCE.activeEditor.remove();
// }
};
Drupal.wysiwyg.editor.instance.tinymce = {
addPlugin: function(plugin, settings, pluginSettings) {
if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
return;
}
tinyMCE.addPlugin(plugin, {
// Register an editor command for this plugin, invoked by the plugin's button.
execCommand: function(editor_id, element, command, user_interface, value) {
switch (command) {
case plugin:
if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
var ed = tinyMCE.getInstanceById(editor_id);
var data = { format: 'html', node: ed.getFocusElement(), content: ed.getFocusElement() };
Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, ed.formTargetElementId);
return true;
}
}
// Pass to next handler in chain.
return false;
},
// Register the plugin button.
getControlHTML: function(control_name) {
switch (control_name) {
case plugin:
return tinyMCE.getButtonHTML(control_name, settings.iconTitle, settings.icon, plugin);
}
return '';
},
// Load custom CSS for editor contents on startup.
initInstance: function(ed) {
if (settings.css) {
tinyMCE.importCSS(ed.getDoc(), settings.css);
}
},
cleanup: function(type, content) {
switch (type) {
case 'insert_to_editor':
// Attach: Replace plain text with HTML representations.
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].attach(content, pluginSettings, tinyMCE.selectedInstance.editorId);
content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(content);
}
break;
case 'get_from_editor':
// Detach: Replace HTML representations with plain text.
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].detach(content, pluginSettings, tinyMCE.selectedInstance.editorId);
}
break;
}
// Pass through to next handler in chain
return content;
},
// isNode: Return whether the plugin button should be enabled for the
// current selection.
handleNodeChange: function(editor_id, node, undo_index, undo_levels, visual_aid, any_selection) {
if (node === null) {
return;
}
if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
if (Drupal.wysiwyg.plugins[plugin].isNode(node)) {
tinyMCE.switchClass(editor_id + '_' + plugin, 'mceButtonSelected');
return true;
}
}
tinyMCE.switchClass(editor_id + '_' + plugin, 'mceButtonNormal');
return true;
},
/**
* Return information about the plugin as a name/value array.
*/
getInfo: function() {
return {
longname: settings.title
};
}
});
},
openDialog: function(dialog, params) {
var editor = tinyMCE.getInstanceById(this.field);
tinyMCE.openWindow({
file: dialog.url + '/' + this.field,
width: dialog.width,
height: dialog.height,
inline: 1
}, params);
},
closeDialog: function(dialog) {
var editor = tinyMCE.getInstanceById(this.field);
tinyMCEPopup.close();
},
prepareContent: function(content) {
// Certain content elements need to have additional DOM properties applied
// to prevent this editor from highlighting an internal button in addition
// to the button of a Drupal plugin.
var specialProperties = {
img: { 'name': 'mce_drupal' }
};
var $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
jQuery.each(specialProperties, function(element, properties) {
$content.find(element).each(function() {
for (var property in properties) {
if (property == 'class') {
$(this).addClass(properties[property]);
}
else {
$(this).attr(property, properties[property]);
}
}
});
});
return $content.html();
},
insert: function(content) {
content = this.prepareContent(content);
var editor = tinyMCE.getInstanceById(this.field);
editor.execCommand('mceInsertContent', false, content);
editor.repaint();
}
};
})(jQuery);
+58 -53
View File
@@ -3,39 +3,46 @@
/**
* Initialize editor instances.
*
* @todo Is the following note still valid for 3.x?
* This function needs to be called before the page is fully loaded, as
* calling tinyMCE.init() after the page is loaded breaks IE6.
*
* @param editorSettings
* An object containing editor settings for each input format.
* @see Drupal.wysiwyg.editor.init.ckeditor()
*/
Drupal.wysiwyg.editor.init.tinymce = function(settings) {
Drupal.wysiwyg.editor.init.tinymce = function(settings, pluginInfo) {
// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
var $drupalToolbar = $('#toolbar', Drupal.overlayChild ? window.parent.document : document);
var $drupalToolbars = $('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.document : document);
tinyMCE.onAddEditor.add(function (mgr, ed) {
if (ed.id == 'mce_fullscreen') {
$drupalToolbar.hide();
$drupalToolbars.hide();
}
});
tinyMCE.onRemoveEditor.add(function (mgr, ed) {
if (ed.id == 'mce_fullscreen') {
$drupalToolbar.show();
$drupalToolbars.show();
}
else {
// Free our reference to the private instance to not risk memory leaks.
delete ed._drupalWysiwygInstance;
}
});
// Register new plugins.
Drupal.wysiwyg.editor.update.tinymce(settings, pluginInfo);
};
// Initialize editor configurations.
for (var format in settings) {
if (Drupal.settings.wysiwyg.plugins[format]) {
// Load native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
for (var plugin in Drupal.settings.wysiwyg.plugins[format]['native']) {
tinymce.PluginManager.load(plugin, Drupal.settings.wysiwyg.plugins[format]['native'][plugin]);
}
// Load Drupal plugins.
for (var plugin in Drupal.settings.wysiwyg.plugins[format].drupal) {
Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, Drupal.settings.wysiwyg.plugins[format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
}
/**
* Update the editor library when new settings are available.
*
* @see Drupal.wysiwyg.editor.update.ckeditor()
*/
Drupal.wysiwyg.editor.update.tinymce = function(settings, pluginInfo) {
// Load native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
for (var plugin in pluginInfo['native']) {
if (!(plugin in tinymce.PluginManager.lookup || plugin in tinymce.PluginManager.urls)) {
tinymce.PluginManager.load(plugin, pluginInfo['native'][plugin]);
}
}
// Load Drupal plugins.
for (var plugin in pluginInfo.drupal) {
if (!(plugin in tinymce.PluginManager.lookup)) {
Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, pluginInfo.drupal[plugin]);
}
}
};
@@ -48,6 +55,7 @@ Drupal.wysiwyg.editor.init.tinymce = function(settings) {
Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
// Configure editor settings for this input format.
var ed = new tinymce.Editor(params.field, settings);
ed._drupalWysiwygInstance = this;
// Reset active instance id on any event.
ed.onEvent.add(function(ed, e) {
Drupal.wysiwyg.activeId = ed.id;
@@ -71,46 +79,43 @@ Drupal.wysiwyg.editor.attach.tinymce = function(context, params, settings) {
// it prior to attaching the editor. This is done on the client-side instead
// of the server-side, as Wysiwyg has no way to figure out where content is
// stored, and the class only affects editing.
$field = $('#' + params.field);
var $field = $('#' + params.field);
$field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));
// Attach editor.
ed.render();
if (tinymce.minorVersion == '5.7') {
// Work around a TinyMCE bug hiding new instances when switching to them.
// @see http://www.tinymce.com/develop/bugtracker_view.php?id=5510
setTimeout(function () {
tinymce.DOM.show(ed.editorContainer);
}, 1);
}
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*
* See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
*/
Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
if (typeof params != 'undefined') {
var instance = tinyMCE.get(params.field);
if (instance) {
instance.save();
if (trigger != 'serialize') {
instance.remove();
}
}
var instance = tinyMCE.get(params.field);
if (!instance) {
return;
}
else {
// Save contents of all editors back into textareas.
tinyMCE.triggerSave();
if (trigger != 'serialize') {
// Remove all editor instances.
for (var instance in tinyMCE.editors) {
tinyMCE.editors[instance].remove();
}
}
instance.save();
if (trigger !== 'serialize') {
// The onRemove event fires before this returns.
instance.remove();
}
};
Drupal.wysiwyg.editor.instance.tinymce = {
addPlugin: function(plugin, settings, pluginSettings) {
addPlugin: function(plugin, pluginSettings) {
if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
return;
}
tinymce.create('tinymce.plugins.' + plugin, {
tinymce.create('tinymce.plugins.drupal_' + plugin, {
/**
* Initialize the plugin, executed after the plugin has been created.
*
@@ -121,7 +126,7 @@ Drupal.wysiwyg.editor.instance.tinymce = {
*/
init: function(ed, url) {
// Register an editor command for this plugin, invoked by the plugin's button.
ed.addCommand(plugin, function() {
ed.addCommand('drupal_' + plugin, function() {
if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
var data = { format: 'html', node: ed.selection.getNode(), content: ed.selection.getContent() };
// TinyMCE creates a completely new instance for fullscreen mode.
@@ -131,16 +136,16 @@ Drupal.wysiwyg.editor.instance.tinymce = {
});
// Register the plugin button.
ed.addButton(plugin, {
title : settings.iconTitle,
cmd : plugin,
image : settings.icon
ed.addButton('drupal_' + plugin, {
title : pluginSettings.title,
cmd : 'drupal_' + plugin,
image : pluginSettings.icon
});
// Load custom CSS for editor contents on startup.
ed.onInit.add(function() {
if (settings.css) {
ed.dom.loadCSS(settings.css);
if (pluginSettings.css) {
ed.dom.loadCSS(pluginSettings.css);
}
});
@@ -149,7 +154,7 @@ Drupal.wysiwyg.editor.instance.tinymce = {
var editorId = (ed.id == 'mce_fullscreen' ? ed.getParam('fullscreen_editor_id') : ed.id);
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
data.content = Drupal.wysiwyg.plugins[plugin].attach(data.content, pluginSettings, editorId);
data.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(data.content);
data.content = ed._drupalWysiwygInstance.prepareContent(data.content);
}
});
@@ -175,13 +180,13 @@ Drupal.wysiwyg.editor.instance.tinymce = {
*/
getInfo: function() {
return {
longname: settings.title
longname: pluginSettings.title
};
}
});
// Register plugin.
tinymce.PluginManager.add(plugin, tinymce.plugins[plugin]);
tinymce.PluginManager.add('drupal_' + plugin, tinymce.plugins['drupal_' + plugin]);
},
openDialog: function(dialog, params) {
@@ -0,0 +1,245 @@
(function ($) {
/**
* Initialize editor instances.
*
* @see Drupal.wysiwyg.editor.init.ckeditor()
*/
Drupal.wysiwyg.editor.init.tinymce = function (settings, pluginInfo) {
// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
var $drupalToolbars = $('#toolbar, #admin-menu', Drupal.overlayChild ? window.parent.document : document);
tinymce.on('AddEditor', function (e) {
e.editor.on('FullscreenStateChanged', function (e) {
if (e.state) {
$drupalToolbars.hide();
}
else {
$drupalToolbars.show();
}
});
});
// Register new plugins.
Drupal.wysiwyg.editor.update.tinymce(settings, pluginInfo);
};
/**
* Update the editor library when new settings are available.
*
* @see Drupal.wysiwyg.editor.update.ckeditor()
*/
Drupal.wysiwyg.editor.update.tinymce = function (settings, pluginInfo) {
// Load native external plugins.
// Array syntax required; 'native' is a predefined token in JavaScript.
var plugin;
for (plugin in pluginInfo['native']) {
if (!(plugin in tinymce.PluginManager.lookup || plugin in tinymce.PluginManager.urls)) {
tinymce.PluginManager.load(plugin, pluginInfo['native'][plugin]);
}
}
// Load Drupal plugins.
for (plugin in pluginInfo.drupal) {
if (!(plugin in tinymce.PluginManager.lookup)) {
Drupal.wysiwyg.editor.instance.tinymce.addPlugin(plugin, pluginInfo.drupal[plugin]);
}
}
};
/**
* Attach this editor to a target element.
*
* See Drupal.wysiwyg.editor.attach.none() for a full desciption of this hook.
*/
Drupal.wysiwyg.editor.attach.tinymce = function (context, params, settings) {
// Remove TinyMCE's internal mceItem class, which was incorrectly added to
// submitted content by Wysiwyg <2.1. TinyMCE only temporarily adds the class
// for placeholder elements. If preemptively set, the class prevents (native)
// editor plugins from gaining an active state, so we have to manually remove
// it prior to attaching the editor. This is done on the client-side instead
// of the server-side, as Wysiwyg has no way to figure out where content is
// stored, and the class only affects editing.
var $field = $('#' + params.field);
$field.val($field.val().replace(/(<.+?\s+class=['"][\w\s]*?)\bmceItem\b([\w\s]*?['"].*?>)/ig, '$1$2'));
// Attach editor.
settings.selector = '#' + params.field;
var oldSetup = settings.setup;
settings.setup = function (editor) {
editor.on('focus', function (e) {
Drupal.wysiwyg.activeId = this.id;
});
if (oldSetup) {
oldSetup(editor);
}
};
tinymce.init(settings);
};
/**
* Detach a single or all editors.
*
* See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
*/
Drupal.wysiwyg.editor.detach.tinymce = function (context, params, trigger) {
var instance;
if (typeof params !== 'undefined') {
instance = tinymce.get(params.field);
if (instance) {
instance.save();
if (trigger !== 'serialize') {
instance.remove();
}
}
}
else {
// Save contents of all editors back into textareas.
tinymce.triggerSave();
if (trigger !== 'serialize') {
// Remove all editor instances.
for (instance in tinymce.editors) {
if (!tinymce.editors.hasOwnProperty(instance)) {
continue;
}
tinymce.editors[instance].remove();
}
}
}
};
Drupal.wysiwyg.editor.instance.tinymce = {
addPlugin: function (plugin, pluginSettings) {
if (typeof Drupal.wysiwyg.plugins[plugin] !== 'object') {
return;
}
// Register plugin.
tinymce.PluginManager.add('drupal_' + plugin, function (editor) {
var button = {
title: pluginSettings.title,
image: pluginSettings.icon,
onPostRender: function () {
var self = this;
editor.on('nodeChange', function (e) {
// isNode: Return whether the plugin button should be enabled for
// the current selection.
if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
self.active(Drupal.wysiwyg.plugins[plugin].isNode(e.element));
}
});
}
};
if (typeof Drupal.wysiwyg.plugins[plugin].invoke == 'function') {
button.onclick = function () {
var data = {format: 'html', node: editor.selection.getNode(), content: editor.selection.getContent()};
// TinyMCE creates a completely new instance for fullscreen mode.
Drupal.wysiwyg.plugins[plugin].invoke(data, pluginSettings, editor.id);
};
}
// Register the plugin button.
editor.addButton('drupal_' + plugin, button);
/**
* Initialize the plugin, executed after the plugin has been created.
*
* @param ed
* The tinymce.Editor instance the plugin is initialized in.
* @param url
* The absolute URL of the plugin location.
*/
editor.on('init', function (e) {
// Load custom CSS for editor contents on startup.
if (pluginSettings.css) {
editor.dom.loadCSS(pluginSettings.css);
}
});
// Attach: Replace plain text with HTML representations.
editor.on('beforeSetContent', function (e) {
if (typeof Drupal.wysiwyg.plugins[plugin].attach === 'function') {
e.content = Drupal.wysiwyg.plugins[plugin].attach(e.content, pluginSettings, e.target.id);
e.content = Drupal.wysiwyg.editor.instance.tinymce.prepareContent(e.content);
}
});
// Detach: Replace HTML representations with plain text.
editor.on('getContent', function (e) {
var editorId = (e.target.id === 'mce_fullscreen' ? e.target.getParam('fullscreen_editor_id') : e.target.id);
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
e.content = Drupal.wysiwyg.plugins[plugin].detach(e.content, pluginSettings, editorId);
}
});
});
},
openDialog: function (dialog, params) {
var instanceId = this.getInstanceId();
var editor = tinymce.get(instanceId);
editor.windowManager.open({
file: dialog.url + '/' + instanceId,
width: dialog.width,
height: dialog.height,
inline: 1
}, params);
},
closeDialog: function (dialog) {
var editor = tinymce.get(this.getInstanceId());
editor.windowManager.close(dialog);
},
prepareContent: function (content) {
// Certain content elements need to have additional DOM properties applied
// to prevent this editor from highlighting an internal button in addition
// to the button of a Drupal plugin.
var specialProperties = {
img: {class: 'mceItem'}
};
// No .outerHTML() in jQuery :(
var $content = $('<div>' + content + '</div>');
// Find all placeholder/replacement content of Drupal plugins.
$content.find('.drupal-content').each(function () {
// Recursively process DOM elements below this element to apply special
// properties.
var $drupalContent = $(this);
$.each(specialProperties, function (element, properties) {
$drupalContent.find(element).andSelf().each(function () {
for (var property in properties) {
if (property === 'class') {
$(this).addClass(properties[property]);
}
else {
$(this).attr(property, properties[property]);
}
}
});
});
});
return $content.html();
},
insert: function (content) {
content = this.prepareContent(content);
tinymce.get(this.field).insertContent(content);
},
setContent: function (content) {
content = this.prepareContent(content);
tinymce.get(this.field).setContent(content);
},
getContent: function () {
return tinymce.get(this.getInstanceId()).getContent();
},
isFullscreen: function () {
var editor = tinymce.get(this.field);
return editor.plugins.fullscreen && editor.plugins.fullscreen.isFullscreen();
},
getInstanceId: function () {
return this.field;
}
};
})(jQuery);
+14 -22
View File
@@ -72,9 +72,8 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
wysiwygWhizzywig.currentField = params.field;
wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
// Whizzywig needs to have the width set 'inline'.
$field = $('#' + params.field);
var originalValues = Drupal.wysiwyg.instances[params.field];
originalValues.originalStyle = $field.attr('style');
var $field = $('#' + params.field);
this.originalStyle = $field.attr('style');
$field.css('width', $field.width() + 'px');
// Attach editor.
@@ -87,35 +86,27 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
* Detach a single or all editors.
*/
Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
var detach = function (index) {
var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
for (var index = 0; index < whizzies.length; index++) {
if (whizzies[index] !== this.field) {
continue;
}
var $field = $('#' + this.field);
// Save contents of editor back into textarea.
$field.val(instance.getContent());
$field.val(this.getContent());
// If the editor is just being serialized (not detached), our work is done.
if (trigger == 'serialize') {
return;
}
// Remove editor instance.
$('#' + id + '-whizzywig').remove();
$('#' + this.field + '-whizzywig').remove();
whizzies.splice(index, 1);
// Restore original textarea styling.
$field.removeAttr('style').attr('style', instance.originalStyle);
};
if (typeof params != 'undefined') {
for (var i = 0; i < whizzies.length; i++) {
if (whizzies[i] == params.field) {
detach(i);
break;
}
}
}
else {
while (whizzies.length > 0) {
detach(0);
if ('originalStyle' in this) {
$field.removeAttr('style').attr('style', this.originalStyle);
}
break;
}
};
@@ -142,12 +133,13 @@ Drupal.wysiwyg.editor.instance.whizzywig = {
},
getContent: function () {
var $field = $('#' + this.field);
// Whizzywig's tidyH() expects a document node. Clone the editing iframe's
// document so tidyH() won't mess with it if this gets called while editing.
var clone = $($('#whizzy' + this.field).contents()[0].documentElement).clone()[0].ownerDocument;
// Whizzywig shows the original textarea in source mode so update the body.
if ($field.css('display') == 'block') {
clone.body.innerHTML = $('#' + this.field).val();
clone.body.innerHTML = $field.val();
}
return tidyH(clone);
}
+24 -23
View File
@@ -21,9 +21,8 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
window.buttonPath = 'textbuttons';
}
// Whizzywig needs to have the width set 'inline'.
$field = $('#' + params.field);
var originalValues = Drupal.wysiwyg.instances[params.field];
originalValues.originalStyle = $field.attr('style');
var $field = $('#' + params.field);
this.originalStyle = $field.attr('style');
$field.css('width', $field.width() + 'px');
// Attach editor.
@@ -33,41 +32,33 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
var detach = function (index) {
var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
for (var index = 0; index < whizzies.length; index++) {
if (whizzies[index] !== this.field) {
continue;
}
var $field = $('#' + this.field);
// Save contents of editor back into textarea.
$field.val(instance.getContent());
$field.val(this.getContent());
// If the editor is just being serialized (not detached), our work is done.
if (trigger == 'serialize') {
return;
}
// Move original textarea back to its previous location.
var $container = $('#CONTAINER' + id);
var $container = $('#CONTAINER' + this.field);
$field.insertBefore($container);
// Remove editor instance.
$container.remove();
whizzies.splice(index, 1);
// Restore original textarea styling.
$field.removeAttr('style').attr('style', instance.originalStyle);
}
if (typeof params != 'undefined') {
for (var i = 0; i < whizzies.length; i++) {
if (whizzies[i] == params.field) {
detach(i);
break;
}
}
}
else {
while (whizzies.length > 0) {
detach(0);
if ('originalStyle' in this) {
$field.removeAttr('style').attr('style', this.originalStyle);
}
break;
}
};
@@ -94,14 +85,24 @@ Drupal.wysiwyg.editor.instance.whizzywig = {
},
getContent: function () {
var $field = $('#' + this.field);
// Whizzywig's tidyH() expects a document node. Clone the editing iframe's
// document so tidyH() won't mess with it if this gets called while editing.
var clone = $($('#whizzy' + this.field).contents()[0].documentElement).clone()[0].ownerDocument;
// Whizzywig shows the original textarea in source mode so update the body.
if ($field.css('display') == 'block') {
clone.body.innerHTML = $('#' + this.field).val();
clone.body.innerHTML = $field.val();
}
return tidyH(clone);
},
isFullscreen: function () {
// This relies on a global function which uses a global variable...
var idTa_old = idTa;
idTa = this.field;
var fullscreen = isFullscreen();
idTa = idTa_old;
return fullscreen;
}
};
})(jQuery);
+12 -21
View File
@@ -63,9 +63,8 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
wysiwygWhizzywig.currentField = params.field;
wysiwygWhizzywig.fields[wysiwygWhizzywig.currentField] = '';
// Whizzywig needs to have the width set 'inline'.
$field = $('#' + params.field);
var originalValues = Drupal.wysiwyg.instances[params.field];
originalValues.originalStyle = $field.attr('style');
var $field = $('#' + params.field);
this.originalStyle = $field.attr('style');
$field.css('width', $field.width() + 'px');
// Attach editor.
@@ -78,35 +77,27 @@ Drupal.wysiwyg.editor.attach.whizzywig = function(context, params, settings) {
* Detach a single or all editors.
*/
Drupal.wysiwyg.editor.detach.whizzywig = function (context, params, trigger) {
var detach = function (index) {
var id = whizzies[index], $field = $('#' + id), instance = Drupal.wysiwyg.instances[id];
for (var index = 0; index < whizzies.length; index++) {
if (whizzies[index] !== this.field) {
continue;
}
var $field = $('#' + this.field);
// Save contents of editor back into textarea.
$field.val(instance.getContent());
$field.val(this.getContent());
// If the editor is just being serialized (not detached), our work is done.
if (trigger == 'serialize') {
return;
}
// Remove editor instance.
$('#' + id + '-whizzywig').remove();
$('#' + this.field + '-whizzywig').remove();
whizzies.splice(index, 1);
// Restore original textarea styling.
$field.removeAttr('style').attr('style', instance.originalStyle);
};
if (typeof params != 'undefined') {
for (var i = 0; i < whizzies.length; i++) {
if (whizzies[i] == params.field) {
detach(i);
break;
}
}
}
else {
while (whizzies.length > 0) {
detach(0);
if ('originalStyle' in this) {
$field.removeAttr('style').attr('style', this.originalStyle);
}
break;
}
};
+12 -25
View File
@@ -17,34 +17,21 @@ Drupal.wysiwyg.editor.attach.wymeditor = function (context, params, settings) {
};
/**
* Detach a single or all editors.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.wymeditor = function (context, params, trigger) {
if (typeof params != 'undefined') {
var $field = $('#' + params.field);
var index = $field.data(WYMeditor.WYM_INDEX);
if (typeof index != 'undefined') {
var instance = WYMeditor.INSTANCES[index];
instance.update();
if (trigger != 'serialize') {
$(instance._box).remove();
$(instance._element).show();
delete instance;
}
}
if (trigger != 'serialize') {
$field.show();
}
var $field = $('#' + params.field, context);
var index = $field.data(WYMeditor.WYM_INDEX);
if (typeof index == 'undefined' || !WYMeditor.INSTANCES[index]) {
return;
}
else {
jQuery.each(WYMeditor.INSTANCES, function () {
this.update();
if (trigger != 'serialize') {
$(this._box).remove();
$(this._element).show();
delete this;
}
});
var instance = WYMeditor.INSTANCES[index];
instance.update();
if (trigger != 'serialize') {
$(instance._box).remove();
$(instance._element).show();
delete WYMeditor.INSTANCES[index];
$field.show();
}
};
+50 -53
View File
@@ -10,45 +10,53 @@ Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) {
// Apply theme.
$('#' + params.field).parent().addClass('yui-skin-' + settings.theme);
var wysiwygInstance = this;
var enabledPlugins = wysiwygInstance.pluginInfo.instances;
// Load plugins stylesheet.
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
settings.extracss += settings.extracss+' @import "'+Drupal.settings.wysiwyg.plugins[params.format].drupal[plugin].css+'"; ';
for (var pluginId in enabledPlugins.drupal) {
if (wysiwygInstance.pluginInfo.global.drupal[pluginId].css) {
settings.extracss += ' @import "' + wysiwygInstance.pluginInfo.global.drupal[pluginId].css + '"; ';
}
}
// Attach editor.
var editor = new YAHOO.widget.Editor(params.field, settings);
editor.on('toolbarLoaded', function() {
// Load Drupal plugins.
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
Drupal.wysiwyg.instances[params.field].addPlugin(plugin, Drupal.settings.wysiwyg.plugins[params.format].drupal[plugin], Drupal.settings.wysiwyg.plugins.drupal[plugin]);
}
});
// Allow plugins to act on setEditorHTML.
var oldSetEditorHTML = editor.setEditorHTML;
editor.setEditorHTML = function (content) {
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
var pluginSettings = Drupal.settings.wysiwyg.plugins.drupal[plugin];
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].attach(content, pluginSettings, params.field);
content = Drupal.wysiwyg.instances[params.field].prepareContent(content);
if (enabledPlugins) {
editor.on('toolbarLoaded', function() {
// 'this' will reference the toolbar while inside the event handler.
var instanceId = params.field;
// Load Drupal plugins.
for (var plugin in enabledPlugins.drupal) {
wysiwygInstance.addPlugin(plugin, wysiwygInstance.pluginInfo.global.drupal[plugin]);
}
}
oldSetEditorHTML.call(this, content);
};
});
// Allow plugins to act on getEditorHTML.
var oldGetEditorHTML = editor.getEditorHTML;
editor.getEditorHTML = function () {
var content = oldGetEditorHTML.call(this);
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
var pluginSettings = Drupal.settings.wysiwyg.plugins.drupal[plugin];
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].detach(content, pluginSettings, params.field);
// Allow plugins to act on setEditorHTML.
var oldSetEditorHTML = editor.setEditorHTML;
editor.setEditorHTML = function (content) {
for (var plugin in enabledPlugins.drupal) {
var pluginSettings = wysiwygInstance.pluginInfo.global.drupal[plugin];
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].attach(content, pluginSettings, params.field);
content = wysiwygInstance.prepareContent(content);
}
}
oldSetEditorHTML.call(this, content);
};
// Allow plugins to act on getEditorHTML.
var oldGetEditorHTML = editor.getEditorHTML;
editor.getEditorHTML = function () {
var content = oldGetEditorHTML.call(this);
for (var plugin in enabledPlugins.drupal) {
var pluginSettings = wysiwygInstance.pluginInfo.global.drupal[plugin];
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
content = Drupal.wysiwyg.plugins[plugin].detach(content, pluginSettings, params.field);
}
}
return content;
}
return content;
}
// Reload the editor contents to give Drupal plugins a chance to act.
@@ -57,7 +65,7 @@ Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) {
});
editor.on('afterNodeChange', function (e) {
for (var plugin in Drupal.settings.wysiwyg.plugins[params.format].drupal) {
for (var plugin in enabledPlugins.drupal) {
if (typeof Drupal.wysiwyg.plugins[plugin].isNode == 'function') {
if (Drupal.wysiwyg.plugins[plugin].isNode(e.target._getSelectedElement())) {
this.toolbar.selectButton(plugin);
@@ -67,33 +75,22 @@ Drupal.wysiwyg.editor.attach.yui = function(context, params, settings) {
});
editor.render();
// This event never gets fired if loaded into a dialog, harmless otherwise.
editor.fireEvent('contentReady');
};
/**
* Detach a single or all editors.
*
* See Drupal.wysiwyg.editor.detach.none() for a full desciption of this hook.
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.yui = function (context, params, trigger) {
var method = (trigger && trigger == 'serialize') ? 'saveHTML' : 'destroy';
if (typeof params != 'undefined') {
var instance = YAHOO.widget.EditorInfo._instances[params.field];
if (instance) {
instance[method]();
if (method == 'destroy') {
delete YAHOO.widget.EditorInfo._instances[params.field];
}
}
var instance = YAHOO.widget.EditorInfo._instances[params.field];
if (!instance) {
return;
}
else {
for (var e in YAHOO.widget.EditorInfo._instances) {
// Save contents of all editors back into textareas.
var instance = YAHOO.widget.EditorInfo._instances[e];
instance[method]();
if (method == 'destroy') {
delete YAHOO.widget.EditorInfo._instances[e];
}
}
instance[method]();
if (method != 'serialize') {
delete YAHOO.widget.EditorInfo._instances[params.field];
}
};
@@ -101,13 +98,13 @@ Drupal.wysiwyg.editor.detach.yui = function (context, params, trigger) {
* Instance methods for YUI Editor.
*/
Drupal.wysiwyg.editor.instance.yui = {
addPlugin: function (plugin, settings, pluginSettings) {
addPlugin: function (plugin, pluginSettings) {
if (typeof Drupal.wysiwyg.plugins[plugin] != 'object') {
return;
}
var editor = YAHOO.widget.EditorInfo.getEditorById(this.field);
var button = editor.toolbar.getButtonByValue(plugin);
$(button._button).parent().css('background', 'transparent url(' + settings.icon + ') no-repeat center');
$(button._button).parent().css('background', 'transparent url(' + pluginSettings.icon + ') no-repeat center');
// 'this' will reference the toolbar while inside the event handler.
var instanceId = this.field;
editor.toolbar.on(plugin + 'Click', function (e) {
@@ -129,7 +126,7 @@ Drupal.wysiwyg.editor.instance.yui = {
},
insert: function (content) {
YAHOO.widget.EditorInfo.getEditorById(this.field).cmd_inserthtml(content);
YAHOO.widget.EditorInfo.getEditorById(this.field).execCommand('inserthtml', content);
},
setContent: function (content) {
+36 -7
View File
@@ -11,23 +11,22 @@
function wysiwyg_jwysiwyg_editor() {
$editor['jwysiwyg'] = array(
'title' => 'jWYSIWYG',
'vendor url' => 'http://code.google.com/p/jwysiwyg/',
'download url' => 'http://code.google.com/p/jwysiwyg/downloads/list',
'vendor url' => 'http://github.com/akzhan/jwysiwyg',
'download url' => 'http://github.com/akzhan/jwysiwyg/tags',
'libraries' => array(
'' => array(
'title' => 'Source',
'files' => array('jquery.wysiwyg.js'),
),
'pack' => array(
'title' => 'Packed',
'files' => array('jquery.wysiwyg.pack.js'),
),
),
'verified version range' => array('0.5', '0.97'),
'version callback' => 'wysiwyg_jwysiwyg_version',
'settings form callback' => 'wysiwyg_jwysiwyg_settings_form',
'settings callback' => 'wysiwyg_jwysiwyg_settings',
// @todo Wrong property; add separate properties for editor requisites.
'css path' => wysiwyg_get_path('jwysiwyg'),
'versions' => array(
'0.5' => array(
'0.97' => array(
'js files' => array('jwysiwyg.js'),
'css files' => array('jquery.wysiwyg.css'),
),
@@ -36,6 +35,36 @@ function wysiwyg_jwysiwyg_editor() {
return $editor;
}
/**
* Enhances the editor profile settings form for jWYSIWYG.
*/
function wysiwyg_jwysiwyg_settings_form(&$form, &$form_state) {
$form['buttons']['#access'] = FALSE;
$form['basic']['language']['#access'] = FALSE;
$form['css']['#access'] = FALSE;
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
* @param $editor
* A processed hook_editor() array of editor properties.
* @param $config
* An array containing wysiwyg editor profile settings.
* @param $theme
* The name of a theme/GUI/skin to use.
*
* @return
* A settings array to be populated in
* Drupal.settings.wysiwyg.configs.{editor}
*/
function wysiwyg_jwysiwyg_settings($editor, $config, $theme) {
$settings = array(
'initialContent' => '',
);
return $settings;
}
/**
* Detect editor version.
*
+20 -2
View File
@@ -24,10 +24,13 @@ function wysiwyg_markitup_editor() {
'files' => array('markitup/jquery.markitup.pack.js'),
),
),
'install note callback' => 'wysiwyg_markitup_install_note',
'verified version range' => array('1.1.5', '1.1.14'),
'version callback' => 'wysiwyg_markitup_version',
'themes callback' => 'wysiwyg_markitup_themes',
'settings form callback' => 'wysiwyg_markitup_settings_form',
'settings callback' => 'wysiwyg_markitup_settings',
'plugin callback' => 'wysiwyg_markitup_plugins',
'plugin callback' => '_wysiwyg_markitup_plugins',
'versions' => array(
'1.1.5' => array(
'js files' => array('markitup.js'),
@@ -37,6 +40,13 @@ function wysiwyg_markitup_editor() {
return $editor;
}
/**
* Return an install note.
*/
function wysiwyg_markitup_install_note() {
return '<p class="warning">' . t('Only rename the extracted folder from "latest" to "markitup", no other changes needed.') . '</p>';
}
/**
* Detect editor version.
*
@@ -81,6 +91,14 @@ function wysiwyg_markitup_themes($editor, $profile) {
return array('simple', 'markitup');
}
/**
* Enhances the editor profile settings form for markItUp.
*/
function wysiwyg_markitup_settings_form(&$form, &$form_state) {
$form['basic']['language']['#access'] = FALSE;
$form['css']['#access'] = FALSE;
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
@@ -171,7 +189,7 @@ function wysiwyg_markitup_settings($editor, $config, $theme) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_markitup_plugins($editor) {
function _wysiwyg_markitup_plugins($editor) {
return array(
'default' => array(
'buttons' => array(
+22 -3
View File
@@ -19,9 +19,11 @@ function wysiwyg_nicedit_editor() {
'files' => array('nicEdit.js'),
),
),
'verified version range' => array('0.9', '0.9'),
'version callback' => 'wysiwyg_nicedit_version',
'settings form callback' => 'wysiwyg_nicedit_settings_form',
'settings callback' => 'wysiwyg_nicedit_settings',
'plugin callback' => 'wysiwyg_nicedit_plugins',
'plugin callback' => '_wysiwyg_nicedit_plugins',
'versions' => array(
'0.9' => array(
'js files' => array('nicedit.js'),
@@ -45,6 +47,18 @@ function wysiwyg_nicedit_version($editor) {
return '0.9';
}
/**
* Enhances the editor profile settings form for NicEdit.
*
* @see http://wiki.nicedit.com/w/page/515/Configuration%20Options
*/
function wysiwyg_nicedit_settings_form(&$form, &$form_state) {
$form['basic']['language']['#access'] = FALSE;
// NicEdit only supports loading a single stylesheet, and only in FF2.
// This essentially means we're dropping stylesheet support for NiceEdit.
$form['css']['#access'] = FALSE;
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
@@ -75,6 +89,7 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) {
}
// Add editor content stylesheet.
// @todo Drop stylsheet support since it's only used in FF2.
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
$css = drupal_get_path('theme', variable_get('theme_default', NULL)) . '/style.css';
@@ -83,7 +98,11 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) {
}
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['externalCSS'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
}
}
@@ -93,7 +112,7 @@ function wysiwyg_nicedit_settings($editor, $config, $theme) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_nicedit_plugins($editor) {
function _wysiwyg_nicedit_plugins($editor) {
return array(
'default' => array(
'buttons' => array(
@@ -1,173 +0,0 @@
<?php
/**
* @file
* Editor integration functions for openWYSIWYG.
*/
/**
* Plugin implementation of hook_editor().
*/
function wysiwyg_openwysiwyg_editor() {
$editor['openwysiwyg'] = array(
'title' => 'openWYSIWYG',
'vendor url' => 'http://www.openwebware.com',
'download url' => 'http://www.openwebware.com/download.shtml',
'library path' => wysiwyg_get_path('openwysiwyg') . '/scripts',
'libraries' => array(
'src' => array(
'title' => 'Source',
'files' => array('wysiwyg.js'),
),
),
'version callback' => 'wysiwyg_openwysiwyg_version',
'themes callback' => 'wysiwyg_openwysiwyg_themes',
'settings callback' => 'wysiwyg_openwysiwyg_settings',
'plugin callback' => 'wysiwyg_openwysiwyg_plugins',
'versions' => array(
'1.4.7' => array(
'js files' => array('openwysiwyg.js'),
'css files' => array('openwysiwyg.css'),
),
),
);
return $editor;
}
/**
* Detect editor version.
*
* @param $editor
* An array containing editor properties as returned from hook_editor().
*
* @return
* The installed editor version.
*/
function wysiwyg_openwysiwyg_version($editor) {
// 'library path' has '/scripts' appended already.
$changelog = $editor['editor path'] . '/changelog';
if (!file_exists($changelog)) {
return;
}
$changelog = fopen($changelog, 'r');
$line = fgets($changelog, 20);
if (preg_match('@v([\d\.]+)@', $line, $version)) {
fclose($changelog);
return $version[1];
}
fclose($changelog);
}
/**
* Determine available editor themes or check/reset a given one.
*
* @param $editor
* A processed hook_editor() array of editor properties.
* @param $profile
* A wysiwyg editor profile.
*
* @return
* An array of theme names. The first returned name should be the default
* theme name.
*/
function wysiwyg_openwysiwyg_themes($editor, $profile) {
return array('default');
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
* @param $editor
* A processed hook_editor() array of editor properties.
* @param $config
* An array containing wysiwyg editor profile settings.
* @param $theme
* The name of a theme/GUI/skin to use.
*
* @return
* A settings array to be populated in
* Drupal.settings.wysiwyg.configs.{editor}
*/
function wysiwyg_openwysiwyg_settings($editor, $config, $theme) {
$settings = array(
'path' => base_path() . $editor['editor path'] . '/',
'Width' => '100%',
);
if (isset($config['path_loc']) && $config['path_loc'] == 'none') {
$settings['StatusBarEnabled'] = FALSE;
}
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
$settings['CSSFile'] = reset(wysiwyg_get_css());
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['CSSFile'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
}
}
$settings['Toolbar'] = array();
if (!empty($config['buttons'])) {
$plugins = wysiwyg_get_plugins($editor['name']);
foreach ($config['buttons'] as $plugin => $buttons) {
foreach ($buttons as $button => $enabled) {
foreach (array('buttons', 'extensions') as $type) {
// Skip unavailable plugins.
if (!isset($plugins[$plugin][$type][$button])) {
continue;
}
// Add buttons.
if ($type == 'buttons') {
$settings['Toolbar'][0][] = $button;
}
}
}
}
}
// @todo
// if (isset($config['block_formats'])) {
// $settings['DropDowns']['headings']['elements'] = explode(',', $config['block_formats']);
// }
return $settings;
}
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_openwysiwyg_plugins($editor) {
$plugins = array(
'default' => array(
'buttons' => array(
'bold' => t('Bold'), 'italic' => t('Italic'), 'underline' => t('Underline'),
'strikethrough' => t('Strike-through'),
'justifyleft' => t('Align left'), 'justifycenter' => t('Align center'), 'justifyright' => t('Align right'), 'justifyfull' => t('Justify'),
'unorderedlist' => t('Bullet list'), 'orderedlist' => t('Numbered list'),
'outdent' => t('Outdent'), 'indent' => t('Indent'),
'undo' => t('Undo'), 'redo' => t('Redo'),
'createlink' => t('Link'),
'insertimage' => t('Image'),
'cleanup' => t('Clean-up'),
'forecolor' => t('Forecolor'), 'backcolor' => t('Backcolor'),
'superscript' => t('Sup'), 'subscript' => t('Sub'),
'blockquote' => t('Blockquote'), 'viewSource' => t('Source code'),
'hr' => t('Horizontal rule'),
'cut' => t('Cut'), 'copy' => t('Copy'), 'paste' => t('Paste'),
'visualaid' => t('Visual aid'),
'removeformat' => t('Remove format'),
'charmap' => t('Character map'),
'headings' => t('HTML block format'), 'font' => t('Font'), 'fontsize' => t('Font size'),
'maximize' => t('Fullscreen'),
'preview' => t('Preview'),
'print' => t('Print'),
'inserttable' => t('Table'),
'help' => t('Help'),
),
'internal' => TRUE,
),
);
return $plugins;
}
+1110 -217
View File
File diff suppressed because it is too large Load Diff
+26 -4
View File
@@ -16,15 +16,23 @@ function wysiwyg_whizzywig_editor() {
'libraries' => array(
'' => array(
'title' => 'Default',
'files' => array('whizzywig.js', 'xhtml.js'),
'files' => array('whizzywig.js'),
),
),
'verified version range' => array('55', '63'),
'version callback' => 'wysiwyg_whizzywig_version',
'settings form callback' => 'wysiwyg_whizzywig_settings_form',
'settings callback' => 'wysiwyg_whizzywig_settings',
'plugin callback' => 'wysiwyg_whizzywig_plugins',
'plugin callback' => '_wysiwyg_whizzywig_plugins',
'versions' => array(
'55' => array(
'js files' => array('whizzywig.js'),
'libraries' => array(
'' => array(
'title' => 'Default',
'files' => array('whizzywig.js', 'xhtml.js'),
),
),
),
'56' => array(
'js files' => array('whizzywig-56.js'),
@@ -62,6 +70,15 @@ function wysiwyg_whizzywig_version($editor) {
fclose($script);
}
/**
* Enhances the editor profile settings form for Whizzywig.
*/
function wysiwyg_whizzywig_settings_form(&$form, &$form_state) {
$form['basic']['language']['#access'] = FALSE;
// @todo CSS settings are currently not used.
$form['css']['#access'] = FALSE;
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
@@ -102,6 +119,7 @@ function wysiwyg_whizzywig_settings($editor, $config, $theme) {
}
// Add editor content stylesheet.
// @todo CSS settings are currently not used.
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
$css = drupal_get_path('theme', variable_get('theme_default', NULL)) . '/style.css';
@@ -110,7 +128,11 @@ function wysiwyg_whizzywig_settings($editor, $config, $theme) {
}
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['externalCSS'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['externalCSS'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
}
}
@@ -120,7 +142,7 @@ function wysiwyg_whizzywig_settings($editor, $config, $theme) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_whizzywig_plugins($editor) {
function _wysiwyg_whizzywig_plugins($editor) {
return array(
'default' => array(
'buttons' => array(
+41 -6
View File
@@ -28,10 +28,12 @@ function wysiwyg_wymeditor_editor() {
'files' => array('jquery.wymeditor.js'),
),
),
'verified version range' => array('0.5', '1.0.0b5'),
'version callback' => 'wysiwyg_wymeditor_version',
'themes callback' => 'wysiwyg_wymeditor_themes',
'settings form callback' => 'wysiwyg_wymeditor_settings_form',
'settings callback' => 'wysiwyg_wymeditor_settings',
'plugin callback' => 'wysiwyg_wymeditor_plugins',
'plugin callback' => '_wysiwyg_wymeditor_plugins',
'versions' => array(
'0.5-rc1' => array(
'js files' => array('wymeditor.js'),
@@ -81,6 +83,30 @@ function wysiwyg_wymeditor_themes($editor, $profile) {
return array('compact', 'default', 'minimal', 'silver', 'twopanels');
}
/**
* Enhances the editor profile settings form for WYMeditor.
*
* @see http://wymeditor.readthedocs.org/en/latest/version_1.0_and_0.5/getting_started/customize.html
*/
function wysiwyg_wymeditor_settings_form(&$form, &$form_state) {
$profile = $form_state['wysiwyg_profile'];
$settings = $profile->settings;
$settings += array(
'block_formats' => 'p,blockquote,pre,h2,h3,h4,h5,h6,div',
);
$form['css']['#description'] = t('Note: WYMeditor can only load a single stylesheet into the editor.');
$form['css']['block_formats'] = array(
'#type' => 'textfield',
'#title' => t('Block formats'),
'#default_value' => $settings['block_formats'],
'#size' => 40,
'#maxlength' => 250,
'#description' => t('Comma separated list of HTML block formats. Possible values: <code>@format-list</code>.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,blockquote,pre,th')),
);
}
/**
* Return runtime editor settings for a given wysiwyg profile.
*
@@ -103,7 +129,11 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) {
'wymPath' => $editor['libraries'][$library]['files'][0],
// @todo Does not work in Drupal; jQuery can live anywhere.
'jQueryPath' => base_path() . 'misc/jquery.js',
'updateSelector' => '.form-submit',
// WYMeditor's update event handler will revert the field contents if
// changes were made after it was detached. Wysiwyg takes care of submit
// events anyway so make sure WYMeditor does not bind it anywhere.
'updateSelector' => '#wysiwyg-no-element',
'updateEvent' => 'wysiwyg-no-event',
'skin' => $theme,
);
@@ -158,7 +188,7 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) {
'blockquote' => 'Blockquote',
'th' => 'Table_Header',
);
foreach (explode(',', $config['block_formats']) as $tag) {
foreach (explode(',', preg_replace('@\s+@', '', $config['block_formats'])) as $tag) {
if (isset($containers[$tag])) {
$settings['containersItems'][] = array(
'name' => strtoupper($tag),
@@ -169,14 +199,19 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) {
}
}
// Add editor content stylesheet.
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
// WYMeditor only supports one CSS file currently.
$css = wysiwyg_get_css();
$css = wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : '');
$settings['stylesheet'] = reset($css);
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['stylesheet'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['stylesheet'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
}
}
@@ -186,7 +221,7 @@ function wysiwyg_wymeditor_settings($editor, $config, $theme) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_wymeditor_plugins($editor) {
function _wysiwyg_wymeditor_plugins($editor) {
$plugins = array(
'default' => array(
'buttons' => array(
+67 -32
View File
@@ -40,19 +40,22 @@ function wysiwyg_yui_editor() {
),
),
),
'install note callback' => 'wysiwyg_yui_install_note',
'verified version range' => array('2.7.0', '2.9.0'),
'version callback' => 'wysiwyg_yui_version',
'themes callback' => 'wysiwyg_yui_themes',
'settings form callback' => 'wysiwyg_yui_settings_form',
'load callback' => 'wysiwyg_yui_load',
'settings callback' => 'wysiwyg_yui_settings',
'plugin callback' => 'wysiwyg_yui_plugins',
'plugin settings callback' => 'wysiwyg_yui_plugin_settings',
'plugin callback' => '_wysiwyg_yui_plugins',
'plugin meta callback' => '_wysiwyg_yui_plugin_meta',
'proxy plugin' => array(
'drupal' => array(
'load' => TRUE,
'proxy' => TRUE,
),
),
'proxy plugin settings callback' => 'wysiwyg_yui_proxy_plugin_settings',
'proxy plugin settings callback' => '_wysiwyg_yui_proxy_plugin_settings',
'versions' => array(
'2.7.0' => array(
'js files' => array('yui.js'),
@@ -62,6 +65,14 @@ function wysiwyg_yui_editor() {
return $editor;
}
/**
* Return an install note.
*/
function wysiwyg_yui_install_note() {
$output = '<p class="warning">' . t('YUI 3 is not supported because it does not contain a complete editor.') . '</p>';
return $output;
}
/**
* Detect editor version.
*
@@ -104,6 +115,39 @@ function wysiwyg_yui_themes($editor, $profile) {
return array('sam');
}
/**
* Enhances the editor profile settings form for YUI.
*
* @see http://developer.yahoo.com/yui/docs/YAHOO.widget.Editor.html
*/
function wysiwyg_yui_settings_form(&$form, &$form_state) {
$profile = $form_state['wysiwyg_profile'];
$settings = $profile->settings;
$settings += array(
'autoHeight' => FALSE,
'block_formats' => 'p,h1,h2,h3,h4,h5,h6',
);
$form['basic']['language']['#access'] = FALSE;
$form['appearance']['autoHeight'] = array(
'#type' => 'checkbox',
'#title' => t('Enable automatic height'),
'#default_value' => $settings['autoHeight'],
'#return_value' => 1,
'#description' => t('When enabled, removes the scrollbars from the edit area and resizes it to fit the content.') . ' ' . t('Uses the <a href="@url">@setting</a> setting internally.', array('@setting' => 'autoHeight', '@url' => url('http://developer.yahoo.com/yui/docs/YAHOO.widget.SimpleEditor.html#config_autoHeight'))),
);
$form['css']['block_formats'] = array(
'#type' => 'textfield',
'#title' => t('Block formats'),
'#default_value' => $settings['block_formats'],
'#size' => 40,
'#maxlength' => 250,
'#description' => t('Comma separated list of HTML block formats. Possible values: <code>@format-list</code>.', array('@format-list' => 'p,h1,h2,h3,h4,h5,h6,')),
);
}
/**
* Perform additional actions upon loading this editor.
*
@@ -143,11 +187,7 @@ function wysiwyg_yui_settings($editor, $config, $theme) {
'ptags' => TRUE,
);
if (isset($config['path_loc']) && $config['path_loc'] != 'none') {
$settings['dompath'] = $config['path_loc'];
}
// Enable auto-height feature when editor should be resizable.
if (!empty($config['resizing'])) {
if (!empty($config['autoHeight'])) {
$settings['autoHeight'] = TRUE;
}
@@ -178,7 +218,7 @@ function wysiwyg_yui_settings($editor, $config, $theme) {
'h5' => array('text' => 'Heading 5', 'value' => 'h5'),
'h6' => array('text' => 'Heading 6', 'value' => 'h6'),
);
foreach (explode(',', $config['block_formats']) as $tag) {
foreach (explode(',', preg_replace('@\s+@', '', $config['block_formats'])) as $tag) {
if (isset($headings[$tag])) {
$extra['menu'][] = $headings[$tag];
}
@@ -208,10 +248,14 @@ function wysiwyg_yui_settings($editor, $config, $theme) {
if (isset($config['css_setting'])) {
if ($config['css_setting'] == 'theme') {
$settings['extracss'] = wysiwyg_get_css();
$settings['extracss'] = wysiwyg_get_css(isset($config['css_theme']) ? $config['css_theme'] : '');
}
elseif ($config['css_setting'] == 'self' && isset($config['css_path'])) {
$settings['extracss'] = strtr($config['css_path'], array('%b' => base_path(), '%t' => drupal_get_path('theme', variable_get('theme_default', NULL))));
$settings['extracss'] = strtr($config['css_path'], array(
'%b' => base_path(),
'%t' => drupal_get_path('theme', variable_get('theme_default', NULL)),
'%q' => variable_get('css_js_query_string', ''),
));
$settings['extracss'] = explode(',', $settings['extracss']);
}
// YUI only supports inline CSS, so we need to use @import directives.
@@ -277,36 +321,27 @@ function wysiwyg_yui_button_setting($editor, $plugin, $button, $extra = array())
}
/**
* Build a JS settings array of native external plugins that need to be loaded separately.
* Build a JS settings array with global metadata for native external plugins.
*/
function wysiwyg_yui_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
if (!empty($plugin['load'])) {
// Add path for native external plugins; internal ones are loaded
// automatically.
if (empty($plugin['internal']) && isset($plugin['path'])) {
$settings[$name] = base_path() . $plugin['path'];
}
function _wysiwyg_yui_plugin_meta($editor, $plugin) {
$meta = NULL;
if (!empty($plugin['load'])) {
// Add path for native external plugins; internal ones are loaded
// automatically.
if (empty($plugin['internal']) && isset($plugin['path'])) {
$meta = base_path() . $plugin['path'];
}
}
return $settings;
return $meta;
}
/**
* Build a JS settings array for Drupal plugins loaded via the proxy plugin.
*/
function wysiwyg_yui_proxy_plugin_settings($editor, $profile, $plugins) {
function _wysiwyg_yui_proxy_plugin_settings($editor, $profile, $plugins) {
$settings = array();
foreach ($plugins as $name => $plugin) {
// Populate required plugin settings.
$settings[$name] = $plugin['dialog settings'] + array(
'title' => $plugin['title'],
'icon' => base_path() . $plugin['icon path'] . '/' . $plugin['icon file'],
'iconTitle' => $plugin['icon title'],
// @todo These should only be set if the plugin defined them.
'css' => base_path() . $plugin['css path'] . '/' . $plugin['css file'],
);
$settings[$name] = array();
}
return $settings;
}
@@ -314,7 +349,7 @@ function wysiwyg_yui_proxy_plugin_settings($editor, $profile, $plugins) {
/**
* Return internal plugins for this editor; semi-implementation of hook_wysiwyg_plugin().
*/
function wysiwyg_yui_plugins($editor) {
function _wysiwyg_yui_plugins($editor) {
return array(
'default' => array(
'buttons' => array(
@@ -0,0 +1,130 @@
<?php
/**
* @file
* Handles adding theme stylesheets into WYSIWYG editors.
*/
/**
* A simple page callback for a checking if a theme is active.
*
* A theme the user does not have permission to use can not be set active.
*
* @see _wysiwyg_theme_callback()
* @see _wysiwyg_delivery_dummy()
*/
function _wysiwyg_theme_check_active($theme) {
global $theme_key;
return $theme === $theme_key;
}
/**
* A simple page delivery dummy implementation.
*
* Acts like a normal HTML delivery mechanism but only "renders" a dummy string
* and prints a short string telling if the page callback result was "true".
*
* Useful for returning the results of an access check, performed by the page
* callback. The actual page callback return value is never printed.
*
* Success:
* - Status header: "200 OK"
* - Content: "OK"
*
* Failure:
* - Status header: "403 Forbidden"
* - Content: "Forbidden"
*
* @see _wysiwyg_theme_check_active()
*/
function _wysiwyg_delivery_dummy($page_callback_result) {
global $theme_key;
drupal_add_http_header('Content-Language', 'en');
drupal_add_http_header('Content-Type', 'text/html; charset=utf-8');
if ($page_callback_result) {
drupal_add_http_header('Status', '200 OK');
}
else {
drupal_add_http_header('Status', '403 Forbidden');
}
// Make sure the theme is always initialized.
drupal_theme_initialize();
// Render a completely themed empty page to catch as many stylesheets as
// possible, but don't actually return anything to speed up the response.
$rendered = drupal_render_page('Dummy');
// In case headers aren't enough, put the status in the response body.
print ($page_callback_result ? 'OK' : 'Forbidden');
// Cleanup and session handling.
drupal_page_footer();
}
/**
* Theme callback to simply suggest a theme based on the page arugment.
*/
function _wysiwyg_theme_callback($theme) {
return $theme;
}
/**
* A filtering pre render callback for style elements.
*
* Invokes hook_wysiwyg_editor_stules_alter() to allow other code to filter the
* list of stylesheets which will be used inside the editors in WYSIWYG mode.
*
* Intended to run before Core sorts/groups/aggregates stylesheets.
*/
function _wysiwyg_filter_editor_styles(&$elements) {
global $theme_key;
if (strpos(current_path(), 'wysiwyg_theme/') !== 0) {
return $elements;
}
$context = array('theme' => $theme_key);
drupal_alter('wysiwyg_editor_styles', $elements, $context); $css = array();
return $elements;
}
/**
* Creates a cache of the stylesheets used by the currently set theme.
*
* Since this is a pre render callback for the styles element, it should run
* late enough to catch all the stylesheets added just before the actual markup
* for them is rendered.
*
* The first time this runs for a theme it's too late for a module to have any
* use of the cache, so wysiwyg_get_css() uses drupal_http_request() to fetch a
* dummy page, filling the cache before the original response is sent.
*
* Intended to run after Core has sorted/grouped/aggregated stylesheets.
*/
function _wysiwyg_pre_render_styles($elements) {
global $theme_key;
if (strpos(current_path(), 'wysiwyg_theme/') !== 0) {
return $elements;
}
$cached = cache_get('wysiwyg_css');
foreach (element_children($elements) as $child) {
if ($elements['#groups'][$child]['group'] != CSS_THEME) {
continue;
}
switch ($elements[$child]['#tag']) {
case 'link':
$css[] = $elements[$child]['#attributes']['href'];
break;
case 'style':
if (!empty($elements[$child]['#attributes']['href'])) {
$css[] = $elements[$child]['#attributes']['href'];
}
elseif (!empty($elements[$child]['#value'])){
preg_match_all('/\@import url\("([^"]+)"\);/', $elements[$child]['#value'], $matches, PREG_SET_ORDER);
foreach ($matches as $val) {
$css[] = $val[1];
}
}
break;
}
$all = empty($cached->data) ? array() : $cached->data;
$all[$theme_key] = array('files' => $css, 'aggregated' => variable_get('preprocess_css', FALSE));
}
$all['_css_js_query_string'] = variable_get('css_js_query_string');
cache_set('wysiwyg_css', $all);
return $elements;
}
View File
View File
View File

Before

Width:  |  Height:  |  Size: 108 B

After

Width:  |  Height:  |  Size: 108 B

Before

Width:  |  Height:  |  Size: 255 B

After

Width:  |  Height:  |  Size: 255 B

Before

Width:  |  Height:  |  Size: 43 B

After

Width:  |  Height:  |  Size: 43 B

View File
View File
View File
View File
View File
+3 -3
View File
@@ -6,9 +6,9 @@ hidden = TRUE
dependencies[] = wysiwyg
files[] = wysiwyg_test.module
; Information added by drupal.org packaging script on 2012-10-02
version = "7.x-2.2"
; Information added by Drupal.org packaging script on 2016-12-31
version = "7.x-2.3"
core = "7.x"
project = "wysiwyg"
datestamp = "1349213776"
datestamp = "1483223295"
View File
View File
View File
+526 -251
View File
File diff suppressed because it is too large Load Diff
+22
View File
@@ -95,3 +95,25 @@ Drupal.wysiwyg.plugins.awesome = {
return '<img src="' + settings.path + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break drupal-content" />';
}
};
/**
* Because some editors add a lot of new elements with the id attribute set,
* Wysiwyg provides a way to exclude such ids from the ajax_html_ids[] parameter
* sent in AJAX requests. Serverside POST limits such as PHP's max_input_vars
* could otherwise cause the request to be rejected.
*
* The filter gathers a list of jQuery selectors from a global list in
* Drupal.wysiwyg.excludeIdSelectors, joins them with a comma separator and
* wraps them in "[id]:not[...]", which is then run on the document the same way
* Drupal core gathers the ids on every AJAX request.
*
* To add to the filter, set a unique key to Drupal.wysiwyg.excludeIdSelectors
* and set its value to an Array holding one or more selector strings which
* would match the element(s) to exclude.
*
* Beware not to match elements which are not removed before the actual request
* is performed, or Drupal may accidentally reuse the same id for new elements.
*
* Below is a sample from ckeditor.inc matching every id starting with 'cke_'.
*/
Drupal.wysiwyg.excludeIdSelectors.wysiwyg_ckeditor = ['[id^="cke_"]'];
+68 -3
View File
@@ -53,6 +53,8 @@ function hook_wysiwyg_plugin($editor, $version) {
// A list of buttons provided by this native plugin. The key has to
// match the corresponding JavaScript implementation. The value is
// is displayed on the editor configuration form only.
// CKEditor-specific note: The internal button name/key is
// capitalized, i.e. Img_assist.
'buttons' => array(
'img_assist' => t('Image Assist'),
),
@@ -75,7 +77,7 @@ function hook_wysiwyg_plugin($editor, $version) {
// Most plugins should define TRUE here.
'load' => TRUE,
// Boolean whether this plugin is a native plugin, i.e. shipped with
// the editor. Definition must be ommitted for plugins provided by
// the editor. Definition must be omitted for plugins provided by
// other modules. TRUE means 'path' and 'filename' above are ignored
// and the plugin is instead loaded from the editor's plugin folder.
'internal' => TRUE,
@@ -204,6 +206,19 @@ function hook_INCLUDE_editor() {
// (optional) A callback to invoke to return additional notes for installing
// the editor library in the administrative list/overview.
'install note callback' => 'wysiwyg_ckeditor_install_note',
// The minimum and maximum versions the implemetation has been tested with.
// Users will be notified if installing a version not within this range.
'verified version range' => array('1.2.3', '3.4.5'),
// (optional) A callback to perform migrations of the settings stored in a
// profile when a library change has been detected. Takes a reference to a
// settings object, the processed editor definition, the profile version and
// the installed library version. Migrations should be performed in the
// order changes were introduced by library versions, and the last version
// migrated to should be returned, or FALSE if no migration was possible.
// The returned version should be less than or equal to the highest version
// ( and >= the lowest version) defined in 'verified version range' and
// be as close as possible to, without passing, the installed version.
'migrate settings callback' => 'wysiwyg_ckeditor_migrate_settings',
// A callback to determine the library's version.
'version callback' => 'wysiwyg_ckeditor_version',
// A callback to return available themes/skins for the editor library.
@@ -224,8 +239,10 @@ function hook_INCLUDE_editor() {
'settings callback' => 'wysiwyg_ckeditor_settings',
// A callback to supply definitions of available editor plugins.
'plugin callback' => 'wysiwyg_ckeditor_plugins',
// A callback to convert administrative plugin settings for a editor profile
// into JavaScript settings.
// A callback to supply global metadata for a single native external plugin.
'plugin meta callback' => 'wysiwyg_ckeditor_plugin_meta',
// A callback to convert administrative plugin settings for an editor
// profile into JavaScript settings per profile.
'plugin settings callback' => 'wysiwyg_ckeditor_plugin_settings',
// (optional) Defines the proxy plugin that handles plugins provided by
// Drupal modules, which work in all editors that support proxy plugins.
@@ -249,6 +266,16 @@ function hook_INCLUDE_editor() {
return $editor;
}
/**
* Alter editor definitions defined by other modules.
*
* @param array $editors
* The Editors to alter.
*/
function hook_wysiwyg_editor_alter(&$editors) {
$editors['editor']['version callback'] = 'my_own_version_callback';
}
/**
* Act on editor profile settings.
*
@@ -279,5 +306,43 @@ function hook_wysiwyg_editor_settings_alter(&$settings, $context) {
if ($context['profile']->editor == 'tinymce') {
// Supported values to JSON data types.
$settings['cleanup_on_startup'] = TRUE;
// Function references (callbacks) need special care.
// @see wysiwyg_wrap_js_callback()
$settings['file_browser_callback'] = wysiwyg_wrap_js_callback('myFileBrowserCallback');
// Regular Expressions need special care.
// @see wysiwyg_wrap_js_regexp()
$settings['stylesheetParser_skipSelectors'] = wysiwyg_wrap_js_regexp('(^body\.|^caption\.|\.high|^\.)', 'i');
}
}
/**
* Act on stylesheets used in WYSIWYG mode.
*
* This hook acts like a pre-render callback to the style element normally
* output in the document header. It is invoked before Core has
* sorted/grouped/aggregated stylehsheets and changes made here will only have
* an effect on the stylesheets used in an editor's WYSIWYG mode.
* Wysiwyg will only keep items if their type is 'file' or 'inline' and only if
* they are in the group CSS_THEME.
*
* This hook may be invoked several times in a row with slightly different or
* altered stylesheets if something like Color module is used by a theme.
* Wysiwyg will cache the final list of stylesheets so this hook will only be
* called while the cache is being rebuilt.
*
* Messages set in this hook will not be displayed because the processing is
* done in an internal HTTP request and the page output is ignored.
*
* @param $elements
* The style element which will be rendered. Added stylesheets are found in
* $element['#items']['path/to/stylesheet.css'].
* @param $context
* An array with the following keys:
* - theme: The name of the theme which was used when the list of stylesheets
* was generated.
*/
function hook_wysiwyg_editor_styles_alter(&$element, $context) {
if ($context['theme'] == 'alpha') {
unset($element['#items']['sites/all/themes/omega/alpha/css/alpha-debug.css']);
}
}
View File
+27
View File
@@ -78,6 +78,27 @@ function wysiwyg_features_revert($module) {
function wysiwyg_features_rebuild($module) {
if ($defaults = features_get_default('wysiwyg', $module)) {
foreach ($defaults as $profile) {
if (empty($profile['settings']['_profile_preferences'])) {
$settings = &$profile['settings'];
// Importing an older profile, move state to its own section.
$preferences = array(
'add_to_summaries' => $settings['add_to_summaries'],
'default' => $settings['default'],
'show_toggle' => $settings['show_toggle'],
'user_choose' => $settings['user_choose'],
'version' => NULL,
);
unset($settings['add_to_summaries'], $settings['default'], $settings['show_toggle'], $settings['user_choose']);
if (!empty($settings['library'])) {
$prefereces['library'] = $settings['library'];
unset($settings['library']);
}
$editor = wysiwyg_get_editor($profile->editor);
if ($editor['installed']) {
$preferences['version'] = $editor['installed version'];
}
$settings['_profile_preferences'] = $preferences;
}
db_merge('wysiwyg')
->key(array('format' => $profile['format']))
->fields(array(
@@ -85,6 +106,12 @@ function wysiwyg_features_rebuild($module) {
'settings' => serialize($profile['settings']),
))
->execute();
// Clear the editing caches.
if (module_exists('ctools')) {
ctools_include('object-cache');
ctools_object_cache_clear_all('wysiwyg_profile', 'format' . $profile['format']);
}
cache_clear_all('wysiwyg_profile:format' . $profile['format'], 'cache');
}
wysiwyg_profile_cache_clear();
}
+3 -3
View File
@@ -9,9 +9,9 @@ configure = admin/config/content/wysiwyg
files[] = wysiwyg.module
files[] = tests/wysiwyg.test
; Information added by drupal.org packaging script on 2012-10-02
version = "7.x-2.2"
; Information added by Drupal.org packaging script on 2016-12-31
version = "7.x-2.3"
core = "7.x"
project = "wysiwyg"
datestamp = "1349213776"
datestamp = "1483223295"
+2 -2
View File
@@ -1,7 +1,7 @@
Drupal.wysiwyg = Drupal.wysiwyg || { 'instances': {} };
Drupal.wysiwyg = Drupal.wysiwyg || { 'instances': {}, 'excludeIdSelectors': { 'tokens': ['[id^="token-"]'] } };
Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'attach': {}, 'detach': {}, 'instance': {} };
Drupal.wysiwyg.editor = Drupal.wysiwyg.editor || { 'init': {}, 'update': {}, 'attach': {}, 'detach': {}, 'instance': {} };
Drupal.wysiwyg.plugins = Drupal.wysiwyg.plugins || {};
+224 -112
View File
@@ -55,7 +55,7 @@ function wysiwyg_schema() {
'description' => 'The {filter_format}.format of the text format.',
'type' => 'varchar',
'length' => 255,
'not null' => FALSE,
'not null' => TRUE,
),
'status' => array(
'description' => 'Boolean indicating whether the format is enabled by default.',
@@ -66,9 +66,9 @@ function wysiwyg_schema() {
'size' => 'tiny',
),
),
'primary key' => array('uid', 'format'),
'indexes' => array(
'uid' => array('uid'),
'format' => array('format'),
),
'foreign keys' => array(
'uid' => array(
@@ -130,117 +130,14 @@ function wysiwyg_update_dependencies() {
}
/**
* Retrieve a list of input formats to associate profiles to.
* Implements hook_update_last_removed().
*/
function _wysiwyg_install_get_formats() {
$formats = array();
$result = db_query("SELECT format, name FROM {filter_formats}");
while ($format = db_fetch_object($result)) {
// Build a list of all formats.
$formats[$format->format] = $format->name;
// Fetch filters.
$result2 = db_query("SELECT module, delta FROM {filters} WHERE format = %d", $format->format);
while ($filter = db_fetch_object($result2)) {
// If PHP filter is enabled, remove this format.
if ($filter->module == 'php') {
unset($formats[$format->format]);
break;
}
}
}
return $formats;
}
/**
* Associate Wysiwyg profiles with input formats.
*
* Since there was no association yet, we can only assume that there is one
* profile only, and that profile must be duplicated and assigned to all input
* formats (except PHP code format). Also, input formats already have
* titles/names, so Wysiwyg profiles do not need an own.
*
* Because input formats are already granted to certain user roles only, we can
* remove our custom Wysiwyg profile permissions. A 1:1 relationship between
* input formats and permissions makes plugin_count obsolete, too.
*
* Since the resulting table is completely different, a new schema is installed.
*/
function wysiwyg_update_6001() {
$ret = array();
if (db_table_exists('wysiwyg')) {
return $ret;
}
// Install new schema.
db_create_table($ret, 'wysiwyg', array(
'fields' => array(
'format' => array('type' => 'int', 'not null' => TRUE, 'default' => 0),
'editor' => array('type' => 'varchar', 'length' => 128, 'not null' => TRUE, 'default' => ''),
'settings' => array('type' => 'text', 'size' => 'normal'),
),
'primary key' => array('format'),
));
// Fetch all input formats.
$formats = _wysiwyg_install_get_formats();
// Fetch all profiles.
$result = db_query("SELECT name, settings FROM {wysiwyg_profile}");
while ($profile = db_fetch_object($result)) {
$profile->settings = unserialize($profile->settings);
// Extract editor name from profile settings.
$profile->editor = $profile->settings['editor'];
// Clean-up.
unset($profile->settings['editor']);
unset($profile->settings['old_name']);
unset($profile->settings['name']);
unset($profile->settings['rids']);
// Sorry. There Can Be Only One. ;)
break;
}
if ($profile) {
// Rebuild profiles and associate with input formats.
foreach ($formats as $format => $name) {
// Insert profiles.
// We can't use update_sql() here because of curly braces in serialized
// array.
db_query("INSERT INTO {wysiwyg} (format, editor, settings) VALUES (%d, '%s', '%s')", $format, $profile->editor, serialize($profile->settings));
$ret[] = array(
'success' => TRUE,
'query' => strtr('Wysiwyg profile %profile converted and associated with input format %format.', array('%profile' => check_plain($profile->name), '%format' => check_plain($name))),
);
}
}
// Drop obsolete tables {wysiwyg_profile} and {wysiwyg_role}.
db_drop_table($ret, 'wysiwyg_profile');
db_drop_table($ret, 'wysiwyg_role');
return $ret;
}
/**
* Clear JS/CSS caches to ensure that clients load fresh copies.
*/
function wysiwyg_update_6200() {
$ret = array();
// Change query-strings on css/js files to enforce reload for all users.
_drupal_flush_css_js();
drupal_clear_css_cache();
drupal_clear_js_cache();
// Rebuild the menu to remove old admin/settings/wysiwyg/profile item.
menu_rebuild();
// Flush content caches.
cache_clear_all();
$ret[] = array(
'success' => TRUE,
'query' => 'Caches have been flushed.',
);
return $ret;
function wysiwyg_update_last_removed() {
// Users should upgrade to the latest 6.x-2.x release before upgrading to
// 7.x-2.x. Some 7xxx functions duplicate work from 6xxx functions in 6.x-2.x
// because both branches are supported in parallel, but changes will only be
// applied once anyway because of safeguards.
return 6202;
}
/**
@@ -311,3 +208,218 @@ function wysiwyg_update_7200() {
));
}
}
/**
* Update enabled font plugin buttons to default plugin in TinyMCE profiles.
*/
function wysiwyg_update_7201() {
$query = db_select('wysiwyg', 'w')
->fields('w', array('format', 'settings'))
->condition('editor', 'tinymce');
foreach ($query->execute() as $profile) {
$settings = unserialize($profile->settings);
// Move enabled 'font' buttons into 'default' plugin buttons.
$changed = FALSE;
foreach (array('formatselect', 'fontselect', 'fontsizeselect', 'styleselect') as $button) {
if (isset($settings['buttons']['font'][$button])) {
$settings['buttons']['default'][$button] = $settings['buttons']['font'][$button];
unset($settings['buttons']['font'][$button]);
$changed = TRUE;
}
}
if ($changed) {
db_update('wysiwyg')
->condition('format', $profile->format)
->fields(array(
'settings' => serialize($settings),
))
->execute();
}
}
}
/**
* Update internal names of settings.
*/
function wysiwyg_update_7202() {
$query = db_select('wysiwyg', 'w')
->fields('w', array('format', 'editor', 'settings'));
foreach ($query->execute() as $profile) {
$settings = unserialize($profile->settings);
$changed = FALSE;
switch ($profile->editor) {
case 'tinymce':
if (isset($settings['path_loc'])) {
$settings['theme_advanced_statusbar_location'] = $settings['path_loc'];
unset($settings['path_loc']);
$changed = TRUE;
}
if (isset($settings['toolbar_loc'])) {
$settings['theme_advanced_toolbar_location'] = $settings['toolbar_loc'];
unset($settings['toolbar_loc']);
$changed = TRUE;
}
if (isset($settings['toolbar_align'])) {
$settings['theme_advanced_toolbar_align'] = $settings['toolbar_align'];
unset($settings['toolbar_align']);
$changed = TRUE;
}
if (isset($settings['block_formats'])) {
$settings['theme_advanced_blockformats'] = $settings['block_formats'];
unset($settings['block_formats']);
$changed = TRUE;
}
if (isset($settings['css_classes'])) {
$settings['theme_advanced_styles'] = $settings['css_classes'];
unset($settings['css_classes']);
$changed = TRUE;
}
if (isset($settings['resizing'])) {
$settings['theme_advanced_resizing'] = $settings['resizing'];
unset($settings['resizing']);
$changed = TRUE;
}
break;
case 'ckeditor':
if (isset($settings['apply_source_formatting'])) {
$settings['simple_source_formatting'] = $settings['apply_source_formatting'];
unset($settings['apply_source_formatting']);
$changed = TRUE;
}
if (isset($settings['resizing'])) {
$settings['resize_enabled'] = $settings['resizing'];
unset($settings['resizing']);
$changed = TRUE;
}
if (isset($settings['toolbar_loc'])) {
$settings['toolbarLocation'] = $settings['toolbar_loc'];
unset($settings['toolbar_loc']);
$changed = TRUE;
}
if (isset($settings['paste_auto_cleanup_on_paste'])) {
$settings['forcePasteAsPlainText'] = $settings['paste_auto_cleanup_on_paste'];
unset($settings['paste_auto_cleanup_on_paste']);
$changed = TRUE;
}
if (isset($settings['css_classes'])) {
$settings['stylesSet'] = $settings['css_classes'];
unset($settings['css_classes']);
$changed = TRUE;
}
break;
case 'fckeditor':
if (isset($settings['apply_source_formatting'])) {
$settings['FormatSource'] = $settings['FormatOutput'] = $settings['apply_source_formatting'];
unset($settings['apply_source_formatting']);
$changed = TRUE;
}
if (isset($settings['paste_auto_cleanup_on_paste'])) {
$settings['ForcePasteAsPlainText'] = $settings['paste_auto_cleanup_on_paste'];
unset($settings['paste_auto_cleanup_on_paste']);
$changed = TRUE;
}
if (isset($settings['block_formats'])) {
$settings['FontFormats'] = strtr($settings['block_formats'], array(',' => ';'));
unset($settings['block_formats']);
$changed = TRUE;
}
break;
case 'yui':
// The resizing setting is triggering autoHeight instead of resize.
if (isset($settings['resizing'])) {
$settings['autoHeight'] = $settings['resizing'];
unset($settings['resizing']);
$changed = TRUE;
}
break;
case 'openwysiwyg':
if (isset($settings['path_loc'])) {
$settings['StatusBarEnabled'] = ($settings['path_loc'] != 'none' );
unset($settings['path_loc']);
$changed = TRUE;
}
break;
default:
// Do not touch any other profiles since the extra settings won't hurt.
}
if ($changed) {
db_update('wysiwyg')
->condition('format', $profile->format)
->fields(array(
'settings' => serialize($settings),
))
->execute();
}
}
}
/**
* Add primary index to {wysiwyg_user}.
*/
function wysiwyg_update_7203() {
db_drop_index('wysiwyg_user', 'uid');
db_drop_index('wysiwyg_user', 'format');
db_change_field('wysiwyg_user', 'format', 'format',
array(
'type' => 'varchar',
'length' => 255,
'not null' => TRUE,
),
array(
'primary key' => array('uid', 'format'),
'indexes' => array(
'uid' => array('uid'),
),
)
);
}
/**
* Remove empty editor profiles and update existing profiles.
*/
function wysiwyg_update_7204() {
// Remove unused profiles.
$query = db_delete('wysiwyg')
->condition('editor', '')
->execute();
$query = db_select('wysiwyg', 'w')
->fields('w', array('format', 'editor', 'settings'));
foreach ($query->execute() as $profile) {
// Clear the editing caches.
if (module_exists('ctools')) {
ctools_include('object-cache');
ctools_object_cache_clear_all('wysiwyg_profile', 'format' . $profile->format);
}
cache_clear_all('wysiwyg_profile:format' . $profile->format, 'cache');
// Move profile state to its own section.
$settings = unserialize($profile->settings);
if (!empty($settings['_profile_preferences'])) {
// Skip in case of re-run.
continue;
}
$preferences = array(
'add_to_summaries' => $settings['add_to_summaries'],
'default' => $settings['default'],
'show_toggle' => $settings['show_toggle'],
'user_choose' => $settings['user_choose'],
'version' => NULL,
);
unset($settings['add_to_summaries'], $settings['default'], $settings['show_toggle'], $settings['user_choose']);
if (!empty($settings['library'])) {
$prefereces['library'] = $settings['library'];
unset($settings['library']);
}
$editor = wysiwyg_get_editor($profile->editor);
if ($editor['installed']) {
$preferences['version'] = $editor['installed version'];
}
$settings['_profile_preferences'] = $preferences;
db_update('wysiwyg')
->condition('format', $profile->format)
->fields(array(
'settings' => serialize($settings),
))
->execute();
}
wysiwyg_profile_cache_clear();
}
+780 -149
View File
File diff suppressed because it is too large Load Diff
+472 -166
View File
@@ -4,6 +4,7 @@
* @file
* Integrates client-side editors with Drupal.
*/
require_once 'includes/styling.inc';
/**
* Implements hook_entity_info().
@@ -35,7 +36,21 @@ class WysiwygProfileController extends DrupalDefaultEntityController {
function attachLoad(&$queried_entities, $revision_id = FALSE) {
// Unserialize the profile settings.
foreach ($queried_entities as $key => $record) {
$queried_entities[$key]->settings = unserialize($record->settings);
$settings = unserialize($record->settings);
// Profile preferences are stored with the editor settings to avoid adding
// an extra table column.
if (isset($settings['_profile_preferences'])) {
$preferences = $settings['_profile_preferences'];
unset($settings['_profile_preferences']);
}
else {
$preferences = array();
}
$queried_entities[$key]->settings = $settings;
$queried_entities[$key]->preferences = $preferences;
// @todo Store the name in the profile when allowing more than one per
// format.
$queried_entities[$key]->name = 'format' . $record->format;
}
// Call the default attachLoad() method.
parent::attachLoad($queried_entities, $revision_id);
@@ -54,31 +69,40 @@ function wysiwyg_menu() {
'access arguments' => array('administer filters'),
'file' => 'wysiwyg.admin.inc',
);
$items['admin/config/content/wysiwyg/profile'] = array(
$items['admin/config/content/wysiwyg/list'] = array(
'title' => 'List',
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items['admin/config/content/wysiwyg/profile/%wysiwyg_profile/edit'] = array(
'title' => 'Edit',
$items['admin/config/content/wysiwyg/profile/%wysiwyg_ui_profile_cache'] = array(
'title callback' => 'wysiwyg_admin_profile_title',
'title arguments' => array(5),
'page callback' => 'drupal_get_form',
'page arguments' => array('wysiwyg_profile_form', 5),
'access arguments' => array('administer filters'),
'file' => 'wysiwyg.admin.inc',
'tab_root' => 'admin/config/content/wysiwyg/profile',
'tab_parent' => 'admin/config/content/wysiwyg/profile/%wysiwyg_profile',
'type' => MENU_LOCAL_TASK,
);
$items['admin/config/content/wysiwyg/profile/%wysiwyg_profile/delete'] = array(
$items['admin/config/content/wysiwyg/profile/%wysiwyg_ui_profile_cache/edit'] = array(
'title' => 'Edit',
'type' => MENU_DEFAULT_LOCAL_TASK,
);
$items['admin/config/content/wysiwyg/profile/%wysiwyg_ui_profile_cache/delete'] = array(
'title' => 'Remove',
'page callback' => 'drupal_get_form',
'page arguments' => array('wysiwyg_profile_delete_confirm', 5),
'access arguments' => array('administer filters'),
'file' => 'wysiwyg.admin.inc',
'tab_root' => 'admin/config/content/wysiwyg/profile',
'tab_parent' => 'admin/config/content/wysiwyg/profile/%wysiwyg_profile',
'type' => MENU_LOCAL_TASK,
'weight' => 10,
);
$items['admin/config/content/wysiwyg/profile/%wysiwyg_ui_profile_cache/break-lock'] = array(
'title' => 'Break lock',
'page callback' => 'drupal_get_form',
'page arguments' => array('wysiwyg_profile_break_lock_confirm', 5),
'access arguments' => array('administer filters'),
'file' => 'wysiwyg.admin.inc',
'type' => MENU_VISIBLE_IN_BREADCRUMB,
);
// @see wysiwyg_dialog()
$items['wysiwyg/%'] = array(
'page callback' => 'wysiwyg_dialog',
@@ -88,9 +112,52 @@ function wysiwyg_menu() {
'type' => MENU_CALLBACK,
'file' => 'wysiwyg.dialog.inc',
);
$items['wysiwyg_theme/%'] = array(
'theme callback' => '_wysiwyg_theme_callback',
'theme arguments' => array(1),
'page callback' => '_wysiwyg_theme_check_active',
'page arguments' => array(1),
'delivery callback' => '_wysiwyg_delivery_dummy',
'access arguments' => array('access content'),
'type' => MENU_CALLBACK,
);
return $items;
}
/**
* Display an editor profile title.
*
* @param $profile
* An editor profile object.
*
* @return
* The unfiltered name of an editor profile.
* Currently the same as the text format name.
*/
function wysiwyg_admin_profile_title($profile) {
$format = filter_format_load($profile->format);
return $format->name;
}
/**
* Implements hook_admin_menu_map().
*/
function wysiwyg_admin_menu_map() {
if (!user_access('administer filters')) {
return;
}
$profiles = wysiwyg_profile_load_all();
$map['admin/config/content/wysiwyg/profile/%wysiwyg_profile'] = array(
'parent' => 'admin/config/content/wysiwyg',
'hide' => 'admin/config/content/wysiwyg/list',
'arguments' => array(
array('%wysiwyg_profile' => array_keys($profiles)),
),
);
return $map;
}
/**
* Implements hook_element_info().
*/
@@ -138,23 +205,18 @@ function wysiwyg_help($path, $arg) {
}
}
/**
* Implementation of hook_form_alter().
*/
function wysiwyg_form_alter(&$form, &$form_state) {
// Teaser splitter is unconditionally removed and NOT supported.
if (isset($form['body_field'])) {
unset($form['body_field']['teaser_js']);
}
}
/**
* Implements hook_element_info_alter().
*/
function wysiwyg_element_info_alter(&$types) {
$types['text_format']['#pre_render'][] = 'wysiwyg_pre_render_text_format';
// For filtering stylesheets before Core aggregates them.
array_unshift($types['styles']['#pre_render'], '_wysiwyg_filter_editor_styles');
// For recording and caching the added stylesheets.
$types['styles']['#pre_render'][] = '_wysiwyg_pre_render_styles';
}
/**
* Process a text format widget to load and attach editors.
*
@@ -164,7 +226,8 @@ function wysiwyg_pre_render_text_format($element) {
// filter_process_format() copies properties to the expanded 'value' child
// element. Skip this text format widget, if it contains no 'format' or when
// the current user does not have access to edit the value.
if (!isset($element['format']) || !empty($element['value']['#disabled'])) {
// Simplify module creates an extra incomplete 'format' on the base field.
if (!isset($element['format']['format']) || !empty($element['value']['#disabled'])) {
return $element;
}
// Allow modules to programmatically enforce no client-side editor by setting
@@ -179,36 +242,46 @@ function wysiwyg_pre_render_text_format($element) {
'field' => $field['#id'],
);
// If this textarea is #resizable and we will load at least one
// editor, then only load the behavior and let the 'none' editor
// attach/detach it to avoid hi-jacking the UI. Due to our CSS class
// parsing, we can add arbitrary parameters for each input format.
// The #resizable property will be removed below, if at least one
// profile has been loaded.
$resizable = 0;
// If this textarea is #resizable the 'none' editor will attach/detach it to
// avoid hi-jacking the UI.
if (!empty($field['#resizable'])) {
$resizable = 1;
drupal_add_js('misc/textarea.js');
$settings['resizable'] = 1;
}
if (isset($element['summary']) && $element['summary']['#type'] == 'textarea') {
$settings['summary'] = $element['summary']['#id'];
}
if (!$format_field['format']['#access'] || (isset($format_field['#access']) && !$format_field['#access'])) {
// Directly specify which the single available format is.
$available_formats = array($format_field['format']['#value'] => $format_field['format']['#options'][$format_field['format']['#value']]);
$settings['activeFormat'] = $format_field['format']['#value'];
}
else {
// Let the client check the selectbox for the active format.
$available_formats = $format_field['format']['#options'];
$settings['select'] = $format_field['format']['#id'];
}
// Determine the available text formats.
foreach ($format_field['format']['#options'] as $format_id => $format_name) {
foreach ($available_formats as $format_id => $format_name) {
$format = 'format' . $format_id;
// Initialize default settings, defaulting to 'none' editor.
$settings[$format] = array(
'editor' => 'none',
'status' => 1,
'toggle' => 1,
'resizable' => $resizable,
);
// Fetch the profile associated to this text format.
$profile = wysiwyg_get_profile($format_id);
if ($profile) {
// Initialize default settings, defaulting to 'none' editor.
$settings[$format] = array(
'editor' => 'none',
'status' => 1,
'toggle' => 1,
);
$loaded = TRUE;
if (isset($profile->settings['add_to_summaries']) && !$profile->settings['add_to_summaries']) {
$settings[$format]['skip_summary'] = 1;
}
$settings[$format]['editor'] = $profile->editor;
$settings[$format]['status'] = (int) wysiwyg_user_get_status($profile);
if (isset($profile->settings['show_toggle'])) {
$settings[$format]['toggle'] = (int) $profile->settings['show_toggle'];
if (isset($profile->preferences['show_toggle'])) {
$settings[$format]['toggle'] = (int) $profile->preferences['show_toggle'];
}
// Check editor theme (and reset it if not/no longer available).
$theme = wysiwyg_get_editor_themes($profile, (isset($profile->settings['theme']) ? $profile->settings['theme'] : ''));
@@ -219,49 +292,17 @@ function wysiwyg_pre_render_text_format($element) {
wysiwyg_add_editor_settings($profile, $theme);
}
}
// Use a hidden element for a single text format.
if (!$format_field['format']['#access']) {
$format_field['wysiwyg'] = array(
'#type' => 'hidden',
'#name' => $format_field['format']['#name'],
'#value' => $format_id,
'#attributes' => array(
'id' => $format_field['format']['#id'],
'class' => array('wysiwyg'),
),
);
$format_field['wysiwyg']['#attached']['js'][] = array(
'data' => array(
'wysiwyg' => array(
'triggers' => array(
$format_field['format']['#id'] => $settings,
),
$element['value']['#attributes']['class'][] = 'wysiwyg';
$element['#attached']['js'][] = array(
'data' => array(
'wysiwyg' => array(
'triggers' => array(
$element['value']['#id'] => $settings,
),
),
'type' => 'setting',
);
}
// Otherwise, attach to text format selector.
else {
$format_field['format']['#attributes']['class'][] = 'wysiwyg';
$format_field['format']['#attached']['js'][] = array(
'data' => array(
'wysiwyg' => array(
'triggers' => array(
$format_field['format']['#id'] => $settings,
),
),
),
'type' => 'setting',
);
}
// If we loaded at least one editor, then the 'none' editor will
// handle resizable textareas instead of core.
if (isset($loaded) && $resizable) {
$field['#resizable'] = FALSE;
}
),
'type' => 'setting',
);
return $element;
}
@@ -319,8 +360,8 @@ function wysiwyg_load_editor($profile) {
);
// Determine library files to load.
// @todo Allow to configure the library/execMode to use.
if (isset($profile->settings['library']) && isset($editor['libraries'][$profile->settings['library']])) {
$library = $profile->settings['library'];
if (isset($profile->preferences['library']) && isset($editor['libraries'][$profile->preferences['library']])) {
$library = $profile->preferences['library'];
$files = $editor['libraries'][$library]['files'];
}
else {
@@ -348,7 +389,7 @@ function wysiwyg_load_editor($profile) {
drupal_add_js($init, array('type' => 'inline') + $default_library_options);
}
else {
drupal_add_js(file_create_url($uri), $default_library_options);
drupal_add_js($uri, $default_library_options);
}
}
}
@@ -390,6 +431,11 @@ function wysiwyg_load_editor($profile) {
}
}
// Check if settings were already added on the page that makes an AJAX call.
if (isset($_POST['ajax_page_state']) && !empty($_POST['ajax_page_state']['js'][$path . '/wysiwyg.js'])) {
$settings_added = TRUE;
}
// Add basic Wysiwyg settings if any editor has been added.
if (!isset($settings_added) && $loaded[$name]) {
drupal_add_js(array('wysiwyg' => array(
@@ -491,24 +537,32 @@ function wysiwyg_add_plugin_settings($profile) {
$proxy = (isset($editor['proxy plugin']) ? key($editor['proxy plugin']) : '');
// Process native editor plugins.
if (isset($editor['plugin settings callback'])) {
// @todo Require PHP 5.1 in 3.x and use array_intersect_key().
$profile_plugins_native = array();
foreach ($plugins[$editor['name']] as $plugin => $meta) {
// Skip Drupal plugins (handled below).
if ($plugin === $proxy) {
continue;
}
// Only keep native plugins that are enabled in this profile.
if (isset($profile->settings['buttons'][$plugin])) {
$profile_plugins_native[$plugin] = $meta;
$profile_plugins_native = array();
foreach ($plugins[$editor['name']] as $plugin => $meta) {
// Skip Drupal plugins (handled below) and 'core' functionality.
if ($plugin === $proxy || $plugin === 'default') {
continue;
}
// Only keep native plugins that are enabled in this profile.
if (isset($profile->settings['buttons'][$plugin])) {
$profile_plugins_native[$plugin] = $meta;
if (!isset($processed_plugins[$editor['name']][$plugin])) {
if (isset($editor['plugin meta callback'])) {
// Invoke the editor's plugin meta callback, so it can populate the
// global metadata for native plugins with required values.
$meta['name'] = $plugin;
if (($native_meta = call_user_func($editor['plugin meta callback'], $editor, $meta))) {
drupal_add_js(array('wysiwyg' => array('plugins' => array('native' => array($editor['name'] => array($plugin => $native_meta))))), 'setting');
}
}
$processed_plugins[$editor['name']][$plugin] = $meta;
}
}
}
if (!empty($profile_plugins_native) && isset($editor['plugin settings callback'])) {
// Invoke the editor's plugin settings callback, so it can populate the
// settings for native external plugins with required values.
$settings_native = call_user_func($editor['plugin settings callback'], $editor, $profile, $profile_plugins_native);
if ($settings_native) {
// format specific settings for native plugins with required values.
if (($settings_native = call_user_func($editor['plugin settings callback'], $editor, $profile, $profile_plugins_native))) {
drupal_add_js(array('wysiwyg' => array('plugins' => array('format' . $profile->format => array('native' => $settings_native)))), 'setting');
}
}
@@ -526,9 +580,13 @@ function wysiwyg_add_plugin_settings($profile) {
// Load the Drupal plugin's JavaScript.
drupal_add_js($meta['js path'] . '/' . $meta['js file']);
// Add plugin-specific settings.
if (isset($meta['settings'])) {
drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin => $meta['settings'])))), 'setting');
$settings = (isset($meta['settings']) ? $meta['settings'] : array());
$settings['title'] = $meta['title'];
$settings['icon'] = base_path() . $meta['icon path'] . '/' . $meta['icon file'];
if (!empty($meta['css path']) && !empty($meta['css file'])) {
$settings['css'] = base_path() . $meta['css path'] . '/' . $meta['css file'];
}
drupal_add_js(array('wysiwyg' => array('plugins' => array('drupal' => array($plugin => $settings)))), 'setting');
}
else {
$profile_plugins_drupal[$plugin] = $processed_plugins[$proxy][$plugin];
@@ -622,8 +680,22 @@ function wysiwyg_get_plugins($editor_name) {
function wysiwyg_get_editor_config($profile, $theme) {
$editor = wysiwyg_get_editor($profile->editor);
$settings = array();
$installed_version = $editor['installed version'];
$settings = $profile->settings;
if (!empty($editor['settings callback']) && function_exists($editor['settings callback'])) {
$settings = $editor['settings callback']($editor, $profile->settings, $theme);
if (!empty($profile->preferences['version']) && !empty($installed_version)) {
$profile_version = $profile->preferences['version'];
$version_status = version_compare($profile_version, $installed_version);
if ($version_status !== 0) {
// Installed version is different from profile version. Silently migrate
// the stored editor settings to the installed version if possible.
$migrated = FALSE;
if (!empty($editor['migrate settings callback']) && function_exists($editor['migrate settings callback'])) {
$migrated = $editor['migrate settings callback']($settings, $editor, $profile_version, $installed_version);
}
}
}
$settings = $editor['settings callback']($editor, $settings, $theme);
// Allow other modules to alter the editor settings for this format.
$context = array('editor' => $editor, 'profile' => $profile, 'theme' => $theme);
@@ -638,32 +710,104 @@ function wysiwyg_get_editor_config($profile, $theme) {
* This assumes that the content editing area only needs stylesheets defined
* for the scope 'theme'.
*
* Performs a background request of a dummy page to cache as many of a theme's
* stylesheets as possible before returning the cached list.
*
* Note: if set to explicitly use the current admin theme by name, no access
* check on the 'view the administration theme' permission is performed.
*
* @param $theme
* The id of a theme to get stylesheets for. Defaults to the current theme.
*
* @return
* An array containing CSS files, including proper base path.
*/
function wysiwyg_get_css() {
static $files;
if (isset($files)) {
return $files;
function wysiwyg_get_css($theme = NULL) {
// Default to the node edit theme, if the user has access.
if (empty($theme)) {
$theme = variable_get('node_admin_theme') && user_access('view the administration theme') ? variable_get('admin_theme') : variable_get('theme_default', 'bartik');
}
// In node form previews, the theme has not been initialized yet.
if (!empty($_POST)) {
// If set to use the admin theme, ensure the user has access.
elseif ($theme == 'wysiwyg_theme_admin' && user_access('view the administration theme') && $admin_theme = variable_get('admin_theme')) {
$theme = $admin_theme;
}
// Make sure the theme system is initialized.
$themes = list_themes();
if (!isset($themes[$theme])) {
drupal_theme_initialize();
}
$files = array();
foreach (drupal_add_css() as $filepath => $info) {
if ($info['group'] >= CSS_THEME && $info['media'] != 'print') {
if ($info['type'] == 'external') {
$files[] = $filepath;
}
elseif (file_exists($filepath)) {
$files[] = base_path() . $filepath;
}
// Ensure the selected theme is enabled (or is the admin theme).
if (!drupal_theme_access($theme)) {
$theme = variable_get('theme_default', 'bartik');
}
$cached = cache_get('wysiwyg_css');
$css = array();
// Trigger a cache update if:
// this is NOT the wysiwyg_theme page (avoid loop),
// the cache is empty or does not have the current theme,
// the CSS/JS cache-busting query string has changed,
// or the theme's aggregation state has changed.
$update_cache = strpos(current_path(), 'wysiwyg_theme/') === FALSE && (
!$cached || (
empty($cached->data[$theme])
|| $cached->data[$theme]['aggregated'] != variable_get('preprocess_css', FALSE))
|| $cached->data['_css_js_query_string'] != variable_get('css_js_query_string'));
if ($update_cache) {
$url = url('wysiwyg_theme/' . $theme, array('absolute' => TRUE, 'max_redirects' => 0));
$response = drupal_http_request($url);;
$cached = cache_get('wysiwyg_css');
if ($cached && !empty($cached->data[$theme])) {
$css = $cached->data[$theme]['files'];
}
}
return $files;
elseif (!empty($cached->data[$theme])) {
$css = $cached->data[$theme]['files'];
}
return $css;
}
/**
* Implements hook_themes_enabled().
*/
function wysiwyg_themes_enabled($theme_list) {
$cached = cache_get('wysiwyg_css');
foreach ($theme_list as $theme) {
if ($cached && !empty($cached->data)) {
$css = $cached->data;
unset($css[$theme]);
}
}
cache_set('wysiwyg_css', $css);
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Alters the system's theme settings form to react when themes change.
*/
function wysiwyg_form_system_theme_settings_alter(&$form, &$form_state, $form_id) {
$form['#submit'][] = '_wysiwyg_system_theme_settings_submit';
}
/**
* Submit callback for the theme settings form.
*
* Removes the edited theme from the cache.
*/
function _wysiwyg_system_theme_settings_submit($form, &$form_state) {
$theme = NULL;
if ($form_state['build_info']['form_id'] == 'system_theme_settings' && !empty($form_state['build_info']['args'])) {
$theme = $form_state['build_info']['args'][0];
}
if ($theme !== NULL) {
$cached = cache_get('wysiwyg_css');
if ($cached && !empty($cached->data)) {
$css = $cached->data;
unset($css[$theme]);
cache_set('wysiwyg_css', $css);
}
}
wysiwyg_get_css($theme);
}
/**
@@ -672,6 +816,11 @@ function wysiwyg_get_css() {
* Since there are commonly not many text formats, and each text format-enabled
* form element will possibly have to load every single profile, all existing
* profiles are loaded and cached once to reduce the amount of database queries.
*
* @param $format
* The machine-name of a text format.
*
* @return A profile if found, else FALSE.
*/
function wysiwyg_profile_load($format) {
$profiles = wysiwyg_profile_load_all();
@@ -680,6 +829,8 @@ function wysiwyg_profile_load($format) {
/**
* Loads all profiles.
*
* @return An array of profiles keyed by format name.
*/
function wysiwyg_profile_load_all() {
// entity_load(..., FALSE) does not re-use its own static cache upon
@@ -694,20 +845,107 @@ function wysiwyg_profile_load_all() {
}
else {
$profiles = entity_load('wysiwyg_profile', FALSE);
$formats = filter_formats();
foreach ($profiles as $key => $profile) {
if (empty($profile->editor) || !isset($formats[$profile->format])) {
unset($profiles[$key]);
}
}
cache_set('wysiwyg_profiles', $profiles);
}
}
return $profiles;
}
/**
* Deletes a profile from the database.
*/
function wysiwyg_profile_delete($format) {
function wysiwyg_profile_delete($profile) {
db_delete('wysiwyg')
->condition('format', $format)
->condition('format', $profile->format)
->execute();
// Clear the editing caches.
if (module_exists('ctools')) {
ctools_include('object-cache');
ctools_object_cache_clear_all('wysiwyg_profile', $profile->name);
}
else {
cache_clear_all('wysiwyg_profile:' . $profile->name, 'cache');
}
wysiwyg_profile_cache_clear();
}
/**
* Specialized menu callback to load a profile and check its locked status.
*
* @param $name
* The machine name of the profile.
*
* @return
* The profile object, with a "locked" property indicating whether or not
* someone else is already editing the profile.
*/
function wysiwyg_ui_profile_cache_load($format) {
$original_profile = wysiwyg_profile_load($format);
$profile = FALSE;
$name = ($original_profile ? $original_profile->name : 'format' . $format);
$profile = wysiwyg_ui_profile_cache_get($name);
if (empty($profile)) {
$profile = $original_profile;
}
if (!empty($profile)) {
$profile->editing = TRUE;
return $profile;
}
return FALSE;
}
/**
* Specialized cache function to load a profile from the editing cache.
*
* @param $name
* The name of a profile to load. Currently the format name prefixed by
* 'format'.
* @return
* The profile object, with a "locked" property indicating whether or not
* someone else is already editing the profile, or FALSE if not cached.
*/
function wysiwyg_ui_profile_cache_get($name) {
$profile = FALSE;
if (module_exists('ctools')) {
ctools_include('object-cache');
$profile = ctools_object_cache_get('wysiwyg_profile', $name);
if ($profile) {
$profile->locked = ctools_object_cache_test('wysiwyg_profile', $name);
}
}
else {
// Fall back on simple caching in its own bin without locking.
$cached = cache_get('wysiwyg_profile:' . $name);
if ($cached) {
$profile = $cached->data;
$profile->locked = FALSE;
}
}
return $profile;
}
/**
* Specialized cache function to add a profile to the editing cache.
*/
function wysiwyg_ui_profile_cache_set(&$profile) {
if (!empty($profile->locked)) {
drupal_set_message(t('Changes can not be made to a locked profile.'), 'error');
return;
}
$profile->changed = TRUE;
if (module_exists('ctools')) {
ctools_include('object-cache');
ctools_object_cache_set('wysiwyg_profile', $profile->name, $profile);
}
else {
cache_set('wysiwyg_profile:' . $profile->name, $profile);
}
}
/**
@@ -723,13 +961,16 @@ function wysiwyg_profile_cache_clear() {
* Implements hook_form_FORM_ID_alter().
*/
function wysiwyg_form_user_profile_form_alter(&$form, &$form_state, $form_id) {
if ($form['#user_category'] != 'account') {
return;
}
$account = $form['#user'];
$user_formats = filter_formats($account);
$options = array();
$options_default = array();
foreach (wysiwyg_profile_load_all() as $format => $profile) {
// Only show profiles that have user_choose enabled.
if (!empty($profile->settings['user_choose']) && isset($user_formats[$format])) {
if (!empty($profile->preferences['user_choose']) && isset($user_formats[$format])) {
$options[$format] = check_plain($user_formats[$format]->name);
if (wysiwyg_user_get_status($profile, $account)) {
$options_default[] = $format;
@@ -793,11 +1034,11 @@ function wysiwyg_user_get_status($profile, $account = NULL) {
))->fetchAllKeyed();
}
if (!empty($profile->settings['user_choose']) && isset($account->wysiwyg_status[$profile->format])) {
if (!empty($profile->preferences['user_choose']) && isset($account->wysiwyg_status[$profile->format])) {
$status = $account->wysiwyg_status[$profile->format];
}
else {
$status = isset($profile->settings['default']) ? $profile->settings['default'] : TRUE;
$status = isset($profile->preferences['default']) ? $profile->preferences['default'] : TRUE;
}
return (bool) $status;
@@ -860,25 +1101,33 @@ function wysiwyg_get_all_editors() {
if (!($editors[$editor]['installed'] = file_exists($editors[$editor]['library path']))) {
continue;
}
$installed_version = NULL;
// Detect library version.
if (function_exists($editors[$editor]['version callback'])) {
$editors[$editor]['installed version'] = $editors[$editor]['version callback']($editors[$editor]);
$installed_version = $editors[$editor]['installed version'] = $editors[$editor]['version callback']($editors[$editor]);
}
if (empty($editors[$editor]['installed version'])) {
if (empty($installed_version)) {
$editors[$editor]['error'] = t('The version of %editor could not be detected.', array('%editor' => $properties['title']));
$editors[$editor]['installed'] = FALSE;
continue;
}
$editors[$editor]['installed version verified'] = TRUE;
if (!empty($editors[$editor]['verified version range'])) {
$version_range = $editors[$editor]['verified version range'];
if (version_compare($installed_version, $version_range[0], '<') || version_compare($installed_version, $version_range[1], '>')) {
$editors[$editor]['installed version verified'] = FALSE;
}
}
// Determine to which supported version the installed version maps.
ksort($editors[$editor]['versions']);
$version = 0;
foreach ($editors[$editor]['versions'] as $supported_version => $version_properties) {
if (version_compare($editors[$editor]['installed version'], $supported_version, '>=')) {
if (version_compare($installed_version, $supported_version, '>=')) {
$version = $supported_version;
}
}
if (!$version) {
$editors[$editor]['error'] = t('The installed version %version of %editor is not supported.', array('%version' => $editors[$editor]['installed version'], '%editor' => $editors[$editor]['title']));
$editors[$editor]['error'] = t('The installed version %version of %editor is not supported.', array('%version' => $installed_version, '%editor' => $editors[$editor]['title']));
$editors[$editor]['installed'] = FALSE;
continue;
}
@@ -886,6 +1135,9 @@ function wysiwyg_get_all_editors() {
$editors[$editor] = array_merge($editors[$editor], $editors[$editor]['versions'][$version]);
unset($editors[$editor]['versions']);
}
drupal_alter('wysiwyg_editor', $editors);
return $editors;
}
@@ -1011,51 +1263,56 @@ function wysiwyg_get_path($library, $base_path = FALSE) {
* @ingroup libraries
*/
function wysiwyg_get_libraries() {
global $profile;
// When this function is called during Drupal's initial installation process,
// the name of the profile that is about to be installed is stored in the
// global $profile variable. At all other times, the regular system variable
// contains the name of the current profile, and we can call variable_get()
// to determine the profile.
if (!isset($profile)) {
$profile = variable_get('install_profile', 'default');
if (function_exists('libraries_get_libraries')) {
$directories = libraries_get_libraries();
}
else {
global $profile;
$directory = 'libraries';
$searchdir = array();
$config = conf_path();
// When this function is called during Drupal's initial installation process,
// the name of the profile that is about to be installed is stored in the
// global $profile variable. At all other times, the regular system variable
// contains the name of the current profile, and we can call variable_get()
// to determine the profile.
if (!isset($profile)) {
$profile = variable_get('install_profile', 'default');
}
// The 'profiles' directory contains pristine collections of modules and
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
}
$directory = 'libraries';
$searchdir = array();
$config = conf_path();
// Always search sites/all/*.
$searchdir[] = 'sites/all/' . $directory;
// The 'profiles' directory contains pristine collections of modules and
// themes as organized by a distribution. It is pristine in the same way
// that /modules is pristine for core; users should avoid changing anything
// there in favor of sites/all or sites/<domain> directories.
if (file_exists("profiles/$profile/$directory")) {
$searchdir[] = "profiles/$profile/$directory";
}
// Also search sites/<domain>/*.
if (file_exists("$config/$directory")) {
$searchdir[] = "$config/$directory";
}
// Always search sites/all/*.
$searchdir[] = 'sites/all/' . $directory;
// Retrieve list of directories.
// @todo Core: Allow to scan for directories.
$directories = array();
$nomask = array('CVS');
foreach ($searchdir as $dir) {
if (is_dir($dir) && $handle = opendir($dir)) {
while (FALSE !== ($file = readdir($handle))) {
if (!in_array($file, $nomask) && $file[0] != '.') {
if (is_dir("$dir/$file")) {
$directories[$file] = "$dir/$file";
// Also search sites/<domain>/*.
if (file_exists("$config/$directory")) {
$searchdir[] = "$config/$directory";
}
// Retrieve list of directories.
// @todo Core: Allow to scan for directories.
$directories = array();
$nomask = array('CVS');
foreach ($searchdir as $dir) {
if (is_dir($dir) && $handle = opendir($dir)) {
while (FALSE !== ($file = readdir($handle))) {
if (!in_array($file, $nomask) && $file[0] != '.') {
if (is_dir("$dir/$file")) {
$directories[$file] = "$dir/$file";
}
}
}
closedir($handle);
}
closedir($handle);
}
}
@@ -1085,6 +1342,55 @@ function wysiwyg_get_directories($plugintype) {
return $directories;
}
/**
* Create a placeholder structure for JavaScript callbacks.
*
* @param $name
* A string with the name of the callback, use 'object.subobject.method'
* syntax for methods in nested objects.
* @param $context
* An optional string with the name of an object for overriding 'this' inside
* the function. Use 'object.subobject' syntax for nested objects. Defaults to
* the window object.
*
* @return
* An array with placeholder information for creating a JavaScript function
* reference on the client.
*/
function wysiwyg_wrap_js_callback($name, $context = NULL) {
$obj = array(
'drupalWysiwygType' => 'callback',
'name' => $name,
);
if ($context) {
$obj['context'] = $context;
}
return $obj;
}
/**
* Create a placeholder structure for JavaScript RegExp objects.
*
* @param $regexp
* A JavaScript Regular Expression as a string, without / wrappers.
* @param $modifiers
* An optional string with modifiers for the RegExp object.
*
* @return
* An array with placeholder information for creating a JavaScript RegExp
* object on the client.
*/
function wysiwyg_wrap_js_regexp($regexp, $modifiers = NULL) {
$obj = array(
'drupalWysiwygType' => 'regexp',
'regexp' => $regexp,
);
if ($modifiers) {
$obj['modifiers'] = $modifiers;
}
return $obj;
}
/**
* Process a single hook implementation of a wysiwyg editor.
*
View File
+293 -228
View File
@@ -1,274 +1,339 @@
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
verbatim copies of this license document, but changing it is not allowed.
Preamble
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
The licenses for most software are designed to take away your freedom to
share and change it. By contrast, the GNU General Public License is
intended to guarantee your freedom to share and change free software--to
make sure the software is free for all its users. This General Public License
applies to most of the Free Software Foundation's software and to any other
program whose authors commit to using it. (Some other Free Software
Foundation software is covered by the GNU Library General Public License
instead.) You can apply it to your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
When we speak of free software, we are referring to freedom, not price. Our
General Public Licenses are designed to make sure that you have the
freedom to distribute copies of free software (and charge for this service if
you wish), that you receive source code or can get it if you want it, that you
can change the software or use pieces of it in new free programs; and that
you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
To protect your rights, we need to make restrictions that forbid anyone to
deny you these rights or to ask you to surrender the rights. These restrictions
translate to certain responsibilities for you if you distribute copies of the
software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
For example, if you distribute copies of such a program, whether gratis or for
a fee, you must give the recipients all the rights that you have. You must make
sure that they, too, receive or can get the source code. And you must show
them these terms so they know their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
We protect your rights with two steps: (1) copyright the software, and (2)
offer you this license which gives you legal permission to copy, distribute
and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Also, for each author's protection and ours, we want to make certain that
everyone understands that there is no warranty for this free software. If the
software is modified by someone else and passed on, we want its recipients
to know that what they have is not the original, so that any problems
introduced by others will not reflect on the original authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
Finally, any free program is threatened constantly by software patents. We
wish to avoid the danger that redistributors of a free program will individually
obtain patent licenses, in effect making the program proprietary. To prevent
this, we have made it clear that any patent must be licensed for everyone's
free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
The precise terms and conditions for copying, distribution and modification
follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
0. This License applies to any program or other work which contains a notice
placed by the copyright holder saying it may be distributed under the terms
of this General Public License. The "Program", below, refers to any such
program or work, and a "work based on the Program" means either the
Program or any derivative work under copyright law: that is to say, a work
containing the Program or a portion of it, either verbatim or with
modifications and/or translated into another language. (Hereinafter, translation
is included without limitation in the term "modification".) Each licensee is
addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
Activities other than copying, distribution and modification are not covered
by this License; they are outside its scope. The act of running the Program is
not restricted, and the output from the Program is covered only if its contents
constitute a work based on the Program (independent of having been made
by running the Program). Whether that is true depends on what the Program
does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this License
and to the absence of any warranty; and give any other recipients of the
Program a copy of this License along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
You may charge a fee for the physical act of transferring a copy, and you
may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
2. You may modify your copy or copies of the Program or any portion of it,
thus forming a work based on the Program, and copy and distribute such
modifications or work under the terms of Section 1 above, provided that you
also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
a) You must cause the modified files to carry prominent notices stating that
you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
b) You must cause any work that you distribute or publish, that in whole or in
part contains or is derived from the Program or any part thereof, to be
licensed as a whole at no charge to all third parties under the terms of this
License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
c) If the modified program normally reads commands interactively when run,
you must cause it, when started running for such interactive use in the most
ordinary way, to print or display an announcement including an appropriate
copyright notice and a notice that there is no warranty (or else, saying that
you provide a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this License.
(Exception: if the Program itself is interactive but does not normally print such
an announcement, your work based on the Program is not required to print
an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
These requirements apply to the modified work as a whole. If identifiable
sections of that work are not derived from the Program, and can be
reasonably considered independent and separate works in themselves, then
this License, and its terms, do not apply to those sections when you distribute
them as separate works. But when you distribute the same sections as part
of a whole which is a work based on the Program, the distribution of the
whole must be on the terms of this License, whose permissions for other
licensees extend to the entire whole, and thus to each and every part
regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest your rights to
work written entirely by you; rather, the intent is to exercise the right to
control the distribution of derivative or collective works based on the
Program.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of a
storage or distribution medium does not bring the other work under the scope
of this License.
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it, under
Section 2) in object code or executable form under the terms of Sections 1
and 2 above provided that you also do one of the following:
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable source
code, which must be distributed under the terms of Sections 1 and 2 above
on a medium customarily used for software interchange; or,
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three years, to give
any third party, for a charge no more than your cost of physically performing
source distribution, a complete machine-readable copy of the corresponding
source code, to be distributed under the terms of Sections 1 and 2 above on
a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer to distribute
corresponding source code. (This alternative is allowed only for
noncommercial distribution and only if you received the program in object
code or executable form with such an offer, in accord with Subsection b
above.)
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source code
means all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation and
installation of the executable. However, as a special exception, the source
code distributed need not include anything that is normally distributed (in
either source or binary form) with the major components (compiler, kernel,
and so on) of the operating system on which the executable runs, unless that
component itself accompanies the executable.
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering access to
copy from a designated place, then offering equivalent access to copy the
source code from the same place counts as distribution of the source code,
even though third parties are not compelled to copy the source along with the
object code.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program except as
expressly provided under this License. Any attempt otherwise to copy,
modify, sublicense or distribute the Program is void, and will automatically
terminate your rights under this License. However, parties who have received
copies, or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not signed it.
However, nothing else grants you permission to modify or distribute the
Program or its derivative works. These actions are prohibited by law if you
do not accept this License. Therefore, by modifying or distributing the
Program (or any work based on the Program), you indicate your acceptance
of this License to do so, and all its terms and conditions for copying,
distributing or modifying the Program or works based on it.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these terms and
conditions. You may not impose any further restrictions on the recipients'
exercise of the rights granted herein. You are not responsible for enforcing
compliance by third parties to this License.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues), conditions
are imposed on you (whether by court order, agreement or otherwise) that
contradict the conditions of this License, they do not excuse you from the
conditions of this License. If you cannot distribute so as to satisfy
simultaneously your obligations under this License and any other pertinent
obligations, then as a consequence you may not distribute the Program at all.
For example, if a patent license would not permit royalty-free redistribution
of the Program by all those who receive copies directly or indirectly through
you, then the only way you could satisfy both it and this License would be to
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply and
the section as a whole is intended to apply in other circumstances.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any patents or
other property right claims or to contest validity of any such claims; this
section has the sole purpose of protecting the integrity of the free software
distribution system, which is implemented by public license practices. Many
people have made generous contributions to the wide range of software
distributed through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing to
distribute software through any other system and a licensee cannot impose
that choice.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to be a
consequence of the rest of this License.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in certain
countries either by patents or by copyrighted interfaces, the original copyright
holder who places the Program under this License may add an explicit
geographical distribution limitation excluding those countries, so that
distribution is permitted only in or among countries not thus excluded. In such
case, this License incorporates the limitation as if written in the body of this
License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will be
similar in spirit to the present version, but may differ in detail to address new
problems or concerns.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program specifies
a version number of this License which applies to it and "any later version",
you have the option of following the terms and conditions either of that
version or of any later version published by the Free Software Foundation. If
the Program does not specify a version number of this License, you may
choose any version ever published by the Free Software Foundation.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free programs
whose distribution conditions are different, write to the author to ask for
permission. For software which is copyrighted by the Free Software
Foundation, write to the Free Software Foundation; we sometimes make
exceptions for this. Our decision will be guided by the two goals of
preserving the free status of all derivatives of our free software and of
promoting the sharing and reuse of software generally.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE,
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT
PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE
STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT
WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND
PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
NECESSARY SERVICING, REPAIR OR CORRECTION.
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR
AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR
ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE
LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL,
SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
ARISING OUT OF THE USE OR INABILITY TO USE THE
PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA
OR DATA BEING RENDERED INACCURATE OR LOSSES
SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE
PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN
IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF
THE POSSIBILITY OF SUCH DAMAGES.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
View File
+5 -1
View File
@@ -22,7 +22,7 @@ function wysiwyg_filter_filter_wysiwyg_settings(&$form, &$form_state, $filter, $
global $base_url;
drupal_add_css(drupal_get_path('module', 'wysiwyg_filter') . '/wysiwyg_filter.admin.css', array('preprocess' => FALSE));
// Load common functions.
module_load_include('inc', 'wysiwyg_filter');
form_load_include($form_state, 'inc', 'wysiwyg_filter');
$settings = $filter->settings;
$settings += $defaults;
@@ -232,6 +232,10 @@ function _wysiwyg_filter_clear_messages() {
*/
function wysiwyg_filter_filter_wysiwyg_settings_validate($form, &$form_state) {
$values =& $form_state['values']['filters']['wysiwyg']['settings'];
// Don't validate disabled filters.
if (empty($form_state['values']['filters']['wysiwyg']['status'])) {
return;
}
// *** validate valid_elements ***
// Check elements against hardcoded backlist.
+4 -1
View File
@@ -61,7 +61,7 @@ EOT;
* Get HTML elements blacklist.
*/
function wysiwyg_filter_get_elements_blacklist() {
return array(
$blacklist = array(
'applet',
'area',
'base',
@@ -93,6 +93,9 @@ function wysiwyg_filter_get_elements_blacklist() {
'textarea',
'title',
);
drupal_alter('wysiwyg_filter_elements_blacklist', $blacklist);
return $blacklist;
}
/**
+3 -3
View File
@@ -9,9 +9,9 @@ files[] = wysiwyg_filter.install
files[] = wysiwyg_filter.module
files[] = wysiwyg_filter.pages.inc
; Information added by drupal.org packaging script on 2011-07-10
version = "7.x-1.6-rc2"
; Information added by Drupal.org packaging script on 2016-02-04
version = "7.x-1.6-rc3"
core = "7.x"
project = "wysiwyg_filter"
datestamp = "1310326321"
datestamp = "1454601540"
View File
View File
+13 -1
View File
@@ -22,7 +22,7 @@
* @return string
* Filtered HTML text.
*/
function wysiwyg_filter_filter_wysiwyg_process($text, $filter, $format, $langcode, $cache, $cache_id) {
function wysiwyg_filter_filter_wysiwyg_process($text, $filter, $format, $langcode = NULL, $cache = NULL, $cache_id = NULL) {
// Only operate on valid UTF-8 strings. This is necessary to prevent cross
// site scripting issues on Internet Explorer 6.
if (!drupal_validate_utf8($text)) {
@@ -429,6 +429,9 @@ function _wysiwyg_filter_xss_attributes($attr, $element = '') {
// Element ID is valid, check_plain result.
$attrinfo['value'] = check_plain($attrinfo['value']);
}
elseif ($attrname == 'media') {
$attrinfo['value'] = check_plain($attrinfo['value']);
}
else {
// All attribute values are checked for bad protocols. This is the same
// exact method used by Drupal's filter_xss().
@@ -450,6 +453,15 @@ function _wysiwyg_filter_xss_attributes($attr, $element = '') {
}
}
// Fix for IE8 broken handling of ` character.
if (strpos($attrinfo['value'], '`') !== FALSE) {
// IE8 quoting would already be triggered by the presence of any "' <>
if (!preg_match('/["\' <>]/', $attrinfo['value'])) {
// Trailing space triggers IE8 to correctly quote the value.
$attrinfo['value'] .= ' ';
}
}
// Build parsed attribute value.
$parsed_attribute .= '=' . $attrinfo['delimiter'] . $attrinfo['value'] . $attrinfo['delimiter'];
}