Parser updates. Added the "styled" keyword to wikitables to allow them to be styled using the current theme's standard table skinning, and changes to how the image tag parser decides how to display an image (framed, inline or raw).
/**
* Creates a control that can be used to edit a rank.
*/
var RankEditorControl = function(rankdata)
{
this.rankdata = ( typeof(rankdata) == 'object' ) ? rankdata : {};
if ( !this.rankdata.rank_style )
{
this.rankdata.rank_style = '';
}
// have the browser parse CSS for us and use an anchor to be as close
// as possible in calculating CSS
// this is kind of a hack as it relies on setAttribute/getAttribute in
// order to obtain stringified versions of CSS data
var cssobj = document.createElement('a');
cssobj.setAttribute('style', this.rankdata.rank_style);
this.style_sim_obj = cssobj;
// figure out if we're editing or creating
this.editing = ( typeof(this.rankdata.rank_id) == 'number' );
this.render = function()
{
var editor = document.createElement('div');
editor.className = 'tblholder';
// stash this editor instance in the parent div for later function calls
editor.editor = this;
this.wrapperdiv = editor;
editor.style.width = '100%';
// tables suck.
var table = document.createElement('table');
table.setAttribute('cellspacing', '1');
table.setAttribute('cellpadding', '4');
table.setAttribute('width', '100%');
// heading: "Edit rank: foo" or "Create a new rank"
var tr_head = document.createElement('tr');
var th_head = document.createElement('th');
th_head.setAttribute('colspan', '2');
if ( this.editing )
{
var th_head_string = 'acpur_th_edit_rank';
var th_head_data = { rank_title: $lang.get(this.rankdata.rank_title) };
}
else
{
var th_head_string = 'acpur_th_create_rank';
var th_head_data = { };
}
th_head.appendChild(document.createTextNode($lang.get(th_head_string, th_head_data)));
tr_head.appendChild(th_head);
this.th_head = th_head;
table.appendChild(tr_head);
// row: rank title
var tr_title = document.createElement('tr');
var td_title_l = document.createElement('td');
var td_title_f = document.createElement('td');
td_title_l.className = td_title_f.className = 'row1';
td_title_l.appendChild(document.createTextNode($lang.get('acpur_field_rank_title')));
// field: rank title
var f_rank_title = document.createElement('input');
f_rank_title.type = 'text';
f_rank_title.size = '30';
f_rank_title.value = ( this.editing ) ? this.rankdata.rank_title : '';
f_rank_title.editor = this;
f_rank_title.onkeyup = function()
{
this.editor.renderPreview();
}
this.f_rank_title = f_rank_title;
td_title_f.appendChild(f_rank_title);
tr_title.appendChild(td_title_l);
tr_title.appendChild(td_title_f);
table.appendChild(tr_title);
// row: basic style options
var tr_basic = document.createElement('tr');
var td_basic_l = document.createElement('td');
var td_basic_f = document.createElement('td');
td_basic_l.className = td_basic_f.className = 'row2';
td_basic_l.appendChild(document.createTextNode($lang.get('acpur_field_style_basic')));
// fieldset: basic style options
// field: bold
var l_basic_bold = document.createElement('label');
var f_basic_bold = document.createElement('input');
f_basic_bold.type = 'checkbox';
f_basic_bold.checked = ( this.style_sim_obj.style.fontWeight == 'bold' ) ? true : false;
f_basic_bold.editor = this;
f_basic_bold.onclick = function()
{
this.editor.style_sim_obj.style.fontWeight = ( this.checked ) ? 'bold' : null;
this.editor.renderPreview();
}
l_basic_bold.style.fontWeight = 'bold';
l_basic_bold.appendChild(f_basic_bold);
l_basic_bold.appendChild(document.createTextNode(' '));
l_basic_bold.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_bold')));
// field: italic
var l_basic_italic = document.createElement('label');
var f_basic_italic = document.createElement('input');
f_basic_italic.type = 'checkbox';
f_basic_italic.checked = ( this.style_sim_obj.style.fontStyle == 'italic' ) ? true : false;
f_basic_italic.editor = this;
f_basic_italic.onclick = function()
{
this.editor.style_sim_obj.style.fontStyle = ( this.checked ) ? 'italic' : null;
this.editor.renderPreview();
}
l_basic_italic.style.fontStyle = 'italic';
l_basic_italic.appendChild(f_basic_italic);
l_basic_italic.appendChild(document.createTextNode(' '));
l_basic_italic.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_italic')));
// field: underline
var l_basic_underline = document.createElement('label');
var f_basic_underline = document.createElement('input');
f_basic_underline.type = 'checkbox';
f_basic_underline.checked = ( this.style_sim_obj.style.textDecoration == 'underline' ) ? true : false;
f_basic_underline.editor = this;
f_basic_underline.onclick = function()
{
this.editor.style_sim_obj.style.textDecoration = ( this.checked ) ? 'underline' : null;
this.editor.renderPreview();
}
l_basic_underline.style.textDecoration = 'underline';
l_basic_underline.appendChild(f_basic_underline);
l_basic_underline.appendChild(document.createTextNode(' '));
l_basic_underline.appendChild(document.createTextNode($lang.get('acpur_field_style_basic_underline')));
// finish up formatting row#1
td_basic_f.appendChild(l_basic_bold);
td_basic_f.appendChild(document.createTextNode(' '));
td_basic_f.appendChild(l_basic_italic);
td_basic_f.appendChild(document.createTextNode(' '));
td_basic_f.appendChild(l_basic_underline);
tr_basic.appendChild(td_basic_l);
tr_basic.appendChild(td_basic_f);
table.appendChild(tr_basic);
// row: rank color
var tr_color = document.createElement('tr');
var td_color_l = document.createElement('td');
var td_color_f = document.createElement('td');
td_color_l.className = td_color_f.className = 'row1';
td_color_l.appendChild(document.createTextNode($lang.get('acpur_field_style_color')));
// field: rank color
var f_rank_color = document.createElement('input');
f_rank_color.type = 'text';
f_rank_color.size = '7';
f_rank_color.value = ( this.editing ) ? this.rgb2hex(this.style_sim_obj.style.color) : '';
f_rank_color.style.backgroundColor = this.style_sim_obj.style.color;
f_rank_color.editor = this;
this.f_rank_color = f_rank_color;
f_rank_color.onkeyup = function(e)
{
if ( !e.keyCode )
e = window.event;
if ( !e )
return false;
var chr = (String.fromCharCode(e.keyCode)).toLowerCase();
this.value = this.value.replace(/[^a-fA-F0-9]/g, '');
if ( this.value.length > 6 )
{
this.value = this.value.substr(0, 6);
}
if ( this.value.length == 6 || this.value.length == 3 )
{
this.style.backgroundColor = '#' + this.value;
this.editor.style_sim_obj.style.color = '#' + this.value;
this.style.color = '#' + this.editor.determineLightness(this.value);
this.editor.renderPreview();
}
else if ( this.value.length == 0 )
{
this.style.backgroundColor = null;
this.editor.style_sim_obj.style.color = null;
this.editor.renderPreview();
}
}
td_color_f.appendChild(f_rank_color);
tr_color.appendChild(td_color_l);
tr_color.appendChild(td_color_f);
table.appendChild(tr_color);
// field: additional CSS
var tr_css = document.createElement('tr');
var td_css_l = document.createElement('td');
td_css_l.className = 'row2';
td_css_l.appendChild(document.createTextNode($lang.get('acpur_field_style_css')));
tr_css.appendChild(td_css_l);
var td_css_f = document.createElement('td');
td_css_f.className = 'row2';
var f_css = document.createElement('input');
f_css.type = 'text';
f_css.value = this.stripBasicCSSAttributes(this.rankdata.rank_style);
f_css.style.width = '98%';
f_css.editor = this;
f_css.onkeyup = function()
{
if ( !(trim(this.value)).match(/^((([a-z-]+):(.+?);)+)?$/) )
return;
var newcss = this.editor.stripExtendedCSSAttributes(String(this.editor.style_sim_obj.getAttribute('style'))) + ' ' + this.value;
this.editor.preview_div.setAttribute('style', 'font-size: x-large; ' + newcss);
this.editor.style_sim_obj.setAttribute('style', newcss);
}
this.f_css = f_css;
td_css_f.appendChild(f_css);
tr_css.appendChild(td_css_f);
table.appendChild(tr_css);
// "field": preview
var tr_preview = document.createElement('tr');
var td_preview_l = document.createElement('td');
td_preview_l.className = 'row1';
td_preview_l.appendChild(document.createTextNode($lang.get('acpur_field_preview')));
tr_preview.appendChild(td_preview_l);
var td_preview_f = document.createElement('td');
td_preview_f.className = 'row1';
var div_preview = document.createElement('a');
this.preview_div = div_preview;
div_preview.style.fontSize = 'x-large';
div_preview.appendChild(document.createTextNode(''));
div_preview.firstChild.nodeValue = ( this.editing ) ? this.rankdata.rank_title : '';
td_preview_f.appendChild(div_preview);
tr_preview.appendChild(td_preview_f);
table.appendChild(tr_preview);
// submit button
var tr_submit = document.createElement('tr');
var th_submit = document.createElement('th');
th_submit.className = 'subhead';
th_submit.setAttribute('colspan', '2');
var btn_submit = document.createElement('input');
btn_submit.type = 'submit';
btn_submit.value = ( this.editing ) ? $lang.get('acpur_btn_save') : $lang.get('acpur_btn_create_submit');
btn_submit.editor = this;
btn_submit.style.fontWeight = 'bold';
btn_submit.onclick = function(e)
{
this.editor.submitEvent(e);
}
this.btn_submit = btn_submit;
th_submit.appendChild(btn_submit);
// delete button
if ( this.editing )
{
var btn_delete = document.createElement('input');
btn_delete.type = 'button';
btn_delete.value = $lang.get('acpur_btn_delete');
btn_delete.editor = this;
btn_delete.onclick = function(e)
{
this.editor.deleteEvent(e);
}
th_submit.appendChild(document.createTextNode(' '));
th_submit.appendChild(btn_delete);
}
tr_submit.appendChild(th_submit);
table.appendChild(tr_submit);
// render preview
this.renderPreview();
// finalize the editor table
editor.appendChild(table);
// stash rendered editor
this.editordiv = editor;
// send output
return editor;
}
/**
* Takes the existing editor div and transforms the necessary elements so that it goes from "create" mode to "edit" mode
* @param object Edit data - same format as the rankdata parameter to the constructor, but we should only need rank_id
*/
this.transformToEditor = function(rankdata)
{
// we need a rank ID
if ( typeof(rankdata.rank_id) != 'number' )
return false;
if ( this.editing )
return false;
this.editing = true;
this.rankdata = rankdata;
this.rankdata.rank_title = this.f_rank_title.value;
this.rankdata.rank_style = this.getCSS();
// transform various controls
this.th_head.firstChild.nodeValue = $lang.get('acpur_th_edit_rank', {
rank_title: $lang.get(this.rankdata.rank_title)
});
this.btn_submit.value = $lang.get('acpur_btn_save');
// add the delete button
var th_submit = this.btn_submit.parentNode;
var btn_delete = document.createElement('input');
btn_delete.type = 'button';
btn_delete.value = $lang.get('acpur_btn_delete');
btn_delete.editor = this;
btn_delete.onclick = function(e)
{
this.editor.deleteEvent(e);
}
th_submit.appendChild(document.createTextNode(' '));
th_submit.appendChild(btn_delete);
return true;
}
/**
* Takes a hex color, averages the three channels, and returns either 'ffffff' or '000000' depending on the luminosity of the color.
* @param string
* @return string
*/
this.determineLightness = function(hexval)
{
var rgb = this.hex2rgb(hexval);
var lumin = ( rgb[0] + rgb[1] + rgb[2] ) / 3;
return ( lumin > 60 ) ? '000000' : 'ffffff';
}
/**
* Strips out basic CSS attributes (color, font-weight, font-style, text-decoration) from a snippet of CSS.
* @param string
* @return string
*/
this.stripBasicCSSAttributes = function(css)
{
return trim(css.replace(/(color|font-weight|font-style|text-decoration): ?([A-z0-9# ,\(\)]+);/g, ''));
}
/**
* Strips out all but basic CSS attributes.
* @param string
* @return string
*/
this.stripExtendedCSSAttributes = function(css)
{
var match;
var final_css = '';
var basics = ['color', 'font-weight', 'font-style', 'text-decoration'];
while ( match = css.match(/([a-z-]+):(.+?);/) )
{
if ( in_array(match[1], basics) )
{
final_css += ' ' + match[0] + ' ';
}
css = css.replace(match[0], '');
}
final_css = trim(final_css);
return final_css;
}
this.getCSS = function()
{
return this.style_sim_obj.getAttribute('style');
}
this.renderPreview = function()
{
if ( !this.preview_div )
return false;
var color = ( this.style_sim_obj.style.color ) ? '#' + this.rgb2hex(this.style_sim_obj.style.color) : null;
this.preview_div.style.color = color;
this.preview_div.style.fontWeight = this.style_sim_obj.style.fontWeight;
this.preview_div.style.fontStyle = this.style_sim_obj.style.fontStyle;
this.preview_div.style.textDecoration = this.style_sim_obj.style.textDecoration;
this.preview_div.firstChild.nodeValue = $lang.get(this.f_rank_title.value);
}
this.submitEvent = function(e)
{
if ( this.onsubmit )
{
this.onsubmit(e);
}
else
{
window.console.error('RankEditorControl: no onsubmit event specified');
}
}
this.deleteEvent = function(e)
{
if ( this.ondelete )
{
this.ondelete(e);
}
else
{
window.console.error('RankEditorControl: no ondelete event specified');
}
}
/**
* Converts a parenthetical color specification (rgb(x, y, z)) to hex form (xxyyzz)
* @param string
* @return string
*/
this.rgb2hex = function(rgb)
{
var p = rgb.match(/^rgb\(([0-9]+), ([0-9]+), ([0-9]+)\)$/);
if ( !p )
return rgb.replace(/^#/, '');
var r = parseInt(p[1]).toString(16), g = parseInt(p[2]).toString(16), b = parseInt(p[3]).toString(16);
if ( r.length < 2 )
r = '0' + r;
if ( g.length < 2 )
g = '0' + g;
if ( b.length < 2 )
b = '0' + b;
return r + g + b;
}
/**
* Get red, green, and blue values for the given hex color
* @param string
* @return array (numbered, e.g. not an object
*/
this.hex2rgb = function(hex)
{
hex = hex.replace(/^#/, '');
if ( hex.length != 3 && hex.length != 6 )
{
return hex;
}
if ( hex.length == 3 )
{
// is there a better way to do this?
hex = hex.charAt(0) + hex.charAt(0) + hex.charAt(1) + hex.charAt(1) + hex.charAt(2) + hex.charAt(2);
}
hex = [ hex.substr(0, 2), hex.substr(2, 2), hex.substr(4, 2) ];
var red = parseInt(hex[0], 16);
var green = parseInt(hex[1], 16);
var blue = parseInt(hex[2], 16);
return [red, green, blue];
}
}
/**
* Perform request for editable rank data and draw editor
*/
function ajaxInitRankEdit(rank_id)
{
load_component('messagebox');
var json_packet = {
mode: 'get_rank',
rank_id: rank_id
};
json_packet = ajaxEscape(toJSONString(json_packet));
ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
var response = String(ajax.responseText + '');
if ( !check_json_response(response) )
{
handle_invalid_json(ajax.responseText);
return false;
}
try
{
var response = parseJSON(ajax.responseText);
}
catch(e)
{
handle_invalid_json(ajax.responseText);
}
if ( response.error )
{
if ( response.error == 'need_auth_to_admin' )
{
load_component('login');
var rid = rank_id;
ajaxDynamicReauth(function()
{
ajaxInitRankEdit(rid);
});
}
else
{
alert(response.error);
}
return false;
}
var editor = new RankEditorControl(response);
editor.onsubmit = ajaxRankEditHandleSaveExisting;
editor.ondelete = ajaxRankEditHandleDelete;
var container = document.getElementById('admin_ranks_container_right');
container.innerHTML = '';
container.appendChild(editor.render());
}
}, true);
}
function ajaxInitRankCreate()
{
load_component('messagebox');
var editor = new RankEditorControl();
editor.onsubmit = ajaxRankEditHandleSaveNew;
var container = document.getElementById('admin_ranks_container_right');
container.innerHTML = '';
container.appendChild(editor.render());
}
function ajaxRankEditHandleSave(editor, switch_new)
{
var whitey = whiteOutElement(editor.wrapperdiv);
// pack it up, ...
var json_packet = {
mode: ( switch_new ) ? 'create_rank' : 'save_rank',
rank_title: editor.f_rank_title.value,
rank_style: editor.getCSS()
}
if ( !switch_new )
{
json_packet.rank_id = editor.rankdata.rank_id;
}
/// ... pack it in
var json_packet = ajaxEscape(toJSONString(json_packet));
ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
var response = String(ajax.responseText + '');
if ( !check_json_response(response) )
{
handle_invalid_json(ajax.responseText);
return false;
}
try
{
var response = parseJSON(ajax.responseText);
}
catch(e)
{
handle_invalid_json(ajax.responseText);
}
if ( response.mode == 'success' )
{
whiteOutReportSuccess(whitey);
if ( switch_new )
{
//
// we have a few more things to do with a newly created rank.
//
// 1. transform editor
editor.transformToEditor(response);
editor.onsubmit = ajaxRankEditHandleSaveExisting;
editor.ondelete = ajaxRankEditHandleDelete;
// 2. append the new rank to the list
var create_link = document.getElementById('rankadmin_createlink');
if ( create_link )
{
var parent = create_link.parentNode;
var edit_link = document.createElement('a');
edit_link.href = '#rank_edit:' + response.rank_id;
edit_link.className = 'rankadmin-editlink';
edit_link.setAttribute('style', editor.getCSS());
edit_link.id = 'rankadmin_editlink_' + response.rank_id;
edit_link.rank_id = response.rank_id;
edit_link.appendChild(document.createTextNode($lang.get(editor.f_rank_title.value)));
parent.insertBefore(edit_link, create_link);
edit_link.onclick = function()
{
ajaxInitRankEdit(this.rank_id);
}
}
}
else
{
// update the rank title on the left
var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
if ( edit_link )
{
edit_link.firstChild.nodeValue = $lang.get(editor.f_rank_title.value);
edit_link.setAttribute('style', editor.getCSS());
}
}
}
else
{
whitey.parentNode.removeChild(whitey);
if ( response.error == 'need_auth_to_admin' )
{
load_component('login');
ajaxDynamicReauth(function()
{
ajaxRankEditHandleSave(editor, switch_new);
});
}
else
{
miniPromptMessage({
title: $lang.get('acpur_err_save_failed_title'),
message: response.error,
buttons: [
{
text: $lang.get('etc_ok'),
color: 'red',
style: {
fontWeight: 'bold'
},
onclick: function()
{
miniPromptDestroy(this);
}
}
]
});
}
}
}
}, true);
}
var ajaxRankEditHandleSaveExisting = function()
{
ajaxRankEditHandleSave(this, false);
}
var ajaxRankEditHandleSaveNew = function()
{
ajaxRankEditHandleSave(this, true);
}
var ajaxRankEditHandleDelete = function()
{
var mp = miniPromptMessage({
title: $lang.get('acpur_msg_rank_delete_confirm_title'),
message: $lang.get('acpur_msg_rank_delete_confirm_body'),
buttons: [
{
text: $lang.get('acpur_btn_delete'),
color: 'red',
style: {
fontWeight: 'bold'
},
onclick: function()
{
var parent = miniPromptGetParent(this);
var editor = parent.editor;
setTimeout(function()
{
ajaxRankEditDeleteConfirmed(editor);
}, 1000);
miniPromptDestroy(parent);
}
},
{
text: $lang.get('etc_cancel'),
onclick: function()
{
miniPromptDestroy(this);
}
}
]
});
console.debug(mp);
mp.editor = this;
}
function ajaxRankEditDeleteConfirmed(editor)
{
var whitey = whiteOutElement(editor.wrapperdiv);
load_component(['jquery', 'jquery-ui']);
var json_packet = {
mode: 'delete_rank',
rank_id: editor.rankdata.rank_id
};
var rank_id = editor.rankdata.rank_id;
json_packet = ajaxEscape(toJSONString(json_packet));
ajaxPost(makeUrlNS('Admin', 'UserRanks/action.json'), 'r=' + json_packet, function(ajax)
{
if ( ajax.readyState == 4 && ajax.status == 200 )
{
var response = String(ajax.responseText + '');
if ( !check_json_response(response) )
{
handle_invalid_json(ajax.responseText);
return false;
}
try
{
var response = parseJSON(ajax.responseText);
}
catch(e)
{
handle_invalid_json(ajax.responseText);
}
if ( response.mode == 'success' )
{
// the deletion was successful, report success and kill off the editor
whiteOutReportSuccess(whitey);
setTimeout(function()
{
// nuke the rank title on the left
var edit_link = document.getElementById('rankadmin_editlink_' + editor.rankdata.rank_id);
if ( edit_link )
{
edit_link.parentNode.removeChild(edit_link);
}
// collapse and destroy the editor
$(editor.wrapperdiv).hide("blind", {}, 500, function()
{
// when the animation finishes, nuke the whole thing
var container = document.getElementById('admin_ranks_container_right');
container.innerHTML = $lang.get('acpur_msg_select_rank');
}
);
}, 1500);
}
else
{
whitey.parentNode.removeChild(whitey);
if ( response.error == 'need_auth_to_admin' )
{
load_component('login');
ajaxDynamicReauth(function()
{
ajaxRankEditDeleteConfirmed(editor);
});
}
else
{
miniPromptMessage({
title: $lang.get('acpur_err_delete_failed_title'),
message: response.error,
buttons: [
{
text: $lang.get('etc_ok'),
color: 'red',
style: {
fontWeight: 'bold'
},
onclick: function()
{
miniPromptDestroy(this);
}
}
]
});
}
}
}
}, true);
}