silverstripe-framework/admin/javascript/LeftAndMain.js
Ingo Schommer 4461cae31b ENHANCEMENT New CMSPreviewable interface class to standardize metadata making a record previewable by the CMS.
ENHANCEMENT New "preview" and "edit" buttons to toggle between preview and cms views. Reinstated utility links to switch between draft and live preview (based on SilverStripeNavigator) (formerly called "AjaxSwitchView")
2011-08-12 17:05:50 +02:00

331 lines
9.4 KiB
JavaScript
Executable File

/**
* File: LeftAndMain.js
*/
(function($) {
$.metadata.setType('html5');
$.entwine('ss', function($){
/**
* Position the loading spinner animation below the ss logo
*/
var positionLoadingSpinner = function() {
var offset = 120; // offset from the ss logo
var spinner = $('.ss-loading-screen .loading-animation');
var top = ($(window).height() - spinner.height()) / 2;
spinner.css('top', top + offset);
spinner.show();
}
$(window).bind('resize', positionLoadingSpinner).trigger('resize');
// setup jquery.entwine
$.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE;
// global ajax error handlers
$.ajaxSetup({
error: function(xmlhttp, status, error) {
var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.statusText;
statusMessage(msg, 'bad');
}
});
/**
* Main LeftAndMain interface with some control panel and an edit form.
*
* Events:
* ajaxsubmit - ...
* validate - ...
* loadnewpage - ...
*/
$('.cms-container').entwine({
CurrentXHR: null,
/**
* Constructor: onmatch
*/
onmatch: function() {
var self = this;
// Browser detection
if($.browser.msie && parseInt($.browser.version, 10) < 7) {
$('.ss-loading-screen').append(
'<p><span class="notice">' +
ss.i18n._t('LeftAndMain.IncompatBrowserWarning') +
'</span></p>'
);
return;
}
// Initialize layouts
this.redraw();
// Monitor window resizes, panel changes and edit form loads for layout changes.
// Also triggers redraw through handleStateChange()
$(window).resize(function() {self.redraw()});
$('.cms-panel').live('toggle', function() {self.redraw();});
$('.cms-edit-form').live('loadnewpage', function() {self.redraw()});
// Remove loading screen
$('.ss-loading-screen').hide();
$('body').removeClass('loading');
$(window).unbind('resize', positionLoadingSpinner);
History.Adapter.bind(window,'statechange',function(){
self.handleStateChange();
});
this._super();
},
redraw: function() {
// Move from inner to outer layouts. Some of the elements might not exist.
this.find('.cms-edit-form[data-layout]').redraw(); // Not all edit forms are layouted
this.find('.cms-preview').redraw();
this.find('.cms-content').redraw();
this.layout({resize: false});
},
/**
* Handles ajax loading of new panels through the window.History object.
* To trigger loading, pass a new URL to window.History.pushState().
*
* Due to the nature of history management, no callbacks are allowed.
* Use the 'beforestatechange' and 'afterstatechange' events instead,
* or overwrite the beforeLoad() and afterLoad() methods on the
* DOM element you're loading the new content into.
* Although you can pass data into pushState(), it shouldn't contain
* DOM elements or callback closures.
*
* The passed URL should allow reconstructing important interface state
* without additional parameters, in the following use cases:
* - Explicit loading through History.pushState()
* - Implicit loading through browser navigation event triggered by the user (forward or back)
* - Full window refresh without ajax
* For example, a ModelAdmin search event should contain the search terms
* as URL parameters, and the result display should automatically appear
* if the URL is loaded without ajax.
*
* Alternatively, you can load new content via $('.cms-content').loadForm(<url>).
* In this case, the action won't be recorded in the browser history.
*/
handleStateChange: function() {
var self = this, h = window.History, state = h.getState();
// Don't allow parallel loading to avoid edge cases
if(this.getCurrentXHR()) this.getCurrentXHR().abort();
var selector = state.data.selector || '.cms-content', contentEl = $(selector);
this.trigger('beforestatechange', {state: state, element: contentEl});
contentEl.addClass('loading');
var xhr = $.ajax({
url: state.url,
success: function(data, status, xhr) {
// Update title
var title = xhr.getResponseHeader('X-Title');
if(title) document.title = title;
// Update panels
jQuery.entwine.synchronous_mode(true);
var newContentEl = $(data);
newContentEl.addClass('loading');
contentEl.replaceWith(newContentEl);
contentEl.remove();
self.redraw();
newContentEl.removeClass('loading');
jQuery.entwine.synchronous_mode(false);
self.trigger('afterstatechange', {data: data, status: status, xhr: xhr, element: newContentEl});
},
error: function(xhr, status, e) {
contentEl.removeClass('loading');
}
});
this.setCurrentXHR(xhr);
}
});
/**
* Make all buttons "hoverable" with jQuery theming.
* Also sets the clicked button on a form submission, making it available through
* a new 'clickedButton' property on the form DOM element.
*/
$('.cms-container :submit, .cms-container button, .cms-container :reset').entwine({
onmatch: function() {
// TODO Adding classes in onmatch confuses entwine
var self = this;
setTimeout(function() {self.addClass('ss-ui-button');}, 10);
this._super();
}
});
/**
* Class: a#profile-link
*
* Link for editing the profile for a logged-in member through a modal dialog.
*/
$('.cms-container .profile-link').entwine({
/**
* Constructor: onmatch
*/
onmatch: function() {
var self = this;
this.bind('click', function(e) {return self._openPopup();});
$('body').append(
'<div id="ss-ui-dialog">'
+ '<iframe id="ss-ui-dialog-iframe" '
+ 'marginWidth="0" marginHeight="0" frameBorder="0" scrolling="auto">'
+ '</iframe>'
+ '</div>'
);
var cookieVal = (jQuery.cookie) ? JSON.parse(jQuery.cookie('ss-ui-dialog')) : false;
$("#ss-ui-dialog").dialog(jQuery.extend({
autoOpen: false,
bgiframe: true,
modal: true,
height: 300,
width: 500,
ghost: true,
resizeStop: function(e, ui) {
self._resize();
self._saveState();
},
dragStop: function(e, ui) {
self._saveState();
},
// TODO i18n
title: 'Edit Profile'
}, cookieVal)).css('overflow', 'hidden');
$('#ss-ui-dialog-iframe').bind('load', function(e) {self._resize();});
},
/**
* Function: _openPopup
*/
_openPopup: function(e) {
$('#ss-ui-dialog-iframe').attr('src', this.attr('href'));
$("#ss-ui-dialog").dialog('open');
return false;
},
/**
* Function: _resize
*/
_resize: function() {
var iframe = $('#ss-ui-dialog-iframe');
var container = $('#ss-ui-dialog');
iframe.attr('width',
container.innerWidth()
- parseFloat(container.css('paddingLeft'))
- parseFloat(container.css('paddingRight'))
);
iframe.attr('height',
container.innerHeight()
- parseFloat(container.css('paddingTop'))
- parseFloat(container.css('paddingBottom'))
);
this._saveState();
},
/**
* Function: _saveState
*/
_saveState: function() {
var container = $('#ss-ui-dialog');
// save size in cookie (optional)
if(jQuery.cookie && container.width() && container.height()) {
jQuery.cookie(
'ss-ui-dialog',
JSON.stringify({
width: parseInt(container.width(), 10),
height: parseInt(container.height(), 10),
position: [
parseInt(container.offset().top, 10),
parseInt(container.offset().left, 10)
]
}),
{ expires: 30, path: '/'}
);
}
}
});
/**
* Duplicates functionality in DateField.js, but due to using entwine we can match
* the DOM element on creation, rather than onclick - which allows us to decorate
* the field with a calendar icon
*/
$('.cms-container .field.date input.text').entwine({
onmatch: function() {
var holder = $(this).parents('.field.date:first'), config = holder.metadata({type: 'class'});
if(!config.showcalendar) return;
config.showOn = 'button';
if(config.locale && $.datepicker.regional[config.locale]) {
config = $.extend(config, $.datepicker.regional[config.locale], {});
}
$(this).datepicker(config);
// // Unfortunately jQuery UI only allows configuration of icon images, not sprites
// this.next('button').button('option', 'icons', {primary : 'ui-icon-calendar'});
this._super();
}
});
/**
* Styled dropdown select fields via chosen. Allows things like search and optgroup
* selection support. Rather than manually adding classes to selects we want
* styled, we style everything but the ones we tell it not to.
*
* For the CMS we also need to tell the parent div that his has a select so
* we can fix the height cropping.
*/
$('.cms-container .field.dropdown').entwine({
onmatch: function() {
$(this).find("select:not(.no-chzn)").chosen();
$(this).addClass("has-chzn");
this._super();
}
});
});
}(jQuery));
// Backwards compatibility
var statusMessage = function(text, type) {
jQuery.noticeAdd({text: text, type: type});
};
var errorMessage = function(text) {
jQuery.noticeAdd({text: text, type: 'error'});
};
returnFalse = function() {
return false;
};
/**
* Find and enable TinyMCE on all htmleditor fields
* Pulled in from old tinymce.template.js
*/
function nullConverter(url) {
return url;
};