crayon-syntax-highlighter/util/theme-editor/theme_editor.js
2019-08-30 19:30:19 +02:00

656 lines
24 KiB
JavaScript

// Crayon Syntax Highlighter Theme Editor JavaScript
(function ($) {
CrayonSyntaxThemeEditor = new function () {
var base = this;
var crayonSettings = CrayonSyntaxSettings;
var adminSettings = CrayonAdminSettings;
var settings = CrayonThemeEditorSettings;
var strings = CrayonThemeEditorStrings;
var adminStrings = CrayonAdminStrings;
var admin = CrayonSyntaxAdmin;
var preview, previewCrayon, previewCSS, status, title, info;
var colorPickerPos;
var changed, loaded;
var themeID, themeJSON, themeCSS, themeStr, themeInfo;
var reImportant = /\s+!important$/gmi;
var reSize = /^[0-9-]+px$/;
var reCopy = /-copy(-\d+)?$/;
var changedAttr = 'data-value';
var borderCSS = {'border': true, 'border-left': true, 'border-right': true, 'border-top': true, 'border-bottom': true};
base.init = function (callback) {
// Called only once
CrayonUtil.log('editor init');
base.initUI();
if (callback) {
callback();
}
};
base.show = function (callback, crayon) {
// Called each time editor is shown
previewCrayon = crayon.find('.crayon-syntax');
preview.append(crayon)
base.load();
if (callback) {
callback();
}
};
base.load = function () {
loaded = false;
themeStr = adminSettings.currThemeCSS;
themeID = adminSettings.currTheme;
changed = false;
themeJSON = CSSJSON.toJSON(themeStr, {
stripComments: true,
split: true
});
themeJSON = base.filterCSS(themeJSON);
CrayonUtil.log(themeJSON);
themeInfo = base.readCSSInfo(themeStr);
base.removeExistingCSS();
base.initInfoUI();
base.updateTitle();
base.updateInfo();
base.setFieldValues(themeInfo);
base.populateAttributes();
base.updateLiveCSS();
base.updateUI();
loaded = true;
};
base.save = function () {
// Update info from form fields
themeInfo = base.getFieldValues($.keys(themeInfo));
// Get the names of the fields and map them to their values
var names = base.getFieldNames(themeInfo);
var info = {};
for (var id in themeInfo) {
info[names[id]] = themeInfo[id];
}
// Update attributes
base.persistAttributes();
// Save
themeCSS = CSSJSON.toCSS(themeJSON);
var newThemeStr = base.writeCSSInfo(info) + themeCSS;
CrayonUtil.postAJAX({
action: 'crayon-theme-editor-save',
id: themeID,
name: base.getName(),
css: newThemeStr
}, function (result) {
status.show();
result = parseInt(result);
if (result > 0) {
status.html(strings.success);
if (result === 2) {
window.GET['theme-editor'] = 1;
CrayonUtil.reload();
}
} else {
status.html(strings.fail);
}
changed = false;
setTimeout(function () {
status.fadeOut();
}, 1000);
});
};
base.del = function (id, name) {
admin.createDialog({
title: strings.del,
html: strings.deleteThemeConfirm.replace('%s', name),
yes: function () {
CrayonUtil.postAJAX({
action: 'crayon-theme-editor-delete',
id: id
}, function (result) {
if (result > 0) {
CrayonUtil.reload();
} else {
admin.createAlert({
html: strings.deleteFail + ' ' + strings.checkLog
});
}
});
},
options: {
selectedButtonIndex: 2
}
});
};
base.duplicate = function (id, name) {
base.createPrompt({
//html: "Are you sure you want to duplicate the '" + name + "' theme?",
title: strings.duplicate,
text: strings.newName,
value: base.getNextAvailableName(id),
ok: function (val) {
CrayonUtil.postAJAX({
action: 'crayon-theme-editor-duplicate',
id: id,
name: val
}, function (result) {
if (result > 0) {
CrayonUtil.reload();
} else {
admin.createAlert({
html: strings.duplicateFail + ' ' + strings.checkLog
});
}
});
}
});
};
base.submit = function (id, name) {
base.createPrompt({
title: strings.submit,
desc: strings.submitText,
text: strings.message,
value: strings.submitMessage,
ok: function (val) {
CrayonUtil.postAJAX({
action: 'crayon-theme-editor-submit',
id: id,
message: val
}, function (result) {
var msg = result > 0 ? strings.submitSucceed : strings.submitFail + ' ' + strings.checkLog;
admin.createAlert({
html: msg
});
});
}
});
};
base.getNextAvailableName = function (id) {
var next = base.getNextAvailableID(id);
return base.idToName(next[1]);
};
base.getNextAvailableID = function (id) {
var themes = adminSettings.themes;
var count = 0;
if (reCopy.test(id)) {
// Remove the "copy" if it already exists
var newID = id.replace(reCopy, '');
if (newID.length > 0) {
id = newID;
}
}
var nextID = id;
while (nextID in themes) {
count++;
if (count == 1) {
nextID = id + '-copy';
} else {
nextID = id + '-copy-' + count.toString();
}
}
return [count, nextID];
};
base.readCSSInfo = function (cssStr) {
var infoStr = /^\s*\/\*[\s\S]*?\*\//gmi.exec(cssStr);
var themeInfo = {};
var match = null;
var infoRegex = /([^\r\n:]*[^\r\n\s:])\s*:\s*([^\r\n]+)/gmi;
while ((match = infoRegex.exec(infoStr)) != null) {
themeInfo[base.nameToID(match[1])] = CrayonUtil.encode_html(match[2]);
}
// Force title case on the name
if (themeInfo.name) {
themeInfo.name = base.idToName(themeInfo.name);
}
return themeInfo;
};
base.getFieldName = function (id) {
var name = '';
if (id in settings.fields) {
name = settings.fields[id];
} else {
name = base.idToName(id);
}
return name;
};
base.getFieldNames = function (fields) {
var names = {};
for (var id in fields) {
names[id] = base.getFieldName(id);
}
return names;
};
base.removeExistingCSS = function () {
// Remove the old <style> tag to prevent clashes
preview.find('link[rel="stylesheet"][href*="' + adminSettings.currThemeURL + '"]').remove()
};
base.initInfoUI = function () {
CrayonUtil.log(themeInfo);
// TODO abstract
var names = base.getFieldNames(themeInfo);
var fields = {};
for (var id in names) {
var name = names[id];
var value = themeInfo[id];
fields[name] = base.createInput(id, value);
}
$('#tabs-1-contents').html(base.createForm(fields));
base.getField('name').bind('change keydown keyup', function () {
themeInfo.name = base.getFieldValue('name');
base.updateTitle();
});
};
base.nameToID = function (name) {
return name.toLowerCase().replace(/\s+/gmi, '-');
};
base.idToName = function (id) {
id = id.replace(/-/gmi, ' ');
return id.toTitleCase();
};
base.getName = function () {
var name = themeInfo.name;
if (!name) {
name = base.idToName(themeID);
}
return name;
};
base.getField = function (id) {
return $('#' + settings.cssInputPrefix + id);
};
base.getFieldValue = function (id) {
return base.getElemValue(base.getField(id));
};
base.getElemValue = function (elem) {
if (elem) {
// TODO add support for checkboxes etc.
return elem.val();
} else {
return null;
}
};
base.getFieldValues = function (fields) {
var info = {};
$(fields).each(function (i, id) {
info[id] = base.getFieldValue(id);
});
return info;
};
base.setFieldValue = function (id, value) {
base.setElemValue(base.getField(id), value);
};
base.setFieldValues = function (obj) {
for (var i in obj) {
base.setFieldValue(i, obj[i]);
}
};
base.setElemValue = function (elem, val) {
if (elem) {
// TODO add support for checkboxes etc.
return elem.val(val);
} else {
return false;
}
};
base.getAttribute = function (element, attribute) {
return base.getField(element + '_' + attribute);
};
base.getAttributes = function () {
return $('.' + settings.cssInputPrefix + settings.attribute);
};
base.visitAttribute = function (attr, callback) {
var elems = themeJSON.children;
var root = settings.cssThemePrefix + base.nameToID(themeInfo.name);
var dataElem = attr.attr('data-element');
var dataAttr = attr.attr('data-attribute');
var elem = elems[root + dataElem];
callback(attr, elem, dataElem, dataAttr, root, elems);
};
base.persistAttributes = function (remove_default) {
remove_default = CrayonUtil.setDefault(remove_default, true);
base.getAttributes().each(function () {
base.persistAttribute($(this), remove_default);
});
};
base.persistAttribute = function (attr, remove_default) {
remove_default = CrayonUtil.setDefault(remove_default, true);
base.visitAttribute(attr, function (attr, elem, dataElem, dataAttr, root, elems) {
if (remove_default && attr.prop('tagName') == 'SELECT' && attr.val() == attr.attr('data-default')) {
if (elem) {
// If default is selected in a dropdown, then remove
delete elem.attributes[dataAttr];
}
return;
}
var val = base.getElemValue(attr);
if ((val == null || val == '')) {
// No value given
if (remove_default && elem) {
delete elem.attributes[dataAttr];
return;
}
} else {
val = base.addImportant(val);
if (!elem) {
elem = elems[root + dataElem] = {
attributes: {},
children: {}
};
}
elem.attributes[dataAttr] = val;
}
CrayonUtil.log(dataElem + ' ' + dataAttr);
});
};
base.populateAttributes = function ($change) {
var elems = themeJSON.children;
var root = settings.cssThemePrefix + base.nameToID(themeInfo.name);
CrayonUtil.log(elems, root);
base.getAttributes().each(function () {
base.visitAttribute($(this), function (attr, elem, dataElem, dataAttr, root, elems) {
if (elem) {
if (dataAttr in elem.attributes) {
var val = base.removeImportant(elem.attributes[dataAttr]);
base.setElemValue(attr, val);
attr.trigger('change');
}
}
});
});
};
base.addImportant = function (attr) {
if (!reImportant.test(attr)) {
attr = attr + ' !important';
}
return attr;
};
base.removeImportant = function (attr) {
return attr.replace(reImportant, '');
};
base.isImportant = function (attr) {
return reImportant.exec(attr) != null;
};
base.appendStyle = function (css) {
previewCSS.html('<style>' + css + '</style>');
};
base.removeStyle = function () {
previewCSS.html('');
};
base.writeCSSInfo = function (info) {
var infoStr = '/*\n';
for (var field in info) {
infoStr += field + ': ' + info[field] + '\n';
}
return infoStr + '*/\n';
};
base.filterCSS = function (css) {
// Split all border CSS attributes into individual attributes
for (var child in css.children) {
var atts = css.children[child].attributes;
for (var att in atts) {
if (att in borderCSS) {
var rules = base.getBorderCSS(atts[att]);
for (var rule in rules) {
atts[att + '-' + rule] = rules[rule];
}
delete atts[att];
}
}
}
return css;
},
base.getBorderCSS = function (css) {
var result = {};
var important = base.isImportant(css);
$.each(strings.borderStyles, function (i, style) {
if (css.indexOf(style) >= 0) {
result.style = style;
}
});
var width = /\d+\s*(px|%|em|rem)/gi.exec(css);
if (width) {
result.width = width[0];
}
var color = /#\w+/gi.exec(css);
if (color) {
result.color = color[0];
}
if (important) {
for (var rule in result) {
result[rule] = base.addImportant(result[rule]);
}
}
return result;
},
base.createPrompt = function (args) {
args = $.extend({
title: adminStrings.prompt,
text: adminStrings.value,
desc: null,
value: '',
options: {
buttons: {
"OK": function () {
if (args.ok) {
args.ok(base.getFieldValue('prompt-text'));
}
$(this).crayonDialog('close');
},
"Cancel": function () {
$(this).crayonDialog('close');
}
},
open: function () {
base.getField('prompt-text').val(args.value).focus();
}
}
}, args);
args.html = '<table class="field-table crayon-prompt-' + base.nameToID(args.title) + '">';
if (args.desc) {
args.html += '<tr><td colspan="2">' + args.desc + '</td></tr>';
}
args.html += '<tr><td>' + args.text + ':</td><td>' + base.createInput('prompt-text') + '</td></tr>';
args.html += '</table>';
var options = {width: '400px'};
admin.createDialog(args, options);
};
base.initUI = function () {
// Bind events
preview = $('#crayon-editor-preview');
previewCSS = $('#crayon-editor-preview-css');
status = $('#crayon-editor-status');
title = $('#crayon-theme-editor-name');
info = $('#crayon-theme-editor-info');
$('#crayon-editor-controls').tabs();
$('#crayon-editor-back').click(function () {
if (changed) {
admin.createDialog({
html: strings.discardConfirm,
title: adminStrings.confirm,
yes: function () {
showMain();
}
});
} else {
showMain();
}
});
$('#crayon-editor-save').click(base.save);
// Set up jQuery UI
base.getAttributes().each(function () {
var attr = $(this);
var type = attr.attr('data-group');
if (type == 'color') {
var args = {
parts: 'full',
showNoneButton: true,
colorFormat: '#HEX'
};
args.open = function (e, color) {
$('.ui-colorpicker-dialog .ui-button').addClass('button-primary');
if (colorPickerPos) {
var picker = $('.ui-colorpicker-dialog:visible');
picker.css('left', colorPickerPos.left);
// picker.css('top', colorPickerPos.top);
}
};
args.select = function (e, color) {
attr.trigger('change');
};
args.close = function (e, color) {
attr.trigger('change');
};
attr.colorpicker(args);
attr.bind('change', function () {
var hex = attr.val();
attr.css('background-color', hex);
attr.css('color', CrayonUtil.getReadableColor(hex));
});
} else if (type == 'size') {
attr.bind('change', function () {
var val = attr.val();
if (!reSize.test(val)) {
val = CrayonUtil.removeChars('^0-9-', val);
if (val != '') {
attr.val(val + 'px');
}
}
});
}
if (type != 'color') {
// For regular text boxes, capture changes on keys
attr.bind('keydown keyup', function () {
if (attr.attr(changedAttr) != attr.val()) {
CrayonUtil.log('triggering', attr.attr(changedAttr), attr.val());
attr.trigger('change');
}
});
}
// Update CSS changes to the live instance
attr.bind('change', function () {
if (attr.attr(changedAttr) == attr.val()) {
return;
} else {
attr.attr(changedAttr, attr.val());
}
if (loaded) {
base.persistAttribute(attr);
base.updateLiveCSS();
}
});
});
$('.ui-colorpicker-dialog').addClass('wp-dialog');
$('.ui-colorpicker-dialog').mouseup(function () {
base.colorPickerMove($(this));
});
};
base.colorPickerMove = function (picker) {
if (picker) {
colorPickerPos = {left: picker.css('left'), top: picker.css('top')};
}
};
base.updateLiveCSS = function (clone) {
clone = CrayonUtil.setDefault(clone, false);
if (previewCrayon) {
var json;
if (clone) {
var id = previewCrayon.attr('id');
json = $.extend(true, {}, themeJSON);
$.each(json.children, function (child) {
json.children['#' + id + child] = json.children[child];
delete json.children[child];
});
} else {
json = themeJSON;
}
base.appendStyle(CSSJSON.toCSS(json));
}
};
base.updateUI = function () {
$('#crayon-editor-controls input, #crayon-editor-controls select').bind('change', function () {
changed = true;
});
};
base.createInput = function (id, value, type) {
value = CrayonUtil.setDefault(value, '');
type = CrayonUtil.setDefault(type, 'text');
return '<input id="' + settings.cssInputPrefix + id + '" class="' + settings.cssInputPrefix + type + '" type="' + type + '" value="' + value + '" />';
};
base.createForm = function (inputs) {
var str = '<form class="' + settings.prefix + '-form"><table>';
$.each(inputs, function (input) {
str += '<tr><td class="field">' + input + '</td><td class="value">' + inputs[input] + '</td></tr>';
});
str += '</table></form>';
return str;
};
var showMain = function () {
admin.resetPreview();
admin.preview_update();
admin.show_theme_info();
admin.show_main();
//preview.html('');
};
base.updateTitle = function () {
var name = base.getName();
if (adminSettings.editing_theme) {
title.html(strings.editingTheme.replace('%s', name));
} else {
title.html(strings.creatingTheme.replace('%s', name));
}
};
base.updateInfo = function () {
info.html('<a target="_blank" href="' + adminSettings.currThemeURL + '">' + adminSettings.currThemeURL + '</a>');
};
};
})(jQueryCrayon);