/** * 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( '

' + ss.i18n._t('LeftAndMain.IncompatBrowserWarning') + '

' ); return; } // Initialize layouts, inner to outer this.redraw(); $(window).resize(function() {self.redraw()}); // Remove loading screen $('.ss-loading-screen').hide(); $('body').removeClass('loading'); $(window).unbind('resize', positionLoadingSpinner); $('.cms-edit-form').live('loadnewpage', function() {self.redraw()}); History.Adapter.bind(window,'statechange',function(){ self.handleStateChange(); }); this._super(); }, redraw: function() { // Not all edit forms are layouted var editForm = $('.cms-edit-form[data-layout]').layout(); $('.cms-content').layout(); $('.cms-container').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(). * 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 contentEl = $(state.data.selector || '.cms-content'); this.trigger('beforestatechange', {state: state}); contentEl.beforeLoad(state.url); 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 contentEl.afterLoad(data, status, xhr); self.redraw(); self.trigger('afterstatechange', {data: data, status: status, xhr: xhr}); } }); this.setCurrentXHR(xhr); } }); /** * Monitor all panels for layout changes */ $('.cms-panel').entwine({ ontoggle: function(e) { this.parents('.cms-container').redraw(); } }); /** * 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( '
' + '' + '
' ); 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: '/'} ); } } }); /** * Class: #switchView a * * Updates the different stage links which are generated through * the SilverStripeNavigator class on the serverside each time a form record * is reloaded. */ $('#switchView').entwine({ onmatch: function() { this._super(); $('.cms-edit-form').bind('loadnewpage delete', function(e) { var updatedSwitchView = $('#AjaxSwitchView'); if(updatedSwitchView.length) { $('#SwitchView').html(updatedSwitchView.html()); updatedSwitchView.remove(); } }); } }); /** * Class: #switchView a * * Links for viewing the currently loaded page * in different modes: 'live', 'stage' or 'archived'. * * Requires: * jquery.metadata */ $('#switchView a').entwine({ /** * Function: onclick */ onclick: function(e) { // Open in popup window.open($(e.target).attr('href')); return false; } }); /** * 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(); } }) }); }(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; };