308 lines
8.4 KiB
JavaScript
308 lines
8.4 KiB
JavaScript
|
/**
|
||
|
* Functions for text editing (toolbar stuff)
|
||
|
*
|
||
|
* @todo most of the stuff in here should be revamped and then moved to toolbar.js
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
*/
|
||
|
|
||
|
/**
|
||
|
* Creates a toolbar button through the DOM
|
||
|
* Called for each entry of toolbar definition array (built by inc/toolbar.php and extended via js)
|
||
|
*
|
||
|
* Style the buttons through the toolbutton class
|
||
|
*
|
||
|
* @param {string} icon image filename, relative to folder lib/images/toolbar/
|
||
|
* @param {string} label title of button, show on mouseover
|
||
|
* @param {string} key hint in title of button for access key
|
||
|
* @param {string} id id of button, and '<id>_ico' of icon
|
||
|
* @param {string} classname for styling buttons
|
||
|
*
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
* @author Michal Rezler <m.rezler@centrum.cz>
|
||
|
*/
|
||
|
function createToolButton(icon,label,key,id,classname){
|
||
|
var $btn = jQuery(document.createElement('button')),
|
||
|
$ico = jQuery(document.createElement('img'));
|
||
|
|
||
|
// prepare the basic button stuff
|
||
|
$btn.addClass('toolbutton');
|
||
|
if(classname){
|
||
|
$btn.addClass(classname);
|
||
|
}
|
||
|
|
||
|
$btn.attr('title', label).attr('aria-controls', 'wiki__text');
|
||
|
if(key){
|
||
|
$btn.attr('title', label + ' ['+key.toUpperCase()+']')
|
||
|
.attr('accessKey', key);
|
||
|
}
|
||
|
|
||
|
// set IDs if given
|
||
|
if(id){
|
||
|
$btn.attr('id', id);
|
||
|
$ico.attr('id', id+'_ico');
|
||
|
}
|
||
|
|
||
|
// create the icon and add it to the button
|
||
|
if(icon.substr(0,1) !== '/'){
|
||
|
icon = DOKU_BASE + 'lib/images/toolbar/' + icon;
|
||
|
}
|
||
|
$ico.attr('src', icon);
|
||
|
$ico.attr('alt', '');
|
||
|
$ico.attr('width', 16);
|
||
|
$ico.attr('height', 16);
|
||
|
$btn.append($ico);
|
||
|
|
||
|
// we have to return a DOM object (for compatibility reasons)
|
||
|
return $btn[0];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Creates a picker window for inserting text
|
||
|
*
|
||
|
* The given list can be an associative array with text,icon pairs
|
||
|
* or a simple list of text. Style the picker window through the picker
|
||
|
* class or the picker buttons with the pickerbutton class. Picker
|
||
|
* windows are appended to the body and created invisible.
|
||
|
*
|
||
|
* @param {string} id the ID to assign to the picker
|
||
|
* @param {Array} props the properties for the picker
|
||
|
* @param {string} edid the ID of the textarea
|
||
|
* @return DOMobject the created picker
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
*/
|
||
|
function createPicker(id,props,edid){
|
||
|
// create the wrapping div
|
||
|
var $picker = jQuery(document.createElement('div'));
|
||
|
|
||
|
$picker.addClass('picker a11y');
|
||
|
if(props['class']){
|
||
|
$picker.addClass(props['class']);
|
||
|
}
|
||
|
|
||
|
$picker.attr('id', id).css('position', 'absolute');
|
||
|
|
||
|
function $makebutton(title) {
|
||
|
var $btn = jQuery(document.createElement('button'))
|
||
|
.addClass('pickerbutton').attr('title', title)
|
||
|
.attr('aria-controls', edid)
|
||
|
.on('click', bind(pickerInsert, title, edid))
|
||
|
.appendTo($picker);
|
||
|
return $btn;
|
||
|
}
|
||
|
|
||
|
jQuery.each(props.list, function (key, item) {
|
||
|
if (!props.list.hasOwnProperty(key)) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if(isNaN(key)){
|
||
|
// associative array -> treat as text => image pairs
|
||
|
if (item.substr(0,1) !== '/') {
|
||
|
item = DOKU_BASE+'lib/images/'+props.icobase+'/'+item;
|
||
|
}
|
||
|
jQuery(document.createElement('img'))
|
||
|
.attr('src', item)
|
||
|
.attr('alt', '')
|
||
|
.appendTo($makebutton(key));
|
||
|
}else if (typeof item == 'string'){
|
||
|
// a list of text -> treat as text picker
|
||
|
$makebutton(item).text(item);
|
||
|
}else{
|
||
|
// a list of lists -> treat it as subtoolbar
|
||
|
initToolbar($picker,edid,props.list);
|
||
|
return false; // all buttons handled already
|
||
|
}
|
||
|
|
||
|
});
|
||
|
jQuery('body').append($picker);
|
||
|
|
||
|
// we have to return a DOM object (for compatibility reasons)
|
||
|
return $picker[0];
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called by picker buttons to insert Text and close the picker again
|
||
|
*
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
*/
|
||
|
function pickerInsert(text,edid){
|
||
|
insertAtCarret(edid,text);
|
||
|
pickerClose();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Add button action for signature button
|
||
|
*
|
||
|
* @param {jQuery} $btn Button element to add the action to
|
||
|
* @param {Array} props Associative array of button properties
|
||
|
* @param {string} edid ID of the editor textarea
|
||
|
* @return {string} picker id for aria-controls attribute
|
||
|
* @author Gabriel Birke <birke@d-scribe.de>
|
||
|
*/
|
||
|
function addBtnActionSignature($btn, props, edid) {
|
||
|
if(typeof SIG != 'undefined' && SIG != ''){
|
||
|
$btn.on('click', function (e) {
|
||
|
insertAtCarret(edid,SIG);
|
||
|
e.preventDefault();
|
||
|
});
|
||
|
return edid;
|
||
|
}
|
||
|
return '';
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Determine the current section level while editing
|
||
|
*
|
||
|
* @param {string} textboxId ID of the text field
|
||
|
*
|
||
|
* @author Andreas Gohr <gohr@cosmocode.de>
|
||
|
*/
|
||
|
function currentHeadlineLevel(textboxId){
|
||
|
var field = jQuery('#' + textboxId)[0],
|
||
|
s = false,
|
||
|
opts = [field.value.substr(0,DWgetSelection(field).start)];
|
||
|
if (field.form && field.form.prefix) {
|
||
|
// we need to look in prefix context
|
||
|
opts.push(field.form.prefix.value);
|
||
|
}
|
||
|
|
||
|
jQuery.each(opts, function (_, opt) {
|
||
|
// Check whether there is a headline in the given string
|
||
|
var str = "\n" + opt,
|
||
|
lasthl = str.lastIndexOf("\n==");
|
||
|
if (lasthl !== -1) {
|
||
|
s = str.substr(lasthl+1,6);
|
||
|
return false;
|
||
|
}
|
||
|
});
|
||
|
if (s === false) {
|
||
|
return 0;
|
||
|
}
|
||
|
return 7 - s.match(/^={2,6}/)[0].length;
|
||
|
}
|
||
|
|
||
|
|
||
|
/**
|
||
|
* global var used for not saved yet warning
|
||
|
*/
|
||
|
window.textChanged = false;
|
||
|
|
||
|
/**
|
||
|
* global var which stores original editor content
|
||
|
*/
|
||
|
window.doku_edit_text_content = '';
|
||
|
/**
|
||
|
* Delete the draft before leaving the page
|
||
|
*/
|
||
|
function deleteDraft() {
|
||
|
if (is_opera || window.keepDraft) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var $dwform = jQuery('#dw__editform');
|
||
|
|
||
|
if($dwform.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// remove a possibly saved draft using ajax
|
||
|
jQuery.post(DOKU_BASE + 'lib/exe/ajax.php',
|
||
|
{
|
||
|
call: 'draftdel',
|
||
|
id: $dwform.find('input[name=id]').val()
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Activate "not saved" dialog, add draft deletion to page unload,
|
||
|
* add handlers to monitor changes
|
||
|
* Note: textChanged could be set by e.g. html_edit() as well
|
||
|
*
|
||
|
* Sets focus to the editbox as well
|
||
|
*/
|
||
|
jQuery(function () {
|
||
|
var $editform = jQuery('#dw__editform');
|
||
|
if ($editform.length == 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var $edit_text = jQuery('#wiki__text');
|
||
|
if ($edit_text.length > 0) {
|
||
|
if($edit_text.attr('readOnly')) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// set focus and place cursor at the start
|
||
|
var sel = DWgetSelection($edit_text[0]);
|
||
|
sel.start = 0;
|
||
|
sel.end = 0;
|
||
|
DWsetSelection(sel);
|
||
|
$edit_text.trigger('focus');
|
||
|
|
||
|
doku_edit_text_content = $edit_text.val();
|
||
|
}
|
||
|
|
||
|
var changeHandler = function() {
|
||
|
doku_hasTextBeenModified();
|
||
|
|
||
|
doku_summaryCheck();
|
||
|
};
|
||
|
|
||
|
$editform.change(changeHandler);
|
||
|
$editform.keydown(changeHandler);
|
||
|
|
||
|
window.onbeforeunload = function(){
|
||
|
if(window.textChanged) {
|
||
|
return LANG.notsavedyet;
|
||
|
}
|
||
|
};
|
||
|
window.onunload = deleteDraft;
|
||
|
|
||
|
// reset change memory var on submit
|
||
|
jQuery('#edbtn__save').on('click',
|
||
|
function() {
|
||
|
window.onbeforeunload = '';
|
||
|
textChanged = false;
|
||
|
}
|
||
|
);
|
||
|
jQuery('#edbtn__preview').on('click',
|
||
|
function() {
|
||
|
window.onbeforeunload = '';
|
||
|
textChanged = false;
|
||
|
window.keepDraft = true; // needed to keep draft on page unload
|
||
|
}
|
||
|
);
|
||
|
|
||
|
var $summary = jQuery('#edit__summary');
|
||
|
$summary.on('change keyup', doku_summaryCheck);
|
||
|
|
||
|
if (textChanged) doku_summaryCheck();
|
||
|
});
|
||
|
|
||
|
/**
|
||
|
* Updates textChanged variable if content of the editor has been modified
|
||
|
*/
|
||
|
function doku_hasTextBeenModified() {
|
||
|
if (!textChanged) {
|
||
|
var $edit_text = jQuery('#wiki__text');
|
||
|
|
||
|
if ($edit_text.length > 0) {
|
||
|
textChanged = doku_edit_text_content != $edit_text.val();
|
||
|
} else {
|
||
|
textChanged = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Checks if a summary was entered - if not the style is changed
|
||
|
*
|
||
|
* @author Andreas Gohr <andi@splitbrain.org>
|
||
|
*/
|
||
|
function doku_summaryCheck(){
|
||
|
var $sum = jQuery('#edit__summary'),
|
||
|
missing = $sum.val() === '';
|
||
|
$sum.toggleClass('missing', missing).toggleClass('edit', !missing);
|
||
|
}
|