AI-in-Healthcare / Developer Meetup in Boston Generative AI Use Cases in Healthcare _files /js__LaXrcsYA67EhgvGLQtIFUg2chDWk-YaSa0MoH6PIRSQ__3kXcyE3tcP4E.js
nmitchko's picture
Upload 77 files
1e4c25f
raw
history blame
No virus
198 kB
(function($) {
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 fields are 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);
}
}
// 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.
*/
Drupal.wysiwyg.editor.attach.ckeditor = function(context, params, settings) {
// Apply editor instance settings.
CKEDITOR.config.customConfig = '';
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;
// Get a list of block, list and table tags from CKEditor's XHTML DTD.
// @see http://docs.cksource.com/CKEditor_3.x/Developers_Guide/Output_Formatting.
var dtd = CKEDITOR.dtd;
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.simple_source_formatting) {
// Mimic FCKeditor output, by breaking lines between tags.
for (var tag in tags) {
if (tag == 'pre') {
continue;
}
this.dataProcessor.writer.setRules(tag, {
indent: true,
breakBeforeOpen: true,
breakAfterOpen: false,
breakBeforeClose: false,
breakAfterClose: true
});
}
}
else {
// CKEditor adds default formatting to <br>, so we want to remove that
// here too.
tags.br = 1;
// No indents or linebreaks;
for (var tag in tags) {
if (tag == 'pre') {
continue;
}
this.dataProcessor.writer.setRules(tag, {
indent: false,
breakBeforeOpen: false,
breakAfterOpen: false,
breakBeforeClose: false,
breakAfterClose: false
});
}
}
},
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 && 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 enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].attach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].attach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name);
data = wysiwygInstance.prepareContent(data);
}
}
return originalToHtml.call(this, data, fixForBody);
};
});
editor.dataProcessor.toDataFormat = CKEDITOR.tools.override(editor.dataProcessor.toDataFormat, function(originalToDataFormat) {
// Convert WYSIWYG mode content to raw data.
return function(data, fixForBody) {
data = originalToDataFormat.call(this, data, fixForBody);
for (var plugin in enabledPlugins) {
if (typeof Drupal.wysiwyg.plugins[plugin].detach == 'function') {
data = Drupal.wysiwyg.plugins[plugin].detach(data, wysiwygInstance.pluginInfo.global.drupal[plugin], editor.name);
}
}
return data;
};
});
}
},
selectionChange: function (event) {
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);
}
}
},
focus: function(ev) {
Drupal.wysiwyg.activeId = ev.editor.name;
},
afterCommandExec: function(ev) {
// Fix Drupal toolbar obscuring editor toolbar in fullscreen mode.
if (ev.data.name != 'maximize') {
return;
}
if (ev.data.command.state == CKEDITOR.TRISTATE_ON) {
$drupalToolbars.hide();
}
else {
$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.
var editorInstance = CKEDITOR.replace(params.field, settings);
};
/**
* Detach a single editor instance.
*/
Drupal.wysiwyg.editor.detach.ckeditor = function (context, params, trigger) {
var method = (trigger == 'serialize') ? 'updateElement' : 'destroy';
var instance = CKEDITOR.instances[params.field];
if (!instance) {
return;
}
instance[method]();
};
Drupal.wysiwyg.editor.instance.ckeditor = {
addPlugin: function (pluginName, pluginSettings) {
CKEDITOR.plugins.add(pluginName, {
// Wrap Drupal plugin in a proxy plugin.
init: function(editor) {
if (pluginSettings.css) {
editor.on('mode', function(ev) {
if (ev.editor.mode == 'wysiwyg') {
// Inject CSS files directly into the editing area head tag.
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" >');
}
});
}
if (typeof Drupal.wysiwyg.plugins[pluginName].invoke == 'function') {
var pluginCommand = {
exec: function (editor) {
var data = { format: 'html', node: null, content: '' };
var selection = editor.getSelection();
if (selection) {
data.node = selection.getSelectedElement();
if (data.node) {
data.node = data.node.$;
}
if (selection.getType() == CKEDITOR.SELECTION_TEXT) {
if (selection.getSelectedText) {
data.content = selection.getSelectedText();
}
else {
// Pre v3.6.1.
if (CKEDITOR.env.ie) {
data.content = selection.getNative().createRange().text;
}
else {
data.content = selection.getNative().toString();
}
}
}
else if (data.node) {
// content is supposed to contain the "outerHTML".
data.content = data.node.parentNode.innerHTML;
}
}
Drupal.wysiwyg.plugins[pluginName].invoke(data, pluginSettings, editor.name);
}
};
editor.addCommand(pluginName, pluginCommand);
}
editor.ui.addButton(pluginName, {
label: pluginSettings.title,
command: pluginName,
icon: pluginSettings.icon
});
// @todo Add button state handling.
}
});
},
prepareContent: function(content) {
// @todo Don't know if we need this yet.
return content;
},
insert: function(content) {
content = this.prepareContent(content);
if (CKEDITOR.version.split('.')[0] === '3' && (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) {
CKEDITOR.instances[this.field].setData(content);
},
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);
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function($) {
/**
* Attach this editor to a target element.
*
* @param context
* A DOM element, supplied by Drupal.attachBehaviors().
* @param params
* An object containing input format parameters. Default parameters are:
* - editor: The internal editor name.
* - theme: The name/key of the editor theme/profile to use.
* - field: The CSS id of the target element.
* @param settings
* An object containing editor settings for all enabled editor themes.
*/
Drupal.wysiwyg.editor.attach.none = function(context, params, settings) {
if (params.resizable) {
var $wrapper = $('#' + params.field, context).parents('.form-textarea-wrapper:first');
$wrapper.addClass('resizable');
if (Drupal.behaviors.textarea) {
Drupal.behaviors.textarea.attach(context);
}
}
};
/**
* 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
* 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.
* Possible triggers are:
* - unload: (default) Another or no editor is about to take its place.
* - move: Currently expected to produce the same result as unload.
* - serialize: The form is about to be serialized before an AJAX request or
* a normal form submission. If possible, perform a quick detach and leave
* the editor's GUI elements in place to avoid flashes or scrolling issues.
* @see Drupal.detachBehaviors
*/
Drupal.wysiwyg.editor.detach.none = function (context, params, trigger) {
if (trigger != 'serialize') {
var $wrapper = $('#' + params.field, context).parents('.form-textarea-wrapper:first');
$wrapper.removeOnce('textarea').removeClass('.resizable-textarea').removeClass('resizable')
.find('.grippie').remove();
}
};
/**
* Instance methods for plain text areas.
*/
Drupal.wysiwyg.editor.instance.none = {
insert: function(content) {
var editor = document.getElementById(this.field);
// IE support.
if (document.selection) {
editor.focus();
var sel = document.selection.createRange();
sel.text = content;
}
// Mozilla/Firefox/Netscape 7+ support.
else if (editor.selectionStart || editor.selectionStart == '0') {
var startPos = editor.selectionStart;
var endPos = editor.selectionEnd;
editor.value = editor.value.substring(0, startPos) + content + editor.value.substring(endPos, editor.value.length);
}
// Fallback, just add to the end of the content.
else {
editor.value += content;
}
},
setContent: function (content) {
$('#' + this.field).val(content);
},
getContent: function () {
return $('#' + this.field).val();
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
Drupal.wysiwyg.plugins.linebreaks = {
invoke: function(data, settings, instanceId) {
alert('This button does nothing, it belongs to the linebreaks plugin.');
},
attach: function(content, settings, instanceId) {
content = this.linebreaks_attach(content);
return content;
},
detach: function(content, settings, instanceId) {
content = this.linebreaks_detach(content);
return content;
},
// Clean up content for saving or turning off WYSIWYG.
linebreaks_detach : function(content) {
var blocklist1, blocklist2, blocklist3;
// Protect pre|script tags.
content = content.replace(/<(pre|script|li)[^>]*>[\s\S]+?<\/\1>/g, function(a) {
a = a.replace(/<br ?\/?>[\r\n]*/g, '<lb_temp>');
return a.replace(/<\/?p( [^>]*)?>[\r\n]*/g, '<lb_temp>');
});
// Pretty it up for the source editor.
blocklist1 = 'blockquote|ul|ol|li|hr|table|thead|tbody|tr|th|td|div|h[1-6]|p';
content = content.replace(new RegExp('\\s*</('+blocklist1+')>\\s*', 'mg'), '</$1>\n');
content = content.replace(new RegExp('\\s*<(('+blocklist1+')[^>]*)>', 'mg'), '\n<$1>');
// Mark </p> if it has any attributes.
content = content.replace(new RegExp('(<p [^>]+>.*?)</p>', 'mg'), '$1</p#>');
// Separate <div> containing <p>.
content = content.replace(new RegExp('<div([^>]*)>\\s*<p>', 'mgi'), '<div$1>\n');
// Remove <p> and <br />.
// content = content.replace(new RegExp('\\s*<p>', 'mgi'), '');
// content = content.replace(new RegExp('\\s*</p>\\s*', 'mgi'), '\n\n');
// content = content.replace(new RegExp('\\n\\s*\\n', 'mgi'), '\n\n');
// content = content.replace(new RegExp('\\s*<br ?/?>\\s*', 'gi'), '\n');
// Fix some block element newline issues.
content = content.replace(new RegExp('\\s*<div', 'mg'), '\n<div');
content = content.replace(new RegExp('</div>\\s*', 'mg'), '</div>\n');
content = content.replace(new RegExp('\\s*\\[caption([^\\[]+)\\[/caption\\]\\s*', 'gi'), '\n\n[caption$1[/caption]\n\n');
content = content.replace(new RegExp('caption\\]\\n\\n+\\[caption', 'g'), 'caption]\n\n[caption');
// Block elements which look nicer with two newlines before and after.
blocklist2 = 'blockquote|ul|ol|table|h[1-6]|pre';
content = content.replace(new RegExp('\\s*<(('+blocklist2+') ?[^>]*)\\s*>', 'mg'), '\n<$1>');
content = content.replace(new RegExp('\\s*</('+blocklist2+')>\\s*', 'mg'), '</$1>\n');
content = content.replace(new RegExp('\\s*<(hr ?[^>]*)\\s*>', 'mg'), '\n<$1>');
// Block elements which look nicer with one newline before and after.
blocklist3 = 'li|thead|tr|th|td';
content = content.replace(new RegExp('\\s*<(('+blocklist3+') ?[^>]*)\\s*>', 'mg'), '\n<$1>');
content = content.replace(new RegExp('\\s*</('+blocklist3+')>\\s*', 'mg'), '</$1>\n');
// content = content.replace(new RegExp('<li([^>]*)>', 'g'), '\t<li$1>');
// Handle <object> and <embed> tags.
if (content.indexOf('<object') != -1) {
content = content.replace(/<object[\s\S]+?<\/object>/g, function(a){
return a.replace(/[\r\n]+/g, '');
});
}
if (content.indexOf('<embed') != -1) {
content = content.replace(/<embed[\s\S]+?<\/embed>/g, function(a){
return a.replace(/[\r\n]+/g, '');
});
}
// Unmark special paragraph closing tags.
content = content.replace(new RegExp('</p#>', 'g'), '</p>\n');
content = content.replace(new RegExp('\\s*(<p [^>]+>.*</p>)', 'mg'), '\n$1');
// Trim whitespace.
content = content.replace(new RegExp('^\\s*', ''), '');
content = content.replace(new RegExp('[\\s\\u00a0]*$', ''), '');
// Put back linebreaks in <pre> and <script> tags.
content = content.replace(/<lb_temp>/g, '\n');
//content = content.replace(new RegExp("<p><\!--(.+?)--></p>", 'gi'), "<!--$1-->");
return content;
},
// Prepare the content for the WYSIWYG Editor.
linebreaks_attach : function(content) {
var blocklist = 'table|thead|tfoot|caption|col|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|hr|pre|select|form|blockquote|address|math|p|h[1-6]';
var allowedlist = 'strong|em|span|u|a';
if (content == "") {
return content;
}
// Handle <object> and <embed> tags.
if (content.indexOf('<object') != -1) {
content = content.replace(/<object[\s\S]+?<\/object>/g, function(a) {
return a.replace(/[\r\n]+/g, '');
});
}
if (content.indexOf('<embed') != -1) {
content = content.replace(/<embed[\s\S]+?<\/embed>/g, function(a) {
return a.replace(/[\r\n]+/g, '');
});
}
content = content.replace(/<[^<>]+>/g, function(a) {
return a.replace(/[\r\n]+/g, ' ');
});
content = content + "\n";
content = content.replace(new RegExp('<br />\\s*<br />', 'gi'), "\n");
content = content.replace(new RegExp('(<(?:'+blocklist+')[^>]*>)', 'gi'), "\n$1");
content = content.replace(new RegExp('(</(?:'+blocklist+')>)', 'gi'), "$1\n");
//content = content.replace(new RegExp("\\r\\n|\\r", 'g'), "\n");
//content = content.replace(new RegExp("\\n\\s*\\n+", 'g'), "\n");
//content = content.replace(new RegExp('([\\s\\S]+?)\\n\\n', 'mg'), "<p>$1</p>\n");
content = content.replace(new RegExp('<p>\\s*?</p>', 'gi'), '');
content = content.replace(new RegExp('<p>\\s*(</?(?:'+blocklist+')[^>]*>)\\s*</p>', 'gi'), "$1");
content = content.replace(new RegExp("<p>(<li.+?)</p>", 'gi'), "$1");
content = content.replace(new RegExp('<p>\\s*<blockquote([^>]*)>', 'gi'), "<blockquote$1><p>");
content = content.replace(new RegExp('</blockquote>\\s*</p>', 'gi'), '</p></blockquote>');
content = content.replace(new RegExp('<p>\\s*(</?(?:'+blocklist+')[^>]*>)', 'gi'), "$1");
content = content.replace(new RegExp('(</?(?:'+blocklist+')[^>]*>)\\s*</p>', 'gi'), "$1");
//content = content.replace(new RegExp('([^>])\\s*\\n', 'gi'), "$1<br />\n");
content = content.replace(new RegExp('<\\/('+allowedlist+')>\\s*\\n', 'gi'), "</$1><br />\n");
content = content.replace(new RegExp('(</?(?:'+blocklist+')[^>]*>)\\s*<br />', 'gi'), "$1");
content = content.replace(new RegExp('<br />(\\s*</?(?:p|li|div|dl|dd|dt|th|pre|td|ul|ol)>)', 'gi'), '$1');
content = content.replace(new RegExp('(?:<p>|<br ?/?>)*\\s*\\[caption([^\\[]+)\\[/caption\\]\\s*(?:</p>|<br ?/?>)*', 'gi'), '[caption$1[/caption]');
// Fix <pre> and <script> tags.
content = content.replace(/<(pre|script|li)[^>]*>[\s\S]+?<\/\1>/g, function(a) {
a = a.replace(/<br ?\/?>[\r\n]*/g, '\n');
return a.replace(/<\/?p( [^>]*)?>[\r\n]*/g, '\n');
});
return content;
}
};
;/*})'"*/
;/*})'"*/
(function ($) {
// @todo Array syntax required; 'break' is a predefined token in JavaScript.
Drupal.wysiwyg.plugins['break'] = {
/**
* Return whether the passed node belongs to this plugin.
*/
isNode: function(node) {
return ($(node).is('img.wysiwyg-break'));
},
/**
* Execute the button.
*/
invoke: function(data, settings, instanceId) {
if (data.format == 'html') {
// Prevent duplicating a teaser break.
if ($(data.node).is('img.wysiwyg-break')) {
return;
}
var content = this._getPlaceholder(settings);
}
else {
// Prevent duplicating a teaser break.
// @todo data.content is the selection only; needs access to complete content.
if (data.content.match(/<!--break-->/)) {
return;
}
var content = '<!--break-->';
}
if (typeof content != 'undefined') {
Drupal.wysiwyg.instances[instanceId].insert(content);
}
},
/**
* Replace all <!--break--> tags with images.
*/
attach: function(content, settings, instanceId) {
content = content.replace(/<!--break-->/g, this._getPlaceholder(settings));
return content;
},
/**
* Replace images with <!--break--> tags in content upon detaching editor.
*/
detach: function(content, settings, instanceId) {
var $content = $('<div>' + content + '</div>'); // No .outerHTML() in jQuery :(
// #404532: document.createComment() required or IE will strip the comment.
// #474908: IE 8 breaks when using jQuery methods to replace the elements.
// @todo Add a generic implementation for all Drupal plugins for this.
$.each($('img.wysiwyg-break', $content), function (i, elem) {
elem.parentNode.insertBefore(document.createComment('break'), elem);
elem.parentNode.removeChild(elem);
});
return $content.html();
},
/**
* Helper function to return a HTML placeholder.
*/
_getPlaceholder: function (settings) {
return '<img src="' + settings.path + '/images/spacer.gif" alt="&lt;--break-&gt;" title="&lt;--break--&gt;" class="wysiwyg-break drupal-content" />';
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function ($) {
/**
* Attaches the autocomplete behavior to all required fields.
*/
Drupal.behaviors.autocomplete = {
attach: function (context, settings) {
var acdb = [];
$('input.autocomplete', context).once('autocomplete', function () {
var uri = this.value;
if (!acdb[uri]) {
acdb[uri] = new Drupal.ACDB(uri);
}
var $input = $('#' + this.id.substr(0, this.id.length - 13))
.attr('autocomplete', 'OFF')
.attr('aria-autocomplete', 'list');
$($input[0].form).submit(Drupal.autocompleteSubmit);
$input.parent()
.attr('role', 'application')
.append($('<span class="element-invisible" aria-live="assertive" aria-atomic="true"></span>')
.attr('id', $input.attr('id') + '-autocomplete-aria-live')
);
new Drupal.jsAC($input, acdb[uri]);
});
}
};
/**
* Prevents the form from submitting if the suggestions popup is open
* and closes the suggestions popup when doing so.
*/
Drupal.autocompleteSubmit = function () {
return $('#autocomplete').each(function () {
this.owner.hidePopup();
}).length == 0;
};
/**
* An AutoComplete object.
*/
Drupal.jsAC = function ($input, db) {
var ac = this;
this.input = $input[0];
this.ariaLive = $('#' + this.input.id + '-autocomplete-aria-live');
this.db = db;
$input
.keydown(function (event) { return ac.onkeydown(this, event); })
.keyup(function (event) { ac.onkeyup(this, event); })
.blur(function () { ac.hidePopup(); ac.db.cancel(); });
};
/**
* Handler for the "keydown" event.
*/
Drupal.jsAC.prototype.onkeydown = function (input, e) {
if (!e) {
e = window.event;
}
switch (e.keyCode) {
case 40: // down arrow.
this.selectDown();
return false;
case 38: // up arrow.
this.selectUp();
return false;
default: // All other keys.
return true;
}
};
/**
* Handler for the "keyup" event.
*/
Drupal.jsAC.prototype.onkeyup = function (input, e) {
if (!e) {
e = window.event;
}
switch (e.keyCode) {
case 16: // Shift.
case 17: // Ctrl.
case 18: // Alt.
case 20: // Caps lock.
case 33: // Page up.
case 34: // Page down.
case 35: // End.
case 36: // Home.
case 37: // Left arrow.
case 38: // Up arrow.
case 39: // Right arrow.
case 40: // Down arrow.
return true;
case 9: // Tab.
case 13: // Enter.
case 27: // Esc.
this.hidePopup(e.keyCode);
return true;
default: // All other keys.
if (input.value.length > 0 && !input.readOnly) {
this.populatePopup();
}
else {
this.hidePopup(e.keyCode);
}
return true;
}
};
/**
* Puts the currently highlighted suggestion into the autocomplete field.
*/
Drupal.jsAC.prototype.select = function (node) {
this.input.value = $(node).data('autocompleteValue');
$(this.input).trigger('autocompleteSelect', [node]);
};
/**
* Highlights the next suggestion.
*/
Drupal.jsAC.prototype.selectDown = function () {
if (this.selected && this.selected.nextSibling) {
this.highlight(this.selected.nextSibling);
}
else if (this.popup) {
var lis = $('li', this.popup);
if (lis.length > 0) {
this.highlight(lis.get(0));
}
}
};
/**
* Highlights the previous suggestion.
*/
Drupal.jsAC.prototype.selectUp = function () {
if (this.selected && this.selected.previousSibling) {
this.highlight(this.selected.previousSibling);
}
};
/**
* Highlights a suggestion.
*/
Drupal.jsAC.prototype.highlight = function (node) {
if (this.selected) {
$(this.selected).removeClass('selected');
}
$(node).addClass('selected');
this.selected = node;
$(this.ariaLive).html($(this.selected).html());
};
/**
* Unhighlights a suggestion.
*/
Drupal.jsAC.prototype.unhighlight = function (node) {
$(node).removeClass('selected');
this.selected = false;
$(this.ariaLive).empty();
};
/**
* Hides the autocomplete suggestions.
*/
Drupal.jsAC.prototype.hidePopup = function (keycode) {
// Select item if the right key or mousebutton was pressed.
if (this.selected && ((keycode && keycode != 46 && keycode != 8 && keycode != 27) || !keycode)) {
this.select(this.selected);
}
// Hide popup.
var popup = this.popup;
if (popup) {
this.popup = null;
$(popup).fadeOut('fast', function () { $(popup).remove(); });
}
this.selected = false;
$(this.ariaLive).empty();
};
/**
* Positions the suggestions popup and starts a search.
*/
Drupal.jsAC.prototype.populatePopup = function () {
var $input = $(this.input);
var position = $input.position();
// Show popup.
if (this.popup) {
$(this.popup).remove();
}
this.selected = false;
this.popup = $('<div id="autocomplete"></div>')[0];
this.popup.owner = this;
$(this.popup).css({
top: parseInt(position.top + this.input.offsetHeight, 10) + 'px',
left: parseInt(position.left, 10) + 'px',
width: $input.innerWidth() + 'px',
display: 'none'
});
$input.before(this.popup);
// Do search.
this.db.owner = this;
this.db.search(this.input.value);
};
/**
* Fills the suggestion popup with any matches received.
*/
Drupal.jsAC.prototype.found = function (matches) {
// If no value in the textfield, do not show the popup.
if (!this.input.value.length) {
return false;
}
// Prepare matches.
var ul = $('<ul></ul>');
var ac = this;
for (key in matches) {
$('<li></li>')
.html($('<div></div>').html(matches[key]))
.mousedown(function () { ac.hidePopup(this); })
.mouseover(function () { ac.highlight(this); })
.mouseout(function () { ac.unhighlight(this); })
.data('autocompleteValue', key)
.appendTo(ul);
}
// Show popup with matches, if any.
if (this.popup) {
if (ul.children().length) {
$(this.popup).empty().append(ul).show();
$(this.ariaLive).html(Drupal.t('Autocomplete popup'));
}
else {
$(this.popup).css({ visibility: 'hidden' });
this.hidePopup();
}
}
};
Drupal.jsAC.prototype.setStatus = function (status) {
switch (status) {
case 'begin':
$(this.input).addClass('throbbing');
$(this.ariaLive).html(Drupal.t('Searching for matches...'));
break;
case 'cancel':
case 'error':
case 'found':
$(this.input).removeClass('throbbing');
break;
}
};
/**
* An AutoComplete DataBase object.
*/
Drupal.ACDB = function (uri) {
this.uri = uri;
this.delay = 300;
this.cache = {};
};
/**
* Performs a cached and delayed search.
*/
Drupal.ACDB.prototype.search = function (searchString) {
var db = this;
this.searchString = searchString;
// See if this string needs to be searched for anyway. The pattern ../ is
// stripped since it may be misinterpreted by the browser.
searchString = searchString.replace(/^\s+|\.{2,}\/|\s+$/g, '');
// Skip empty search strings, or search strings ending with a comma, since
// that is the separator between search terms.
if (searchString.length <= 0 ||
searchString.charAt(searchString.length - 1) == ',') {
return;
}
// See if this key has been searched for before.
if (this.cache[searchString]) {
return this.owner.found(this.cache[searchString]);
}
// Initiate delayed search.
if (this.timer) {
clearTimeout(this.timer);
}
this.timer = setTimeout(function () {
db.owner.setStatus('begin');
// Ajax GET request for autocompletion. We use Drupal.encodePath instead of
// encodeURIComponent to allow autocomplete search terms to contain slashes.
$.ajax({
type: 'GET',
url: Drupal.sanitizeAjaxUrl(db.uri + '/' + Drupal.encodePath(searchString)),
dataType: 'json',
jsonp: false,
success: function (matches) {
if (typeof matches.status == 'undefined' || matches.status != 0) {
db.cache[searchString] = matches;
// Verify if these are still the matches the user wants to see.
if (db.searchString == searchString) {
db.owner.found(matches);
}
db.owner.setStatus('found');
}
},
error: function (xmlhttp) {
Drupal.displayAjaxError(Drupal.ajaxError(xmlhttp, db.uri));
}
});
}, this.delay);
};
/**
* Cancels the current autocomplete request.
*/
Drupal.ACDB.prototype.cancel = function () {
if (this.owner) this.owner.setStatus('cancel');
if (this.timer) clearTimeout(this.timer);
this.searchString = '';
};
})(jQuery);
;/*})'"*/
;/*})'"*/
(function ($) {
/**
* Automatically display the guidelines of the selected text format.
*/
Drupal.behaviors.filterGuidelines = {
attach: function (context) {
$('.filter-guidelines', context).once('filter-guidelines')
.find(':header').hide()
.closest('.filter-wrapper').find('select.filter-list')
.bind('change', function () {
$(this).closest('.filter-wrapper')
.find('.filter-guidelines-item').hide()
.siblings('.filter-guidelines-' + this.value).show();
})
.change();
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
/**
* @file
* Triggers on changes of input formats to render the appropriate
* editor from bueditor.
*/
(function($){
var instances = {}, ckBound = false;
function TextareaToBue(id) {
var parent = $("#" + id).parents('.form-item');
var bue = $(".bue-ui", parent);
return bue.length ? bue : null;
}
Drupal.behaviors.BUEditorPlus = {
attach: function (context, settings) {
if (BUE && BUE.behavior){
BUE.behavior(context, settings); // BUEditor < 7.x-1.5
} else {
Drupal.behaviors.BUE.attach(context, settings); // BUEditor >= 7.x-1.5
}
for (var id in Drupal.settings.BUEPlus.preset){
if (!instances[id]) {
// Create a storage for our BEEditorPlus instances. This is for future
// use only as of right now.
instances[id] = new Drupal.bueditorPlus(id);
}
}
if (!ckBound) {
ckBound = true;
if (typeof CKEDITOR !== 'undefined') {
CKEDITOR.on('instanceDestroyed', function(evt){
var bue = TextareaToBue(evt.editor.name);
if (bue) {
bue.show().css('visibility', 'visible');
}
});
CKEDITOR.on('instanceReady', function(evt){
var bue = TextareaToBue(evt.editor.name);
if (bue) {
bue.hide().css('visibility', 'hidden');
}
});
}
}
}
};
Drupal.bueditorPlus = function (id) {
this.id = id;
this.body = $("#" + id + "-value");
if (this.body.hasClass('bupProcessed')){
return;
}
this.body.addClass('bupProcessed');
this.summary = $("#" + id + "-summary");
this.format = $("#" + id + "-format .filter-list");
this.currentEID = 0;
this.editor = null;
this.summaryEditor = null;
this.pset = Drupal.settings.BUEPlus.preset[id];
var self = this;
if (this.format.length > 0){
this.format.bind('change', function(){
var selectedFormat = $('option:selected', this).val();
// Only switch if it isn't the same editor instance
if (self.pset[selectedFormat] != self.currentEID){
self.currentEID = self.pset[selectedFormat];
if (self.editor){
self.editor.UI.remove();
delete(BUE.instances[self.body.get(0).bue.index]);
delete(self.body.get(0).bue);
}
if (self.summaryEditor){
self.summaryEditor.UI.remove();
delete(BUE.instances[self.summary.get(0).bue.index]);
delete(self.summary.get(0).bue);
}
self.editor = null;
self.summaryEditor = null;
if (self.pset[selectedFormat]){
BUE.preset[id] = self.pset[selectedFormat];
self.body.show().css('visibility', 'visible');
self.editor = BUE.processTextarea(self.body.get(0), self.pset[selectedFormat]);
if (self.summary.length){
self.summaryEditor = BUE.processTextarea(self.summary.get(0), self.pset[selectedFormat]);
}
}
}
}).change();
} else {
// No format selector so we use the default, if selected.
if (Drupal.settings.BUEPlus.defaults[id]){
var format = self.pset[Drupal.settings.BUEPlus.defaults[id]];
BUE.preset[id] = format;
self.body.show().css('visibility', 'visible');
self.editor = BUE.processTextarea(self.body.get(0), format);
if (self.summary.length){
self.summaryEditor = BUE.processTextarea(self.summary.get(0), format);
}
}
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
jQuery(document).ready(function ($) {$.fn.dataImg=function(a){function b(a){var b=$(window).width();return a.data(b>d.med?"lrg":b<=d.med&&b>d.sml?"med":"sml")}function c(){e.each(function(){var a=$(this),c=b(a);void 0!=c&&(a.is("img")?a.attr("src",c):a.css("background-image","url("+c+")"))})}var d=$.extend({sml:400,med:800,lrg:1e3,resize:!1},a),e=$(this);c(),1==d.resize&&$(window).resize(function(){c()})};});
;/*})'"*/
;/*})'"*/
(function ($) {
$('#edit-field-background .form-textarea-wrapper').attr('id','addbio');
$.fn.DeletePinModal = function(page) {
$('body #nemodalclick').trigger('click');
};
$.fn.ClosePinModal = function(page) {
$('body #deletepin-modal .close').trigger('click');
};
$.fn.IntsysAddComment = function(selector,id) {
//window.location.hash=selector;
var win = $(window);var marker = $('#'+selector);
if((win.scrollTop() + win.height() < marker.offset().top) || (win.scrollTop() + win.height() < marker.offset().top + marker.height())) {
IntsysCommentAnimation(marker,470);
}
for(var instanceName in CKEDITOR.instances){CKEDITOR.instances[instanceName].setData('');}
$('#edit-comment-body-und-0-value--'+id).val('');
};
$.fn.IntsysAddtoComment = function(selector,id) {
var win = $(window);var marker = $('#'+selector);
if((win.scrollTop() + win.height() < marker.offset().top) || (win.scrollTop() + win.height() < marker.offset().top + marker.height())) {
IntsysCommentAnimation(marker,470);
}
for(var instanceName in CKEDITOR.instances){CKEDITOR.instances[instanceName].setData('');}
$('#edit-comment-body-und-0-value--'+id).val('');
};
$.fn.IntsysStopAnimation = function(id) {
$("#edit-submit--"+id).show();$("#edit-submit-an-"+id).hide();$("#edit-actions--"+id).removeClass("pointer-events-none");
};
function IntsysCommentAnimation(marker,topmargin)
{
/*if ($(marker).length) {
$("html, body").stop().animate({"scrollTop": $(marker).offset().top - topmargin}, 1000, "swing", function () {
var bgcolor=$(marker).css('backgroundColor');
$(marker).animate({backgroundColor:"#bae6e2",opacity:0.8},300,"swing",function(){setTimeout(
function(){$(marker).animate({backgroundColor:bgcolor,opacity:1},300)}, 1000);});});
}*/
if ($(marker).length) {
$("html, body").stop().animate({"scrollTop": $(marker).offset().top - topmargin}, 1000, "swing", function () {
let whatsnew = getUrlParameter('whatsnew');
if(marker!=('#'+whatsnew)) {
$(marker).addClass('whatsnew');
setTimeout(function(){$(marker).addClass('whatsnewhide');}, 3000);
setTimeout(function(){$(marker).removeClass('whatsnewhide').removeClass('whatsnew');}, 4000);
}
});
}
}
if (location.hash!="") {
$("html, body").stop().animate(IntsysCommentAnimation(location.hash,150));}
$(window).bind('hashchange', function() {
if (location.hash!="") {
$("html, body").stop(); IntsysCommentAnimation(location.hash,150);}
});
'use strict';
Drupal.behaviors.ajaxConfirmLink = {
attach: function (context, settings) {
if ($(window).width() <= '1200')
{
$('pre').each(function(i, obj) {
if(obj.parentNode.offsetWidth>0) $(obj).width(obj.parentNode.offsetWidth-11).css("max-width","100%");
});
}
$(document).on('click',"#admin-menu-start", function() {$("#admin-menu").slideDown(200); });
$(document).on('click',function(e){if (!jQuery(e.target).closest('#admin-menu,#admin-menu-start').length){$('#admin-menu').hide();}e.stopPropagation();});
$('.use-ajax-confirm').filter('a').once('use-ajax-confirm').on('click', function (event) {
var $this = $(this);
// Allow to provide confirmation message in
// data-use-ajax-confirm-message element attribute.
var message = $this.data('use-ajax-confirm-message') || Drupal.t('Are you sure you want to do this?');
if (confirm(message)) {
// Create ajax event only if action was confirmed.
var id = $this.attr('id');
// Generate unique id, if the element does not already have one.
if (!id || id.trim() == '') {
id = 'use-ajax-confirm' + new Date().getTime() + Math.floor(Math.random() * 1000);
$this.attr('id', id);
}
Drupal.ajax[id] = new Drupal.ajax(id, this, {
// 'nojs' to 'ajax' replacement in path performed by Drupal.ajax().
url: $this.attr('href'),
event: 'load.use-ajax-confirm'
});
$this.trigger('load.use-ajax-confirm');
}
return false;
});
if ($(window).width() >= '768') { $("pre code").once().after(function() {
let button=$(this).parent().attr('lang');
if (button!='' && button!=undefined) button='<span>'+button+'</span>';
else button='';
return '<div class="codecopy" title="'+Drupal.t('Copy')+'">'+button+'</div><div class="codecopyok" title="'+Drupal.t('Copied')+'">'+button+'</div>';}) }
else {$("pre code").once().after(function() {
let button=$(this).parent().attr('lang');
if (button!='' && button!=undefined) button='<span>'+button+'</span>';
else button='';
return '<div class="codecopy">'+button+'</div><div class="codecopyok" title="'+Drupal.t('Copied')+'">'+button+'</div>';});}
if ($(window).width() <= '768') {
$('pre').on("click",function(){
if ($(this).children('.codecopyok').is(':hidden')) $(this).children('.codecopy').show();
});
};
$('pre .codecopy').on("click",function(){
var element=$(this);
element.hide();
$('pre .codecopy').hide();
element.siblings('.codecopyok').show();
setTimeout(function(){
show_isc_tooltipmob(element.siblings('.codecopyok'));
}, 100);
setTimeout(function(){
$('.isc_tooltip').remove();
$('pre .codecopy').css({"display":""});
$('pre .codecopyok').hide();
}, 2000);
});
$(".comment-moder-action-more").once().on('click', function() {
$(this).children(".comment-moders-action").toggle();
});
$(".comment").mouseout(function() {
if ($('.comment:hover').length == 0) $(".comment-moders-action").hide();
});
$(document).on('click',function(e){if (!jQuery(e.target).closest('.link-wechat').length){$('.qrs_wechat').hide();}e.stopPropagation();
});
$(document).on('click',function(e){if (!jQuery(e.target).closest('.comment-moders-action,.comment-moder-action-more').length){$('.comment-moders-action').hide();}e.stopPropagation();
});
$("a[highlightingmenu=highlightingmenu]").once().addClass('highlightingmenu');
if($('.highlightingmenu').length>0) $(".navbar-toggle,#navmenubutton").addClass('highlightingmenu');
$("#navmenubutton").once().on('click', function() {shownavmenu(); });
function shownavmenu()
{
$('.logo,#navmenubutton,.block-intsys-search-area').hide();
$('.block-tb-megamenu,#navmenuclose').css('display', 'inline-block');
}
$("#navmenuclose").once().on('click', function() { hidenavmenu(); });
function hidenavmenu()
{
$('.logo,#navmenubutton').css('display', 'inline-block');
$('.block-intsys-search-area').css('display', 'flex');
$('.block-tb-megamenu,#navmenuclose').hide();
}
$(".pagination a,iframe,textarea,input[type=text],.language-switcher-locale-url a").removeAttr('title');
$('[title]').each(function() {
if($(this).attr('title')=='') $(this).removeAttr('title');
});
$('[title]').each(function() {
var $t = $(this);
$t.attr({istitle: $t.attr('title')}).removeAttr('title');
});
$('.cke_button,.cke_combo_button').each(function() { var $t = $(this);
$t.attr({
title: $t.attr('istitle')
}).removeAttr('istitle');});
if ($(window).width() >= '768') {
$('[istitle]').once().hover(function(){
var element=$(this);
timerId = setTimeout(function() {
show_isc_tooltip(element);
}, 100);
},function(){
clearTimeout(timerId);
});
$('[istitle]').on('mouseleave',function(){
$('.isc_tooltip').remove();
});
var pathname=window.location.pathname.split( '/' );
if(pathname[1]=='post') {
$('article.comment').each(function() {
if($(this).attr('pid')!=0)
{
var nameuser=$('#comment-'+$(this).attr('pid')+' .default-submitted a.username').text();
if ($('[upid='+$(this).attr('pid')+'-'+$(this).attr('cid')+'] small').html().indexOf('·') > -1) {
$('[upid='+$(this).attr('pid')+'-'+$(this).attr('cid')+'] small').html('<span class="commentreplyed" href="#comment-'+$(this).attr('pid')+'">'+ $('[upid='+$(this).attr('pid')+'-'+$(this).attr('cid')+'] small').text().replace("· ","")+' to '+nameuser+'</span>');
}}
});
$('.commentreplyed').on('click',function(){ IntsysCommentAnimation($(this).attr('href'),150);});
}
}
$(window).scroll( function() {
if($(this).scrollTop() > document.documentElement.clientHeight) {
$('#toTop').fadeIn();
} else {
$('#toTop').fadeOut();
}
});
$('#toTop').click(function() {
$('body,html').animate({scrollTop:0},800);
});
function show_isc_tooltip(e)
{
if(e.attr=='' || e.attr==undefined || $('.isc_tooltip').length) return;
if (e.is(':hover')) {
var position=e.position();
var width = e.outerWidth(true);
var height = e.outerHeight();
var top=position.top+height+5;
var left=position.left+(width/2)+5;
if(e.parents('pre').length == 1) {
//left=left+7;
}
e.after('<div class="isc_tooltip" style="top:'+top+'px;left: '+left+'px;"><div class="isc_tooltip_cnt">'+e.attr('istitle')+'</div></div>');
$('.isc_tooltip').fadeIn(100);
}
}
function show_isc_tooltipmob(e)
{
var position=e.position();
var width = e.outerWidth(true);
var height = e.outerHeight();
var top=position.top+height+5;
var left=position.left+(width/2)-10;
if(e.parents('pre').length == 1) {
left=left+10;
}
e.after('<div class="isc_tooltip" style="top:'+top+'px;left: '+left+'px;"><div class="isc_tooltip_cnt">'+e.attr('istitle')+'</div></div>');
$('.isc_tooltip').fadeIn(100);
}
$("#edit-delete").click( function() {
$("#edit-delete").attr("usesend","verify");
$("#edit-delete").click();
});
///////////
$("#privatemsg-list").on("submit", function(e) {
// e.preventDefault();
if($("#edit-delete").attr("usesend")=='verify') {
//if($(e.originalEvent.submitter).attr('use')=='no') {
e.preventDefault();
if(!e.isDefaultPrevented()){
e.returnValue = false;
}
return false;
}
return true;
});
////////
$("#deletemodalpmurl").click( function() {
$("#edit-delete").attr("usesend","yes");
$("#privatemsg-list").submit();
});
///////////////
$("#nodeletemodalpmurl").click( function() {
$("#edit-delete").attr("usesend","no");
});
$("#nodeletemodalpmcloseurl").click( function() {
$("#edit-delete").attr("usesend","no");
});
//////////
$("#deletemodalpmsurl").click( function() {
$.ajax({
url: "/messages/delete/"+$('#pmdel').attr("data"),
method: 'post',
dataType: 'html',
data: {},
success: function(data){
window.location.replace("/messages");
}});
});
$(".deletemodalcomment").click( function() {
$("#deletemodalcommenturl").attr("href",$(this).attr("cid"));
});
$("#deletemodalcommenturl").click( function() {
$.ajax("/ajax/content/delete/comment/"+$(this).attr("href"));
$('[data-article-id="'+$(this).attr("href")+'"]').remove();
});
$(".deletemodalnode").click( function() {
$("#deletemodalnodeurl").attr("href",$(this).attr("nid"));
});
$("#deletemodalnodeurl").click( function() {
$('#editing-actions,#editing-prevactions').show();
$.ajax({
url: "/ajax/post/delete/"+$(this).attr("href"),
method: 'post',
dataType: 'html',
data: {},
success: function(data){
const url = new URL(window.location.href);
let arr = url.pathname.split('/');
if(arr[1]=='post' || arr[1]=='node') window.location.replace("/");
else location.reload();
}});
});
$(".unflag-action").click( function() {
$("#unspammodalnodeurl,#unspammodalcommenturl").attr("href",$(this).attr("unflag"));
//$('.isc_tooltip .isc_tooltip_cnt').html(Drupal.t('Copied'));
});
$(".codecopy").click( function(e) {
var text=$($(this).parent().prevObject.context.previousElementSibling).text();
copyToClipboardCode(text);
});
$(".filters").click( function() {
var idfilter="#"+$(this).attr('id')+" .filtersub";
var filterstatus=0;
if($(idfilter).is(":hidden")) filterstatus=1;
$(".filtersub").addClass("hidden");
if(filterstatus==1) $(idfilter).removeClass("hidden");
else $(idfilter).addClass("hidden");
});
$(document).click(function(event) {
if ($(event.target).closest(".filtersub").length || $(event.target).closest(".filters").length) return;
$(".filtersub").addClass("hidden");
event.stopPropagation();
});
$(".sorting").on('click', function() {
var idfilter="#"+$(this).attr('id')+" .sortingsub";
var sortingtatus=0;
if($(idfilter).is(":hidden")) sortingtatus=1;
$(".sortingsub").addClass("hidden");
if(sortingtatus==1) $(idfilter).removeClass("hidden");
else $(idfilter).addClass("hidden");
});
$(document).click(function(event) {
if ($(event.target).closest(".sortingsub").length || $(event.target).closest(".sorting").length) return;
$(".sortingsub").addClass("hidden");
event.stopPropagation();
});
if ($(window).width() <= '768') {
$(document).click(function(event) {
if ($(event.target).closest(".navbar-collapse").length || $(event.target).closest(".navbar-toggle").length) return;
if ($('.navbar-collapse.collapse.in').length) $(".navbar-toggle-close").click();
event.stopPropagation();
}); }
$(".comment-form").once().on("submit", function(form){
var id=form.target.attributes.fid.value;
var pid=form.target.attributes.pid.value;
var nid=form.target.attributes.nid.value;
var cid=form.target.attributes.cid.value;
var medialist=$(".media-list").length;
var op=$("#edit-submit--"+id).val();
var indented=$('article[cid="'+pid+'"] ~ div.indented').length;
$("#edit-submit--"+id).hide();$("#edit-submit-an-"+id).show();
$("#edit-actions--"+id).addClass("pointer-events-none");
for(var instanceName in CKEDITOR.instances){
CKEDITOR.instances[instanceName].updateElement();
}
var text=$("#edit-comment-body-und-0-value--"+id).val();
var format=$("#comment-body-add-more-wrapper--"+id+" select").val();
var settings = {url : "/ajax/checkcomment",submit : {text:text,id:id,pid:pid,format:format,nid:nid,op:op,cid:cid,async:false,medialist:medialist,indented:indented}};
var ajax = new Drupal.ajax(false, false, settings);
ajax.eventResponse(ajax, {});
return false;
});
$('.link-clipboard').click(function(e) {
$('.isc_tooltip_cnt').text(Drupal.t('Copied'));
copyToClipboardC($(this).attr('data-clipboard-text'));
});
function copyToClipboardB(text) {
var copytext = document.createElement('input');copytext.value =text;document.body.appendChild(copytext);copytext.select();document.execCommand('copy');document.body.removeChild(copytext);
}
async function copyToClipboardC(text) {
try {
await navigator.clipboard.writeText(text);
//console.log('Copied');
} catch (err) {
//console.error('error: ', err);
}
}
}
};
$("pre").each(function (index, el){
if ($(el).has('code').length) {
$(el).css(({"overflow":"inherit"}));
}
});
$("#block-system-main").on("mouseover",".indented", function(e){if(e.target.className=="indented") {$(this).addClass("indentedhover");}});
$("#block-system-main").on("mouseout",".indented", function(e){if(e.target.className=="indented indentedhover") {$(this).removeClass("indentedhover");}});
$("#block-system-main").once().on("click",".indented", function(e){if(e.target.className=="indented indentedhover") {var time=$.now();$(this).attr("id", "hide"+time);
var numberreplyes=$("#hide"+time+" article").size();
$(this).before("<span class=\"showreplies\" onclick=\"event.stopPropagation();jQuery(this).hide();jQuery('#hide"+time+"').slideDown(500);\">"+Drupal.t((numberreplyes > 1) ? 'show !number replies' : 'show 1 reply', {'!number': numberreplyes})+"</span>");$(this).slideUp(500);event.stopPropagation();}});
}(jQuery));
function copyToClipboardCode(text) {
var copytext = document.createElement('textarea');copytext.value =text;document.body.appendChild(copytext);copytext.select();document.execCommand('copy');document.body.removeChild(copytext);
}
var getUrlParameter = function getUrlParameter(sParam) {
var sPageURL = decodeURIComponent(window.location.search.substring(1)),
sURLVariables = sPageURL.split('&'),
sParameterName,
i;
for (i = 0; i < sURLVariables.length; i++) {
sParameterName = sURLVariables[i].split('=');
if (sParameterName[0] === sParam) {
return sParameterName[1] === undefined ? true : sParameterName[1];
}
}
};
;/*})'"*/
;/*})'"*/
jQuery(function() {
jQuery('.spoiler-title').click(function() {
jQuery(this)
.children()
.first()
.toggleClass('show-icon')
.toggleClass('hide-icon');
jQuery(this)
.parent().children().last().toggle();
});
});
;/*})'"*/
;/*})'"*/
(function ($) {
Drupal.behaviors.intersys_post = {
'attach': function (context) {
$('.close-form:not(.processed)', context)
.addClass('processed')
.bind('click', function () {
$('[data-form="' + $(this).attr('data-selector') + '"]').hide();
});
$('.ajax-form:not(.processed)', context)
.addClass('processed')
.bind('click', function () {
$('[data-group="' + $(this).attr('data-form-group') + '"]').hide();
var form = $('[data-form="' + $(this).attr('data-action') + '-' + $(this).attr('data-id') + '"]');
if (form.length) {
form.show();
}
else {
var url = '/ajax/post/' + $(this).attr('data-action') + '/' + $(this).attr('data-id');
var ajax = new Drupal.ajax(false, false, {url: url});
ajax.eventResponse(ajax, {});
}
return false;
});
}
};
//Mark right answer
Drupal.ajax.prototype.commands.update_answers = function (ajax, response, status) {
$('.mark-right-answer').each(function () {
var current_answer = $(this).attr('data-answer-id');
console.log(current_answer);
console.log(response);
if (response.right_answer === current_answer) {
$('#answer-' + current_answer).addClass('border-green');
$(this).removeClass('no-marked-as-right').addClass('marked-as-right');
$(this).html(response.text.is_right);
$(this).attr('title', response.text.is_right);
}
else {
$('#answer-' + current_answer).removeClass('border-green');
$(this).removeClass('marked-as-right').addClass('no-marked-as-right');
$(this).html(response.text.is_wrong);
$(this).attr('title', response.text.is_wrong);
}
});
};
})(jQuery);
;/*})'"*/
;/*})'"*/
jQuery(document).ready(function () {
if (jQuery('#edit-body, #edit-comment-body').length == 2) {
window.onbeforeunload = function(e) {
e = e || window.event;
if (jQuery('#edit-comment-body iFrame').contents().find('body').text() != '' || jQuery('#edit-body iFrame').contents().find('body').text() != '') {
// For IE and Firefox
if (e) {
e.returnValue = "You have unsaved changes.";
}
// For Safari
return "You have unsaved changes.";
}
};
} else {
$this = jQuery('#edit-field-body, #edit-comment-body, #edit-body');
function waitIframe() {
if (!$this.find('iFrame').length) {
setTimeout(waitIframe, 100);
return false;
} else {
$this.find('iFrame').load(function() {
$initVal = $this.find('iFrame').contents().find('body').text();
window.onbeforeunload = function(e) {
e = e || window.event;
if ($initVal != $this.find('iFrame').contents().find('body').text()) {
// For IE and Firefox
if (e) {
e.returnValue = "You have unsaved changes.";
}
// For Safari
return "You have unsaved changes.";
}
};
});
}
};
waitIframe();
}
jQuery('.comment-form, .node-form').find('input.form-submit').on('click', function() {
window.onbeforeunload = function(e) {};
});
});
;/*})'"*/
;/*})'"*/
!function(r,n){"function"==typeof define&&define.amd?define(["jquery"],n):"object"==typeof exports?module.exports=n(require("jquery")):n(r.jQuery)}(this,function(s,f){var u,t={},n=t.toString,c=/^([\-+])=\s*(\d+\.?\d*)/,r=[{re:/rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(r){return[r[1],r[2],r[3],r[4]]}},{re:/rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,parse:function(r){return[2.55*r[1],2.55*r[2],2.55*r[3],r[4]]}},{re:/#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})?/,parse:function(r){return[parseInt(r[1],16),parseInt(r[2],16),parseInt(r[3],16),r[4]?(parseInt(r[4],16)/255).toFixed(2):1]}},{re:/#([a-f0-9])([a-f0-9])([a-f0-9])([a-f0-9])?/,parse:function(r){return[parseInt(r[1]+r[1],16),parseInt(r[2]+r[2],16),parseInt(r[3]+r[3],16),r[4]?(parseInt(r[4]+r[4],16)/255).toFixed(2):1]}},{re:/hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,space:"hsla",parse:function(r){return[r[1],r[2]/100,r[3]/100,r[4]]}}],p=s.Color=function(r,n,t,e){return new s.Color.fn.parse(r,n,t,e)},d={rgba:{props:{red:{idx:0,type:"byte"},green:{idx:1,type:"byte"},blue:{idx:2,type:"byte"}}},hsla:{props:{hue:{idx:0,type:"degrees"},saturation:{idx:1,type:"percent"},lightness:{idx:2,type:"percent"}}}},h={byte:{floor:!0,max:255},percent:{max:1},degrees:{mod:360,floor:!0}},b=s.each;function g(r){return null==r?r+"":"object"==typeof r?t[n.call(r)]||"object":typeof r}function m(r,n,t){var e=h[n.type]||{};return null==r?t||!n.def?null:n.def:(r=e.floor?~~r:parseFloat(r),e.mod?(r+e.mod)%e.mod:Math.min(e.max,Math.max(0,r)))}function l(e){var o=p(),a=o._rgba=[];return e=e.toLowerCase(),b(r,function(r,n){var t=n.re.exec(e),t=t&&n.parse(t),n=n.space||"rgba";if(t)return t=o[n](t),o[d[n].cache]=t[d[n].cache],a=o._rgba=t._rgba,!1}),a.length?("0,0,0,0"===a.join()&&s.extend(a,u.transparent),o):u[e]}function o(r,n,t){return 6*(t=(t+1)%1)<1?r+(n-r)*t*6:2*t<1?n:3*t<2?r+(n-r)*(2/3-t)*6:r}b(d,function(r,n){n.cache="_"+r,n.props.alpha={idx:3,type:"percent",def:1}}),s.each("Boolean Number String Function Array Date RegExp Object Error Symbol".split(" "),function(r,n){t["[object "+n+"]"]=n.toLowerCase()}),p.fn=s.extend(p.prototype,{parse:function(o,r,n,t){if(o===f)return this._rgba=[null,null,null,null],this;(o.jquery||o.nodeType)&&(o=s(o).css(r),r=f);var a=this,e=g(o),i=this._rgba=[];return r!==f&&(o=[o,r,n,t],e="array"),"string"===e?this.parse(l(o)||u._default):"array"===e?(b(d.rgba.props,function(r,n){i[n.idx]=m(o[n.idx],n)}),this):"object"===e?(b(d,o instanceof p?function(r,n){o[n.cache]&&(a[n.cache]=o[n.cache].slice())}:function(r,t){var e=t.cache;b(t.props,function(r,n){if(!a[e]&&t.to){if("alpha"===r||null==o[r])return;a[e]=t.to(a._rgba)}a[e][n.idx]=m(o[r],n,!0)}),a[e]&&s.inArray(null,a[e].slice(0,3))<0&&(null==a[e][3]&&(a[e][3]=1),t.from&&(a._rgba=t.from(a[e])))}),this):void 0},is:function(r){var o=p(r),a=!0,i=this;return b(d,function(r,n){var t,e=o[n.cache];return e&&(t=i[n.cache]||n.to&&n.to(i._rgba)||[],b(n.props,function(r,n){if(null!=e[n.idx])return a=e[n.idx]===t[n.idx]})),a}),a},_space:function(){var t=[],e=this;return b(d,function(r,n){e[n.cache]&&t.push(r)}),t.pop()},transition:function(r,i){var n=(l=p(r))._space(),t=d[n],r=0===this.alpha()?p("transparent"):this,s=r[t.cache]||t.to(r._rgba),u=s.slice(),l=l[t.cache];return b(t.props,function(r,n){var t=n.idx,e=s[t],o=l[t],a=h[n.type]||{};null!==o&&(null===e?u[t]=o:(a.mod&&(a.mod/2<o-e?e+=a.mod:a.mod/2<e-o&&(e-=a.mod)),u[t]=m((o-e)*i+e,n)))}),this[n](u)},blend:function(r){if(1===this._rgba[3])return this;var n=this._rgba.slice(),t=n.pop(),e=p(r)._rgba;return p(s.map(n,function(r,n){return(1-t)*e[n]+t*r}))},toRgbaString:function(){var r="rgba(",n=s.map(this._rgba,function(r,n){return null!=r?r:2<n?1:0});return 1===n[3]&&(n.pop(),r="rgb("),r+n.join(", ")+")"},toHslaString:function(){var r="hsla(",n=s.map(this.hsla(),function(r,n){return null==r&&(r=2<n?1:0),n&&n<3&&(r=Math.round(100*r)+"%"),r});return 1===n[3]&&(n.pop(),r="hsl("),r+n.join(", ")+")"},toHexString:function(r){var n=this._rgba.slice(),t=n.pop();return r&&n.push(~~(255*t)),"#"+s.map(n,function(r){return("0"+(r||0).toString(16)).substr(-2)}).join("")},toString:function(){return this.toRgbaString()}}),p.fn.parse.prototype=p.fn,d.hsla.to=function(r){if(null==r[0]||null==r[1]||null==r[2])return[null,null,null,r[3]];var n=r[0]/255,t=r[1]/255,e=r[2]/255,o=r[3],a=Math.max(n,t,e),i=Math.min(n,t,e),s=a-i,u=a+i,r=.5*u,t=i===a?0:n===a?60*(t-e)/s+360:t===a?60*(e-n)/s+120:60*(n-t)/s+240,u=0==s?0:r<=.5?s/u:s/(2-u);return[Math.round(t)%360,u,r,null==o?1:o]},d.hsla.from=function(r){if(null==r[0]||null==r[1]||null==r[2])return[null,null,null,r[3]];var n=r[0]/360,t=r[1],e=r[2],r=r[3],t=e<=.5?e*(1+t):e+t-e*t,e=2*e-t;return[Math.round(255*o(e,t,n+1/3)),Math.round(255*o(e,t,n)),Math.round(255*o(e,t,n-1/3)),r]},b(d,function(u,r){var a=r.props,i=r.cache,s=r.to,l=r.from;p.fn[u]=function(r){if(s&&!this[i]&&(this[i]=s(this._rgba)),r===f)return this[i].slice();var n,t=g(r),e="array"===t||"object"===t?r:arguments,o=this[i].slice();return b(a,function(r,n){r=e["object"===t?r:n.idx];null==r&&(r=o[n.idx]),o[n.idx]=m(r,n)}),l?((n=p(l(o)))[i]=o,n):p(o)},b(a,function(i,s){p.fn[i]||(p.fn[i]=function(r){var n,t=g(r),e="alpha"===i?this._hsla?"hsla":"rgba":u,o=this[e](),a=o[s.idx];return"undefined"===t?a:("function"===t&&(t=g(r=r.call(this,a))),null==r&&s.empty?this:("string"===t&&(n=c.exec(r))&&(r=a+parseFloat(n[2])*("+"===n[1]?1:-1)),o[s.idx]=r,this[e](o)))})})}),p.hook=function(r){r=r.split(" ");b(r,function(r,e){s.cssHooks[e]={set:function(r,n){var t;"transparent"===n||"string"===g(n)&&!(t=l(n))||(n=(n=p(t||n)).toRgbaString()),r.style[e]=n}},s.fx.step[e]=function(r){r.colorInit||(r.start=p(r.elem,e),r.end=p(r.end),r.colorInit=!0),s.cssHooks[e].set(r.elem,r.start.transition(r.end,r.pos))}})},p.hook("backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor"),s.cssHooks.borderColor={expand:function(t){var e={};return b(["Top","Right","Bottom","Left"],function(r,n){e["border"+n+"Color"]=t}),e}},u=s.Color.names={aqua:"#00ffff",black:"#000000",blue:"#0000ff",fuchsia:"#ff00ff",gray:"#808080",green:"#008000",lime:"#00ff00",maroon:"#800000",navy:"#000080",olive:"#808000",purple:"#800080",red:"#ff0000",silver:"#c0c0c0",teal:"#008080",white:"#ffffff",yellow:"#ffff00",transparent:[null,null,null,0],_default:"#ffffff"}});
;/*})'"*/
;/*})'"*/
//Merged and minified: bue.popup.js, bue.markup.js, bue.preview.js, bue.imce.js, bue.misc.js
(function(b,a){BUE.popups=BUE.popups||{};BUE.popHtml='<table class="bue-popup" style="display: none;" role="dialog"><tbody class="bue-zero"><tr class="bue-zero"><td class="bue-zero"><div class="bue-popup-head clearfix"><div class="bue-popup-title"></div><button class="bue-popup-close" type="button">x</button></div><div class="bue-popup-body"><div class="bue-popup-content clearfix"></div></div></td></tr></tbody></table>';BUE.openPopup=function(f,e,d,c){return BUE.createPopup(f).open(e,d,c)};BUE.createPopup=function(i,h,f){if(BUE.popups[i]){return BUE.popups[i]}var c=BUE.$html(BUE.popHtml).appendTo("body").attr("id",i);var e=c.find(".bue-popup-title").html(h||"");var d=c.find(".bue-popup-content").html(f||"");var g=BUE.popups[i]=c[0];g.open=function(o,l,k){if(o!==undefined&&o!==null){e.html(o)}if(l!==undefined&&l!==null){d.html(l)}var m=g.bue=BUE.active,q=m.buttons[m.bindex||0];k=typeof k=="string"?{effect:k}:k;k=a.extend({effect:"show",speed:"normal",callback:g.onopen},k);k.onopen=k.onopen||k.callback;if(!k.offset&&q){var p=a(q).offset(),j=c.width(),n=Math.max(15,p.left-j/2+15);k.offset={left:n-Math.max(0,n+j-a(window).width()+15),top:p.top+15};q.pops=true}c.css(k.offset);if(k.effect=="show"){c.show();k.onopen&&k.onopen.call(g)}else{c[k.effect](k.speed,k.onopen)}g.onclose=k.onclose||false;return g};g.close=function(j){c.stop(true,true)[j||"hide"]();g.onclose&&g.onclose.call(g);return g};g.closenfocus=function(){g.close().bue.focus();return g};g.onopen=function(){if(c.css("display")!="none"){var j=c.focus().find("form");if(j.length){a(j[0].elements[0]).focus()}else{c.find("a:first").focus()}}return g};c.attr("tabindex",0);c.find(".bue-popup-close").click(g.closenfocus)[0].title=Drupal.t("Close");c.keydown(function(j){if(j.keyCode==27){g.closenfocus();return false}});c.find(".bue-popup-head").mousedown(function(l){var m={X:parseInt(c.css("left"))-l.pageX,Y:parseInt(c.css("top"))-l.pageY};var k=function(n){c.css({left:m.X+n.pageX,top:m.Y+n.pageY});return false};var j=function(n){a(document).unbind("mousemove",k).unbind("mouseup",j)};a(document).mousemove(k).mouseup(j);return false});return g};BUE.preprocess=a.extend({popup:function(j,f){if(j.index){return}var c=b.dialog=BUE.dialog=BUE.createPopup("bue-dialog");var e=function(){this.blur()};var i=c.open,d=c.close;c.open=function(p,n,m){c.esp&&c.close();var o=BUE.active;o.buttonsDisabled(true).stayClicked(true);c.esp=o.posSelection();f(o.textArea).bind("focus.bue",e);return i(p,n,m)};c.close=function(m){if(!c.esp){return c}var n=c.bue;f(n.textArea).unbind("focus.bue",e);n.buttonsDisabled(false).stayClicked(false);n==BUE.active&&n.makeSelection(c.esp.start,c.esp.end);c.esp=null;return d(m)};var g=b.quickPop=BUE.quickPop=BUE.createPopup("bue-quick-pop");var l=g.open,h=g.close,k=f(g);g.open=function(n,m){f(document).mouseup(g.close);return l(null,n,m)};g.close=function(){f(document).unbind("mouseup",g.close);return h()};k.keydown(function(o){switch(o.keyCode){case 13:setTimeout(g.closenfocus);break;case 38:case 40:var n=k.find("a"),m=n.index(document.activeElement);n.eq(m+o.keyCode-39).focus();return false}});k.find(".bue-popup-head").css({display:"none"})}},BUE.preprocess)})(BUE.instance.prototype,jQuery);(function(d,c){BUE.html=function(g,l,f){var e=f||{},h=l||"";var k="<"+g;for(var j in e){k+=e[j]==null?"":" "+j+'="'+e[j]+'"'}k+=a(g)?(" />"+h):(">"+h+"</"+g+">");return g?k:h};BUE.objHtml=function(e){return e&&e.tag?b(e.tag,e.html,e.attributes):""};BUE.input=function(g,h,f,e){return b("input","",c.extend({type:g,name:h,value:f||null},e))};BUE.selectbox=function(k,f,j,e){var j=j||{},h="";for(var g in j){h+=b("option",j[g],{value:g,selected:g==f?"selected":null})}return b("select",h,c.extend({},e,{name:k}))};BUE.table=function(j,e){for(var h,g="",f=0;h=j[f];f++){g+=h.data===undefined?BUE.trow(h):BUE.trow(h.data,h.attr)}return b("table",g,e)};BUE.trow=function(f,e){for(var j,h="",g=0;j=f[g];g++){h+=j.data===undefined?b("td",j):b("td",j.data,j.attr)}return b("tr",h,e)};BUE.regesc=function(e){return e.replace(/([\\\^\$\*\+\?\.\(\)\[\]\{\}\|\:])/g,"\\$1")};BUE.nctag=function(e){return !e||/^(img|input|hr|br|embed|param)$/.test(e)};BUE.parseHtml=function(k,g){var l=new RegExp("^<("+(g||"[a-z][a-z0-9]*")+")([^>]*)>($|((?:.|[\r\n])*)</\\1>$)");if(!(h=k.match(l))||(!h[3]&&!a(h[1]))){return null}var g=h[1],f=[],e={},h;if((f=h[2].split('"')).length>1){for(var j=0;f[j+1]!==undefined;j+=2){e[f[j].replace(/\s|\=/g,"")]=f[j+1]}}return{tag:g,attributes:e,html:h[4]}};d.insertObj=function(l,h){if(!l||!l.tag){return this}var j=this,e=l.tag,h=c.extend({cursor:null,extend:true,toggle:false},h);var k,i=j.getSelection(),f=i&&h.extend&&BUE.parseHtml(i);if(k=f&&f.tag==e){if(h.toggle){return j.replaceSelection(f.html,h.cursor)}var l={tag:e,html:typeof l.html!="string"||l.html==i?f.html:l.html,attributes:c.extend(f.attributes,l.attributes)}}if(k||a(e)||l.html){return j.replaceSelection(BUE.objHtml(l),h.cursor)}var g=b(e,"",l.attributes);return j.tagSelection(g.substr(0,g.length-e.length-3),"</"+e+">",h.cursor)};var b=BUE.html;var a=BUE.nctag})(BUE.instance.prototype,jQuery);eDefHTML=BUE.html;eDefInput=BUE.input;eDefSelectBox=BUE.selectbox;eDefTable=BUE.table;eDefRow=BUE.trow;eDefNoEnd=BUE.nctag;eDefRegEsc=BUE.regesc;eDefParseTag=BUE.parseHtml;eDefInputText=function(c,a,b){return BUE.input("text",c,a,{size:b||null})};eDefInputSubmit=function(b,a){return BUE.input("submit",b,a)};(function(b,a){b.prv=function(e){var d=this;if(d.prvOn){return d.prvHide()}var e=e===undefined?true:e;var c=d.getContent();if(e&&!(d.safeToPreview=d.safeToPreview||c.indexOf("<")==-1)){c='<div class="warning">'+Drupal.t("The preview is disabled due to previously inserted HTML code in the content. This aims to protect you from any potentially harmful code inserted by other editors or users. If you own the content, just preview an empty text to re-enable the preview.")+"</div>"}return d.prvShow(BUE.autop(c))};b.prvShow=function(d,e){var f=this;var g=a(f.textArea);var c=a(f.preview=f.preview||BUE.$html('<div class="preview bue-preview" style="display:none; overflow:auto"></div>').insertBefore(g)[0]);if(e===undefined||e){d='<div class="'+(f.textArea.name=="comment"?"comment":"node")+'"><div class="content">'+d+"</div></div>"}if(f.prvOn){c.html(d);return f}f.prvPos=f.posSelection();c.show().height(g.height()).width(g.width()).html(d);g.height(1);f.buttonsDisabled(true,f.bindex).stayClicked(true);f.prvOn=true;return f};b.prvHide=function(){var d=this;if(d.prvOn){var c=a(d.preview);a(d.textArea).height(c.height());c.hide();d.buttonsDisabled(false).stayClicked(false);d.prvOn=false;d.prvPos&&(d.makeSelection(d.prvPos.start,d.prvPos.end).prvPos=null)}return d};b.prvAjax=function(e,f){var d=this,c;if(d.prvOn){return d.prvHide()}if(!(c=a.ajaxMarkup)){return d.prvShow(Drupal.t('Preview requires <a href="http://drupal.org/project/ajax_markup">Ajax markup</a> module with proper permissions set.'))}if(e&&e.call){f=e;e=0}d.prvShow('<div class="bue-prv-loading">'+Drupal.t("Loading...")+"</div>");c(d.getContent(),e||c.getFormat(d.textArea),function(h,g,i){d.prvOn&&d.prvShow(g?h:h.replace(/\n/g,"<br />"))&&(f||Drupal.attachBehaviors)(d.preview)});return d};BUE.autop=function(d){if(d==""||!(/\n|\r/).test(d)){return d}var f=function(h,i,g){return h.replace(new RegExp(i,"g"),g)};var c=function(h,g){return d=f(d,h,g)};var e="(table|thead|tfoot|caption|colgroup|tbody|tr|td|th|div|dl|dd|dt|ul|ol|li|pre|select|form|blockquote|address|math|style|script|object|input|param|p|h[1-6])";d+="\n";c("<br />\\s*<br />","\n\n");c("(<"+e+"[^>]*>)","\n$1");c("(</"+e+">)","$1\n\n");c("\r\n|\r","\n");c("\n\n+","\n\n");c("\n?((.|\n)+?)\n\\s*\n","<p>$1</p>\n");c("\n?((.|\n)+?)$","<p>$1</p>\n");c("<p>\\s*?</p>","");c("<p>(<div[^>]*>\\s*)","$1<p>");c("<p>([^<]+)\\s*?(</(div|address|form)[^>]*>)","<p>$1</p>$2");c("<p>\\s*(</?"+e+"[^>]*>)\\s*</p>","$1");c("<p>(<li.+?)</p>","$1");c("<p><blockquote([^>]*)>","<blockquote$1><p>");c("</blockquote></p>","</p></blockquote>");c("<p>\\s*(</?"+e+"[^>]*>)","$1");c("(</?"+e+"[^>]*>)\\s*</p>","$1");c("<(script|style)(.|\n)*?</\\1>",function(g){return f(g,"\n","<PNL>")});c("(<br />)?\\s*\n","<br />\n");c("<PNL>","\n");c("(</?"+e+"[^>]*>)\\s*<br />","$1");c("<br />(\\s*</?(p|li|div|dl|dd|dt|th|pre|td|ul|ol)[^>]*>)","$1");if(d.indexOf("<pre")!=-1){c("(<pre(.|\n)*?>)((.|\n)*?)</pre>",function(j,i,h,g){return f(i,"\\\\(['\"\\\\])","$1")+f(f(f(g,"<p>","\n"),"</p>|<br />",""),"\\\\(['\"\\\\])","$1")+"</pre>"})}return c("\n</p>$","</p>")}})(BUE.instance.prototype,jQuery);eDefAutoP=BUE.autop;eDefPreview=function(){BUE.active.prv()};eDefPreviewShow=function(c,b,a){c.prvShow(b,a)};eDefPreviewHide=function(a){a.prvHide()};eDefAjaxPreview=function(){BUE.active.prvAjax()};(function(c,b){var a=c.imce=BUE.imce={};b(function(){a.url=Drupal.settings.BUE.imceURL||""});a.button=function(e,d){return a.url?'<input type="button" id="bue-imce-button" name="bue_imce_button" class="form-submit" value="'+(d||Drupal.t("Browse"))+'" onclick="BUE.imce.open(this.form.elements[\''+e+"'])\">":""};a.open=function(e){if(!a.url){return}a.ready=a.sendto=function(){},a.target=null;b.extend(a,e.focus?{target:e,ready:a.readyDefault,sendto:a.sendtoDefault}:e);if(a.pop){a.setPos();a.ready(a.win,a.pop)}else{var d=a.url+(a.url.indexOf("?")<0?"?":"&")+"app=bue|imceload@bueImceLoad|";a.pop=BUE.createPopup("bue-imce-pop",Drupal.t("File Browser"),'<iframe src="'+d+'" frameborder="0"></iframe>');a.setPos()}};a.setPos=function(){var d=b(a.pop),f=b(window),e=window.opera?window.innerHeight:f.height();a.pop.open(null,null,{offset:{left:Math.max(0,(f.width()-d.width())/2),top:f.scrollTop()+Math.max(0,(e-d.height())/2)}})};a.finish=function(d,e){a.sendto(d,e,a.pop)};a.sendtoDefault=function(f,j,d){var h=a.target,g=h.form.elements,k={alt:f.name,width:f.width,height:f.height};h.value=f.url;for(var e in k){if(g["attr_"+e]){g["attr_"+e].value=k[e]}}d.close();h.focus()};a.readyDefault=function(g,d){var e=g.imce,f=a.target&&a.target.value;f&&e.highlight(f.substr(f.lastIndexOf("/")+1));if(e.fileKeys&&!e.fileKeys.k27){e.fileKeys.k27=function(h){d.closenfocus();a.target&&a.target.focus()}}!window.opera&&!("WebkitAppearance" in document.documentElement.style)&&b(e.FLW).focus()};window.bueImceLoad=function(d){(a.win=d).imce.setSendTo(Drupal.t("Send to editor"),a.finish);a.ready(d,a.pop);if((window.opera||("WebkitAppearance" in document.documentElement.style))&&b(a.pop).is(":visible")){b(a.win.imce.FLW).one("focus",function(){a.pop.close();a.setPos()})}}})(BUE.instance.prototype,jQuery);eDefBrowseButton=function(a,c,b){return BUE.imce.button(c,b)};(function(d,c){d.wrapLines=function(h,n,m,g){var o=this,k=o.getSelection().replace(/\r\n|\r/g,"\n"),l=BUE.regesc;if(!k){return o.tagSelection(h+n,m+g)}var j,i=new RegExp("^"+l(h+n)+"((.|\n)*)"+l(m+g)+"$");if(j=k.match(i)){i=new RegExp(l(m)+"\n"+l(n),"g");return o.replaceSelection(j[1].replace(i,"\n"))}return o.replaceSelection(h+n+k.replace(/\n/g,m+"\n"+n)+m+g)};d.toggleTag=function(g,h,k){var i=this,j={tag:g,html:i.getSelection(),attributes:h};return i.insertObj(j,{cursor:k,toggle:true})};d.help=function(h){var k=this;if(!k.helpHTML){for(var l,j=[],g=0;l=k.buttons[g];g++){j[g]=[BUE.input(l.type,null,l.value||null,{"class":l.className,src:l.src||null,style:c(l).attr("style")}),l.title]}k.helpHTML=BUE.table(j,{id:"bue-help","class":"bue-"+k.tplid})}k.quickPop.open(k.helpHTML,h);return k};d.tagChooser=function(g,i){var k=this,i=c.extend({wrapEach:"li",wrapAll:"ul",applyTag:true,effect:"slideDown"},i);var l=BUE.html(i.wrapAll||"div","",{"class":"tag-chooser"}),j=b(l);var h=BUE.html(i.wrapEach,"",{"class":"choice"});var m=BUE.html("a","",{href:"#","class":"choice-link"});c.each(g,function(n,o){var p={tag:o[0],html:o[1],attributes:o[2]};b(m).html(i.applyTag?BUE.objHtml(p):p.html).click(function(){k.insertObj(c.extend(p,{html:null}));return false}).appendTo(j)[h?"wrap":"end"](h)});k.quickPop.open(j,i.effect);return k};d.tagDialog=function(w,q,h){var v=this,k=v.getSelection(),o=BUE.parseHtml(k,w)||{attributes:{}};for(var r,p="",u=[],m=0,l=0;r=q[m];m++,l++){r=e(r,o,k);if(r.type=="hidden"){p+=a(r);l--;continue}u[l]=[BUE.html("label",r.title,{"for":r.attributes.id}),a(r)];while(r.getnext&&(r=q[++m])){u[l][1]+=a(e(r,o,k))}}var g=c.extend({title:Drupal.t("Tag editor - @tag",{"@tag":w.toUpperCase()}),stitle:Drupal.t("OK"),validate:false,submit:function(n,i){return v.tgdSubmit(n,i)},effect:"show"},h);var s=BUE.table(u,{"class":"bue-tgd-table"});var j=BUE.html("div",BUE.input("submit","bue_tgd_submit",g.stitle,{"class":"form-submit"}));var t=b(BUE.html("form",s+j+p,{name:"bue_tgd_form",id:"bue-tgd-form"}));v.dialog.open(g.title,t,h);t.submit(function(){return f(w,this,g,v)});return v};d.tgdSubmit=function(g,l){var m=this,n={tag:g,html:null,attributes:{}};for(var h,k,j=0;k=l.elements[j];j++){if(k.name.substr(0,5)=="attr_"){h=k.name.substr(5);if(h=="html"){n.html=k.value}else{n.attributes[h]=k.value.replace(/\x22/g,"&quot;").replace(/>/g,"&gt;").replace(/</g,"&lt;")||null}}}return m.insertObj(n)};var b=BUE.$html;var a=function(i){var g=i.prefix||"";switch(i.type){case"select":g+=BUE.selectbox(i.fname,i.value,i.options||{},i.attributes);break;case"textarea":g+=BUE.html("textarea","\n"+i.value,i.attributes);break;default:g+=BUE.input(i.type,i.fname,i.value,i.attributes);break}return g+(i.suffix||"")};var e=function(h,i,g){h=typeof(h)=="string"?{name:h}:h;if(h.name=="html"){h.value=typeof i.html=="string"?i.html:(g||h.value||"")}h.value=Drupal.checkPlain(typeof i.attributes[h.name]=="string"?i.attributes[h.name]:(h.value||""));h.title=typeof h.title=="string"?h.title:h.name.substr(0,1).toUpperCase()+h.name.substr(1);h.fname="attr_"+h.name;h.type=h.value.indexOf("\n")>-1?"textarea":(h.type||"text");h.attributes=c.extend({name:h.fname,id:h.fname,"class":""},h.attributes);h.attributes["class"]+=" form-"+h.type;if(h.required){h.attributes["class"]+=" required";h.attributes.title=Drupal.t("This field is required.")}return h};var f=function(p,g,h,o){for(var j,m=0;j=g.elements[m];m++){if(c(j).hasClass("required")&&!j.value){return BUE.noticeRequired(j)}}var k=h.validate;if(k){try{if(!k(p,g,h,o)){return false}}catch(n){alert(n.name+": "+n.message)}}o.dialog.close();var l=h.submit;l=typeof l=="string"?window[l]:l;if(l){try{l(p,g,h,o)}catch(n){alert(n.name+": "+n.message)}}return false};BUE.noticeRequired=function(g){c(g).fadeOut("fast").fadeIn("fast",function(){c(this).focus()});return false}})(BUE.instance.prototype,jQuery);eDefSelProcessLines=eDefTagLines=function(f,e,h,g){BUE.active.wrapLines(f,e,h,g)};eDefTagger=function(e,d,f){BUE.active.toggleTag(e,d,f)};eDefHelp=function(a){BUE.active.help(a)};eDefTagDialog=function(h,g,l,k,j,i){BUE.active.tagDialog(h,g,{title:l,stitle:k,submit:j,effect:i})};eDefTagInsert=function(d,c){BUE.active.tgdSubmit(d,c)};eDefTagChooser=function(g,f,j,i,h){BUE.active.tagChooser(g,{applyTag:f,wrapEach:j,wrapAll:i,effect:h})};
;/*})'"*/
;/*})'"*/
/**
* @file
* MarkdownEditor JS library for BUEditor.
*
* @author Jakob Persson <jakob@nodeone.se>
* @author Adam Bergmark
*/
/*******************************************************************************
* IMPORTED HELPERS
*******************************************************************************/
Cactus = window.Cactus || {};
Cactus.Addon = Cactus.Addon || {};
Cactus.DOM = Cactus.DOM || {};
Cactus.Addon.Function = (function () {
/**
* @param Object scope The scope to call the function in
* @param mixed *args Arguments to pass to the function
* bind is called on
* @return Function(arg1)
* @param mixed *args Additional arguments to concatenate
* to the outer args before calling the function
*
* Called on a function A, bind returns a function B
* which executes A in the scope of the first argument
* given to bind, passing the rest of bind's arguments
* concatenated with the arguments to B as arguments to A.
*
* Implementation notes:
* apply is retrieved through Function.prototype since setTimeout
* has no properties in safari 2 (fixed in webkit nightly - 2007-x).
*/
Function.prototype.bind = function (scope, arg1) {
var args = Array.prototype.slice.call (arguments, 1);
var func = this;
return function () {
return Function.prototype.apply.call (
func,
scope,
args.concat (
Array.prototype.slice.call (arguments)));
};
};
/**
* Executes a function and returns the specified value afterwards.
* This is useful when a function does not normally
* return a value. Example of usage would be
* if you bind a function to a DOM event but want the event to return
* false afterwards in order to halt the event.
* This would be writtes like this:
* foo.bar.bind (foo).returning (false);
*
* Any arguments passed to the function returned will be
* relayed to the inner function.
*
* Concise explanation:
* Applied to a function A and given an argument V, returning returns a function
* B that executes A in the global scope applying arguments sent to B to A,
* followed by B returning V.
*
* @param mixed value The value to return after executing the function
* @return Function A function that executes the function and
* then returns the value specified.
* @param mixed *args arguments that are passed through to the inner
* function.
*/
Function.prototype.returning = function (value) {
var func = this;
return function () {
func.apply (null, arguments);
return value;
};
};
/**
* Applied to a function F, wait returns a function G that sets a timeout that
* executes F after the specified delay. Any additional arguments passed to G
* are forwarded to F.
*
* @param natural delay
* The delay in milli seconds before calling the function F.
* @return Function
* The new function, that when executed sets a timeout to call F.
*/
Function.prototype.wait = function (delay) {
delay = delay || 0;
return Function.prototype.bind.call(setTimeout, null, this, delay);
};
})();
Cactus.DOM.Array = (function () {
/**
* Empties an array, use this function when there are several
* references to an array and you can't modify all of them to
* point to a new array instance.
*
* @param Array array
*/
Array.empty = function (array) {
array.length = 0;
};
/**
* Removes the specified element from the given array. If removeAll is set
* element is removed from every index in the array, should it exist
* under several indices. Otherwise only the first occurence is removed.
* The function returns true if it found a match, otherwise false.
* Any indices to the right of the index are shifted to the left
*
* @param Array array
* The array to remove the element from.
* @param mixed element
* The element to remove.
* @param optional boolean removeAll = true
* If more than one matching element should be removed (if found).
* @return boolean
* The index of the element that was removed, -1 if nothing was removed.
*/
Array.remove = function (array, element, removeAll) {
removeAll = removeAll === undefined ? false : !!removeAll
var newArray = [];
var removed = -1;
function shouldRemove (matchingElements) {
return matchingElements && (removeAll || removed === -1);
}
/*
* Append the elements we want to keep to newArray
*/
for (var i = 0; i < array.length; i++) {
if (shouldRemove (element === array [i])) {
removed = i;
} else {
newArray.push (array[i]);
}
}
/*
* Move contents of newArray to array
*/
if (array.length > newArray.length) {
Array.empty (array);
while (newArray.length) {
array.push (newArray.shift());
}
}
return removed;
};
}) ();
/**
* @creation 2006
*
* ClassNames is a singleton which provides static methods for modifying CSS
* class names for HTML Elements.
*/
Cactus.DOM.ClassNames = (function () {
function ClassNames () {
} ClassNames.prototype = {
/**
* Adds a class to an object. But only if the class doesn't already
* exist on the object
*
* @param HTMLElement o
* @param string className
*/
add : function (o, className) {
// Only add if the className isn't already added
if (!this.has(o, className)) {
// If the className property is empty, we can simply overwrite
// it.
if (!o.className) {
o.className = className;
// if it isn't empty, we have to prepend a space so that
// "a" and "b" becomes "a b".
} else {
o.className += " " + className;
}
}
},
/**
* Checks if a given className is as a className of o. It assumes that
* class names are separated by spaces and all other characters will be
* counted as part of class names.
*
* @param HTMLElement o
* @param string className
* @return boolean
*/
has : function (o, className) {
return RegExp("(?:^| )" + className + "(?: |$)").test (o.className);
},
/**
* Removes a class from o
*
* @param HTMLElement o
* @param string className
*/
del : function (o, className) {
/*
* Make sure the class exists, so we don't waste time doing
* what isn't necessary
*/
if (this.has (o, className)) {
var classNames = this.get (o);
Array.remove (classNames, className);
o.className = classNames.join(" ");
}
},
/**
* Returns an array containing all classnames of an element
*
* @param HTMLElement o
* @return Array
*/
get : function (o) {
return o.className.split (/\s+/);
}
};
return new ClassNames();
})();
Cactus.DOM.tag = (function () {
/**
* Checks if a collection can be iterated through using
* numeric indices and the length property
*
* @param mixed collection
* @return boolean
*/
var isArrayLike = function (collection) {
return !! (collection &&
(typeof collection === "object") &&
("length" in collection) &&
isFinite (collection.length) &&
(collection !== window) &&
!("tagName" in collection));
};
function append (o, contents) {
if (typeof (contents) === "string" || typeof contents === "number") {
o.appendChild (document.createTextNode (contents));
}
else if (isArrayLike (contents)) {
if (o.tagName.toLowerCase() === "select") {
for (var i = 0, option; i < contents.length; i++) {
option = contents[i];
o.appendChild (option);
if (option._selected) {
option._selected = undefined;
o.selectedIndex = i;
}
}
}
else {
for (var j = 0; j < contents.length; j++) {
append (o, contents[j]);
}
}
}
else if (contents) {
o.appendChild (contents);
}
}
/**
* @param string name the tag name of the element created.
* @param optional Hash attributes cantains html attributes to assign to
* the elements, among other things.
* @param optional mixed contents
* string: a text node with the value is appended to the el.
* HTMLElement: the element is appended to the new element
* Array<HTMLElement>: all elements are appended.
*/
function tag (name, attributes, contents) {
if (!attributes) {
attributes = {};
}
var o;
o = document.createElement (name);
if (contents === null || contents === undefined) {
contents = [];
}
else if (!isArrayLike(contents)) {
contents = [contents];
}
var style = attributes.style;
if (style) {
for (var p in style) if (style.hasOwnProperty (p)) {
o.style [p] = style [p];
}
}
delete attributes.style;
for (var q in attributes) if (attributes.hasOwnProperty (q)) {
// opera will set selected=undefined if it's set on an
// option that isn't appended to a select so we set
// _selected instead and then check for the value when we
// append to a select.
if (q === "selected") {
o._selected = attributes.selected;
}
else {
o [q] = attributes [q];
}
}
if (contents !== undefined && contents !== null) {
append (o, contents);
}
return o;
}
return tag;
})();
/*******************************************************************************
* MARKDOWN EDITOR
******************************************************************************/
window.markdownEditor = window.markdownEditor || {};
/**
* Localization function, this can be overwritten if localization is available.
* By default it always returns the argument it gets.
*
* @param string
* The string to localize.
* @param args
* String arguments to be replaced.
* @return
* The localized string.
*/
markdownEditor.t = Drupal.t || function (string, args) {
return string;
};
// Whether development options should be enabled.
// This is used to prevent CSS caching.
markdownEditor.development = true;
/*******************************************************************************
* SETTINGS
******************************************************************************/
// Settings for the script.
markdownEditor.settings = {};
/**
* Loads the style sheet for the editor dialog.
* It is only loaded once, and is called by the various buttons that use dialogs.
*/
markdownEditor.settings.addStyleSheet = (function () {
// Create a persistant variable only accessible to this function.
var added = false;
var tag = Cactus.DOM.tag;
/**
* Adds a link element to the document's head.
*
* @param url
* The URL of the css file to add.
*/
function addCSS (url) {
// Prevent caching by appending a random GET parameter.
if (markdownEditor.development) {
url += "?" + Math.random();
}
// Create a link element to include the CSS file.
document.getElementsByTagName("head")[0].appendChild(tag("link", {
rel : "stylesheet",
href : url
}));
}
// Persistant variable for the returned function.
var added = false;
// Returns a function that adds the CSS files once only.
return function () {
if (!added) {
addCSS(markdownEditor.settings.cssPath);
added = true;
}
};
})();
/*******************************************************************************
* EXTRAS
******************************************************************************/
markdownEditor.extras = {
/**
* Gets the first available descendant of the parent with the specified
* class name.
*
* @param className
* The classname to look for. Matches where an element has several class
* names is supported.
* @param parent
* Optional. An ancestor of the element to look for. Defaults to the
* document object.
* @return
* A HTMLElement. null if no element is found.
*/
getElementByClassName : function (className, parent) {
return markdownEditor.extras.getElementsByClassName(className, parent)[0];
},
/**
* Gets all descendants of the parent with the specified class name.
*
* @param className
* The classname of the elements to look for.
* @param parent
* The parent element to search under, default is document.
* @return
* An array of all found elements, an empty array if none is found.
*/
getElementsByClassName : function (className, parent) {
parent = parent || document;
var elements = parent.getElementsByTagName("*");
var foundElements = [];
for (var i = 0; i < elements.length; i++) {
// Match the class name, boundaries are either the start
// or end of the string, or a space.
if (new RegExp("(^| )" + className + "( |$)").test(elements[i].className)) {
foundElements.push(elements[i]);
}
}
return foundElements;
}
};
markdownEditor.extras.string = {
/**
* Repeats a string a given amount of times.
* Example: repeat("xy", 3) => "xyxyxy"
*
* @param string
* The string to repeat.
* @param count
* Natural number. The amount of times to repeat the string.
* @return
* The repeated string.
*/
repeat : function (string, count) {
var finalString = "";
for (var i = 0; i < count; i++) {
finalString += string;
}
return finalString;
}
};
markdownEditor.extras.collection = {
/**
* Executes the given function on every item in the collection, and returns
* an array of all the results.
*
* @param collection
* An object with a length property and numbered indices.
* @param func
* The function to execute on every item.
* @return
* An array of func results.
*/
map : function (collection, func) {
var results = [];
// Loop through each item and push the result into the results array.
for (var i = 0; i < collection.length; i++) {
results.push(func(collection[i]));
}
return results;
},
/**
* Checks which index an object has in a collection.
* The first match is returned.
*
* @param collection
* The collection to search through.
* @param object
* The object to look for.
* @return
* The index of the object, or -1 if the object isn't found.
*/
indexOf : function (collection, object) {
for (var i = 0; i < collection.length; i++) {
// If the object matches in the current iteration, the
// index is returned.
if (object === collection[i]) {
return i;
}
}
return -1;
}
};
markdownEditor.extras.dom = {
/**
* Inserts a node after another one.
*
* @param newNode
* The node to add.
* @param previousNode
* The node that will become the previousSibling of newNode.
*/
insertAfter : function (newNode, previousNode) {
// Insert before the next sibling if it exists.
if (previousNode.nextSibling) {
previousNode.parentNode.insertBefore(newNode, previousNode.nextSibling);
}
// Otherwise append.
else {
previousNode.parentNode.appendChild(newNode);
}
},
/**
* Inserts an element before another one, or append it if the next element
* doesn't exist.
*
* @param newNode
* The node to add.
* @param nextNode
* The node to add the new node before, may be falsy to indicate that
* an append to the parent should be done.
* @param parent
* The parent of the nodes, if unspecified the parent of nextNode is
* retrieved.
*/
insertBefore : function (newNode, nextNode, parent) {
parent = parent || nextNode.parentNode;
// Insert before nextNode if it exists.
if (nextNode) {
parent.insertBefore(newNode, nextNode);
}
// Otherwise append to the parent.
else {
parent.appendChild(newNode);
}
}
};
/*******************************************************************************
* DIALOG
******************************************************************************/
markdownEditor.dialog = {
/**
* Gets the element containing the dialog's contents.
*
* @return
* An HTMLElement.
*/
getContent : function () {
return BUE.dialog.popup ? markdownEditor.extras.getElementByClassName("cnt", BUE.dialog.popup) : markdownEditor.extras.getElementByClassName("bue-popup-content", BUE.dialog);
},
/**
* Gets the first form element inside the dialog.
*
* @return
* A HTMLFormElemnt, or null if none is found.
*/
getForm : function () {
return markdownEditor.dialog.getContent().getElementsByTagName("form")[0];
},
/**
* Wipes the dialog clean.
*/
clear : function () {
var content = markdownEditor.dialog.getContent();
while (content.firstChild) {
content.removeChild(content.firstChild);
}
},
/**
* Clears and closes the dialog.
*/
close : function () {
markdownEditor.dialog.clearWidth();
markdownEditor.dialog.clear();
BUE.dialog.close();
},
/**
* Clears and opens the dialog. Also attaches event listeners for submitting
* and closing the dialog.
*
* @param title
* The title of the dialog.
* @param cssID
* The ID to assign to the dialog, for styling with CSS.
* @param HTMLContents
* A string of contents to insert into the dialog.
*/
open : function (title, cssID, HTMLContents) {
markdownEditor.dialog.clear();
cssID = cssID || "markdowneditor-dialog";
if (!/^markdowneditor-dialog/.test(cssID)) {
cssID = "markdowneditor-dialog-" + cssID;
}
markdownEditor.dialog.getContent().id = cssID;
// Event keycodes.
var keys = {
esc : 27,
enter : 13
};
// Open the dialog and set any content specified.
BUE.dialog.open(title, HTMLContents || "");
// Create a keylistener to enable the user to close or submit the
// dialog by pressing enter or escape.
markdownEditor.dialog.getContent().onkeydown = function (e) {
e = e || window.event;
var target = e.target || e.srcElement;
switch (e.keyCode) {
// Close the dialog if esc is pressed.
case keys.esc:
markdownEditor.dialog.close();
break;
// Submit any existing form if enter is pressed.
case keys.enter:
// Don't submit if enter was pressed inside a textarea.
if (target && target.tagName.toLowerCase() === "textarea") {
break;
}
// Submit the dialog by calling the onsubmit handler.
var form = markdownEditor.dialog.getContent().getElementsByTagName("form")[0];
if (form && form.onsubmit) {
form.onsubmit();
return false;
}
break;
}
};
},
/**
* Creates a form containing a table with label/form element pairs on each
* row. Table headers can be specified. Each form element is assigned a
* unique ID so label elements can be associated with them.
*
* This function can not create forms with more than two columns. The amount
* of rows is arbitrary.
*
* @param *arguments
* Each argument corresponds to a label/form element pair or a table
* header row.
* @return
* A HTMLFormElement with a table of form elements and associated labels.
*/
createForm : function () {
var tag = Cactus.DOM.tag;
var rows = [];
for (var i = 0; i < arguments.length; i++) {
var element = arguments[i];
// If the row contains table headers, add them.
if (element.headers) {
var tr = tag("tr");
for (var j = 0; j < element.headers.length; j++) {
tr.appendChild(tag("th", null, element.headers[i]));
}
rows.push(tr);
}
// Otherwise create a label/form element pair.
else {
element.tagName = element.tagName || "input";
element.attributes = element.attributes || {};
// Set the default type for inputs, if none is specified.
if (element.tagName === "input" && !element.attributes.type) {
element.attributes.type = "text";
}
// Add the ID into the attributes hash, unless an ID
// is specified already.
element.attributes.id = element.attributes.id || "dialog_element_" + i;
element.attributes.className = element.attributes.className || "form-" + element.attributes.type;
// Add the ID as the name, if no name is specified.
element.attributes.name = element.attributes.name || element.attributes.id;
// Create the form element.
var formElement = tag(element.tagName, element.attributes, element.contents);
// If the element is a select and options were
// specified they are created and added to the
// select. If a selected option was specified it is
// marked as selected in the select element.
if (element.tagName === "select" && element.options) {
var optionIndex = 0;
// Options are passed in as a hash where the key
// is the option value and the value is the option
// text.
for (var value in element.options) if (element.options.hasOwnProperty(value)) {
// Append the option.
formElement.appendChild(tag("option", { value : value }, element.options[value]));
// If this element is the selected one, mark
// it as such in the select.
if (element.selected == value) {
formElement.selectedIndex = optionIndex;
}
optionIndex++;
}
delete optionIndex;
}
// If the element is mandatory, a red asterisk is
// appended to the label, and a class name is added to
// the element.
if (element.mandatory) {
element.label = [element.label, tag("span", { style : { color : "red" } }, "*")];
element.attributes.className = "mandatory";
}
// Create the label element.
var label = tag("label", { htmlFor : element.attributes.id }, element.label);
// Create the table row, the label is also created
// here and is associated with the form element.
rows.push(tag("tr", null, [
tag("td", { className : "td-label" }, label),
tag("td", { className : "td-element" }, formElement)
]));
}
}
// Create the form and table/tbody and return the whole structure.
return tag("form", {
id : "markdowneditor-dialog-form",
onsubmit : function () { return false; }
}, tag("fieldset", null, tag("table", null, tag("tbody", null, rows))));
},
/**
* Sets the onsubmit handler for a form, the form submission is
* automatically halted.
*
* @param form
* The HTMLFormElement to assign the event to.
* @param onsubmit
* The function to add as the onsubmit handler.
*/
setOnsubmit : function (form, onsubmit) {
form.onsubmit = onsubmit.wait(0).returning(false);
},
/**
* Adds an submit button to the dialog.
*
* @param form
* The form that the button should be added to.
* @param onclick
* A function that should run when the button is clicked.
* @param title
* An optional localized title of the button. Defaults to t("OK").
*/
addSubmitButton : function (form, onclick, title) {
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
title = title || t("OK");
// Extract the function with a timeout so errors won't keep
// the function from halting propagation.
var button = tag("input", {
type : "button",
value : title,
className : "markdowneditor-dialog-submit form-submit",
onclick : onclick.wait(0).returning(false)
});
form.appendChild(button);
return button;
},
/**
* Adds a cancel button that closes the dialog when clicked.
*
* @param form
* The form that the button should be added to.
* @param title
* An optional localized title for the button. Defaults to t("Cancel").
* @param prepend
* Whether the button should be prepended to the dialog contents, if
* false, or if the argument is omitted the button is appended.
* Defaults to false.
* @param confirmation
* Whether the user should be prompted to confirm the action.
* Defaults to false.
* @return
* The button that was added.
*/
addCancelButton : function (form, title, prepend, confirmation) {
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
prepend = !!prepend;
confirmation = !!confirmation;
title = title || t("Cancel");
// Append or prepend the button and set the onclick handler.
var button = tag("input", {
type : "button",
value : title,
className : "markdowneditor-dialog-cancel form-submit",
onclick : function () {
// Close the dialog if no confirmation is needed or the user
// confirms.
if (!confirmation || confirm(t("Any changes will be lost. Are you sure you want to cancel?"))) {
markdownEditor.dialog.close();
}
return false;
}
});
// Add the button to the start of the dialog if prepend is set.
if (prepend) {
form.insertBefore(button, form.firstChild);
}
// Otherwise append to the end of the dialog.
else {
form.appendChild(button);
}
return button;
},
/**
* Gets the title of the dialog.
*
* @return
* The string title.
*/
getTitle : function () {
return BUE.dialog.popup ? BUE.dialog.popup.rows[0].cells[0].innerHTML : markdownEditor.extras.getElementByClassName("bue-popup-title", BUE.dialog).innerHTML;
},
/**
* @return
* The HTML list containing error messages in the editor dialog.
*/
_getErrorContainer : function () {
var tag = Cactus.DOM.tag;
var errors = document.getElementById("markdowneditor-dialog-errors");
// Lazy initialization for the element.
if (!errors) {
var content = markdownEditor.dialog.getContent();
errors = tag("ul", { id : "markdowneditor-dialog-errors", style : { display : "none" } });
// Insert the element into the dialog.
if (content.childNodes.length) {
content.insertBefore(errors, content.firstChild);
}
else {
content.appendChild(errors);
}
}
return errors;
},
/**
* Adds an error message to the error list.
*
* @param message
* The message to display, it can take the same arguments as
* Cactus.DOM.tag does for its content attribute.
*/
addError : function (message) {
var t = markdownEditor.t;
var tag = Cactus.DOM.tag;
message = message || t("An unspecified error occured");
// Add a LI with the message to the error container.
var container = markdownEditor.dialog._getErrorContainer();
container.style.display = "block";
container.appendChild(tag("li", { className : "error" }, message));
},
/**
* Removes all messages from the error container.
*/
clearErrors : function () {
// Clear the contents.
markdownEditor.dialog._getErrorContainer().innerHTML = "";
},
/**
* Adds a button that opens an IMCE dialog, but only if IMCE is marked as
* enabled.
*
* @param parent
* The element to append the button to.
* @param resultElement
* The element to put the result from the IMCE window in.
*/
addIMCELink : function (parent, resultElement) {
//require URL set.
if (!BUE.imce.url) {
return;
}
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
var triggerFunction = function () {
BUE.imce.open(resultElement);
};
// Append the button and assign its onclick handler that opens the
// IMCE browse window.
parent.appendChild(tag("input", {
type : "button",
id : "bue-imce-button",
className : "imce-button form-submit",
name : "bue_imce_button",
value : t("Browse"),
onclick : triggerFunction
}));
},
/**
* Display "send to bueditor" link and bind function to IMCE window unload event
* @param {Object} win
*/
imceWindowLoad : function (win) {
win.imce.setSendTo(markdownEditor.t('Send to @app', {'@app': 'BUEditor'}), markdownEditor.dialog.imceWindowFinish);
// TODO: Do not use jQuery here:
$(window).unload(function() {
if (MDEImceWindow && !MDEImceWindow.closed) MDEImceWindow.close();
});
},
/**
* Fill BUE dialog fields and close IMCE window
* @param {Object} file
* @param {Object} win
*/
imceWindowFinish : function (file, win) {
var el = document.forms['markdowneditor-dialog-form'].elements;
var val = {'text' : file.name, 'title' : file.name, 'href' : file.url}
for (var i in val) {
if (!el[i].value) el[i].value = val[i];
}
win.blur();//or close()
el[el.length-1].focus();//focus on last element.
},
/**
* Gives focus to the first form element in the dialog.
*/
focusFirst : function () {
// Get all elements form the dialog.
var elements = markdownEditor.dialog.getContent().getElementsByTagName("*");
var element = null;
// Loop through the elements and try to find a form element.
for (var i = 0; i < elements.length; i++) {
if (/^(button|input|textarea|select)$/.test(elements[i].tagName.toLowerCase())) {
element = elements[i];
break;
}
}
// If a form element was found, it's given focus.
if (element) {
element.focus();
}
},
/**
* Clears any width settings that were set by setWidth.
*/
clearWidth : function () {
var dialog = document.getElementById("bue-dialog");
dialog.style.maxWidth = "";
},
/**
* Sets the maximum width of the dialog, useful for dialogs containing text.
*/
setWidth : function () {
var dialog = document.getElementById("bue-dialog");
dialog.style.maxWidth = "600px";
}
};
/*******************************************************************************
* SELECTION
******************************************************************************/
markdownEditor.selection = {
/**
* Selects all characters to the end of the the line the end of the selection
* belongs to.
*/
selectToEndOfLine : function () {
var content = BUE.active.getContent();
var end = BUE.active.posSelection().end;
while (content.charAt(end) !== "\n" && end < content.length) {
end++;
}
BUE.active.makeSelection(BUE.active.posSelection().start, end);
},
/**
* Returns a given line from the current selection
*
* @param lineNumber
* An natural number representing the line number.
* @return
* A string. The line with the given line number.
* Returns undefined if the line doesn't exist.
*/
getLine : function (lineNumber) {
return BUE.active.getSelection().split(/(\r\n?|\n)/)[lineNumber];
},
/**
* Excludes all preceeding and succeeding line breaks from the current
* selection.
*/
excludeLineBreaks : function () {
var pos;
if (/^(\n+)/.test(BUE.active.getSelection())) {
pos = BUE.active.posSelection();
BUE.active.makeSelection(Math.min(pos.start + RegExp.$1.length, pos.end), pos.end);
}
if (/(\n+)$/.test(BUE.active.getSelection())) {
pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.start, Math.max(pos.start, pos.end - RegExp.$1.length));
}
},
/**
* Inserts characters around the current selection, the contents of the
* selection is left unchanged.
*
* @param string
* The string to insert around the selection.
*/
insertAround : function (string) {
mSelection = markdownEditor.selection;
mSelection.insertBefore(string);
mSelection.insertAfter(string);
},
/**
* Inserts characters before the current selection, the contents of the
* selection is left unchanged
*
* @param string
* The string to insert before the selection.
*/
insertBefore : function (string) {
markdownEditor.selection.replace(/^/, string);
var pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.start + string.length, pos.end);
},
/**
* Inserts characters after the current selection, the contents of the
* selection is left unchanged
*
* @param string
* The string to insert after the selection.
*/
insertAfter : function (string) {
markdownEditor.selection.replace(/$/, string);
var pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.start, pos.end - string.length);
},
/**
* Checks if the selection is empty.
*
* @return
* Whether nothing is selected.
*/
isEmpty : function () {
return BUE.active.getSelection() === "";
},
/**
* Gets the remaining characters on the same line to the left of where the
* selection starts.
*
* @return
* A string of the prefixing characters, or "" if there is no prefix.
*/
getPrefix : function () {
var content = BUE.active.getContent();
var pos = BUE.active.posSelection();
// If the selection starts at the first character of the document,
// no prefix exists.
if (pos.start === 0) return "";
// The position where the prefix ends is one step to the left of
// where the selection starts.
var prefixEnd = pos.start - 1;
// Loop to the left, and stop when a line break is found or when
// the start of the document is reached.
for (var i = prefixEnd; i > 0; i--) {
if (/[\r\n]/.test(content.charAt(i))) {
i++;
break;
}
}
// The prefix is the leftmost value of the end of the prefix or
// the start of the line.
i = Math.min(i, prefixEnd);
// Add one to prefixEnd to include the rightmost character to the
// left of the selection.
return content.substring(i, prefixEnd + 1).replace(/[\r\n]/g, "");
},
/**
* Gets the remaining characters on the same line to the right of where the
* selection ends.
*
* @return
* A string of the suffixing character, or "" if there is no suffix.
*/
getSuffix : function () {
var content = BUE.active.getContent();
// The suffix starts where the selection ends.
var suffixStart = BUE.active.posSelection().end;
// Start at the start of the suffix, and loop until a line break
// or the end of the document is reached.
for (var i = suffixStart; i < content.length - 1; i++) {
// Match a line break.
if (/[\r\n]/.test(content.charAt(i))) {
// decrement i since this character isn't a part of the
// suffix.
i--;
break;
}
}
// Add one to i since substring ends it selection before the i'th
// character.
return content.substring(suffixStart, i + 1);
},
/**
* @return
* Whether the selection has a prefix.
*/
hasPrefix : function () {
return markdownEditor.selection.getPrefix() !== "";
},
/**
* @return
* Whether the selection has a suffix.
*/
hasSuffix : function () {
return markdownEditor.selection.getSuffix() !== "";
},
/**
* Inserts a space if the caret was positioned next to a non white
* space character, both before and after the selection. The spaces are then
* excluded from the selection. The space is not added to the start or end if
* the selection is at the start or end of a line, respectively.
*/
space : function () {
// If the selection is not prefixed with a space and the selection
// is not at the start of a row.
if (!/(^|[ \t])$/m.test(markdownEditor.selection.getPrefix())) {
// Insert a space at the start of the selection.
markdownEditor.selection.replace(/^/, " ");
// Make the current selection exclude the space that was just
// inserted.
var pos = BUE.active.posSelection();
if (pos.start === pos.end) {
pos.end++;
}
BUE.active.makeSelection(pos.start + 1, pos.end);
delete pos;
}
// If the selection is not suffixed with a space and the selection
// is not ath the end of a row.
if (!/^([ \t]|$)/m.test(markdownEditor.selection.getSuffix())) {
// Suffix the selection with a space.
markdownEditor.selection.replace(/$/, " ");
// Adjust the selection to exclude the space that was just
// inserted.
var pos = BUE.active.posSelection();
if (pos.end > pos.start) {
pos.end--;
}
BUE.active.makeSelection(pos.start, pos.end);
delete pos;
}
},
/**
* Inserts line breaks before and after the current selection if the selection
* has a prefix or suffix respectively. The line breaks are then excluded from
* the selection. Any preceeding spaces are removed.
*/
lineBreak : function () {
var selection = markdownEditor.selection;
var getPos = BUE.active.posSelection.bind(BUE.active);
var getContent = BUE.active.getContent.bind(BUE.active);
// Remove any prefixing spaces.
var content = getContent();
var charactersToRemove = 0;
for (var i = getPos().start - 1; i >= 0; i--) {
if (content.charAt(i) === " ") {
charactersToRemove++;
}
else {
break;
}
}
delete content;
if (charactersToRemove) {
BUE.active.makeSelection(getPos().start - charactersToRemove, getPos().end);
selection.replace(new RegExp ("^ {" + charactersToRemove + "}"), "");
}
// Remove any suffixing spaces.
var content = getContent();
charactersToRemove = 0;
for (var j = getPos().end; j < content.length; j++) {
if (content.charAt(j) === " ") {
charactersToRemove++;
}
else {
break;
}
}
delete content;
if (charactersToRemove) {
BUE.active.makeSelection(getPos().start, getPos().end + charactersToRemove);
selection.replace(new RegExp(" {" + charactersToRemove + "}$"), "");
}
// If the first row of the selection has a prefix, or if the
// previous two characters aren't linebreaks we want to insert
// line breaks.
var charactersBeforeSelection = getContent().substring(0, getPos().start);
if (selection.hasPrefix() || (getPos().start !== 0 && !/\n\n$/.test(charactersBeforeSelection))) {
// Loop and include any prefixing line breaks in the selection
// so we only get 2 of them in total after the insertion.
var content = getContent();
for (var i = getPos().start - 1; content.charAt(i) === "\n"; i--) {
BUE.active.makeSelection(getPos().start - 1, getPos().end);
}
delete content;
// Replace the start of the selection with two line breaks, and
// adjust the selection to exclude those line breaks.
selection.replace(/^\n{0,2}/, "\n\n");
var pos = getPos();
BUE.active.makeSelection(Math.min(pos.start + 2, pos.end), pos.end);
delete pos;
}
var lastCharacterIndex = getContent().length - 1;
var charactersAfterSelection = getContent().substring(getPos().start);
if (selection.hasSuffix() || (getPos().end !== lastCharacterIndex && !/^\n\n/.test(charactersAfterSelection))) {
// Loop and include any suffixing line breaks in the selection
// so we can calculate the amount.
var content = getContent();
for (var i = getPos().end; content.charAt(i) === "\n"; i++) {
BUE.active.makeSelection(getPos().start, getPos().end + 1);
}
delete content;
selection.replace(/\n{0,2}$/, "\n\n");
var pos = getPos();
BUE.active.makeSelection(pos.start, Math.max(pos.end - 2, pos.start));
delete pos;
}
},
/**
* Checks if the selection is prefixed with a substring.
*
* @param substring
* The prefix to look for.
* @return
* A boolean.
*/
startsWith : function (substring) {
return BUE.active.getSelection().substring(0, substring.length) === substring;
},
/**
* Checks if the selection is suffixed with a substring.
*
* @param substring
* The suffix to look for.
* @return
* A boolean.
*/
endsWith : function (substring) {
var selection = BUE.active.getSelection();
return selection.substring(selection.length - substring.length) === substring;
},
/**
* Checks if the selection is prefixed and suffixed with a substring.
*
* @param prefix
* The prefix to look for
* @param suffix
* The suffix to look for, if not specified the prefix is used as
* the suffix as well.
* @return
* A boolean.
*/
surroundedBy : function (prefix, suffix) {
if (!(1 in arguments)) {
suffix = prefix;
}
return markdownEditor.selection.startsWith(prefix) && markdownEditor.selection.endsWith(suffix);
},
/**
* Puts the caret at the end of the current selection.
*/
caretAtEnd : function () {
var pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.end, pos.end);
},
/**
* Surrounds the selection by a prefix and suffix.
* The inserted characters are made part of the selection.
*
* @param prefix
* The string to prepend to the selection.
* @param suffix
* The string to append to the selection, prefix is used as suffix if
* suffix is left out.
*/
wrap : function (prefix, suffix) {
// If suffix isn't specified, it defaults to the prefix.
if (!(1 in arguments)) {
suffix = prefix;
}
markdownEditor.selection.replaceAll(prefix + BUE.active.getSelection() + suffix);
},
/**
* If the selection starts with empty rows, they are removed
* from the selection since the behavior would be unexpected
* otherwise. The same is done if it ends with empty rows.
*/
trim : function () {
// Match newlines and line breaks, and make sure the match
// ends with a line break, otherwise spaces in front of the
// first row will be excluded.
if (/^([\r\n\s]*[\r\n])/.test(BUE.active.getSelection())) {
var pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.start + RegExp.$1.length, pos.end);
}
// Equivalent as above, but for the end of the line.
if (/([\r\n][\r\n\s]*)$/.test(BUE.active.getSelection())) {
var pos = BUE.active.posSelection();
BUE.active.makeSelection(pos.start, pos.end - RegExp.$1.length);
}
},
/**
* Adds the given prefix to every row of the current selection.
*
* @param prefix
* The string to prefix rows with.
*/
prefixRows : function (prefix) {
markdownEditor.selection.replace(/^/gm, prefix);
},
/**
* Adds the given suffix to every row of the current selection.
*
* @param suffix
* The string to append to every row.
*/
suffixRows : function (suffix) {
markdownEditor.selection.replace(/$/gm, suffix);
},
/**
* Replaces the whole selection with the given replacement.
*
* @param replacement
* The string to insert.
* @param caret
* Optional. Specifies where the caret should be positioned afterwards,
* see replace.
*/
replaceAll : function (replacement, caret) {
markdownEditor.selection.replace(/^[\s\S]*$/, replacement, caret);
},
/**
* Does a search in the current selection and replaces matches.
* Behaves exactly like String:replace in how it replaces.
*
* @param search
* String or RegExp, passed directly to String:replace.
* @param replacement
* String or Function, passed directly to String:replace.
* @param caret
* Optional. Where the caret should be positioned after the replacement,
* valid values are "start" and "end".
* If omitted, the previous selection is maintained.
*/
replace : function (search, replacement, caret) {
// Only pass caret if it's specified, we don't make any assumption
// on how replaceSelection handles optional arguments.
if (caret) {
BUE.active.replaceSelection(BUE.active.getSelection().replace(search, replacement), caret);
}
else {
BUE.active.replaceSelection(BUE.active.getSelection().replace(search, replacement));
}
}
};
/*******************************************************************************
* LINK
******************************************************************************/
/**
* Displays a dialog where the user can create a link. The reference is added to
* the reference section of the BUE.
*/
markdownEditor.link = function () {
var t = markdownEditor.t;
var tag = Cactus.DOM.tag;
var createForm = markdownEditor.dialog.createForm;
var mDialog = markdownEditor.dialog;
// Default values for form fields.
var hrefValue = "";
var referenceValue = "";
var titleValue = "";
var textValue = BUE.active.getSelection();
var inlineValue = null;
// Create the dialog form.
var form = createForm(
{ label : t("Text"), mandatory : true, attributes : { name : "text", value : textValue } },
{ label : t("Description"), attributes : { name : "title", value : titleValue } },
{ label : t("Reference"), attributes : { name : "reference", value : referenceValue } },
{ label : t("URL"), mandatory : true, attributes : { name : "href", value : hrefValue } },
{ label : t("Inline"), attributes : { name : "inline", type : "checkbox", checked: "checked", value: inlineValue } }
);
// Add a submit handler and various buttons.
var submitFunction = markdownEditor.link._process.bind(null, form, "Links");
mDialog.setOnsubmit(form, submitFunction);
mDialog.addIMCELink(form.elements.href.parentNode, form.elements.href);
mDialog.addSubmitButton(form, submitFunction);
mDialog.addCancelButton(form);
// Open the dialog and add display the form.
mDialog.open(t("Insert link"), "link");
mDialog.getContent().appendChild(form);
mDialog.focusFirst();
};
// Processes the form submission.
markdownEditor.link._process = function (form) {
var Reference = markdownEditor.Reference;
var t = markdownEditor.t;
var referenceType = "Links";
var text = form.elements.text.value;
var reference = form.elements.reference.value || text;
var href = form.elements.href.value;
var title = form.elements.title.value;
var inline = form.elements.inline.checked || false;
// Validate input.
markdownEditor.dialog.clearErrors();
var valid = true;
if (!text) {
markdownEditor.dialog.addError(t("Text is a required field."));
valid = false;
}
if (!href) {
markdownEditor.dialog.addError(t("URL is a required field."));
valid = false;
}
if (!valid) {
return;
}
if (inline) {
// Insert inline link after caret position
var replaceString = "[" + text + "](" + href + ( title ? ' "' + title + '"' : '' ) + ")";
markdownEditor.selection.replaceAll(replaceString);
BUE.dialog.close();
}
else {
// The text inserted at the caret position.
var textString = text !== reference ? "[" + text + "][" + reference + "]" : "[" + reference + "][]";
// The reference to add to the reference section of the BUE.
var ref = new Reference(referenceType, reference, href + (title ? ' "' + title + '"' : ""));
markdownEditor.references._callback(textString, ref);
}
};
/*******************************************************************************
* IMAGE
******************************************************************************/
/**
* Displays a dialog where the user can add an inline image. The reference is
* added to the reference section of the BUE. The IMCE dialog is integrated
* if IMCE is enabled.
*/
markdownEditor.image = function () {
var t = markdownEditor.t;
var tag = Cactus.DOM.tag;
var createForm = markdownEditor.dialog.createForm;
// Default values for form fields.
var hrefValue = "";
var referenceValue = "";
var titleValue = "";
var textValue = BUE.active.getSelection();
var inlineValue = null;
// Creating the form for the dialog.
var form = createForm(
{ label : t("Alt"), mandatory : true, attributes : { name : "alt", value : textValue } },
{ label : t("Title"), attributes : { name : "title", value : titleValue } },
{ label : t("Reference"), attributes : { name : "reference", value : referenceValue } },
{ label : t("URL"), mandatory : true, attributes : { name : "href", value : hrefValue } },
{ label : t("Inline"), attributes : { name : "inline", type : "checkbox", value: inlineValue } }
);
// Create an onsubmit handler and various buttons.
var submitFunction = markdownEditor.image._process.bind(null, form, "Images");
var mDialog = markdownEditor.dialog;
mDialog.setOnsubmit(form, submitFunction);
mDialog.addIMCELink(form.elements.href.parentNode, form.elements.href);
mDialog.addSubmitButton(form, submitFunction);
mDialog.addCancelButton(form);
// Open the dialog and display the form.
mDialog.open(t("Insert image"), "image");
mDialog.getContent().appendChild(form);
mDialog.focusFirst();
};
/**
* Handles submissions for adding images.
*
* @param form
* The form element of the dialog.
*/
markdownEditor.image._process = function (form) {
var Reference = markdownEditor.Reference;
var t = markdownEditor.t;
var referenceType = "Images";
var alt = form.elements.alt.value;
var title = form.elements.title.value;
var reference = form.elements.reference.value || alt;
var href = form.elements.href.value;
var inline = form.elements.inline.checked || false;
// Validate input.
markdownEditor.dialog.clearErrors();
var valid = true;
if (!alt) {
markdownEditor.dialog.addError(t("Alt is a required field."));
valid = false;
}
if (!href) {
markdownEditor.dialog.addError(t("URL is a required field."));
valid = false;
}
if (!valid) {
return;
}
if (inline) {
// Insert inline link after caret position
var replaceString = "![" + alt + "](" + href + ( title ? ' "' + title + '"' : '' ) + ")";
markdownEditor.selection.replaceAll(replaceString);
BUE.dialog.close();
}
else {
// The text added at the caret position.
var textString = alt ? "![" + alt + "][" + reference + "]" : "![" + reference + "]";
// The reference to add to the reference section.
var ref = new Reference(referenceType, reference, href + (title ? ' "' + title + '"' : ""));
markdownEditor.references._callback(textString, ref);
}
};
/*******************************************************************************
* FOOTNOTE
******************************************************************************/
/**
* Opens a dialog that lets the user create a footnote and its associated
* reference. A default reference name is specified.
*/
markdownEditor.footnote = function () {
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
var createForm = markdownEditor.dialog.createForm;
var mDialog = markdownEditor.dialog;
var referenceValue = "";
var textValue = "";
var contents = BUE.active.getContent();
// Get the first available reference number.
for (var i = 1; new RegExp("\\[\\^" + i + "\\]", "m").test(contents); i++);
referenceValue = i;
// Create the dialog form.
var form = createForm(
{ label : t("Reference"), mandatory : true, attributes : { name : "reference", value : referenceValue } },
{ label : t("Text"), tagName : "textarea", attributes : { name : "text", value : textValue, className : 'form-textarea' } }
);
// Create the onsubmit function
var submitFunction = markdownEditor.footnote._process.bind(null, form, "Footnotes");
// Add buttons.
mDialog.setOnsubmit(form, submitFunction);
mDialog.addSubmitButton(form, submitFunction);
mDialog.addCancelButton(form);
// Open the dialog and display the form.
mDialog.open(t("Insert footnote"), "footnote");
mDialog.getContent().appendChild(form);
mDialog.focusFirst();
};
/**
* Handles the submission when creating footnotes.
*
* @param form
* The HTMLFormElement of the dialog.
*/
markdownEditor.footnote._process = function (form) {
var Reference = markdownEditor.Reference;
var t = markdownEditor.t;
var referenceType = "Notes";
var reference = form.elements.reference.value;
var text = form.elements.text.value;
// Validate input.
markdownEditor.dialog.clearErrors();
if (!reference) {
markdownEditor.dialog.addError(t("Reference is a required field."));
return;
}
// The text to insert at the caret position.
var textString = "[^" + reference + "]";
// The reference to add to the reference section.
var ref = new Reference(referenceType, reference, text);
ref.setPrefix("[^");
window.r = ref;
markdownEditor.references._callback(textString, ref, false, t("You cannot make two references to the same footnote."), false);
};
/*******************************************************************************
* ABBREVIATION
******************************************************************************/
/**
* Opens a dialog that lets the user add an abbreviation along with its
* definiton.
*/
markdownEditor.abbreviation = function () {
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
var createForm = markdownEditor.dialog.createForm;
// Default values for the form elements.
var abbreviationValue = BUE.active.getSelection();
var textValue = "";
// Create the dialog form.
var form = createForm(
{ label : t("Abbreviation"), mandatory : true, attributes : { name : "abbreviation", value : abbreviationValue } },
{ label : t("Definition"), attributes : { name : "text", value : textValue } }
);
// Create the submit button and have it assign an onclick handler
// that processes the form submission. Also add a cancel button.
var submitFunction = markdownEditor.abbreviation._process.bind(null, form, "Abbreviations");
var mDialog = markdownEditor.dialog;
mDialog.setOnsubmit(form, submitFunction);
mDialog.addSubmitButton(form, submitFunction);
mDialog.addCancelButton(form);
// Open the dialog and display the form.
mDialog.open(t("Insert abbreviation"), "abbreviation");
mDialog.getContent().appendChild(form);
mDialog.focusFirst();
};
/**
* Handles submissions when adding abbreviations.
*
* @param form
* The HTMLFormElement of the dialog.
*/
markdownEditor.abbreviation._process = function (form) {
var Reference = markdownEditor.Reference;
var t = markdownEditor.t;
var referenceType = "Abbreviations";
var abbreviation = form.elements.abbreviation.value;
// The text to be inserted at the caret position.
var text = form.elements.text.value;
if (!text) {
text = abbreviation;
}
// Validate input.
markdownEditor.dialog.clearErrors();
if (!abbreviation) {
markdownEditor.dialog.addError(t("Abbreviation is a required field"));
return;
}
// The reference to be added to the reference section.
var ref = new Reference(referenceType, abbreviation, text);
ref.setPrefix("*[");
markdownEditor.references._callback(abbreviation, ref);
};
/*******************************************************************************
* AUTO LINK
******************************************************************************/
/**
* Turns the selection into an auto link. An auto link is just an URL inside
* angle brackets, <http://www.example.com>.
*/
markdownEditor.autoLink = function () {
var mSelection = markdownEditor.selection;
// Insert brackets around the caret if nothing is selected.
if (mSelection.isEmpty()) {
mSelection.space();
mSelection.insertBefore("<");
mSelection.insertAfter(">");
}
// If the selection is an auto link, the angle brackets are removed.
else if (mSelection.surroundedBy("<", ">")) {
mSelection.replace(/^<([\s\S]+)>/m, "$1");
}
// Otherwise angle brackets are added around the selection.
else {
mSelection.wrap("<", ">");
}
};
/*******************************************************************************
* UNORDERED LIST
******************************************************************************/
/**
* Toggles the current selection into or out of being an unordered list, it can
* also convert from an ordered list into an unordered.
*/
markdownEditor.unorderedList = function () {
var mSelection = markdownEditor.selection;
mSelection.trim();
// Remove OL enumeration if it's present
if (/^\d+\. /.test(BUE.active.getSelection())) {
mSelection.replace(/^\d+\. /gm, "");
mSelection.replace(/^ {4}/gm, "");
}
// If the selection is an unordered list.
if (/^\* /m.test(BUE.active.getSelection())) {
// Remove the list characters.
mSelection.replace(/^\* +/gm, "");
mSelection.replace(/^ {4}/gm, "");
}
// If the selection is empty, insert a list item and put the caret
// at the end of the insertion.
else if (BUE.active.getSelection() === "") {
mSelection.replaceAll("* ");
}
// Append an asterisk to every row that isn't indented.
// Does not prefix empty rows.
else {
var lines = BUE.active.getSelection().split(/\r?\n|\r/);
var newLines = [];
var prefixReg = /^(?: {4}|> )/;
newLines.push("* " + lines[0]);
// Gets the current prefix of the line, a prefix in this sense is
// only a block quote "> " or a code block " ".
function getLinePrefix(line) {
var match = prefixReg.exec(line);
return match ? match[0] : "";
}
for (var i = 1, line; i < lines.length; i++) {
var linePrefix = getLinePrefix(lines[i]);
var previousLinePrefix = getLinePrefix(lines[i - 1]);
// Do nothing with empty lines.
if (lines[i] === "") {
newLines.push(lines[i]);
}
// If the line has no prefix, it should be prefixed.
else if (!linePrefix) {
newLines.push("* " + lines[i]);
}
// A prefix should be added if the line does not have the
// same prefix as the previous line. For example a block quote
// line below another one means that they both belong to the
// same list item.
else if (lines[i] !== "" && linePrefix !== previousLinePrefix && previousLinePrefix) {
newLines.push("* " + lines[i]);
}
// We've concluded that the line belongs to the previous line,
// so we just indent it.
else {
newLines.push(" " + lines[i]);
}
}
mSelection.replaceAll(newLines.join("\n"));
}
mSelection.lineBreak();
mSelection.caretAtEnd();
};
/*******************************************************************************
* ORDERED LIST
******************************************************************************/
/**
* Turns the selection into an ordered list, if the selection is alreadf an
* ordered list the enumeration is removed. The function can also turn an
* unordered list into an ordered one.
*/
markdownEditor.orderedList = function () {
var mSelection = markdownEditor.selection;
mSelection.trim();
// If the selection is an unordered list, we remove the bullets.
if (/^\* /.test(BUE.active.getSelection())) {
mSelection.replace(/^\* {3}/gm, "");
mSelection.replace(/^ {4}/gm, "");
}
// If the list is formatted as ordered already, we remove the
// enumeration.
if (/^ *\d+\./.test(BUE.active.getSelection())) {
mSelection.replace(/^ *\d+\.\s*/gm, "");
mSelection.replace(/^ {4}/gm, "");
}
// If the selection is empty, insert a list item and put the caret
// at the end of the insertion.
else if (BUE.active.getSelection() === "") {
mSelection.replaceAll("1. ");
}
// Insert numbers for every row of the selection, if the selection
// is an unordered list the asterisks are removed in the process.
else {
var prefixCounter = 1;
// Get the number prefix for the next row. An internal counter is kept.
function createLinePrefix() {
return (prefixCounter++) + ". ";
}
var lines = BUE.active.getSelection().split(/\r?\n|\r/);
var newLines = [];
// Matches a code block or block quote line.
var prefixReg = /^(?: {4}|> )/;
newLines.push(createLinePrefix() + lines[0]);
function getLinePrefix(line) {
var match = prefixReg.exec(line);
return match ? match[0] : null;
}
// Handle each line separately.
for (var i = 1, line; i < lines.length; i++) {
var linePrefix = getLinePrefix(lines[i]);
var previousLinePrefix = getLinePrefix(lines[i - 1]);
// Skip empty lines.
if (lines[i] === "") {
newLines.push(lines[i]);
}
// A prefix should only be added if the line does not have the
// same prefix as the previous line. For example a block quote
// line below another one means that they both belong to the
// same list item.
else if (!linePrefix || (lines[i] !== "" && linePrefix !== previousLinePrefix)) {
newLines.push(createLinePrefix() + lines[i]);
}
// The line belongs to the previous line, so it's indented to match this.
else {
newLines.push(" " + lines[i]);
}
}
mSelection.replaceAll(newLines.join("\n"));
}
mSelection.lineBreak();
mSelection.caretAtEnd();
};
/*******************************************************************************
* HEADER
******************************************************************************/
/**
* Gives the user a dialog for creating headers. Selected headers can also be
* modified.
*/
markdownEditor.header = function () {
var t = markdownEditor.t;
var tag = Cactus.DOM.tag;
var createForm = markdownEditor.dialog.createForm;
var headerValue, textValue, idValue;
var selection = BUE.active.getSelection();
// If a header is selected, we extract the values.
// If a value is unavailable, a default value is specified.
if (/(#+)\s*(\S*)\s*\{#([^\}]+)\}/.test(selection)) {
headerValue = RegExp.$1.length || "2";
textValue = RegExp.$2 || "";
idValue = RegExp.$3 || "";
}
// Otherwise we assign default values.
else {
headerValue = 2;
textValue = BUE.active.getSelection();
idValue = "";
}
// Available header depths. Used to create the select in the dialog form.
var options = {
2 : 2,
3 : 3,
4 : 4,
5 : 5,
6 : 6
};
// Create the dialog form.
var form = createForm(
{ label : t("Header Level"), tagName : "select", options : options, selected : headerValue, attributes : { name : "header_type" } },
{ label : t("Text"), mandatory : true, attributes : { name : "text", value : textValue } },
{ label : t("ID"), attributes : { name : "id", value : idValue } }
);
// Create a submit button and add an onclick/onsubmit handler. Also
// create a cancel button.
var mDialog = markdownEditor.dialog;
var submitFunction = markdownEditor.header._callback.bind(null, form);
mDialog.setOnsubmit(form, submitFunction);
// The event is passed on through the submit button to onsubmit.
var submitButton = mDialog.addSubmitButton(form, function () {});
submitButton.onmousedown = submitFunction;
mDialog.addCancelButton(form);
// Create the dialog and display the form.
mDialog.open(t("Insert header"), "header");
mDialog.getContent().appendChild(form);
mDialog.focusFirst();
};
/**
* Handles submissions when adding headers
*
* @param form
* The HTMLFormElement of the dialog.
*/
markdownEditor.header._callback = function (form) {
var t = markdownEditor.t;
// Try to parse the integer value, default to 2 if that isn't possible.
var headerType = parseInt(form.elements.header_type.value, 10) || 2;
var text = form.elements.text.value;
var id = form.elements.id.value ? " {#" + form.elements.id.value + "}" : "";
// Validate input.
markdownEditor.dialog.clearErrors();
if (!text) {
markdownEditor.dialog.addError(t("Text is a required field"));
return;
}
// Add one # for every form level (2 becomes ##, 3 becomes ###).
var headerHashes = markdownEditor.extras.string.repeat("#", headerType);
// Insert the information and close the dialog.
BUE.active.replaceSelection(headerHashes + " " + text + " " + id);
markdownEditor.selection.lineBreak();
markdownEditor.selection.caretAtEnd();
BUE.dialog.close();
};
/*******************************************************************************
* INLINE CODE
******************************************************************************/
/**
* Wraps the selection with backticks, or unwraps if the selection is wrapped
* already.
*/
markdownEditor.codeInline = function () {
if (markdownEditor.selection.isEmpty()) {
markdownEditor.selection.space();
markdownEditor.selection.insertAround("`");
}
// If the selection is already code, the back ticks are removed.
else if (markdownEditor.selection.surroundedBy("`")) {
markdownEditor.selection.replace(/^\`([\s\S]+)\`/m, "$1");
}
// Otherwise back ticks are added around the selection.
else {
BUE.active.replaceSelection("`" + BUE.active.getSelection() + "`");
}
};
/*******************************************************************************
* EMPHASIS
******************************************************************************/
/**
* Wraps the selection with asterisks or unwraps if the selection is emphasized
* already.
*/
markdownEditor.emphasis = function () {
if (markdownEditor.selection.isEmpty()) {
markdownEditor.selection.space();
markdownEditor.selection.insertAround("*");
}
// If the selection is emphasized, the asterisks are removed.
else if (markdownEditor.selection.surroundedBy("*")) {
markdownEditor.selection.replace(/^\*([\s\S]+)\*/m, "$1");
}
// Otherwise asterisks are added around the selection.
else {
markdownEditor.selection.wrap("*");
}
};
/*******************************************************************************
* STRONG EMPHASIS
******************************************************************************/
/**
* Wraps the selection with double asterisks, or unwraps if it's already
* emphasized.
*/
markdownEditor.strongEmphasis = function () {
if (markdownEditor.selection.isEmpty()) {
markdownEditor.selection.space();
markdownEditor.selection.insertAround("**");
}
// If the selection is emphasized, the asterisks are removed.
if (markdownEditor.selection.surroundedBy("**")) {
markdownEditor.selection.replace(/^\*+([\s\S]+?)\*+$/, "$1");
}
// Otherwise asterisks are added around the selection.
else {
markdownEditor.selection.replace(/^\**([\s\S]+?)\**$/, "**$1**");
}
};
/*******************************************************************************
* BLOCK QUOTES
******************************************************************************/
/**
* Toggles block quoting of the selection.
*/
markdownEditor.blockQuote = function () {
var mSelection = markdownEditor.selection;
mSelection.excludeLineBreaks();
// Remove ">" in the beginning of rows if they are there.
if (mSelection.startsWith(">")) {
mSelection.replace(/^>\s?/gm, "");
}
// Otherwise "> " is prepended to every row, existing code blocks
// are removed in the process.
else {
mSelection.prefixRows("> ");
}
mSelection.lineBreak();
mSelection.caretAtEnd();
};
/*******************************************************************************
* HORIZONTAL RULER
******************************************************************************/
/**
* Inserts a horizontal ruler in its own paragraph.
*/
markdownEditor.horizontalRuler = function () {
var selection = markdownEditor.selection;
selection.replaceAll("---------");
selection.lineBreak();
selection.caretAtEnd();
};
/*******************************************************************************
* CODE BLOCK
******************************************************************************/
/**
* Toggles a code block for the selection.
*/
markdownEditor.codeBlock = function () {
var mSelection = markdownEditor.selection;
mSelection.excludeLineBreaks();
if (mSelection.startsWith(" ")) {
mSelection.replace(/^ {4}/gm, "");
}
else {
mSelection.prefixRows(" ");
}
// Make sure the block is located on its own row, and place the
// caret at the end.
mSelection.selectToEndOfLine();
mSelection.lineBreak();
mSelection.caretAtEnd();
};
/*******************************************************************************
* LINE BREAK
******************************************************************************/
/**
* Inserts a line break.
*/
markdownEditor.lineBreak = function () {
// Replace selection with two spaces and a line break.
markdownEditor.selection.replaceAll(" \n", "end");
};
/*******************************************************************************
* TABLE
******************************************************************************/
/**
* Creates a dynamic table dialog for creating a Markdown Extra table.
*
* Implementation:
* The table used to structure the form has at least three rows and at least
* one column. The first row contains table headings, the second one contains
* alignments for the content cells. Finally, the rest of the rows (the amount
* of these is dynamic) contain the actual table data. The table will end up
* being structured like it is in the dialog.
*/
markdownEditor.Table = (function() {
var tag = Cactus.DOM.tag;
var t = markdownEditor.t;
var string = markdownEditor.extras.string;
var collection = markdownEditor.extras.collection;
var dom = markdownEditor.extras.dom;
var ClassNames = Cactus.DOM.ClassNames;
function Table () {
this.createForm();
this.columns = this.startingColumns;
this.openDialog();
} Table.prototype = {
// The amount of rows to display when the dialog opens.
startingRows : 2,
// The amount of columns to display when the dialog opens.
startingColumns : 2,
// The amount of content rows and content columns currently in the dialog.
rows : 0,
columns : 0,
// References to the form elements.
form : null,
table : null,
tbody : null,
/**
* @return
* A TD with an input box for a table header.
*/
createHeaderCell : function () {
return tag("td", null, tag("input", {
type : "text",
name : "header",
className : "form-text"
}));
},
/**
* Creates a cell for choosing the alignment of a column, consists of a
* select with four values, "none" (or ""), "lef", "center" and "right".
*
* @return
* A TD with three radio buttons along with their labels.
*/
createAlignmentCell : function () {
return tag("td", { className : "alignment-cell" }, tag("select", { className : "form-select" }, [
tag("option", { value : "none", selected : true }, t("- None -")),
tag("option", { value : "left" }, t("Left")),
tag("option", { value : "center" }, t("Center")),
tag("option", { value : "right" }, t("Right"))
]));
},
/**
* Creates a cell for setting the value of a table cell.
*
* @return
* A TD with an input.
*/
createContentCell : function () {
return tag("td", null, [
tag("input", {
type : "text",
className : "form-text"
})
]);
},
/**
* Removes a content row from the table if there are at least 2 rows
* already.
*
* @param row
* The row to remove.
*/
removeRow : function (row) {
if (this.rows >= 2) {
this.tbody.removeChild(row);
this.rows--;
if (this.rows === 1) {
this.getRemoveRowButton(this.getContentRows()[0]).disabled = true;
}
}
},
/**
* Gets the remove button for the given row.
*
* @param row
* The row to find the button on.
* @return
* The button that removes the specified row.
*/
getRemoveRowButton : function (row) {
var cell = row.cells[row.cells.length - 1];
var buttons = cell.getElementsByTagName("button");
for (var i = 0; i < buttons.length; i++) {
if (buttons[i].innerHTML === "-") {
return buttons[i];
}
}
throw new Error("No button found");
},
/**
* Adds a content row to the document.
*
* @param previousRow
* The row that will be the previousSibling of the row created.
*/
addRow : function (previousRow) {
if (this.rows === 1) {
this.getRemoveRowButton(this.getContentRows()[0]).disabled = false;
}
if (this.tbody.lastChild === previousRow) {
this.tbody.appendChild(this.createContentRow());
}
else {
this.tbody.insertBefore(this.createContentRow(), previousRow.nextSibling);
}
},
/**
* Creates a row of content cells, takes into account the current number
* of columns.
*
* @return
* A TR containing content cells.
*/
createContentRow : function () {
var cells = [tag("th")];
var columns = this.columns || this.startingColumns;
for (var i = 0; i < columns; i++) {
cells.push(this.createContentCell());
}
var modificationContainer = tag("div", {
className : "row-modification-container"
});
cells.push(tag("td", { className : "row-modification-cell" }, modificationContainer));
var row = tag("tr", {
className : "content-row"
}, cells);
// Add buttons for adding and removing rows.
modificationContainer.appendChild(this.createRemoveRowButton(row));
modificationContainer.appendChild(this.createAddRowButton(row));
this.rows++;
return row;
},
/**
* Adds a button for adding rows.
*
* @param previousRow
* The row to insert new rows after if the button is clicked.
* @param title
* An optional title text for the button.
* @return
* A button that adds a row below another one.
*/
createAddRowButton : function (previousRow, title) {
title = title || t("Insert a row below this row.");
var addButton = tag("button", {
title : title,
className : "add-row-button"
}, "+");
addButton.onclick = this.addRow.bind(this, previousRow).wait(0).returning(false);
return addButton;
},
/**
* Creates a button for removing the given row.
*
* @param row
* The row to remove when the button is clicked.
* @param title
* An optional title for the button.
*/
createRemoveRowButton : function (row, title) {
title = title || t("Remove this row.");
return tag("button", {
title : title,
className : "remove-row-button",
onclick : this.removeRow.bind(this, row).wait(0).returning(false)
}, "-");
},
/**
* Creates a button for adding a column after the given cell.
*
* @param cell
* The column this cell belongs to will be the column that the new column
* is inserted after.
* @param title
* An optional title for the button.
* @return
* The HTMLButtonElement created.
*/
createAddColumnButton : function (cell, title) {
title = title || t("Insert a new column to the right of this column.");
var addButton = tag("button", { title : title }, "+");
addButton.onclick = this.addColumn.bind(this, cell).wait(0).returning(false);
return addButton;
},
/**
* Creates a button that adds a column before the given cell's column.
*
* @param cell
* The table cell whose column the new column is added before.
* @param title
* Optional. The localized text of the button.
* @return
* A HTMLButtonElement that can add a new column.
*/
createPrependColumnButton : function (cell, title) {
title = title || t("Insert a new column to the left of this column.");
return tag("button", {
className : "prepend-column-button",
title : title,
onclick : this.prependColumn.bind(this, cell).wait(0).returning(false)
}, "+");
},
/**
* Creates a button for removing the column the given cell belongs to.
*
* @param cell
* When clicked the column this cell belongs to will be removed.
* @return
* The HTMLButtonElement created.
*/
createRemoveColumnButton : function (cell) {
var removeButton = tag("button", { title : t("Remove this column.") }, "-");
removeButton.onclick = this.removeColumn.bind(this, cell).wait(0).returning(false);
return removeButton;
},
/**
* Creates a cell with buttons for adding and removing rows.
*
* @return
* A TD with two buttons.
*/
createColumnModificationCell : function () {
var td = tag("td", {
className : "column-modification-cell"
});
td.appendChild(this.createPrependColumnButton(td));
td.appendChild(this.createRemoveColumnButton(td));
td.appendChild(this.createAddColumnButton(td));
return td;
},
/**
* Adds a row of buttons for adding and removing columns.
*/
addColumnModificationRow : function () {
var row = tag("tr", {
className : "column-modification-row"
});
var cells = [];
// Add an empty column on the left side.
var td = tag("td", null, null);
row.appendChild(td);
// Add a modification cell for every starting column of the table.
for (var i = 0; i < this.startingColumns; i++) {
row.appendChild(this.createColumnModificationCell(row));
}
this.tbody.appendChild(row);
},
/**
* Creates the form and the initial table for the dialog.
*/
createForm : function () {
// Create the two top rows.
var headers = [tag("th", null, t("Headers"))];
var alignments = [tag("th", null, t("Col Align"))];
for (var i = 0; i < this.startingColumns; i++) {
headers.push(this.createHeaderCell());
alignments.push(this.createAlignmentCell());
}
// cell for the + button.
headers.push(tag("td"));
// Add a tbody for all the table's contents.
var alignmentRow = tag("tr", {
className : "alignment-row"
}, alignments);
var headerRow = tag("tr", {
className : "header-row"
}, headers);
this.tbody = tag("tbody", null, [
alignmentRow,
headerRow
]);
// Add a + button to the last alignment cell.
var removeButton = this.createRemoveRowButton(headerRow, "");
removeButton.onclick = function () {};
removeButton.disabled = true;
headers[headers.length - 1].appendChild(removeButton);
headers[headers.length - 1].appendChild(this.createAddRowButton(headerRow, t("Insert a new row above the first row.")));
// Add all default content rows.
for (var j = 0; j < this.startingRows; j++) {
this.tbody.appendChild(this.createContentRow());
}
// Add a row with +/- buttons for adding and removing columns.
this.addColumnModificationRow();
// Set the table and form to instance variables.
this.table = tag("table", null, this.tbody);
this.fieldset = tag("fieldset", null, this.table);
this.form = tag("form", {
id : "markdowneditor-dialog-form"
}, this.fieldset);
this.setFirstAlignmentColumnBehavior();
},
/**
* Sets a CSS class name for the first alignment column and removes it from
* the second one if it exists.
*/
setFirstAlignmentColumnBehavior : function () {
var modificationRow = this.getColumnModificationRow();
// Add the classname to the first cell, and remove it from the
// second.
ClassNames.add(modificationRow.cells[1], "first");
if (modificationRow.cells [2]) {
ClassNames.del(modificationRow.cells[2], "first");
}
},
/**
* Removes the column the given cell belongs to
*
* @param cell
* A table cell whose column is removed.
*/
removeColumn : function (cell) {
if (this.columns > 1) {
// Get the index of the column to remove.
var cellIndex = collection.indexOf(this.getColumnModificationRow().cells, cell);
// Loop through and remove the cell on each row.
var rows = this.table.rows;
for (var i = 0; i < rows.length; i++) {
rows[i].removeChild(rows[i].cells[cellIndex]);
}
this.columns--;
// Disable the button if this column is the last one.
if (this.columns === 1) {
this.getRemoveColumnButton(this.getColumnModificationRow().cells[1]).disabled = true;
}
}
this.setFirstAlignmentColumnBehavior();
},
/**
* Gets the button that removes a column from a cell.
*
* @param cell
* The cell to look for the button in.
* @return
* The button that removes columns.
*/
getRemoveColumnButton : function (cell) {
var buttons = cell.getElementsByTagName("button");
// Fetch the button with the correct title.
for (var i = 0; i < buttons.length; i++) {
if (buttons[i].innerHTML === "-") {
return buttons[i];
}
}
throw new Error("No button found.");
},
/**
* @return
* The table row containing column modification buttons.
*/
getColumnModificationRow : function () {
return this.table.rows[this.table.rows.length - 1];
},
/**
* @return
* The row containing header inputs.
*/
getHeaderRow : function () {
return this.table.rows[1];
},
/**
* @return
* The row containing alignment selects.
*/
getAlignmentRow : function () {
return this.table.rows[0];
},
/**
* @return
* The rows containing table content inputs.
*/
getContentRows : function () {
var rows = [];
// The first two rows are headers and alignments, and the last
// one is +/- buttons.
for (var i = 2; i < this.table.rows.length - 1; i++) {
rows.push(this.table.rows[i]);
}
return rows;
},
/**
* Adds a new column after the given cell's column.
*
* @param cell
* The column this cell belongs to will be the previous sibling of the new
* one.
*/
addColumn : function (cell) {
if (this.columns === 1) {
this.getRemoveColumnButton(this.getColumnModificationRow().cells[1]).disabled = false;
}
var cellIndex = collection.indexOf(this.getColumnModificationRow().childNodes, cell);
var previousHeaderCell = this.getHeaderRow().cells[cellIndex];
var previousAlignmentCell = this.getAlignmentRow().cells[cellIndex];
var previousColumnModificationCell = this.getColumnModificationRow().cells[cellIndex];
// Insert header and alignment.
dom.insertAfter(this.createHeaderCell(), previousHeaderCell);
dom.insertAfter(this.createAlignmentCell(), previousAlignmentCell);
// Insert rows.
var contentRows = this.getContentRows();
for (var i = 0; i < contentRows.length; i++) {
// Offset of 2 since header cells and alignment cells are above the content cells.
dom.insertAfter(this.createContentCell(), contentRows[i].cells[cellIndex]);
}
// Insert a column modification cell.
dom.insertAfter(this.createColumnModificationCell(), previousColumnModificationCell);
this.columns++;
this.setFirstAlignmentColumnBehavior();
},
prependColumn : function (cell) {
this.addColumn(cell.previousSibling);
},
// If the form was submitted, to prevent the user from
// accidentally submitting twice.
submitted : false,
/**
* Handles submissions, extracts the data from the form and serializes and
* inserts into the document.
*/
submitHandler : function () {
// Make sure only one submit is triggered.
if (this.submitted) return;
this.submitted = true;
// Get data from the dialog.
var headers = [];
var alignments = [];
// Fetch headers and alignments.
var headerElements = this.table.rows[1].getElementsByTagName("input");
var alignmentElements = this.table.rows[0].getElementsByTagName("select");
for (var i = 0; i < headerElements.length; i++) {
headers.push(headerElements[i].value);
var select = alignmentElements[i];
alignments.push(select.options[select.selectedIndex].value);
}
delete headerElements;
delete alignmentElements;
// Fetch all content.
var contentRows = [];
var row, rowElements;
for (var j = 2; j < this.table.rows.length; j++) {
row = this.table.rows[j];
rowElements = row.getElementsByTagName("input");
contentRows.push([]);
for (var k = 0; k < rowElements.length; k++) {
contentRows[contentRows.length - 1].push(rowElements[k].value);
}
}
delete row;
delete rowElements;
// Transform into the markdown extra string.
var tableString = "";
var headerString = "| " + headers.join(" | ") + " |";
var alignmentString = "";
var rowStrings = [];
var rowString = "";
// Gather the header/content separators, with their alignment.
for (var l = 0; l < headers.length; l++) {
var dashes = string.repeat("-", Math.max(headers[l].length, 3));
switch (alignments[l]) {
case "none":
// Don't modify the string.
break;
case "right":
// Suffix with a colon.
dashes = dashes.replace(/(.+)-$/, "$1:");
break;
case "left":
// Prefix with a colon.
dashes = dashes.replace(/^-(.+)/, ":$1");
break;
case "center":
// Surround by colons.
dashes = dashes.replace(/^-(.+)-$/, ":$1:");
break;
}
alignmentString += "| " + dashes + " ";
}
alignmentString += "|";
// Format all the content rows.
for (var m = 0; m < this.rows; m++) {
rowStrings.push("| " + contentRows[m].join(" | ") + " |");
}
// Join the rows into a string.
rowString = rowStrings.join("\n");
// Create the final table string.
tableString = headerString + "\n" + alignmentString + "\n" + rowString;
// Insert the table and close the dialog.
markdownEditor.selection.replaceAll(tableString);
markdownEditor.selection.lineBreak();
markdownEditor.selection.caretAtEnd();
BUE.dialog.close();
},
/**
* Opens the dialog, appends the content and creates buttons.
*/
openDialog : function () {
var mDialog = markdownEditor.dialog;
// Add buttons.
mDialog.setOnsubmit(this.form, this.submitHandler.bind(this));
mDialog.addSubmitButton(this.form, this.submitHandler.bind(this));
mDialog.addCancelButton(this.form);
// Open and setup the dialog.
mDialog.open(t("Insert table"), "table");
mDialog.getContent().appendChild(this.form);
mDialog.focusFirst();
var tmp = document.getElementById("bue-dialog");
function f () {
tmp.style.left = parseInt(tmp.style.left) + 1 + "px";
}
setTimeout(f, 1);
setTimeout(f, 250);
}
};
return Table;
})();
/*******************************************************************************
* DEFINITION LIST
******************************************************************************/
/**
* Gives the user a dynamic form in a dialog for adding definition lists.
* Rows can be added and removed.
*/
markdownEditor.DefinitionList = (function () {
var t = markdownEditor.t;
var tag = Cactus.DOM.tag;
var dom = markdownEditor.extras.dom;
var mDialog = markdownEditor.dialog;
var ClassNames = Cactus.DOM.ClassNames;
var getElementByClassName = markdownEditor.extras.getElementByClassName;
var getElementsByClassName = markdownEditor.extras.getElementsByClassName;
function DefinitionList () {
this.createForm();
this.openDialog();
} DefinitionList.prototype = {
// Current number of rows of the dialog.
rows : 0,
// Default amount of rows.
startingRows : 2,
form : null,
/**
* Creates the default dialog contents.
*/
createForm : function () {
this.fieldset = tag("fieldset");
this.form = tag("form", {
id : "markdowneditor-dialog-form",
onsubmit : this.process.bind(this).wait(0).returning(false)
}, this.fieldset);
this.contentContainer = tag("table", {
className : "content-container"
});
this.fieldset.appendChild(this.contentContainer);
this.contentContainer.appendChild(this.createHeaderRow());
// Create the default content rows.
for (var i = 0; i < this.startingRows; i++) {
this.contentContainer.appendChild(this.createContentRow());
}
this.setFirstRowBehavior();
// Add buttons.
mDialog.addSubmitButton(this.form, this.process.bind(this));
mDialog.addCancelButton(this.form);
},
createHeaderRow : function () {
return tag("tr", {
className : "header-row"
}, [tag("th", {
className : "header-cell"
}, t("Term")), tag("th", {
className : "header-cell"
}, t("Definition"))]);
},
/**
* Creates a content row, a div with an input and textarea representing a dt
* and dd combo. Buttons for adding a new row and removing the current one
* are added.
*
* @return
* The created div.
*/
createContentRow : function () {
var row = tag("tr", {
className : "content-row"
});
// Append the different parts of the row.
row.appendChild(this.createTitleCell());
row.appendChild(this.createDescriptionCell());
row.appendChild(this.createModificationCell(row));
this.rows++;
return row;
},
// The default value for title inputs.
titleCellDefaultValue : "",
/**
* @return
* A new element holding an input for a title text.
*/
createTitleCell : function () {
var self = this;
// Create a div with an input, the input's default value is
// cleared when it gains focus.
return tag("td", {
className : "title-cell"
}, tag("input", {
value : this.titleCellDefaultValue,
type : "text",
className : "title-cell-input form-text",
onfocus : function () {
if (this.value === self.titleCellDefaultValue) {
this.value = "";
}
}
}));
},
// The default value for description textareas.
descriptionCellDefaultValue : "",
/**
* @return
* A new element holding a textarea for a definition text.
*/
createDescriptionCell : function () {
var self = this;
// Create a div with a textarea, the textarea's default value is
// cleared when the element gains focus.
return tag("td", {
className : "description-cell"
}, tag("textarea", {
value : this.descriptionCellDefaultValue,
className : 'form-textarea',
onfocus : function () {
if (this.value === self.descriptionCellDefaultValue) {
this.value = "";
}
}
}));
},
/**
* @param row
* The content row this modification cell belongs to.
* @return
* A new element containing buttons for adding and removing rows.
*/
createModificationCell : function (row) {
return tag("td", {
className : "modification"
}, [
this.createPrependRowButton(row),
this.createRemoveRowButton(row),
this.createAddRowButton(row)
]);
},
/**
* Creates a button for removing the given row.
*
* @param row
* The row to remove when the button is clicked.
* @param title
* An optional title for the button.
* @return
* The created HTMLButtonElement.
*/
createRemoveRowButton : function (row, title) {
title = title || t("Remove this row.");
return tag("button", {
title : title,
className : "remove-row-button",
onclick : this.removeRow.bind(this, row).wait(0).returning(false)
}, "-");
},
/**
* Creates a button for adding a row after the given row.
*
* @param row
* The row to add new rows after.
* @param title
* An optional title for the button.
* @param append
* Whether the new row should be appended if row is null.
* @return
* The created HTMLButtonElement.
*/
createAddRowButton : function (row, title, append) {
title = title || t("Insert a row below this row.");
var addButton = tag("button", {
title : title,
className : "add-row-button"
}, "+");
addButton.onclick = this.addRow.bind(this, row, append).wait(0).returning(false);
return addButton;
},
/**
* Creates a button that inserts a row above the given one.
*
* @param row
* The row to add new rows before.
* @param title
* An optional title for the button.
* @return
* The created button.
*/
createPrependRowButton : function (row, title) {
title = title || t("Insert a row above this row.");
var prependButton = tag("button", {
title : title,
className : "prepend-row-button"
}, "+");
prependButton.onclick = this.prependRow.bind(this, row).wait(0).returning(false);
return prependButton;
},
/**
* @return
* The content containers.
*/
getContentRows : function () {
return markdownEditor.extras.getElementsByClassName("content-row", this.form);
},
/**
* @param row
* The row whose modification cell that's wanted.
* @return
* The cell of the row that contains buttons for modifying rows.
*/
getModificationCell : function (row) {
return markdownEditor.extras.getElementByClassName("modification", row);
},
/**
* Gets the "remove row" button for a row.
*
* @param row
* The row to get the button from.
* @return
* The "remove row" button.
*/
getRemoveButton : function (row) {
var button = getElementByClassName("remove-row-button", this.getModificationCell(row));
if (!button) {
throw new Error("No button found");
}
return button;
},
/**
* Removes the given row from the table.
*
* @param row
* The row to remove
*/
removeRow : function (row) {
// Only remove a row if there will be at least one left after
// the removal.
if (this.rows > 1) {
this.contentContainer.removeChild(row);
this.rows--;
if (this.rows === 1) {
this.getRemoveButton(this.getContentRows()[0]).disabled = true;
}
this.setFirstRowBehavior();
}
},
/**
* Adds special behavior to the first row, and makes sure no other rows
* still have this behavior.
*/
setFirstRowBehavior : function () {
var firstRowClassName = "first";
var contentRows = this.getContentRows();
// Set to the first element if it doesn't have the class name already.
if (contentRows[0]) {
ClassNames.add(contentRows[0], firstRowClassName);
if (contentRows[1]) {
// Remove the behavior from the second element.
ClassNames.del(contentRows[1], firstRowClassName);
}
}
},
/**
* Adds a content row to the dialog.
*
* @param previousRow
* The row to insert the new row after.
* @param append
* If true the row will be appended to the document, otherwise it's added
* after the given row, or before the first row if previousRow is null.
*/
addRow : function (previousRow, append) {
if (this.rows === 1) {
this.getRemoveButton(this.getContentRows()[0]).disabled = false;
}
// Append if append was set.
if (append) {
this.contentContainer.appendChild(this.createContentRow());
}
// prepend if there is no previousRow.
else if (!previousRow) {
dom.insertBefore(this.createContentRow(), this.contentContainer.firstChild, this.contentContainer);
this.setFirstRowBehavior();
}
// Otherwise insert after the previousRow.
else {
dom.insertAfter(this.createContentRow(), previousRow);
}
this.setFirstRowBehavior();
},
/**
* Adds a new row above the given row
*
* @param nextRow
* The row to add the new row above.
*/
prependRow : function (nextRow) {
// Enable the remove row button if there is only one row, since
// another button will be added.
if (this.rows === 1) {
this.getRemoveButton(this.getContentRows()[0]).disabled = false;
}
// Insert the new row.
dom.insertBefore(this.createContentRow(), nextRow);
this.setFirstRowBehavior();
},
/**
* Opens the dialog and appends the contents.
*/
openDialog : function () {
mDialog.open(t("Insert definition list"), "definition-list");
mDialog.getContent().appendChild(this.form);
mDialog.focusFirst();
},
/**
* Handles form submissions. Serializes the information to markdown syntax
* and inserts it into the document.
*/
process : function () {
var dts = [];
var dds = [];
var titleElements = getElementsByClassName("title-cell-input", this.contentContainer);
var descriptionElements = this.contentContainer.getElementsByTagName("textarea");
for (var i = 0; i < titleElements.length; i++) {
// Don't add the values if both form elements are empty.
if (!((titleElements[i].value === "" || titleElements[i].value === this.titleCellDefaultValue) && (descriptionElements[i].value === "" || descriptionElements[i].value === this.descriptionCellDefaultValue))) {
dts.push(titleElements[i].value);
dds.push(descriptionElements[i].value);
}
}
// Serialize the information into markdown extra syntax.
var lines = [];
for (var j = 0; j < dts.length; j++) {
dds[j] = ": " + dds[j];
lines.push(dts[j] + "\n" + dds[j].replace(/^(?!: {3})/gm, " "));
}
// Insert the data and close the dialog.
BUE.active.replaceSelection(lines.join("\n\n"));
markdownEditor.selection.lineBreak();
markdownEditor.selection.caretAtEnd();
BUE.dialog.close();
}
};
return DefinitionList;
}) ();
/*******************************************************************************
* REFERENCES
******************************************************************************/
markdownEditor.Reference = (function () {
/**
* Represents a reference stored at the bottom of a markdown document.
* A Reference consists of a type, which are Links, Abbreviatons and so on,
* an identifier which is the actual reference name, and text which is the
* data the reference defines.
*
* @param type
* The type of reference, Abbreviations, Links, Footnotes or Images.
* @param identifier
* The name of the referenc.
* @param text
* The information the reference defines.
*/
function Reference(type, identifier, text) {
this.type = type;
this.identifier = identifier;
this.text = text;
} Reference.prototype = {
// Instance variables.
prefix : "[",
infix : "]: ",
type : null,
identifier : null,
text : null,
/**
* @return
* The string representation of a reference, this is the string that's
* valid inside markdown documents.
*/
toString : function () {
return this.prefix + this.identifier + this.infix + this.text;
},
/**
* Setter for prefix
*
* @param prefix
* A string, the prefix of the reference string.
*/
setPrefix : function (prefix) {
this.prefix = prefix;
},
/**
* Setter for suffix
*
* @param suffix
* The new suffix for the reference string.
*/
setSuffix : function (suffix) {
this.suffix = suffix;
},
/**
* Checks if two references are equal, meaning all properties are equal.
*
* @param reference
* The reference to compare to.
* @return
* A boolean signifying whether the references are equal.
*/
equals : function (reference) {
return this.type === reference.type && this.identifier === reference.identifier && this.text === reference.text;
}
};
/**
* Unserializes a reference string. Extracts the correct prefix.
*
* @param referenceString
* The serialized reference.
* @param type
* The type of reference, matching the type property of a Reference.
*/
Reference.fromString = function (referenceString, type) {
// Matches a serialized reference, also extracts the prefix
// format.
if (!/^(\*?\[\^?)([^\]]+)\]: ([\s\S]*)$/.test(referenceString)) {
throw new Error("String does not match a serialized Reference.");
}
var reference = new Reference(type, RegExp.$2, RegExp.$3);
reference.setPrefix(RegExp.$1);
return reference;
};
return Reference;
})();
markdownEditor.references = (function () {
var Reference = markdownEditor.Reference;
var collection = markdownEditor.extras.collection;
// The available reference types, in the order they should appear
// in the document.
var order = ["Abbreviations", "Notes", "Links", "Images"];
/**
* A singleton for working with references.
*/
function References() {
} References.prototype = {
// Instance variables.
headerPrefix : "<!-- ",
headerSuffix : " -->",
references : null,
/**
* Fetches any existing references from the document.
*
* @return
* A hash where each property is a reference header and the values are
* arrays of References.
*/
parseReferences : function () {
var lines = BUE.active.getContent().split(/\r?\n|\r/);
// Available reference types.
var references = {
"Abbreviations" : "",
"Links" : "",
"Images" : "",
"Notes" : ""
};
var line;
var currentReference = null;
for (var i = 0; i < lines.length; i++) {
line = lines[i];
// If this line is a reference header.
if (/^<!-- (\S+) -->$/.test(line)) {
if (RegExp.$1 in references) {
currentReference = RegExp.$1;
}
}
// If this line is below a reference header it's a reference
// or part of one.
else if (currentReference) {
references[currentReference] += line + "\n";
}
}
// Convert to Reference instances.
for (var type in references) if (references.hasOwnProperty(type)) {
// If the contents is empty, just replace with an empty array
// and do nothing else.
if (references[type] === "") {
references[type] = [];
continue;
}
// Fetch all separate references from the stored string, it
// also matches multi-line references.
var referenceStrings = references[type].match(/(?:^|\n).?\[([^\]])+\]:[^\[]+(?=^\[|\n|$)/g) || [];
references[type] = [];
// Loop through each reference string and convert it to a
// Reference.
for (var j = 0; j < referenceStrings.length; j++) {
references[type].push(Reference.fromString(referenceStrings[j].replace(/\n+$/, "").replace(/^\n+/, ""), type));
}
}
this.references = references;
return references;
},
/**
* Gets all references of a given type.
*
* @param type
* The reference type.
* @return
* An Array of References.
*/
getReferences : function (type) {
return this.parseReferences()[type];
},
/**
* Checks if the given reference is stored under the given type. The
* match is attempted using only the identifier.
*
* @param reference
* The reference to look for.
* @return
* Boolean signifying whether the reference exists.
*/
hasReference : function (reference) {
var references = this.getReferences(reference.type);
for (var i = 0; i < references.length; i++) {
if (references[i].identifier === reference.identifier) {
return true;
}
}
return false;
},
/**
* Checks if the given header exists in the document.
*
* @param header
* The string name of the header.
* @return
* Whether the header exists in the document.
*/
hasHeader : function (header) {
return new RegExp(this.headerPrefix + header + this.headerSuffix).test(BUE.active.getContent());
},
/**
* Prints the references into the textarea.
*
* The reference type.
* @param references
* An Array of reference lines.
*/
_printReferences : function (references) {
// Remove existing references.
this._clearReferences();
var textArea = BUE.active.textArea;
// Replace trailing line breaks with three line breaks.
textArea.value = textArea.value.replace(/\n+$/, "\n\n\n");
// Loop through all reference headers.
for (var i = 0; i < order.length; i++) {
var iReferences = this.references[order[i]];
// If the reference header exists and has References under it.
if (iReferences && iReferences.length > 0) {
// Insert the header.
textArea.value += this.headerPrefix + order[i] + this.headerSuffix + "\n";
// Loop through references and insert.
for (var j = 0; j < iReferences.length; j++) {
textArea.value += iReferences[j].toString() + "\n";
}
textArea.value += "\n\n";
}
}
// Make sure all headers have 3 line breaks in front of them.
for (var k = 0; k < order.length; k++) {
if (this.hasHeader(order[k])) {
BUE.active.setContent(BUE.active.getContent().replace(new RegExp("\n*(?=<!-- " + order[k] + " -->)"), "\n\n"));
}
}
// Truncate trailing white space.
textArea.value = textArea.value.replace(/[\s\n\r]*$/, "\n");
},
/**
* Removes all reference data from the document.
*/
_clearReferences : function () {
var content = BUE.active.getContent();
var index;
// Try to find the index of the first header.
for (var i = 0; i < order.length; i++) {
index = content.search(new RegExp(this.headerPrefix + order[i] + this.headerSuffix));
if (index >= 0) {
break;
}
}
// If no reference data exists, nothing needs to be done.
if (index == -1) return;
// Set the content to itself but excluding the references.
BUE.active.setContent(content.substring(0, index));
},
/**
* Replaces a reference with the new specified text. This function
* modifies the textarea.
*
* @type reference
* The Reference to replace to.
*/
_replaceReference : function (reference) {
var references = this.getReferences(reference.type);
for (var i = 0; i < references.length; i++) {
if (reference.identifier === references[i].identifier) {
references[i].text = reference.text;
}
}
this._printReferences(reference.type, references);
},
/**
* Adds the reference at the end of the content area for its type.
*
* @param reference
* The reference to add to the textarea.
*/
_pushReference : function (reference) {
var references = this.getReferences(reference.type);
references.push(reference);
this._printReferences(reference.type, references);
},
/**
* Adds a reference at the appropriate location in the textarea.
*
* @param reference
* The reference to add.
*/
addReference : function (reference) {
var references = this.getReferences(reference.type);
if (this.hasReference(reference)) {
this._replaceReference(reference);
}
else {
this._pushReference(reference);
}
},
/**
* Tries to find a reference matching the given identifier from the
* BUE.
*
* @param type
* A reference type.
* @param identifier
* The identifier to look for.
* @return
* The found Reference, or null if none is found.
*/
getByIdentifier : function (type, identifier) {
// Get the references of the given type and loop through them.
var references = this.getReferences(type);
for (var i = 0; i < references.length; i++) {
if (references[i].identifier === identifier) {
// Found the reference.
return references[i];
}
}
// Nothing was found.
return null;
},
/**
* Callback for adding references to the textarea.
* If the reference exists the user is prompted for whether the reference
* should be overwritten.
*
* @param textString
* The text string to add at the current selection. This text is the
* markup refering to the reference.
* @param reference
* The reference to attempt to add.
* @param allowOverride
* Whether the reference may override another reference with the same
* identifier. Default value is true.
* @param message
* The message to display if the reference already exists. Has default
* values.
* @param space
* Whether spaces should be added around the inserted text if it's next
* to non-whitespace characters. Defaults to true.
*/
_callback : function (textString, reference, allowOverride, message, space) {
var references = markdownEditor.references;
var t = markdownEditor.t;
// Only evaluates to false if allowOverride === false.
allowOverride = !!allowOverride;
space = !!(space === undefined || space);
message = message || null;
// Store the current selection, since it may be modified before we read it
// otherwise.
var selection = BUE.active.posSelection();
// If there exists a reference by this name already.
if (references.hasReference(reference)) {
// If the user may choose to overwrite the existing reference.
if (allowOverride) {
// If the reference doesn't match the existing one, and
// the user does not want to overwrite it.
if (!references.getByIdentifier(reference.type, reference.identifier).equals(reference) && !confirm(message || t("There is a reference with this name, should it be overwritten?"))) {
// Let the user modify the dialog further.
return;
}
}
// The user may not modify an existing reference, an error
// message is sent and the user is returned to the dialog to
// modiy the reference name.
else {
markdownEditor.dialog.addError(message || t("There is an existing reference by that name, you may not modify it. Please choose a different name for the reference."));
return;
}
}
// Add the reference to the BUE.
references.addReference(reference);
// Select the initial selection, replace data and close the dialog.
BUE.active.makeSelection(selection.start, selection.end);
BUE.active.replaceSelection(textString);
if (space) {
markdownEditor.selection.space();
}
BUE.dialog.close();
}
};
return new References();
})();
;/*})'"*/
;/*})'"*/
/**
* @file
* Attaches behaviors for the Chosen module.
*/
(function($) {
Drupal.behaviors.chosen = {
attach: function(context, settings) {
settings.chosen = settings.chosen || Drupal.settings.chosen;
// Prepare selector and add unwantend selectors.
var selector = settings.chosen.selector;
// Function to prepare all the options together for the chosen() call.
var getElementOptions = function (element) {
var options = $.extend({}, settings.chosen.options);
// The width default option is considered the minimum width, so this
// must be evaluated for every option.
if (settings.chosen.minimum_width > 0) {
if ($(element).width() < settings.chosen.minimum_width) {
options.width = settings.chosen.minimum_width + 'px';
}
else {
options.width = $(element).width() + 'px';
}
}
// Some field widgets have cardinality, so we must respect that.
// @see chosen_pre_render_select()
if ($(element).attr('multiple') && $(element).data('cardinality')) {
options.max_selected_options = $(element).data('cardinality');
}
return options;
};
// Process elements that have opted-in for Chosen.
// @todo Remove support for the deprecated chosen-widget class.
$('select.chosen-enable, select.chosen-widget', context).once('chosen', function() {
options = getElementOptions(this);
$(this).chosen(options);
});
$(selector, context)
// Disabled on:
// - Field UI
// - WYSIWYG elements
// - Tabledrag weights
// - Elements that have opted-out of Chosen
// - Elements already processed by Chosen.
.not('#field-ui-field-overview-form select, #field-ui-display-overview-form select, .wysiwyg, .draggable select[name$="[weight]"], .draggable select[name$="[position]"], .chosen-disable, .chosen-processed')
.filter(function() {
// Filter out select widgets that do not meet the minimum number of
// options.
var minOptions = $(this).attr('multiple') ? settings.chosen.minimum_multiple : settings.chosen.minimum_single;
if (!minOptions) {
// Zero value means no minimum.
return true;
}
else {
return $(this).find('option').length >= minOptions;
}
})
.once('chosen', function() {
options = getElementOptions(this);
$(this).chosen(options);
});
}
};
})(jQuery);
;/*})'"*/
;/*})'"*/
BUE.postprocess.ocupload = function (E, $) {
// Upload button click handler
E.showFileSelectionDialog = function () {
if (!Drupal.ocupload.bueditorPlugin.flow.support) {
alert(Drupal.t('Your browser not support HTML5 File API'));
return;
}
var $button = $(this.buttons[this.bindex]);
$button.parent().click();
}
// Search upload button
var $button;
for (var i = 0; i < E.tpl.buttons.length; i++) {
if ($.trim(E.tpl.buttons[i][1]) == 'js: E.showFileSelectionDialog();') {
$button = $('#bue-' + E.index + '-button-' + i);
break;
}
}
if (!$button) {
return;
}
if (!Drupal.settings.ocupload || !Drupal.settings.ocupload.allowedExt) {
$button.remove();
return;
}
// Lazy create and configure Flow.js object
if (!Drupal.ocupload.bueditorPlugin.flow) {
Drupal.ocupload.bueditorPlugin.createFlow();
}
// Process upload button
if (Drupal.ocupload.bueditorPlugin.flow.support) {
$buttonWrapper = $button.wrap('<span class="ocupload-button-wrapper"></span>').parent();
Drupal.ocupload.bueditorPlugin.flow.assignBrowse($buttonWrapper[0]);
Drupal.ocupload.bueditorPlugin.flow.assignDrop($buttonWrapper[0]);
}
};
(function ($) {
Drupal.ocupload = Drupal.ocupload || {};
Drupal.ocupload.bueditorPlugin = Drupal.ocupload.bueditorPlugin || {};
/**
* Create and configure Flow.js object.
*/
Drupal.ocupload.bueditorPlugin.createFlow = function () {
Drupal.ocupload.bueditorPlugin.flow = Drupal.ocupload.createFlow();
if (!Drupal.ocupload.bueditorPlugin.flow.support) {
return false;
}
Drupal.ocupload.bueditorPlugin.flow.on('filesSubmitted', Drupal.ocupload.bueditorPlugin.onFilesSubmitted);
Drupal.ocupload.bueditorPlugin.flow.on('fileSuccess', Drupal.ocupload.bueditorPlugin.onFileSuccess);
Drupal.ocupload.bueditorPlugin.flow.on('complete', Drupal.ocupload.bueditorPlugin.onComplete);
return true;
};
/**
* Get BUEditor instance by textarea id.
*/
Drupal.ocupload.bueditorPlugin.getBueditorInstance = function (textareaId) {
var instance;
$.each(BUE.instances, function (index, value) {
if (value.textArea.id == textareaId) {
instance = value;
}
});
return instance;
};
/**
* Files selected handler.
*/
Drupal.ocupload.bueditorPlugin.onFilesSubmitted = function (files, event) {
var $textarea = $(event.target).closest('.form-item').find('textarea');
var editorInstance = Drupal.ocupload.bueditorPlugin.getBueditorInstance($textarea.attr('id'));
Drupal.ocupload.bueditorPlugin.flow.opts.query.selectedText = editorInstance.getSelection();
Drupal.ocupload.bueditorPlugin.flow.upload();
editorInstance.textArea.disabled = true;
// Save textarea id in global var, because event 'complete' not contains this information
Drupal.ocupload.bueditorPlugin.activeTextareaId = $textarea.attr('id');
};
/**
* File uploaded handler.
*/
Drupal.ocupload.bueditorPlugin.onFileSuccess = function (file, response, chunk) {
if (!Drupal.ocupload.checkResponse(response)) {
return;
}
response = $.parseJSON(response);
if (response.status) {
var editorInstance = Drupal.ocupload.bueditorPlugin.getBueditorInstance(Drupal.ocupload.bueditorPlugin.activeTextareaId);
var src=/src=\"([^"]*|[^']*)\"/.exec(response.data);
var out="![image]("+src[1]+")";
var insertedData = editorInstance.getSelection() ? out : out + "\n";
//var insertedData = editorInstance.getSelection() ? response.data : response.data + "\n";
editorInstance.replaceSelection(insertedData, 'end');
}
else {
alert(response.data);
}
};
/**
* Files uploaded handler.
*/
Drupal.ocupload.bueditorPlugin.onComplete = function () {
var editorInstance = Drupal.ocupload.bueditorPlugin.getBueditorInstance(Drupal.ocupload.bueditorPlugin.activeTextareaId);
editorInstance.textArea.disabled = false;
editorInstance.focus();
};
})(jQuery);
;/*})'"*/
;/*})'"*/