sheercms
Version:
Sheer Cliff CMS is a simple and powerful content management system (CMS) for Node JS.
486 lines (431 loc) • 18 kB
JavaScript
$(document).ready(function () {
var pd = new PageDesigner();
pd.init();
});
function PageDesigner() {
var self = this;
self.init = function() {
self.loadDesignerStyles();
self.loadToolbar();
};
self.loadDesignerStyles = function() {
var head = document.getElementsByTagName('head')[0];
var link = document.createElement('link');
link.id = "designerStyles";
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = '/cms/static/css/designer.css';
link.media = 'all';
head.appendChild(link);
};
self.loadToolbar = function() {
if ($("#DesignToolbar").length == 0) {
//create the toolbar
$("body").prepend('<div id="DesignToolbar"><div class="design-toolbar-loading">Loading designer toolbar...</div></div>');
}
var contentId = $("#DesignerScript").attr("data-item");
if ($("#DesignToolbar").length > 0) {
$("#DesignToolbar").designer({
contentId: contentId
});
}
};
}
(function ($) {
$.Designer = function (element, options) {
// plugin's default options
var defaults = {
contentId: ''
};
var plugin = this;
plugin.settings = {};
//other variables
var $element = $(element), // reference to the jQuery version of DOM element
element = element // reference to the actual DOM element
//CONSTRUCTOR
plugin.init = function () {
plugin.settings = $.extend({}, defaults, options);
//show the dashed border when you hover over a field
$(".design-field").hover(function () {
$(this).addClass("design-hover");
}, function () {
$(this).removeClass("design-hover");
});
//load the toolbar content
$element.load('/cms/designer/toolbar', { contentId: plugin.settings.contentId }, function () {
//on loaded
var mainbar = $('#DesignToolbar').detach();
$('body').append(mainbar);
//add margin to the body to push it down below the toolbar
$("body").css({ "margin-top": "65px" });
updateLinks();
//figure out which mode to start in
var bDesign = plugin.cmsCookieMode() === 'design';
setupPageMode(bDesign);
setupContentEditables();
$element.find("a").on("click", function () {
if ($(this).hasClass("disabled"))
return false;
var btnName = $(this).attr("data-cmd");
if (btnName)
btnName = btnName.toLowerCase();
if (btnName == "design") {
setupPageMode(true);
}
else if (btnName === 'preview') {
var designMode = $(this).hasClass("selected");
setupPageMode(designMode);
}
else if (btnName === 'save') {
plugin.save();
}
else if (btnName === 'publish') {
plugin.publish();
}
else if (btnName === 'unpublish') {
if (confirm('Are you sure you want to unpublish & remove this page from the live website?')) {
plugin.unpublish();
}
}
else if (btnName === 'outline') {
if ($(this).hasClass("selected")) {
$(this).removeClass("selected");
$("body").removeClass("outline");
}
else {
$(this).addClass('selected');
$("body").addClass("outline");
}
}
else if (btnName === 'reload') {
if (plugin.needsSaving() === true) {
if (confirm('Are you sure you want to reload the page and lose any unsaved changes?')) {
plugin.reloadPage();
}
}
else
plugin.reloadPage();
}
else if (btnName === 'exit') {
plugin.exit();
}
else {
var cmd = $(this).attr("data-name");
plugin.execute(cmd);
}
});
});
}
//PUBLIC METHODS
plugin.save = function () {
var data = getFieldData(false);
if (!data) {
setDesignerMessage("There are no changes to save!", true, false, 5000);
}
else {
setDesignerMessage("Saving changes...", false, true);
executeCommand("save", data);
}
},
plugin.publish = function () {
var data = getFieldData(true);
if (!data) {
setDesignerMessage("There are no changes to publish!", true, false, 5000);
}
else {
setDesignerMessage("Publishing changes...", false, true);
executeCommand("publish", data);
}
},
plugin.unpublish = function () {
setDesignerMessage("Removing page from live site...", false, true);
executeCommand("unpublish");
},
plugin.execute = function (cmd) {
setDesignerMessage("Processing...", false, true);
$.post("/cms/design/command", { itemId: plugin.settings.itemId, command: cmd }, function (data) {
if (data == "success")
plugin.reloadPage();
else
setDesignerMessage(data, true, false);
});
},
plugin.reloadPage = function () {
window.location.href = window.location.href;
},
plugin.exit = function () {
plugin.cmsCookieMode('live');
var status = plugin.designData('status');
if (status === "P" || status === "M") //published or modified
plugin.reloadPage();
else //this page is not live, so just close the window
window.close();
},
plugin.needsSaving = function() {
return needsSaving();
},
plugin.cmsCookieMode = function(mode) {
if (mode) {
//set the mode
$.cookie('cmsmode', mode, {path:'/'});
}
else {
//get the mode
return $.cookie('cmsmode');
}
},
plugin.designData = function(key, value) {
if (key) {
if (key.indexOf('data-') !== 0)
key = 'data-' + key;
var $this = $("#DesignData");
if (value) {
//set the value
$this.attr(key, value);
}
else {
//get the value
return $this.attr(key);
}
}
};
//PRIVATE METHODS
function setupContentEditables() {
$('body').on('focus', '[contenteditable]', function() {
var $this = $(this);
$this.data('before', $this.html());
$this.addClass('design-editing');
return $this;
}).on('blur keyup paste input', '[contenteditable]', function() {
var $this = $(this);
$this.removeClass('design-editing');
if ($this.data('before') !== $this.html()) {
$this.data('before', $this.html());
$this.addClass('design-changed');
updateNeedsSavingLabel();
}
return $this;
});
$('[data-placeholder]').each(function() {
var $this = $(this);
checkPlaceholder($this);
$this.on('focus', function() {
var ph = $this.attr('data-placeholder');
if ($this.text() === ph)
$this.html("<br>");
}).on('blur', function() {
//see if we need to set the placeholder
checkPlaceholder($(this));
});
});
}
function checkPlaceholder(editable) {
var ph = editable.attr('data-placeholder');
if (ph && ph.length > 0) {
var isRich = editable.attr("data-rich") === 'true';
var val = isRich ? editable.html() : editable.text();
if (val.length === 0)
editable.text(ph);
}
}
function setupDesignEditors() {
// This property tells CKEditor to not activate every element with contenteditable=true element.
CKEDITOR.disableAutoInline = true;
$(".design-field[data-rich='true']").each(function () {
var element = $(this);
var obj = element[0];
var editor = CKEDITOR.inline(obj, {
/*customConfig: '/areas/cms/libraries/CKEditor/designer_config.js',
filebrowserWindowWidth: 900,
filebrowserWindowHeight: 700*/
});
editor.on('afterSetData', function() {
element.addClass('design-changed');
updateNeedsSavingLabel();
});
//assign the editor as data on the dom element so we can access it later
element.data("editor", editor);
});
}
function destroyEditors() {
for (name in CKEDITOR.instances) {
var editor = CKEDITOR.instances[name];
if (editor) {
if (editor.element)
$(editor.element).removeData("editor");
editor.destroy();
}
}
}
function setStatusText(status) {
//update the status code on the toolbar
plugin.designData('status', status);
var lbl = $("#DesignStatusLabel");
lbl.removeClass("design-status-new design-status-saved design-status-published design-status-modified design-status-deleted");
if (status == "N") //New
lbl.addClass("design-status-new").text("New");
else if (status == "S") //Saved
lbl.addClass("design-status-saved").text("Saved");
else if (status == "D") //Deleted
lbl.addClass("design-status-deleted").text("Deleted");
else if (status == "P") //Published
lbl.addClass("design-status-published").text("Published");
else if (status == "M") //Modified
lbl.addClass("design-status-modified").text("Modified");
}
function updateNeedsSavingLabel() {
if (needsSaving() === true)
$("#NeedsSavingLabel").addClass("needs-saving").text("Yes");
else
$("#NeedsSavingLabel").removeClass("needs-saving").text("No");
}
function saveCleanup() {
$(".design-changed").removeClass("design-changed");
updateNeedsSavingLabel();
}
function needsSaving() {
if ($(".design-changed").length > 0)
return true;
else
return false;
}
function updateLinks() {
$("body").delegate("a", "click.sheercms", function () {
if (!$(this).hasClass("design-link")) {
if ($("body").hasClass("design")) //don't redirect to link's URL if we are in design mode
return false;
}
});
}
function setupPageMode(isDesign) {
//set the cookie
var cmsmode = isDesign ? 'design' : 'preview';
plugin.cmsCookieMode(cmsmode);
if (isDesign) {
//enable/disable main toolbar buttons
$("#DesignToolbar").find("a").removeClass("selected").removeClass("disabled");
$("#DesignToolbar").find("a[data-cmd='preview']").removeClass("selected");
//tell the page we are in design mode (for css)
$("body").removeClass("preview").addClass("design");
//mark all of the designable fields as editable
$(".design-field").attr("contenteditable", true);
setupDesignEditors();
}
else {
$("#DesignToolbar").find("a[data-cmd='preview']").addClass("selected");
//disable some buttons
$("#DesignToolbar").find("a").each(function() {
var cmd = $(this).attr("data-cmd");
if (cmd !== 'preview' && cmd !== 'exit')
$(this).addClass("disabled").removeClass("selected");
});
//tell the page we are in preview mode (for css)
$("body").removeClass("design").removeClass("outline").addClass("preview");
//mark all of the designable fields as NOT editable
$(".design-field").attr("contenteditable", false);
destroyEditors();
}
}
function getFieldData(allData) {
var arr = new Array();
var selector = allData === true ? ".design-field" : ".design-changed";
$(selector).each(function () {
var $this = $(this);
var val = "";
var isRich = $this.attr("data-rich") === 'true';
//see if there is an editor associated with this field
var editor = $this.data("editor");
if (editor)
val = editor.getData();
else if (isRich)
val = $this.html();
else
val = $this.text();
if (val == $this.attr("data-placeholder"))
val = "";
var obj = {
itemId: $this.attr("data-item"),
fieldName: $this.attr("data-field"),
value: val
};
arr.push(obj);
});
if (arr.length === 0)
return null;
else {
var d = {
pageItemId: plugin.settings.contentId,
fields: arr
};
return d;
}
}
function executeCommand(command, data, callback) {
var url = "/cms/designer/command";
var sData = data ? JSON.stringify(data) : null;
$.ajax({
type: "POST",
url: url,
data: {
json: sData,
command: command,
contentId: plugin.settings.contentId
}
}).done(function (result, textStatus, jqXHR) {
if (result) {
if (result.success === true)
saveCleanup();
setDesignerMessage(result.message, (result.success !== true), false, 5000);
if (result.status)
setStatusText(result.status);
}
callback(null, result);
})
.fail(function (xhr, textStatus, errorThrown) {
setDesignerMessage("Error executing the command!", true, false, 5000);
console.error(xhr.responseText);
callback(xhr.responseText);
});
}
/*=== Designer Message ===*/
var designerMsgTimeout = 0;
function setDesignerMessage(msg, isError, persist, ms) {
if (isError)
$("#DesignMessage").addClass("design-error");
else
$("#DesignMessage").removeClass("design-error");
$("#DesignMessage").text(msg);
var dWidth = $(document).width();
var mWidth = $("#DesignMessage").width();
var left = (dWidth / 2) - (mWidth / 2);
$("#DesignMessage").css("left", left).show();
clearTimeout(designerMsgTimeout);
var msTimeout = ms ? ms : 5000;
if (!persist) {
designerMsgTimeout = setTimeout(function () {
clearDesignerMessage();
}, msTimeout);
}
}
function clearDesignerMessage() {
$("#DesignMessage").fadeOut('slow');
}
/*=== End of Designer Message ===*/
//CALL CONSTRUCTOR
plugin.init();
};
// add the plugin to the jQuery.fn object
$.fn.designer = function (options) {
// iterate through the DOM elements we are attaching the plugin to
return this.each(function () {
// if plugin has not already been attached to the element
if (undefined == $(this).data('designer')) {
var plugin = new $.Designer(this, options);
// in the jQuery version of the element
// element.data('designer').publicMethod(arg1, arg2, ... argn) or
// element.data('designer').settings.propertyName
$(this).data('designer', plugin);
}
});
}
}(jQuery));