From 9a4b93a058ee06a66867d64264d9720a1f5cde85 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Fri, 22 Apr 2016 17:21:14 +1200 Subject: [PATCH] BUG Fix baseurl in IE missing leading / Reformat LeftAndMain.js to new indentation standard --- admin/client/dist/js/LeftAndMain.js | 2151 +++++++++--------- admin/client/dist/js/bundle-legacy.js | 4 +- admin/client/src/legacy/LeftAndMain.js | 2895 ++++++++++++------------ 3 files changed, 2528 insertions(+), 2522 deletions(-) diff --git a/admin/client/dist/js/LeftAndMain.js b/admin/client/dist/js/LeftAndMain.js index 456625566..866a003d3 100644 --- a/admin/client/dist/js/LeftAndMain.js +++ b/admin/client/dist/js/LeftAndMain.js @@ -1,1138 +1,1141 @@ (function (global, factory) { - if (typeof define === "function" && define.amd) { - define('ss.LeftAndMain', ['jQuery', 'lib/Router', 'lib/Config'], factory); - } else if (typeof exports !== "undefined") { - factory(require('jQuery'), require('lib/Router'), require('lib/Config')); - } else { - var mod = { - exports: {} - }; - factory(global.jQuery, global.Router, global.Config); - global.ssLeftAndMain = mod.exports; - } + if (typeof define === "function" && define.amd) { + define('ss.LeftAndMain', ['jQuery', 'lib/Router', 'lib/Config'], factory); + } else if (typeof exports !== "undefined") { + factory(require('jQuery'), require('lib/Router'), require('lib/Config')); + } else { + var mod = { + exports: {} + }; + factory(global.jQuery, global.Router, global.Config); + global.ssLeftAndMain = mod.exports; + } })(this, function (_jQuery, _Router, _Config) { - 'use strict'; - - var _jQuery2 = _interopRequireDefault(_jQuery); - - var _Router2 = _interopRequireDefault(_Router); - - var _Config2 = _interopRequireDefault(_Config); - - function _interopRequireDefault(obj) { - return obj && obj.__esModule ? obj : { - default: obj - }; - } - - var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { - return typeof obj; - } : function (obj) { - return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; - }; - - var windowWidth, windowHeight; - - _jQuery2.default.noConflict(); - - window.ss = window.ss || {}; - window.ss.router = _Router2.default; - - window.ss.debounce = function (func, wait, immediate) { - var timeout, context, args; - - var later = function later() { - timeout = null; - if (!immediate) func.apply(context, args); - }; - - return function () { - var callNow = immediate && !timeout; - - context = this; - args = arguments; - - clearTimeout(timeout); - timeout = setTimeout(later, wait); - - if (callNow) { - func.apply(context, args); - } - }; - }; - - function getUrlPath(url) { - var anchor = document.createElement('a'); - anchor.href = url; - - return anchor.pathname; - } - - (0, _jQuery2.default)(window).bind('resize.leftandmain', function (e) { - (0, _jQuery2.default)('.cms-container').trigger('windowresize'); - }); - - _jQuery2.default.entwine.warningLevel = _jQuery2.default.entwine.WARN_LEVEL_BESTPRACTISE; - - _jQuery2.default.entwine('ss', function ($) { - $(window).on("message", function (e) { - var target, - event = e.originalEvent, - data = _typeof(event.data) === 'object' ? event.data : JSON.parse(event.data); - - if ($.path.parseUrl(window.location.href).domain !== $.path.parseUrl(event.origin).domain) return; - - target = typeof data.target === 'undefined' ? $(window) : $(data.target); - - switch (data.type) { - case 'event': - target.trigger(data.event, data.data); - break; - case 'callback': - target[data.callback].call(target, data.data); - break; - } - }); - - var positionLoadingSpinner = function positionLoadingSpinner() { - var offset = 120; - var spinner = $('.ss-loading-screen .loading-animation'); - var top = ($(window).height() - spinner.height()) / 2; - spinner.css('top', top + offset); - spinner.show(); - }; - - var applyChosen = function applyChosen(el) { - if (el.is(':visible')) { - el.addClass('has-chosen').chosen({ - allow_single_deselect: true, - disable_search_threshold: 20, - display_disabled_options: true - }); - } else { - setTimeout(function () { - el.show(); - applyChosen(el); - }, 500); - } - }; - - var isSameUrl = function isSameUrl(url1, url2) { - var baseUrl = $('base').attr('href'); - url1 = $.path.isAbsoluteUrl(url1) ? url1 : $.path.makeUrlAbsolute(url1, baseUrl), url2 = $.path.isAbsoluteUrl(url2) ? url2 : $.path.makeUrlAbsolute(url2, baseUrl); - var url1parts = $.path.parseUrl(url1), - url2parts = $.path.parseUrl(url2); - return url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') && url1parts.search == url2parts.search; - }; - - var ajaxCompleteEvent = window.ss.debounce(function () { - $(window).trigger('ajaxComplete'); - }, 1000, true); - - $(window).bind('resize', positionLoadingSpinner).trigger('resize'); - - $(document).ajaxComplete(function (e, xhr, settings) { - var origUrl, - url = xhr.getResponseHeader('X-ControllerURL'), - destUrl = settings.url, - msg = xhr.getResponseHeader('X-Status') !== null ? xhr.getResponseHeader('X-Status') : xhr.statusText, - msgType = xhr.status < 200 || xhr.status > 399 ? 'bad' : 'good', - ignoredMessages = ['OK']; - if (window.history.state) { - origUrl = window.history.state.path; - } else { - origUrl = document.URL; - } - - if (url !== null && (!isSameUrl(origUrl, url) || !isSameUrl(destUrl, url))) { - _Router2.default.show(url, { - id: new Date().getTime() + String(Math.random()).replace(/\D/g, ''), - pjax: xhr.getResponseHeader('X-Pjax') ? xhr.getResponseHeader('X-Pjax') : settings.headers['X-Pjax'] - }); - } - - if (xhr.getResponseHeader('X-Reauthenticate')) { - $('.cms-container').showLoginDialog(); - return; - } - - if (xhr.status !== 0 && msg && $.inArray(msg, ignoredMessages)) { - statusMessage(decodeURIComponent(msg), msgType); - } - - ajaxCompleteEvent(this); - }); - - $('.cms-container').entwine({ - StateChangeXHR: null, - - FragmentXHR: {}, - - StateChangeCount: 0, - - LayoutOptions: { - minContentWidth: 940, - minPreviewWidth: 400, - mode: 'content' - }, - - onadd: function onadd() { - var self = this, - basePath = getUrlPath($('base')[0].href); - - basePath = basePath.replace(/\/$/, ''); - _Router2.default.base(basePath); - - _Config2.default.getTopLevelRoutes().forEach(function (route) { - (0, _Router2.default)('/' + route + '(/*?)?', function (ctx, next) { - if (document.readyState !== 'complete') { - return next(); - } - - self.handleStateChange(null, ctx.state).done(next); - }); - }); - - _Router2.default.start(); - - if ($.browser.msie && parseInt($.browser.version, 10) < 8) { - $('.ss-loading-screen').append('

' + 'Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.' + '

').css('z-index', $('.ss-loading-screen').css('z-index') + 1); - $('.loading-animation').remove(); - - this._super(); - return; - } - - this.redraw(); - - $('.ss-loading-screen').hide(); - $('body').removeClass('loading'); - $(window).unbind('resize', positionLoadingSpinner); - this.restoreTabState(); - this._super(); - }, - - fromWindow: { - onstatechange: function onstatechange(event, historyState) { - this.handleStateChange(event, historyState); - } - }, - - 'onwindowresize': function onwindowresize() { - this.redraw(); - }, - - 'from .cms-panel': { - ontoggle: function ontoggle() { - this.redraw(); - } - }, - - 'from .cms-container': { - onaftersubmitform: function onaftersubmitform() { - this.redraw(); - } - }, - - 'from .cms-menu-list li a': { - onclick: function onclick(e) { - var href = $(e.target).attr('href'); - if (e.which > 1 || href == this._tabStateUrl()) return; - this.splitViewMode(); - } - }, - - updateLayoutOptions: function updateLayoutOptions(newSpec) { - var spec = this.getLayoutOptions(); - - var dirty = false; - - for (var k in newSpec) { - if (spec[k] !== newSpec[k]) { - spec[k] = newSpec[k]; - dirty = true; - } - } + 'use strict'; + + var _jQuery2 = _interopRequireDefault(_jQuery); + + var _Router2 = _interopRequireDefault(_Router); + + var _Config2 = _interopRequireDefault(_Config); + + function _interopRequireDefault(obj) { + return obj && obj.__esModule ? obj : { + default: obj + }; + } + + var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { + return typeof obj; + } : function (obj) { + return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; + }; + + var windowWidth, windowHeight; + + _jQuery2.default.noConflict(); + + window.ss = window.ss || {}; + window.ss.router = _Router2.default; + + window.ss.debounce = function (func, wait, immediate) { + var timeout, context, args; + + var later = function later() { + timeout = null; + if (!immediate) func.apply(context, args); + }; + + return function () { + var callNow = immediate && !timeout; + + context = this; + args = arguments; + + clearTimeout(timeout); + timeout = setTimeout(later, wait); + + if (callNow) { + func.apply(context, args); + } + }; + }; + + function getUrlPath(url) { + var anchor = document.createElement('a'); + anchor.href = url; + + return anchor.pathname; + } + + (0, _jQuery2.default)(window).bind('resize.leftandmain', function (e) { + (0, _jQuery2.default)('.cms-container').trigger('windowresize'); + }); + + _jQuery2.default.entwine.warningLevel = _jQuery2.default.entwine.WARN_LEVEL_BESTPRACTISE; + + _jQuery2.default.entwine('ss', function ($) { + $(window).on("message", function (e) { + var target, + event = e.originalEvent, + data = _typeof(event.data) === 'object' ? event.data : JSON.parse(event.data); + + if ($.path.parseUrl(window.location.href).domain !== $.path.parseUrl(event.origin).domain) return; + + target = typeof data.target === 'undefined' ? $(window) : $(data.target); + + switch (data.type) { + case 'event': + target.trigger(data.event, data.data); + break; + case 'callback': + target[data.callback].call(target, data.data); + break; + } + }); + + var positionLoadingSpinner = function positionLoadingSpinner() { + var offset = 120; + var spinner = $('.ss-loading-screen .loading-animation'); + var top = ($(window).height() - spinner.height()) / 2; + spinner.css('top', top + offset); + spinner.show(); + }; + + var applyChosen = function applyChosen(el) { + if (el.is(':visible')) { + el.addClass('has-chosen').chosen({ + allow_single_deselect: true, + disable_search_threshold: 20, + display_disabled_options: true + }); + } else { + setTimeout(function () { + el.show(); + applyChosen(el); + }, 500); + } + }; + + var isSameUrl = function isSameUrl(url1, url2) { + var baseUrl = $('base').attr('href'); + url1 = $.path.isAbsoluteUrl(url1) ? url1 : $.path.makeUrlAbsolute(url1, baseUrl), url2 = $.path.isAbsoluteUrl(url2) ? url2 : $.path.makeUrlAbsolute(url2, baseUrl); + var url1parts = $.path.parseUrl(url1), + url2parts = $.path.parseUrl(url2); + return url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') && url1parts.search == url2parts.search; + }; + + var ajaxCompleteEvent = window.ss.debounce(function () { + $(window).trigger('ajaxComplete'); + }, 1000, true); + + $(window).bind('resize', positionLoadingSpinner).trigger('resize'); + + $(document).ajaxComplete(function (e, xhr, settings) { + var origUrl, + url = xhr.getResponseHeader('X-ControllerURL'), + destUrl = settings.url, + msg = xhr.getResponseHeader('X-Status') !== null ? xhr.getResponseHeader('X-Status') : xhr.statusText, + msgType = xhr.status < 200 || xhr.status > 399 ? 'bad' : 'good', + ignoredMessages = ['OK']; + if (window.history.state) { + origUrl = window.history.state.path; + } else { + origUrl = document.URL; + } + + if (url !== null && (!isSameUrl(origUrl, url) || !isSameUrl(destUrl, url))) { + _Router2.default.show(url, { + id: new Date().getTime() + String(Math.random()).replace(/\D/g, ''), + pjax: xhr.getResponseHeader('X-Pjax') ? xhr.getResponseHeader('X-Pjax') : settings.headers['X-Pjax'] + }); + } + + if (xhr.getResponseHeader('X-Reauthenticate')) { + $('.cms-container').showLoginDialog(); + return; + } + + if (xhr.status !== 0 && msg && $.inArray(msg, ignoredMessages)) { + statusMessage(decodeURIComponent(msg), msgType); + } + + ajaxCompleteEvent(this); + }); + + $('.cms-container').entwine({ + StateChangeXHR: null, + + FragmentXHR: {}, + + StateChangeCount: 0, + + LayoutOptions: { + minContentWidth: 940, + minPreviewWidth: 400, + mode: 'content' + }, + + onadd: function onadd() { + var self = this, + basePath = getUrlPath($('base')[0].href); + + basePath = basePath.replace(/\/$/, ''); + if (basePath.match(/^[^\/]/)) { + basePath = '/' + basePath; + } + _Router2.default.base(basePath); + + _Config2.default.getTopLevelRoutes().forEach(function (route) { + (0, _Router2.default)('/' + route + '(/*?)?', function (ctx, next) { + if (document.readyState !== 'complete') { + return next(); + } + + self.handleStateChange(null, ctx.state).done(next); + }); + }); + + _Router2.default.start(); + + if ($.browser.msie && parseInt($.browser.version, 10) < 8) { + $('.ss-loading-screen').append('

' + 'Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.' + '

').css('z-index', $('.ss-loading-screen').css('z-index') + 1); + $('.loading-animation').remove(); + + this._super(); + return; + } + + this.redraw(); + + $('.ss-loading-screen').hide(); + $('body').removeClass('loading'); + $(window).unbind('resize', positionLoadingSpinner); + this.restoreTabState(); + this._super(); + }, + + fromWindow: { + onstatechange: function onstatechange(event, historyState) { + this.handleStateChange(event, historyState); + } + }, + + 'onwindowresize': function onwindowresize() { + this.redraw(); + }, + + 'from .cms-panel': { + ontoggle: function ontoggle() { + this.redraw(); + } + }, + + 'from .cms-container': { + onaftersubmitform: function onaftersubmitform() { + this.redraw(); + } + }, + + 'from .cms-menu-list li a': { + onclick: function onclick(e) { + var href = $(e.target).attr('href'); + if (e.which > 1 || href == this._tabStateUrl()) return; + this.splitViewMode(); + } + }, + + updateLayoutOptions: function updateLayoutOptions(newSpec) { + var spec = this.getLayoutOptions(); + + var dirty = false; + + for (var k in newSpec) { + if (spec[k] !== newSpec[k]) { + spec[k] = newSpec[k]; + dirty = true; + } + } + + if (dirty) this.redraw(); + }, - if (dirty) this.redraw(); - }, + splitViewMode: function splitViewMode() { + this.updateLayoutOptions({ + mode: 'split' + }); + }, - splitViewMode: function splitViewMode() { - this.updateLayoutOptions({ - mode: 'split' - }); - }, + contentViewMode: function contentViewMode() { + this.updateLayoutOptions({ + mode: 'content' + }); + }, - contentViewMode: function contentViewMode() { - this.updateLayoutOptions({ - mode: 'content' - }); - }, + previewMode: function previewMode() { + this.updateLayoutOptions({ + mode: 'preview' + }); + }, - previewMode: function previewMode() { - this.updateLayoutOptions({ - mode: 'preview' - }); - }, + RedrawSuppression: false, - RedrawSuppression: false, + redraw: function redraw() { + if (this.getRedrawSuppression()) return; + + if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); - redraw: function redraw() { - if (this.getRedrawSuppression()) return; + this.data('jlayout', jLayout.threeColumnCompressor({ + menu: this.children('.cms-menu'), + content: this.children('.cms-content'), + preview: this.children('.cms-preview') + }, this.getLayoutOptions())); - if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); + this.layout(); - this.data('jlayout', jLayout.threeColumnCompressor({ - menu: this.children('.cms-menu'), - content: this.children('.cms-content'), - preview: this.children('.cms-preview') - }, this.getLayoutOptions())); + this.find('.cms-panel-layout').redraw(); + this.find('.cms-content-fields[data-layout-type]').redraw(); + this.find('.cms-edit-form[data-layout-type]').redraw(); + this.find('.cms-preview').redraw(); + this.find('.cms-content').redraw(); + }, - this.layout(); + checkCanNavigate: function checkCanNavigate(selectors) { + var contentEls = this._findFragments(selectors || ['Content']), + trackedEls = contentEls.find(':data(changetracker)').add(contentEls.filter(':data(changetracker)')), + safe = true; - this.find('.cms-panel-layout').redraw(); - this.find('.cms-content-fields[data-layout-type]').redraw(); - this.find('.cms-edit-form[data-layout-type]').redraw(); - this.find('.cms-preview').redraw(); - this.find('.cms-content').redraw(); - }, + if (!trackedEls.length) { + return true; + } - checkCanNavigate: function checkCanNavigate(selectors) { - var contentEls = this._findFragments(selectors || ['Content']), - trackedEls = contentEls.find(':data(changetracker)').add(contentEls.filter(':data(changetracker)')), - safe = true; + trackedEls.each(function () { + if (!$(this).confirmUnsavedChanges()) { + safe = false; + } + }); - if (!trackedEls.length) { - return true; - } + return safe; + }, - trackedEls.each(function () { - if (!$(this).confirmUnsavedChanges()) { - safe = false; - } - }); + loadPanel: function loadPanel(url) { + var title = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1]; + var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; + var forceReload = arguments[3]; + var forceReferer = arguments.length <= 4 || arguments[4] === undefined ? window.history.state.path : arguments[4]; - return safe; - }, + if (!this.checkCanNavigate(data.pjax ? data.pjax.split(',') : ['Content'])) { + return; + } - loadPanel: function loadPanel(url) { - var title = arguments.length <= 1 || arguments[1] === undefined ? '' : arguments[1]; - var data = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; - var forceReload = arguments[3]; - var forceReferer = arguments.length <= 4 || arguments[4] === undefined ? window.history.state.path : arguments[4]; + this.saveTabState(); - if (!this.checkCanNavigate(data.pjax ? data.pjax.split(',') : ['Content'])) { - return; - } + data.__forceReferer = forceReferer; - this.saveTabState(); + if (forceReload) { + data.__forceReload = Math.random(); + } - data.__forceReferer = forceReferer; + _Router2.default.show(url, data); + }, - if (forceReload) { - data.__forceReload = Math.random(); - } + reloadCurrentPanel: function reloadCurrentPanel() { + this.loadPanel(window.history.state.path, null, null, true); + }, - _Router2.default.show(url, data); - }, + submitForm: function submitForm(form, button, callback, ajaxOptions) { + var self = this; - reloadCurrentPanel: function reloadCurrentPanel() { - this.loadPanel(window.history.state.path, null, null, true); - }, + if (!button) button = this.find('.Actions :submit[name=action_save]'); - submitForm: function submitForm(form, button, callback, ajaxOptions) { - var self = this; + if (!button) button = this.find('.Actions :submit:first'); - if (!button) button = this.find('.Actions :submit[name=action_save]'); + form.trigger('beforesubmitform'); + this.trigger('submitform', { form: form, button: button }); - if (!button) button = this.find('.Actions :submit:first'); + $(button).addClass('loading'); - form.trigger('beforesubmitform'); - this.trigger('submitform', { form: form, button: button }); + var validationResult = form.validate(); + if (typeof validationResult !== 'undefined' && !validationResult) { + statusMessage("Validation failed.", "bad"); - $(button).addClass('loading'); + $(button).removeClass('loading'); - var validationResult = form.validate(); - if (typeof validationResult !== 'undefined' && !validationResult) { - statusMessage("Validation failed.", "bad"); + return false; + } - $(button).removeClass('loading'); + var formData = form.serializeArray(); - return false; - } + formData.push({ name: $(button).attr('name'), value: '1' }); - var formData = form.serializeArray(); + formData.push({ name: 'BackURL', value: window.history.state.path.replace(/\/$/, '') }); - formData.push({ name: $(button).attr('name'), value: '1' }); + this.saveTabState(); - formData.push({ name: 'BackURL', value: window.history.state.path.replace(/\/$/, '') }); + jQuery.ajax(jQuery.extend({ + headers: { "X-Pjax": "CurrentForm,Breadcrumbs" }, + url: form.attr('action'), + data: formData, + type: 'POST', + complete: function complete() { + $(button).removeClass('loading'); + }, + success: function success(data, status, xhr) { + form.removeClass('changed'); + if (callback) callback(data, status, xhr); - this.saveTabState(); + var newContentEls = self.handleAjaxResponse(data, status, xhr); + if (!newContentEls) return; - jQuery.ajax(jQuery.extend({ - headers: { "X-Pjax": "CurrentForm,Breadcrumbs" }, - url: form.attr('action'), - data: formData, - type: 'POST', - complete: function complete() { - $(button).removeClass('loading'); - }, - success: function success(data, status, xhr) { - form.removeClass('changed'); - if (callback) callback(data, status, xhr); + newContentEls.filter('form').trigger('aftersubmitform', { status: status, xhr: xhr, formData: formData }); + } + }, ajaxOptions)); - var newContentEls = self.handleAjaxResponse(data, status, xhr); - if (!newContentEls) return; + return false; + }, - newContentEls.filter('form').trigger('aftersubmitform', { status: status, xhr: xhr, formData: formData }); - } - }, ajaxOptions)); + LastState: null, - return false; - }, + PauseState: false, - LastState: null, + handleStateChange: function handleStateChange(event) { + var historyState = arguments.length <= 1 || arguments[1] === undefined ? window.history.state : arguments[1]; - PauseState: false, + if (this.getPauseState()) { + return; + } - handleStateChange: function handleStateChange(event) { - var historyState = arguments.length <= 1 || arguments[1] === undefined ? window.history.state : arguments[1]; + if (this.getStateChangeXHR()) { + this.getStateChangeXHR().abort(); + } - if (this.getPauseState()) { - return; - } + var self = this, + fragments = historyState.pjax || 'Content', + headers = {}, + fragmentsArr = fragments.split(','), + contentEls = this._findFragments(fragmentsArr); - if (this.getStateChangeXHR()) { - this.getStateChangeXHR().abort(); - } + this.setStateChangeCount(this.getStateChangeCount() + 1); - var self = this, - fragments = historyState.pjax || 'Content', - headers = {}, - fragmentsArr = fragments.split(','), - contentEls = this._findFragments(fragmentsArr); + if (!this.checkCanNavigate()) { + var lastState = this.getLastState(); - this.setStateChangeCount(this.getStateChangeCount() + 1); + this.setPauseState(true); - if (!this.checkCanNavigate()) { - var lastState = this.getLastState(); + if (lastState !== null) { + _Router2.default.show(lastState.url); + } else { + _Router2.default.back(); + } - this.setPauseState(true); + this.setPauseState(false); - if (lastState !== null) { - _Router2.default.show(lastState.url); - } else { - _Router2.default.back(); - } + return; + } - this.setPauseState(false); + this.setLastState(historyState); - return; - } + if (contentEls.length < fragmentsArr.length) { + fragments = 'Content', fragmentsArr = ['Content']; + contentEls = this._findFragments(fragmentsArr); + } - this.setLastState(historyState); + this.trigger('beforestatechange', { state: historyState, element: contentEls }); - if (contentEls.length < fragmentsArr.length) { - fragments = 'Content', fragmentsArr = ['Content']; - contentEls = this._findFragments(fragmentsArr); - } + headers['X-Pjax'] = fragments; - this.trigger('beforestatechange', { state: historyState, element: contentEls }); + if (typeof historyState.__forceReferer !== 'undefined') { + var url = historyState.__forceReferer; - headers['X-Pjax'] = fragments; + try { + url = decodeURI(url); + } catch (e) {} finally { + headers['X-Backurl'] = encodeURI(url); + } + } - if (typeof historyState.__forceReferer !== 'undefined') { - var url = historyState.__forceReferer; + contentEls.addClass('loading'); - try { - url = decodeURI(url); - } catch (e) {} finally { - headers['X-Backurl'] = encodeURI(url); - } - } + var promise = $.ajax({ + headers: headers, + url: historyState.path + }).done(function (data, status, xhr) { + var els = self.handleAjaxResponse(data, status, xhr, historyState); + self.trigger('afterstatechange', { data: data, status: status, xhr: xhr, element: els, state: historyState }); + }).always(function () { + self.setStateChangeXHR(null); - contentEls.addClass('loading'); + contentEls.removeClass('loading'); + }); - var promise = $.ajax({ - headers: headers, - url: historyState.path - }).done(function (data, status, xhr) { - var els = self.handleAjaxResponse(data, status, xhr, historyState); - self.trigger('afterstatechange', { data: data, status: status, xhr: xhr, element: els, state: historyState }); - }).always(function () { - self.setStateChangeXHR(null); + this.setStateChangeXHR(promise); - contentEls.removeClass('loading'); - }); + return promise; + }, + + loadFragment: function loadFragment(url, pjaxFragments) { + + var self = this, + xhr, + headers = {}, + baseUrl = $('base').attr('href'), + fragmentXHR = this.getFragmentXHR(); - this.setStateChangeXHR(promise); + if (typeof fragmentXHR[pjaxFragments] !== 'undefined' && fragmentXHR[pjaxFragments] !== null) { + fragmentXHR[pjaxFragments].abort(); + fragmentXHR[pjaxFragments] = null; + } - return promise; - }, - - loadFragment: function loadFragment(url, pjaxFragments) { - - var self = this, - xhr, - headers = {}, - baseUrl = $('base').attr('href'), - fragmentXHR = this.getFragmentXHR(); + url = $.path.isAbsoluteUrl(url) ? url : $.path.makeUrlAbsolute(url, baseUrl); + headers['X-Pjax'] = pjaxFragments; - if (typeof fragmentXHR[pjaxFragments] !== 'undefined' && fragmentXHR[pjaxFragments] !== null) { - fragmentXHR[pjaxFragments].abort(); - fragmentXHR[pjaxFragments] = null; - } - - url = $.path.isAbsoluteUrl(url) ? url : $.path.makeUrlAbsolute(url, baseUrl); - headers['X-Pjax'] = pjaxFragments; - - xhr = $.ajax({ - headers: headers, - url: url, - success: function success(data, status, xhr) { - var elements = self.handleAjaxResponse(data, status, xhr, null); - - self.trigger('afterloadfragment', { data: data, status: status, xhr: xhr, elements: elements }); - }, - error: function error(xhr, status, _error) { - self.trigger('loadfragmenterror', { xhr: xhr, status: status, error: _error }); - }, - complete: function complete() { - var fragmentXHR = self.getFragmentXHR(); - if (typeof fragmentXHR[pjaxFragments] !== 'undefined' && fragmentXHR[pjaxFragments] !== null) { - fragmentXHR[pjaxFragments] = null; - } - } - }); - - fragmentXHR[pjaxFragments] = xhr; - - return xhr; - }, - - handleAjaxResponse: function handleAjaxResponse(data, status, xhr, state) { - var self = this, - url, - selectedTabs, - guessFragment, - fragment, - $data; - - if (xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) { - var baseUrl = $('base').attr('href'), - rawURL = xhr.getResponseHeader('X-ControllerURL'), - url = $.path.isAbsoluteUrl(rawURL) ? rawURL : $.path.makeUrlAbsolute(rawURL, baseUrl); - - document.location.href = url; - return; - } - - if (!data) return; - - var title = xhr.getResponseHeader('X-Title'); - if (title) document.title = decodeURIComponent(title.replace(/\+/g, ' ')); - - var newFragments = {}, - newContentEls; - - if (xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) { - newFragments = data; - } else { - fragment = document.createDocumentFragment(); - - jQuery.clean([data], document, fragment, []); - $data = $(jQuery.merge([], fragment.childNodes)); - - guessFragment = 'Content'; - if ($data.is('form') && !$data.is('[data-pjax-fragment~=Content]')) guessFragment = 'CurrentForm'; - - newFragments[guessFragment] = $data; - } - - this.setRedrawSuppression(true); - try { - $.each(newFragments, function (newFragment, html) { - var contentEl = $('[data-pjax-fragment]').filter(function () { - return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1; - }), - newContentEl = $(html); - - if (newContentEls) newContentEls.add(newContentEl);else newContentEls = newContentEl; - - if (newContentEl.find('.cms-container').length) { - throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops'; - } - - var origStyle = contentEl.attr('style'); - var origParent = contentEl.parent(); - var origParentLayoutApplied = typeof origParent.data('jlayout') !== 'undefined'; - var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden']; - var elemClasses = contentEl.attr('class'); - var origLayoutClasses = []; - if (elemClasses) { - origLayoutClasses = $.grep(elemClasses.split(' '), function (val) { - return $.inArray(val, layoutClasses) >= 0; - }); - } - - newContentEl.removeClass(layoutClasses.join(' ')).addClass(origLayoutClasses.join(' ')); - if (origStyle) newContentEl.attr('style', origStyle); - - var styles = newContentEl.find('style').detach(); - if (styles.length) $(document).find('head').append(styles); - - contentEl.replaceWith(newContentEl); - - if (!origParent.is('.cms-container') && origParentLayoutApplied) { - origParent.layout(); - } - }); - - var newForm = newContentEls.filter('form'); - if (newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset'); - } finally { - this.setRedrawSuppression(false); - } - - this.redraw(); - this.restoreTabState(state && typeof state.tabState !== 'undefined' ? state.tabState : null); - - return newContentEls; - }, - - _findFragments: function _findFragments(fragments) { - return $('[data-pjax-fragment]').filter(function () { - var i, - nodeFragments = $(this).data('pjaxFragment').split(' '); - for (i in fragments) { - if ($.inArray(fragments[i], nodeFragments) != -1) return true; - } - return false; - }); - }, - - refresh: function refresh() { - $(window).trigger('statechange'); - - $(this).redraw(); - }, - - saveTabState: function saveTabState() { - if (typeof window.sessionStorage == "undefined" || window.sessionStorage === null) return; - - var selectedTabs = [], - url = this._tabStateUrl(); - this.find('.cms-tabset,.ss-tabset').each(function (i, el) { - var id = $(el).attr('id'); - if (!id) return; - if (!$(el).data('tabs')) return; - if ($(el).data('ignoreTabState') || $(el).getIgnoreTabState()) return; - - selectedTabs.push({ id: id, selected: $(el).tabs('option', 'selected') }); - }); - - if (selectedTabs) { - var tabsUrl = 'tabs-' + url; - try { - window.sessionStorage.setItem(tabsUrl, JSON.stringify(selectedTabs)); - } catch (err) { - if (err.code === DOMException.QUOTA_EXCEEDED_ERR && window.sessionStorage.length === 0) { - return; - } else { - throw err; - } - } - } - }, - - restoreTabState: function restoreTabState(overrideStates) { - var self = this, - url = this._tabStateUrl(), - hasSessionStorage = typeof window.sessionStorage !== "undefined" && window.sessionStorage, - sessionData = hasSessionStorage ? window.sessionStorage.getItem('tabs-' + url) : null, - sessionStates = sessionData ? JSON.parse(sessionData) : false; - - this.find('.cms-tabset, .ss-tabset').each(function () { - var index, - tabset = $(this), - tabsetId = tabset.attr('id'), - tab, - forcedTab = tabset.find('.ss-tabs-force-active'); - - if (!tabset.data('tabs')) { - return; - } - - tabset.tabs('refresh'); - - if (forcedTab.length) { - index = forcedTab.index(); - } else if (overrideStates && overrideStates[tabsetId]) { - tab = tabset.find(overrideStates[tabsetId].tabSelector); - if (tab.length) { - index = tab.index(); - } - } else if (sessionStates) { - $.each(sessionStates, function (i, sessionState) { - if (tabset.is('#' + sessionState.id)) { - index = sessionState.selected; - } - }); - } - if (index !== null) { - tabset.tabs('option', 'active', index); - self.trigger('tabstaterestored'); - } - }); - }, - - clearTabState: function clearTabState(url) { - if (typeof window.sessionStorage == "undefined") return; - - var s = window.sessionStorage; - if (url) { - s.removeItem('tabs-' + url); - } else { - for (var i = 0; i < s.length; i++) { - if (s.key(i).match(/^tabs-/)) s.removeItem(s.key(i)); - } - } - }, - - clearCurrentTabState: function clearCurrentTabState() { - this.clearTabState(this._tabStateUrl()); - }, - - _tabStateUrl: function _tabStateUrl() { - return window.history.state.path.replace(/\?.*/, '').replace(/#.*/, '').replace($('base').attr('href'), ''); - }, - - showLoginDialog: function showLoginDialog() { - var tempid = $('body').data('member-tempid'), - dialog = $('.leftandmain-logindialog'), - url = 'CMSSecurity/login'; - - if (dialog.length) dialog.remove(); - - url = $.path.addSearchParams(url, { - 'tempid': tempid, - 'BackURL': window.location.href - }); - - dialog = $('
'); - dialog.attr('id', new Date().getTime()); - dialog.data('url', url); - $('body').append(dialog); - } - }); - - $('.leftandmain-logindialog').entwine({ - onmatch: function onmatch() { - this._super(); - - this.ssdialog({ - iframeUrl: this.data('url'), - dialogClass: "leftandmain-logindialog-dialog", - autoOpen: true, - minWidth: 500, - maxWidth: 500, - minHeight: 370, - maxHeight: 400, - closeOnEscape: false, - open: function open() { - $('.ui-widget-overlay').addClass('leftandmain-logindialog-overlay'); - }, - close: function close() { - $('.ui-widget-overlay').removeClass('leftandmain-logindialog-overlay'); - } - }); - }, - onunmatch: function onunmatch() { - this._super(); - }, - open: function open() { - this.ssdialog('open'); - }, - close: function close() { - this.ssdialog('close'); - }, - toggle: function toggle(bool) { - if (this.is(':visible')) this.close();else this.open(); - }, - - reauthenticate: function reauthenticate(data) { - if (typeof data.SecurityID !== 'undefined') { - $(':input[name=SecurityID]').val(data.SecurityID); - } - - if (typeof data.TempID !== 'undefined') { - $('body').data('member-tempid', data.TempID); - } - this.close(); - } - }); - - $('form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ - onmatch: function onmatch() { - this.append('
'); - this._super(); - }, - onunmatch: function onunmatch() { - this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); - this._super(); - } - }); - - $('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({ - onadd: function onadd() { - this.addClass('ss-ui-button'); - if (!this.data('button')) this.button(); - this._super(); - }, - onremove: function onremove() { - if (this.data('button')) this.button('destroy'); - this._super(); - } - }); - - $('.cms .cms-panel-link').entwine({ - onclick: function onclick(e) { - if ($(this).hasClass('external-link')) { - e.stopPropagation(); - - return; - } - - var href = this.attr('href'), - url = href && !href.match(/^#/) ? href : this.data('href'), - data = { pjax: this.data('pjaxTarget') }; - - $('.cms-container').loadPanel(url, null, data); - e.preventDefault(); - } - }); - - $('.cms .ss-ui-button-ajax').entwine({ - onclick: function onclick(e) { - $(this).removeClass('ui-button-text-only'); - $(this).addClass('ss-ui-button-loading ui-button-text-icons'); - - var loading = $(this).find(".ss-ui-loading-icon"); - - if (loading.length < 1) { - loading = $("").addClass('ss-ui-loading-icon ui-button-icon-primary ui-icon'); - - $(this).prepend(loading); - } - - loading.show(); - - var href = this.attr('href'), - url = href ? href : this.data('href'); - - jQuery.ajax({ - url: url, - - complete: function complete(xmlhttp, status) { - var msg = xmlhttp.getResponseHeader('X-Status') ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText; - - try { - if (typeof msg != "undefined" && msg !== null) eval(msg); - } catch (e) {} - - loading.hide(); - - $(".cms-container").refresh(); - - $(this).removeClass('ss-ui-button-loading ui-button-text-icons'); - $(this).addClass('ui-button-text-only'); - }, - dataType: 'html' - }); - e.preventDefault(); - } - }); - - $('.cms .ss-ui-dialog-link').entwine({ - UUID: null, - onmatch: function onmatch() { - this._super(); - this.setUUID(new Date().getTime()); - }, - onunmatch: function onunmatch() { - this._super(); - }, - onclick: function onclick() { - this._super(); - - var self = this, - id = 'ss-ui-dialog-' + this.getUUID(); - var dialog = $('#' + id); - if (!dialog.length) { - dialog = $('
'); - $('body').append(dialog); - } - - var extraClass = this.data('popupclass') ? this.data('popupclass') : ''; - - dialog.ssdialog({ iframeUrl: this.attr('href'), autoOpen: true, dialogExtraClass: extraClass }); - return false; - } - }); - - $('.cms-content .Actions').entwine({ - onmatch: function onmatch() { - this.find('.ss-ui-button').click(function () { - var form = this.form; - - if (form) { - form.clickedButton = this; - - setTimeout(function () { - form.clickedButton = null; - }, 10); - } - }); - - this.redraw(); - this._super(); - }, - onunmatch: function onunmatch() { - this._super(); - }, - redraw: function redraw() { - if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); - - this.contents().filter(function () { - return this.nodeType == 3 && !/\S/.test(this.nodeValue); - }).remove(); - - this.find('.ss-ui-button').each(function () { - if (!$(this).data('button')) $(this).button(); - }); - - this.find('.ss-ui-buttonset').buttonset(); - } - }); - - $('.cms .field.date input.text').entwine({ - onmatch: function onmatch() { - var holder = $(this).parents('.field.date:first'), - config = holder.data(); - if (!config.showcalendar) { - this._super(); - return; - } - - config.showOn = 'button'; - if (config.locale && $.datepicker.regional[config.locale]) { - config = $.extend(config, $.datepicker.regional[config.locale], {}); - } - - $(this).datepicker(config); - - - this._super(); - }, - onunmatch: function onunmatch() { - this._super(); - } - }); - - $('.cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown').entwine({ - onmatch: function onmatch() { - if (this.is('.no-chosen')) { - this._super(); - return; - } - - if (!this.data('placeholder')) this.data('placeholder', ' '); - - this.removeClass('has-chosen').chosen("destroy"); - this.siblings('.chosen-container').remove(); - - applyChosen(this); - - this._super(); - }, - onunmatch: function onunmatch() { - this._super(); - } - }); - - $(".cms-panel-layout").entwine({ - redraw: function redraw() { - if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); - } - }); - - $('.cms .ss-gridfield').entwine({ - showDetailView: function showDetailView(url) { - var params = window.location.search.replace(/^\?/, ''); - if (params) url = $.path.addSearchParams(url, params); - $('.cms-container').loadPanel(url); - } - }); - - $('.cms-search-form').entwine({ - onsubmit: function onsubmit(e) { - var nonEmptyInputs, url; - - nonEmptyInputs = this.find(':input:not(:submit)').filter(function () { - var vals = $.grep($(this).fieldValue(), function (val) { - return val; - }); - return vals.length; - }); - - url = this.attr('action'); - - if (nonEmptyInputs.length) { - url = $.path.addSearchParams(url, nonEmptyInputs.serialize().replace('+', '%20')); - } - - var container = this.closest('.cms-container'); - container.find('.cms-edit-form').tabs('select', 0); - container.loadPanel(url, "", {}, true); - - return false; - } - }); - - $(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({ - onclick: function onclick(e) { - e.preventDefault(); - - var form = $(this).parents('form'); - - form.clearForm(); - form.find(".dropdown select").prop('selectedIndex', 0).trigger("chosen:updated"); - form.submit(); - } - }); - - window._panelDeferredCache = {}; - $('.cms-panel-deferred').entwine({ - onadd: function onadd() { - this._super(); - this.redraw(); - }, - onremove: function onremove() { - if (window.debug) console.log('saving', this.data('url'), this); - - if (!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); - this._super(); - }, - redraw: function redraw() { - if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); - - var self = this, - url = this.data('url'); - if (!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; - - this._super(); - - if (!this.children().length) { - if (!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { - this.html(window._panelDeferredCache[url]); - } else { - this.addClass('loading'); - $.ajax({ - url: url, - complete: function complete() { - self.removeClass('loading'); - }, - success: function success(data, status, xhr) { - self.html(data); - } - }); - } - } - } - }); - - $('.cms-tabset').entwine({ - onadd: function onadd() { - this.redrawTabs(); - this._super(); - }, - onremove: function onremove() { - if (this.data('tabs')) this.tabs('destroy'); - this._super(); - }, - redrawTabs: function redrawTabs() { - this.rewriteHashlinks(); - - var id = this.attr('id'), - activeTab = this.find('ul:first .ui-tabs-active'); - - if (!this.data('uiTabs')) this.tabs({ - active: activeTab.index() != -1 ? activeTab.index() : 0, - beforeLoad: function beforeLoad(e, ui) { - return false; - }, - activate: function activate(e, ui) { - var actions = $(this).closest('form').find('.Actions'); - if ($(ui.newTab).closest('li').hasClass('readonly')) { - actions.fadeOut(); - } else { - actions.show(); - } - } - }); - }, - - rewriteHashlinks: function rewriteHashlinks() { - $(this).find('ul a').each(function () { - if (!$(this).attr('href')) return; - var matches = $(this).attr('href').match(/#.*/); - if (!matches) return; - $(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]); - }); - } - }); - - $('#filters-button').entwine({ - onmatch: function onmatch() { - this._super(); - - this.data('collapsed', true); - this.data('animating', false); - }, - onunmatch: function onunmatch() { - this._super(); - }, - showHide: function showHide() { - var self = this, - $filters = $('.cms-content-filters').first(), - collapsed = this.data('collapsed'); - - if (this.data('animating')) { - return; - } - - this.toggleClass('active'); - this.data('animating', true); - - $filters[collapsed ? 'slideDown' : 'slideUp']({ - complete: function complete() { - self.data('collapsed', !collapsed); - self.data('animating', false); - } - }); - }, - onclick: function onclick() { - this.showHide(); - } - }); - }); - - var statusMessage = function statusMessage(text, type) { - text = jQuery('
').text(text).html(); - jQuery.noticeAdd({ text: text, type: type, stayTime: 5000, inEffect: { left: '0', opacity: 'show' } }); - }; - - var errorMessage = function errorMessage(text) { - jQuery.noticeAdd({ text: text, type: 'error', stayTime: 5000, inEffect: { left: '0', opacity: 'show' } }); - }; + xhr = $.ajax({ + headers: headers, + url: url, + success: function success(data, status, xhr) { + var elements = self.handleAjaxResponse(data, status, xhr, null); + + self.trigger('afterloadfragment', { data: data, status: status, xhr: xhr, elements: elements }); + }, + error: function error(xhr, status, _error) { + self.trigger('loadfragmenterror', { xhr: xhr, status: status, error: _error }); + }, + complete: function complete() { + var fragmentXHR = self.getFragmentXHR(); + if (typeof fragmentXHR[pjaxFragments] !== 'undefined' && fragmentXHR[pjaxFragments] !== null) { + fragmentXHR[pjaxFragments] = null; + } + } + }); + + fragmentXHR[pjaxFragments] = xhr; + + return xhr; + }, + + handleAjaxResponse: function handleAjaxResponse(data, status, xhr, state) { + var self = this, + url, + selectedTabs, + guessFragment, + fragment, + $data; + + if (xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) { + var baseUrl = $('base').attr('href'), + rawURL = xhr.getResponseHeader('X-ControllerURL'), + url = $.path.isAbsoluteUrl(rawURL) ? rawURL : $.path.makeUrlAbsolute(rawURL, baseUrl); + + document.location.href = url; + return; + } + + if (!data) return; + + var title = xhr.getResponseHeader('X-Title'); + if (title) document.title = decodeURIComponent(title.replace(/\+/g, ' ')); + + var newFragments = {}, + newContentEls; + + if (xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) { + newFragments = data; + } else { + fragment = document.createDocumentFragment(); + + jQuery.clean([data], document, fragment, []); + $data = $(jQuery.merge([], fragment.childNodes)); + + guessFragment = 'Content'; + if ($data.is('form') && !$data.is('[data-pjax-fragment~=Content]')) guessFragment = 'CurrentForm'; + + newFragments[guessFragment] = $data; + } + + this.setRedrawSuppression(true); + try { + $.each(newFragments, function (newFragment, html) { + var contentEl = $('[data-pjax-fragment]').filter(function () { + return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1; + }), + newContentEl = $(html); + + if (newContentEls) newContentEls.add(newContentEl);else newContentEls = newContentEl; + + if (newContentEl.find('.cms-container').length) { + throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops'; + } + + var origStyle = contentEl.attr('style'); + var origParent = contentEl.parent(); + var origParentLayoutApplied = typeof origParent.data('jlayout') !== 'undefined'; + var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden']; + var elemClasses = contentEl.attr('class'); + var origLayoutClasses = []; + if (elemClasses) { + origLayoutClasses = $.grep(elemClasses.split(' '), function (val) { + return $.inArray(val, layoutClasses) >= 0; + }); + } + + newContentEl.removeClass(layoutClasses.join(' ')).addClass(origLayoutClasses.join(' ')); + if (origStyle) newContentEl.attr('style', origStyle); + + var styles = newContentEl.find('style').detach(); + if (styles.length) $(document).find('head').append(styles); + + contentEl.replaceWith(newContentEl); + + if (!origParent.is('.cms-container') && origParentLayoutApplied) { + origParent.layout(); + } + }); + + var newForm = newContentEls.filter('form'); + if (newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset'); + } finally { + this.setRedrawSuppression(false); + } + + this.redraw(); + this.restoreTabState(state && typeof state.tabState !== 'undefined' ? state.tabState : null); + + return newContentEls; + }, + + _findFragments: function _findFragments(fragments) { + return $('[data-pjax-fragment]').filter(function () { + var i, + nodeFragments = $(this).data('pjaxFragment').split(' '); + for (i in fragments) { + if ($.inArray(fragments[i], nodeFragments) != -1) return true; + } + return false; + }); + }, + + refresh: function refresh() { + $(window).trigger('statechange'); + + $(this).redraw(); + }, + + saveTabState: function saveTabState() { + if (typeof window.sessionStorage == "undefined" || window.sessionStorage === null) return; + + var selectedTabs = [], + url = this._tabStateUrl(); + this.find('.cms-tabset,.ss-tabset').each(function (i, el) { + var id = $(el).attr('id'); + if (!id) return; + if (!$(el).data('tabs')) return; + if ($(el).data('ignoreTabState') || $(el).getIgnoreTabState()) return; + + selectedTabs.push({ id: id, selected: $(el).tabs('option', 'selected') }); + }); + + if (selectedTabs) { + var tabsUrl = 'tabs-' + url; + try { + window.sessionStorage.setItem(tabsUrl, JSON.stringify(selectedTabs)); + } catch (err) { + if (err.code === DOMException.QUOTA_EXCEEDED_ERR && window.sessionStorage.length === 0) { + return; + } else { + throw err; + } + } + } + }, + + restoreTabState: function restoreTabState(overrideStates) { + var self = this, + url = this._tabStateUrl(), + hasSessionStorage = typeof window.sessionStorage !== "undefined" && window.sessionStorage, + sessionData = hasSessionStorage ? window.sessionStorage.getItem('tabs-' + url) : null, + sessionStates = sessionData ? JSON.parse(sessionData) : false; + + this.find('.cms-tabset, .ss-tabset').each(function () { + var index, + tabset = $(this), + tabsetId = tabset.attr('id'), + tab, + forcedTab = tabset.find('.ss-tabs-force-active'); + + if (!tabset.data('tabs')) { + return; + } + + tabset.tabs('refresh'); + + if (forcedTab.length) { + index = forcedTab.index(); + } else if (overrideStates && overrideStates[tabsetId]) { + tab = tabset.find(overrideStates[tabsetId].tabSelector); + if (tab.length) { + index = tab.index(); + } + } else if (sessionStates) { + $.each(sessionStates, function (i, sessionState) { + if (tabset.is('#' + sessionState.id)) { + index = sessionState.selected; + } + }); + } + if (index !== null) { + tabset.tabs('option', 'active', index); + self.trigger('tabstaterestored'); + } + }); + }, + + clearTabState: function clearTabState(url) { + if (typeof window.sessionStorage == "undefined") return; + + var s = window.sessionStorage; + if (url) { + s.removeItem('tabs-' + url); + } else { + for (var i = 0; i < s.length; i++) { + if (s.key(i).match(/^tabs-/)) s.removeItem(s.key(i)); + } + } + }, + + clearCurrentTabState: function clearCurrentTabState() { + this.clearTabState(this._tabStateUrl()); + }, + + _tabStateUrl: function _tabStateUrl() { + return window.history.state.path.replace(/\?.*/, '').replace(/#.*/, '').replace($('base').attr('href'), ''); + }, + + showLoginDialog: function showLoginDialog() { + var tempid = $('body').data('member-tempid'), + dialog = $('.leftandmain-logindialog'), + url = 'CMSSecurity/login'; + + if (dialog.length) dialog.remove(); + + url = $.path.addSearchParams(url, { + 'tempid': tempid, + 'BackURL': window.location.href + }); + + dialog = $('
'); + dialog.attr('id', new Date().getTime()); + dialog.data('url', url); + $('body').append(dialog); + } + }); + + $('.leftandmain-logindialog').entwine({ + onmatch: function onmatch() { + this._super(); + + this.ssdialog({ + iframeUrl: this.data('url'), + dialogClass: "leftandmain-logindialog-dialog", + autoOpen: true, + minWidth: 500, + maxWidth: 500, + minHeight: 370, + maxHeight: 400, + closeOnEscape: false, + open: function open() { + $('.ui-widget-overlay').addClass('leftandmain-logindialog-overlay'); + }, + close: function close() { + $('.ui-widget-overlay').removeClass('leftandmain-logindialog-overlay'); + } + }); + }, + onunmatch: function onunmatch() { + this._super(); + }, + open: function open() { + this.ssdialog('open'); + }, + close: function close() { + this.ssdialog('close'); + }, + toggle: function toggle(bool) { + if (this.is(':visible')) this.close();else this.open(); + }, + + reauthenticate: function reauthenticate(data) { + if (typeof data.SecurityID !== 'undefined') { + $(':input[name=SecurityID]').val(data.SecurityID); + } + + if (typeof data.TempID !== 'undefined') { + $('body').data('member-tempid', data.TempID); + } + this.close(); + } + }); + + $('form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ + onmatch: function onmatch() { + this.append('
'); + this._super(); + }, + onunmatch: function onunmatch() { + this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); + this._super(); + } + }); + + $('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({ + onadd: function onadd() { + this.addClass('ss-ui-button'); + if (!this.data('button')) this.button(); + this._super(); + }, + onremove: function onremove() { + if (this.data('button')) this.button('destroy'); + this._super(); + } + }); + + $('.cms .cms-panel-link').entwine({ + onclick: function onclick(e) { + if ($(this).hasClass('external-link')) { + e.stopPropagation(); + + return; + } + + var href = this.attr('href'), + url = href && !href.match(/^#/) ? href : this.data('href'), + data = { pjax: this.data('pjaxTarget') }; + + $('.cms-container').loadPanel(url, null, data); + e.preventDefault(); + } + }); + + $('.cms .ss-ui-button-ajax').entwine({ + onclick: function onclick(e) { + $(this).removeClass('ui-button-text-only'); + $(this).addClass('ss-ui-button-loading ui-button-text-icons'); + + var loading = $(this).find(".ss-ui-loading-icon"); + + if (loading.length < 1) { + loading = $("").addClass('ss-ui-loading-icon ui-button-icon-primary ui-icon'); + + $(this).prepend(loading); + } + + loading.show(); + + var href = this.attr('href'), + url = href ? href : this.data('href'); + + jQuery.ajax({ + url: url, + + complete: function complete(xmlhttp, status) { + var msg = xmlhttp.getResponseHeader('X-Status') ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText; + + try { + if (typeof msg != "undefined" && msg !== null) eval(msg); + } catch (e) {} + + loading.hide(); + + $(".cms-container").refresh(); + + $(this).removeClass('ss-ui-button-loading ui-button-text-icons'); + $(this).addClass('ui-button-text-only'); + }, + dataType: 'html' + }); + e.preventDefault(); + } + }); + + $('.cms .ss-ui-dialog-link').entwine({ + UUID: null, + onmatch: function onmatch() { + this._super(); + this.setUUID(new Date().getTime()); + }, + onunmatch: function onunmatch() { + this._super(); + }, + onclick: function onclick() { + this._super(); + + var self = this, + id = 'ss-ui-dialog-' + this.getUUID(); + var dialog = $('#' + id); + if (!dialog.length) { + dialog = $('
'); + $('body').append(dialog); + } + + var extraClass = this.data('popupclass') ? this.data('popupclass') : ''; + + dialog.ssdialog({ iframeUrl: this.attr('href'), autoOpen: true, dialogExtraClass: extraClass }); + return false; + } + }); + + $('.cms-content .Actions').entwine({ + onmatch: function onmatch() { + this.find('.ss-ui-button').click(function () { + var form = this.form; + + if (form) { + form.clickedButton = this; + + setTimeout(function () { + form.clickedButton = null; + }, 10); + } + }); + + this.redraw(); + this._super(); + }, + onunmatch: function onunmatch() { + this._super(); + }, + redraw: function redraw() { + if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); + + this.contents().filter(function () { + return this.nodeType == 3 && !/\S/.test(this.nodeValue); + }).remove(); + + this.find('.ss-ui-button').each(function () { + if (!$(this).data('button')) $(this).button(); + }); + + this.find('.ss-ui-buttonset').buttonset(); + } + }); + + $('.cms .field.date input.text').entwine({ + onmatch: function onmatch() { + var holder = $(this).parents('.field.date:first'), + config = holder.data(); + if (!config.showcalendar) { + this._super(); + return; + } + + config.showOn = 'button'; + if (config.locale && $.datepicker.regional[config.locale]) { + config = $.extend(config, $.datepicker.regional[config.locale], {}); + } + + $(this).datepicker(config); + + + this._super(); + }, + onunmatch: function onunmatch() { + this._super(); + } + }); + + $('.cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown').entwine({ + onmatch: function onmatch() { + if (this.is('.no-chosen')) { + this._super(); + return; + } + + if (!this.data('placeholder')) this.data('placeholder', ' '); + + this.removeClass('has-chosen').chosen("destroy"); + this.siblings('.chosen-container').remove(); + + applyChosen(this); + + this._super(); + }, + onunmatch: function onunmatch() { + this._super(); + } + }); + + $(".cms-panel-layout").entwine({ + redraw: function redraw() { + if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); + } + }); + + $('.cms .ss-gridfield').entwine({ + showDetailView: function showDetailView(url) { + var params = window.location.search.replace(/^\?/, ''); + if (params) url = $.path.addSearchParams(url, params); + $('.cms-container').loadPanel(url); + } + }); + + $('.cms-search-form').entwine({ + onsubmit: function onsubmit(e) { + var nonEmptyInputs, url; + + nonEmptyInputs = this.find(':input:not(:submit)').filter(function () { + var vals = $.grep($(this).fieldValue(), function (val) { + return val; + }); + return vals.length; + }); + + url = this.attr('action'); + + if (nonEmptyInputs.length) { + url = $.path.addSearchParams(url, nonEmptyInputs.serialize().replace('+', '%20')); + } + + var container = this.closest('.cms-container'); + container.find('.cms-edit-form').tabs('select', 0); + container.loadPanel(url, "", {}, true); + + return false; + } + }); + + $(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({ + onclick: function onclick(e) { + e.preventDefault(); + + var form = $(this).parents('form'); + + form.clearForm(); + form.find(".dropdown select").prop('selectedIndex', 0).trigger("chosen:updated"); + form.submit(); + } + }); + + window._panelDeferredCache = {}; + $('.cms-panel-deferred').entwine({ + onadd: function onadd() { + this._super(); + this.redraw(); + }, + onremove: function onremove() { + if (window.debug) console.log('saving', this.data('url'), this); + + if (!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); + this._super(); + }, + redraw: function redraw() { + if (window.debug) console.log('redraw', this.attr('class'), this.get(0)); + + var self = this, + url = this.data('url'); + if (!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; + + this._super(); + + if (!this.children().length) { + if (!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { + this.html(window._panelDeferredCache[url]); + } else { + this.addClass('loading'); + $.ajax({ + url: url, + complete: function complete() { + self.removeClass('loading'); + }, + success: function success(data, status, xhr) { + self.html(data); + } + }); + } + } + } + }); + + $('.cms-tabset').entwine({ + onadd: function onadd() { + this.redrawTabs(); + this._super(); + }, + onremove: function onremove() { + if (this.data('tabs')) this.tabs('destroy'); + this._super(); + }, + redrawTabs: function redrawTabs() { + this.rewriteHashlinks(); + + var id = this.attr('id'), + activeTab = this.find('ul:first .ui-tabs-active'); + + if (!this.data('uiTabs')) this.tabs({ + active: activeTab.index() != -1 ? activeTab.index() : 0, + beforeLoad: function beforeLoad(e, ui) { + return false; + }, + activate: function activate(e, ui) { + var actions = $(this).closest('form').find('.Actions'); + if ($(ui.newTab).closest('li').hasClass('readonly')) { + actions.fadeOut(); + } else { + actions.show(); + } + } + }); + }, + + rewriteHashlinks: function rewriteHashlinks() { + $(this).find('ul a').each(function () { + if (!$(this).attr('href')) return; + var matches = $(this).attr('href').match(/#.*/); + if (!matches) return; + $(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]); + }); + } + }); + + $('#filters-button').entwine({ + onmatch: function onmatch() { + this._super(); + + this.data('collapsed', true); + this.data('animating', false); + }, + onunmatch: function onunmatch() { + this._super(); + }, + showHide: function showHide() { + var self = this, + $filters = $('.cms-content-filters').first(), + collapsed = this.data('collapsed'); + + if (this.data('animating')) { + return; + } + + this.toggleClass('active'); + this.data('animating', true); + + $filters[collapsed ? 'slideDown' : 'slideUp']({ + complete: function complete() { + self.data('collapsed', !collapsed); + self.data('animating', false); + } + }); + }, + onclick: function onclick() { + this.showHide(); + } + }); + }); + + var statusMessage = function statusMessage(text, type) { + text = jQuery('
').text(text).html(); + jQuery.noticeAdd({ text: text, type: type, stayTime: 5000, inEffect: { left: '0', opacity: 'show' } }); + }; + + var errorMessage = function errorMessage(text) { + jQuery.noticeAdd({ text: text, type: 'error', stayTime: 5000, inEffect: { left: '0', opacity: 'show' } }); + }; }); \ No newline at end of file diff --git a/admin/client/dist/js/bundle-legacy.js b/admin/client/dist/js/bundle-legacy.js index 4c89022f7..e629c3d56 100644 --- a/admin/client/dist/js/bundle-legacy.js +++ b/admin/client/dist/js/bundle-legacy.js @@ -1,4 +1,4 @@ !function e(t,n,i){function s(o,r){if(!n[o]){if(!t[o]){var l="function"==typeof require&&require;if(!r&&l)return l(o,!0);if(a)return a(o,!0);var d=new Error("Cannot find module '"+o+"'");throw d.code="MODULE_NOT_FOUND",d}var c=n[o]={exports:{}};t[o][0].call(c.exports,function(e){var n=t[o][1][e];return s(n?n:e)},c,c.exports,e,t,n,i)}return n[o].exports}for(var a="function"==typeof require&&require,o=0;o0&&a.each(function(t,n){e(n).contents().on("click.ss-ui-action-tabset",i)})},riseUp:function(t,n){var i,s,a,o,r,l,d,c,u;return i=e(this).find(".ui-tabs-panel").outerHeight(),s=e(this).find(".ui-tabs-nav").outerHeight(),a=e(window).height()+e(document).scrollTop()-s,o=e(this).find(".ui-tabs-nav").offset().top,r=n.newPanel,l=n.newTab,o+i>=a&&o-i>0?(this.addClass("rise-up"),null!==l.position()&&(d=-r.outerHeight(),c=r.parents(".south"),c&&(u=l.offset().top-c.offset().top,d-=u),e(r).css("top",d+"px"))):(this.removeClass("rise-up"),null!==l.position()&&e(r).css("top","0px")),!1}}),e(".cms-content-actions .ss-tabset.ss-ui-action-tabset").entwine({ontabsbeforeactivate:function(t,n){this._super(t,n),e(n.newPanel).length>0&&e(n.newPanel).css("left",n.newTab.position().left+"px")}}),e(".cms-actions-row.ss-tabset.ss-ui-action-tabset").entwine({ontabsbeforeactivate:function(t,n){this._super(t,n),e(this).closest(".ss-ui-action-tabset").removeClass("tabset-open tabset-open-last")}}),e(".cms-content-fields .ss-tabset.ss-ui-action-tabset").entwine({ontabsbeforeactivate:function(t,n){this._super(t,n),e(n.newPanel).length>0&&(e(n.newTab).hasClass("last")?(e(n.newPanel).css({left:"auto",right:"0px"}),e(n.newPanel).parent().addClass("tabset-open-last")):(e(n.newPanel).css("left",n.newTab.position().left+"px"),e(n.newTab).hasClass("first")&&(e(n.newPanel).css("left","0px"),e(n.newPanel).parent().addClass("tabset-open"))))}}),e(".cms-tree-view-sidebar .cms-actions-row.ss-tabset.ss-ui-action-tabset").entwine({"from .ui-tabs-nav li":{onhover:function(t){e(t.target).parent().find("li .active").removeClass("active"),e(t.target).find("a").addClass("active")}},ontabsbeforeactivate:function(t,n){this._super(t,n),e(n.newPanel).css({left:"auto",right:"auto"}),e(n.newPanel).length>0&&e(n.newPanel).parent().addClass("tabset-open")}})})},{jQuery:"jQuery"}],3:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s),o=e("i18n"),r=i(o);a["default"].entwine("ss.tree",function(e){e("#Form_BatchActionsForm").entwine({Actions:[],getTree:function(){return e(".cms-tree")},fromTree:{oncheck_node:function(e,t){this.serializeFromTree()},onuncheck_node:function(e,t){this.serializeFromTree()}},registerDefault:function(){this.register("admin/pages/batchactions/publish",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_PUBLISH_PROMPT","You have {num} page(s) selected.\n\nDo you really want to publish?"),{num:e.length}));return t?e:!1}),this.register("admin/pages/batchactions/unpublish",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_UNPUBLISH_PROMPT","You have {num} page(s) selected.\n\nDo you really want to unpublish"),{num:e.length}));return t?e:!1}),this.register("admin/pages/batchactions/delete",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_DELETE_PROMPT","You have {num} page(s) selected.\n\nDo you really want to delete?"),{num:e.length}));return t?e:!1}),this.register("admin/pages/batchactions/archive",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_ARCHIVE_PROMPT","You have {num} page(s) selected.\n\nAre you sure you want to archive these pages?\n\nThese pages and all of their children pages will be unpublished and sent to the archive."),{num:e.length}));return t?e:!1}),this.register("admin/pages/batchactions/restore",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_RESTORE_PROMPT","You have {num} page(s) selected.\n\nDo you really want to restore to stage?\n\nChildren of archived pages will be restored to the root level, unless those pages are also being restored."),{num:e.length}));return t?e:!1}),this.register("admin/pages/batchactions/deletefromlive",function(e){var t=confirm(r["default"].inject(r["default"]._t("CMSMAIN.BATCH_DELETELIVE_PROMPT","You have {num} page(s) selected.\n\nDo you really want to delete these pages from live?"),{num:e.length}));return t?e:!1})},onadd:function(){this.registerDefault(),this._super()},register:function(e,t){this.trigger("register",{type:e,callback:t});var n=this.getActions();n[e]=t,this.setActions(n)},unregister:function(e){this.trigger("unregister",{type:e});var t=this.getActions();t[e]&&delete t[e],this.setActions(t)},refreshSelected:function(t){var n=this,i=this.getTree(),s=this.getIDs(),a=[],o=e(".cms-content-batchactions-button"),r=this.find(":input[name=Action]").val();null==t&&(t=i);for(var l in s)e(e(i).getNodeByID(l)).addClass("selected").attr("selected","selected");if(!r||-1==r||!o.hasClass("active"))return void e(t).find("li").each(function(){e(this).setEnabled(!0)});e(t).find("li").each(function(){a.push(e(this).data("id")),e(this).addClass("treeloading").setEnabled(!1)});var d=e.path.parseUrl(r),c=d.hrefNoSearch+"/applicablepages/";c=e.path.addSearchParams(c,d.search),c=e.path.addSearchParams(c,{csvIDs:a.join(",")}),jQuery.getJSON(c,function(i){jQuery(t).find("li").each(function(){e(this).removeClass("treeloading");var t=e(this).data("id");0==t||e.inArray(t,i)>=0?e(this).setEnabled(!0):(e(this).removeClass("selected").setEnabled(!1),e(this).prop("selected",!1))}),n.serializeFromTree()})},serializeFromTree:function(){var e=this.getTree(),t=e.getSelectedIDs();return this.setIDs(t),!0},setIDs:function(e){this.find(":input[name=csvIDs]").val(e?e.join(","):null)},getIDs:function(){var e=this.find(":input[name=csvIDs]").val();return e?e.split(","):[]},onsubmit:function(t){var n=this,i=this.getIDs(),s=this.getTree(),a=this.getActions();if(!i||!i.length)return alert(r["default"]._t("CMSMAIN.SELECTONEPAGE","Please select at least one page")),t.preventDefault(),!1;var o=this.find(":input[name=Action]").val();if(a[o]&&(i=this.getActions()[o].apply(this,[i])),!i||!i.length)return t.preventDefault(),!1;this.setIDs(i),s.find("li").removeClass("failed");var l=this.find(":submit:first");return l.addClass("loading"),jQuery.ajax({url:o,type:"POST",data:this.serializeArray(),complete:function(e,t){l.removeClass("loading"),s.jstree("refresh",-1),n.setIDs([]),n.find(":input[name=Action]").val("").change();var i=e.getResponseHeader("X-Status");i&&statusMessage(decodeURIComponent(i),"success"==t?"good":"bad")},success:function(t,n){var i,a;if(t.modified){var o=[];for(i in t.modified)a=s.getNodeByID(i),s.jstree("set_text",a,t.modified[i].TreeTitle),o.push(a);e(o).effect("highlight")}if(t.deleted)for(i in t.deleted)a=s.getNodeByID(i),a.length&&s.jstree("delete_node",a);if(t.error)for(i in t.error)a=s.getNodeByID(i),e(a).addClass("failed")},dataType:"json"}),t.preventDefault(),!1}}),e(".cms-content-batchactions-button").entwine({onmatch:function(){this._super(),this.updateTree()},onunmatch:function(){this._super()},onclick:function(e){this.updateTree()},updateTree:function(){var t=e(".cms-tree"),n=e("#Form_BatchActionsForm");this._super(),this.data("active")?(t.addClass("multiple"),t.removeClass("draggable"),n.serializeFromTree()):(t.removeClass("multiple"),t.addClass("draggable")),e("#Form_BatchActionsForm").refreshSelected()}}),e("#Form_BatchActionsForm select[name=Action]").entwine({onchange:function(t){var n=e(t.target.form),i=n.find(":submit"),s=e(t.target).val();s&&-1!=s?i.removeAttr("disabled").button("refresh"):i.attr("disabled","disabled").button("refresh"),e("#Form_BatchActionsForm").refreshSelected(),this.trigger("chosen:updated"),this._super(t)}})})},{i18n:"i18n",jQuery:"jQuery"}],4:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss",function(e){e(".cms-content").entwine({onadd:function(){this.find(".cms-tabset").redrawTabs(),this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.add(this.find(".cms-tabset")).redrawTabs(),this.find(".cms-content-header").redraw(),this.find(".cms-content-actions").redraw()}}),e(".cms-content .cms-tree").entwine({onadd:function(){var t=this;this._super(),this.bind("select_node.jstree",function(n,i){var s=i.rslt.obj,a=t.find(":input[name=ID]").val(),o=i.args[2],r=e(".cms-container");if(!o)return!1;if(e(s).hasClass("disabled"))return!1;if(e(s).data("id")!=a){var l=e(s).find("a:first").attr("href");l&&"#"!=l?(l=l.split("?")[0],t.jstree("deselect_all"),t.jstree("uncheck_all"),e.path.isExternal(e(s).find("a:first"))&&(l=l=e.path.makeUrlAbsolute(l,e("base").attr("href"))),document.location.search&&(l=e.path.addSearchParams(l,document.location.search.replace(/^\?/,""))),r.loadPanel(l)):t.removeForm()}})}}),e(".cms-content .cms-content-fields").entwine({redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0))}}),e(".cms-content .cms-content-header, .cms-content .cms-content-actions").entwine({redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.height("auto"),this.height(this.innerHeight()-this.css("padding-top")-this.css("padding-bottom"))}})})},{jQuery:"jQuery"}],5:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s),o=e("i18n"),r=i(o);window.onbeforeunload=function(e){var t=(0,a["default"])(".cms-edit-form");return t.trigger("beforesubmitform"),t.is(".changed")&&!t.is(".discardchanges")?r["default"]._t("LeftAndMain.CONFIRMUNSAVEDSHORT"):void 0},a["default"].entwine("ss",function(e){e(".cms-edit-form").entwine({PlaceholderHtml:"",ChangeTrackerOptions:{ignoreFieldSelector:".no-change-track, .ss-upload :input, .cms-navigator :input"},onadd:function(){this.attr("autocomplete","off"),this._setupChangeTracker();for(var t in{action:!0,method:!0,enctype:!0,name:!0}){var n=this.find(":input[name=_form_"+t+"]");n&&(this.attr(t,n.val()),n.remove())}if(this.hasClass("validationerror")){var i=this.find(".message.validation, .message.required").first().closest(".tab");e(".cms-container").clearCurrentTabState(),i.closest(".ss-tabset").tabs("option","active",i.index(".tab"))}this._super()},onremove:function(){this.changetracker("destroy"),this._super()},onmatch:function(){this._super()},onunmatch:function(){this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.add(this.find(".cms-tabset")).redrawTabs(),this.find(".cms-content-header").redraw()},_setupChangeTracker:function(){this.changetracker(this.getChangeTrackerOptions())},confirmUnsavedChanges:function(){if(this.trigger("beforesubmitform"),!this.is(".changed")||this.is(".discardchanges"))return!0;var e=confirm(r["default"]._t("LeftAndMain.CONFIRMUNSAVED"));return e&&this.addClass("discardchanges"),e},onsubmit:function(e,t){return"_blank"!=this.prop("target")?(t&&this.closest(".cms-container").submitForm(this,t),!1):void 0},validate:function(){var e=!0;return this.trigger("validate",{isValid:e}),e},"from .htmleditor":{oneditorinit:function(t){var n=this,i=e(t.target).closest(".field.htmleditor"),s=i.find("textarea.htmleditor").getEditor().getInstance();s.onClick.add(function(e){n.saveFieldFocus(i.attr("id"))})}},"from .cms-edit-form :input:not(:submit)":{onclick:function(t){this.saveFieldFocus(e(t.target).attr("id"))},onfocus:function(t){this.saveFieldFocus(e(t.target).attr("id"))}},"from .cms-edit-form .treedropdown *":{onfocusin:function(t){var n=e(t.target).closest(".field.treedropdown");this.saveFieldFocus(n.attr("id"))}},"from .cms-edit-form .dropdown .chosen-container a":{onfocusin:function(t){var n=e(t.target).closest(".field.dropdown");this.saveFieldFocus(n.attr("id"))}},"from .cms-container":{ontabstaterestored:function(e){this.restoreFieldFocus()}},saveFieldFocus:function(t){if("undefined"!=typeof window.sessionStorage&&null!==window.sessionStorage){var n=e(this).attr("id"),i=[];if(i.push({id:n,selected:t}),i)try{window.sessionStorage.setItem(n,JSON.stringify(i))}catch(s){if(s.code===DOMException.QUOTA_EXCEEDED_ERR&&0===window.sessionStorage.length)return;throw s}}},restoreFieldFocus:function(){if("undefined"!=typeof window.sessionStorage&&null!==window.sessionStorage){var t,n,i,s,a,o=this,r="undefined"!=typeof window.sessionStorage&&window.sessionStorage,l=r?window.sessionStorage.getItem(this.attr("id")):null,d=l?JSON.parse(l):!1,c=0!==this.find(".ss-tabset").length;if(r&&d.length>0){if(e.each(d,function(n,i){o.is("#"+i.id)&&(t=e("#"+i.selected))}),e(t).length<1)return void this.focusFirstInput();if(n=e(t).closest(".ss-tabset").find(".ui-tabs-nav .ui-tabs-active .ui-tabs-anchor").attr("id"),i="tab-"+e(t).closest(".ss-tabset .ui-tabs-panel").attr("id"),c&&i!==n)return;s=e(t).closest(".togglecomposite"),s.length>0&&s.accordion("activate",s.find(".ui-accordion-header")),a=e(t).position().top,e(t).is(":visible")||(t="#"+e(t).closest(".field").attr("id"),a=e(t).position().top),e(t).focus(),a>e(window).height()/2&&o.find(".cms-content-fields").scrollTop(a)}else this.focusFirstInput()}},focusFirstInput:function(){this.find(':input:not(:submit)[data-skip-autofocus!="true"]').filter(":visible:first").focus()}}),e(".cms-edit-form .Actions input.action[type=submit], .cms-edit-form .Actions button.action").entwine({onclick:function(e){return this.hasClass("gridfield-button-delete")&&!confirm(r["default"]._t("TABLEFIELD.DELETECONFIRMMESSAGE"))?(e.preventDefault(),!1):(this.is(":disabled")||this.parents("form").trigger("submit",[this]),e.preventDefault(),!1)}}),e(".cms-edit-form .Actions input.action[type=submit].ss-ui-action-cancel, .cms-edit-form .Actions button.action.ss-ui-action-cancel").entwine({onclick:function(e){window.history.length>1?window.history.back():this.parents("form").trigger("submit",[this]),e.preventDefault()}}),e(".cms-edit-form .ss-tabset").entwine({onmatch:function(){if(!this.hasClass("ss-ui-action-tabset")){var e=this.find("> ul:first");1==e.children("li").length&&e.hide().parent().addClass("ss-tabset-tabshidden")}this._super()},onunmatch:function(){this._super()}})})},{i18n:"i18n",jQuery:"jQuery"}],6:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss",function(e){e(".cms-description-toggle").entwine({onadd:function(){var e=!1,t=this.prop("id").substr(0,this.prop("id").indexOf("_Holder")),n=this.find(".cms-description-trigger"),i=this.find(".description");this.hasClass("description-toggle-enabled")||(0===n.length&&(n=this.find(".middleColumn").first().after('').next()),this.addClass("description-toggle-enabled"),n.on("click",function(){i[e?"hide":"show"](),e=!e}),i.hide())}})})},{jQuery:"jQuery"}],7:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss",function(e){e(".cms .field.cms-description-tooltip").entwine({onmatch:function(){this._super();var e=this.find(".description");e.length&&(this.attr("title",e.text()).tooltip({content:e.html()}),e.remove())}}),e(".cms .field.cms-description-tooltip :input").entwine({onfocusin:function(e){this.closest(".field").tooltip("open")},onfocusout:function(e){this.closest(".field").tooltip("close")}})})},{jQuery:"jQuery"}],8:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].fn.layout.defaults.resize=!1,jLayout="undefined"==typeof jLayout?{}:jLayout,jLayout.threeColumnCompressor=function(e,t){function n(e){var t=e+"Size";return function(e){var n=s[t](),i=o[t](),a=r[t](),l=e.insets();return width=n.width+i.width+a.width,height=Math.max(n.height,i.height,a.height),{width:l.left+l.right+width,height:l.top+l.bottom+height}}}if("undefined"==typeof e.menu||"undefined"==typeof e.content||"undefined"==typeof e.preview)throw'Spec is invalid. Please provide "menu", "content" and "preview" elements.';if("undefined"==typeof t.minContentWidth||"undefined"==typeof t.minPreviewWidth||"undefined"==typeof t.mode)throw'Spec is invalid. Please provide "minContentWidth", "minPreviewWidth", "mode"';if("split"!==t.mode&&"content"!==t.mode&&"preview"!==t.mode)throw'Spec is invalid. "mode" should be either "split", "content" or "preview"';var i={options:t},s=a["default"].jLayoutWrap(e.menu),o=a["default"].jLayoutWrap(e.content),r=a["default"].jLayoutWrap(e.preview);return i.layout=function(n){var i=n.bounds(),a=n.insets(),l=a.top,d=i.height-a.bottom,c=a.left,u=i.width-a.right,h=e.menu.width(),f=0,p=0;"preview"===this.options.mode?(f=0,p=u-c-h):"content"===this.options.mode?(f=u-c-h,p=0):(f=(u-c-h)/2,p=u-c-(h+f),f').fadeIn(),n.children(".child-flyout-indicator").fadeIn()}},siteTreePresent:function(){return e("#cms-content-tools-CMSMain").length>0},getPersistedStickyState:function(){var t,n;return void 0!==e.cookie&&(n=e.cookie("cms-menu-sticky"),void 0!==n&&null!==n&&(t="true"===n)),t},setPersistedStickyState:function(t){void 0!==e.cookie&&e.cookie("cms-menu-sticky",t,{path:"/",expires:31})},getEvaluatedCollapsedState:function(){var t,n=this.getPersistedCollapsedState(),i=e(".cms-menu").getPersistedStickyState(),s=this.siteTreePresent();return t=void 0===n?s:n!==s&&i?n:s},onadd:function(){var t=this;setTimeout(function(){t.togglePanel(!t.getEvaluatedCollapsedState(),!1,!1)},0),e(window).on("ajaxComplete",function(e){setTimeout(function(){t.togglePanel(!t.getEvaluatedCollapsedState(),!1,!1)},0)}),this._super()}}),e(".cms-menu-list").entwine({onmatch:function(){this.find("li.current").select(),this.updateItems(),this._super()},onunmatch:function(){this._super()},updateMenuFromResponse:function(e){var t=e.getResponseHeader("X-Controller");if(t){var n=this.find("li#Menu-"+t.replace(/\\/g,"-").replace(/[^a-zA-Z0-9\-_:.]+/,""));n.hasClass("current")||n.select()}this.updateItems()},"from .cms-container":{onafterstatechange:function(e,t){this.updateMenuFromResponse(t.xhr)},onaftersubmitform:function(e,t){this.updateMenuFromResponse(t.xhr)}},"from .cms-edit-form":{onrelodeditform:function(e,t){this.updateMenuFromResponse(t.xmlhttp)}},getContainingPanel:function(){return this.closest(".cms-panel")},fromContainingPanel:{ontoggle:function(t){this.toggleClass("collapsed",e(t.target).hasClass("collapsed")),e(".cms-container").trigger("windowresize"),this.hasClass("collapsed")&&this.find("li.children.opened").removeClass("opened"),this.hasClass("collapsed")||e(".toggle-children.opened").closest("li").addClass("opened")}},updateItems:function(){var t=this.find("#Menu-CMSMain");t[t.is(".current")?"show":"hide"]();var n=e(".cms-content input[name=ID]").val();n&&this.find("li").each(function(){e.isFunction(e(this).setRecordID)&&e(this).setRecordID(n)})}}),e(".cms-menu-list li").entwine({toggleFlyout:function(t){var n=e(this);if(n.children("ul").first().hasClass("collapsed-flyout"))if(t){if(!n.children("ul").first().children("li").first().hasClass("clone")){var i=n.clone();i.addClass("clone").css({}),i.children("ul").first().remove(),i.find("span").not(".text").remove(),i.find("a").first().unbind("click"),n.children("ul").prepend(i)}e(".collapsed-flyout").show(),n.addClass("opened"),n.children("ul").find("li").fadeIn("fast")}else i&&i.remove(),e(".collapsed-flyout").hide(),n.removeClass("opened"),n.find("toggle-children").removeClass("opened"),n.children("ul").find("li").hide()}}),e(".cms-menu-list li").hoverIntent(function(){e(this).toggleFlyout(!0)},function(){e(this).toggleFlyout(!1)}),e(".cms-menu-list .toggle").entwine({onclick:function(t){t.preventDefault(),e(this).toogleFlyout(!0)}}),e(".cms-menu-list li").entwine({onmatch:function(){this.find("ul").length&&this.find("a:first").append(''),this._super()},onunmatch:function(){this._super()},toggle:function(){this[this.hasClass("opened")?"close":"open"]()},open:function(){var e=this.getMenuItem();e&&e.open(),this.find("li.clone")&&this.find("li.clone").remove(),this.addClass("opened").find("ul").show(),this.find(".toggle-children").addClass("opened")},close:function(){this.removeClass("opened").find("ul").hide(),this.find(".toggle-children").removeClass("opened")},select:function(){var e=this.getMenuItem();if(this.addClass("current").open(),this.siblings().removeClass("current").close(),this.siblings().find("li").removeClass("current"),e){var t=e.siblings();e.addClass("current"),t.removeClass("current").close(),t.find("li").removeClass("current").close()}this.getMenu().updateItems(),this.trigger("select")}}),e(".cms-menu-list *").entwine({getMenu:function(){return this.parents(".cms-menu-list:first")}}),e(".cms-menu-list li *").entwine({getMenuItem:function(){return this.parents("li:first")}}),e(".cms-menu-list li a").entwine({onclick:function(t){var n=e.path.isExternal(this.attr("href"));if(!(t.which>1||n)&&"_blank"!=this.attr("target")){t.preventDefault();var i=this.getMenuItem(),s=this.attr("href");n||(s=e("base").attr("href")+s);var a=i.find("li");if(a.length)a.first().find("a").click();else if(!e(".cms-container").loadPanel(s))return!1;i.select()}}}),e(".cms-menu-list li .toggle-children").entwine({onclick:function(e){var t=this.closest("li");return t.toggle(),!1}}),e(".cms .profile-link").entwine({onclick:function(){return e(".cms-container").loadPanel(this.attr("href")),e(".cms-menu-list li").removeClass("current").close(),!1}}),e(".cms-menu .sticky-toggle").entwine({onadd:function(){var t=!!e(".cms-menu").getPersistedStickyState();this.toggleCSS(t),this.toggleIndicator(t),this._super()},toggleCSS:function(e){this[e?"addClass":"removeClass"]("active")},toggleIndicator:function(e){this.next(".sticky-status-indicator").text(e?"fixed":"auto")},onclick:function(){var e=this.closest(".cms-menu"),t=e.getPersistedCollapsedState(),n=e.getPersistedStickyState(),i=void 0===n?!this.hasClass("active"):!n;void 0===t?e.setPersistedCollapsedState(e.hasClass("collapsed")):void 0!==t&&i===!1&&e.clearPersistedCollapsedState(),e.setPersistedStickyState(i),this.toggleCSS(i),this.toggleIndicator(i),this._super()}})})},{jQuery:"jQuery"}],10:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss",function(e){e.entwine.warningLevel=e.entwine.WARN_LEVEL_BESTPRACTISE,e(".cms-panel").entwine({WidthExpanded:null,WidthCollapsed:null,canSetCookie:function(){return void 0!==e.cookie&&void 0!==this.attr("id")},getPersistedCollapsedState:function(){var t,n;return this.canSetCookie()&&(n=e.cookie("cms-panel-collapsed-"+this.attr("id")),void 0!==n&&null!==n&&(t="true"===n)),t},setPersistedCollapsedState:function(t){this.canSetCookie()&&e.cookie("cms-panel-collapsed-"+this.attr("id"),t,{path:"/",expires:31})},clearPersistedCollapsedState:function(){this.canSetCookie()&&e.cookie("cms-panel-collapsed-"+this.attr("id"),"",{path:"/",expires:-1})},getInitialCollapsedState:function(){var e=this.getPersistedCollapsedState();return void 0===e&&(e=this.hasClass("collapsed")),e},onadd:function(){var t,n;if(!this.find(".cms-panel-content").length)throw new Exception('Content panel for ".cms-panel" not found');this.find(".cms-panel-toggle").length||(n=e("
").append('»').append('«'),this.append(n)),this.setWidthExpanded(this.find(".cms-panel-content").innerWidth()),t=this.find(".cms-panel-content-collapsed"),this.setWidthCollapsed(t.length?t.innerWidth():this.find(".toggle-expand").innerWidth()),this.togglePanel(!this.getInitialCollapsedState(),!0,!1),this._super()},togglePanel:function(e,t,n){var i,s;t||(this.trigger("beforetoggle.sspanel",e),this.trigger(e?"beforeexpand":"beforecollapse")),this.toggleClass("collapsed",!e),i=e?this.getWidthExpanded():this.getWidthCollapsed(),this.width(i),s=this.find(".cms-panel-content-collapsed"),s.length&&(this.find(".cms-panel-content")[e?"show":"hide"](),this.find(".cms-panel-content-collapsed")[e?"hide":"show"]()),n!==!1&&this.setPersistedCollapsedState(!e),this.trigger("toggle",e),this.trigger(e?"expand":"collapse")},expandPanel:function(e){(e||this.hasClass("collapsed"))&&this.togglePanel(!0)},collapsePanel:function(e){!e&&this.hasClass("collapsed")||this.togglePanel(!1)}}),e(".cms-panel.collapsed .cms-panel-toggle").entwine({onclick:function(e){this.expandPanel(),e.preventDefault()}}),e(".cms-panel *").entwine({getPanel:function(){return this.parents(".cms-panel:first")}}),e(".cms-panel .toggle-expand").entwine({onclick:function(e){e.preventDefault(),e.stopPropagation(),this.getPanel().expandPanel(),this._super(e)}}),e(".cms-panel .toggle-collapse").entwine({onclick:function(e){e.preventDefault(),e.stopPropagation(),this.getPanel().collapsePanel(),this._super(e)}}),e(".cms-content-tools.collapsed").entwine({onclick:function(e){this.expandPanel(),this._super(e)}})})},{jQuery:"jQuery"}],11:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s),o=e("i18n"),r=i(o);a["default"].entwine("ss.preview",function(e){e(".cms-preview").entwine({AllowedStates:["StageLink","LiveLink","ArchiveLink"],CurrentStateName:null,CurrentSizeName:"auto",IsPreviewEnabled:!1,DefaultMode:"split",Sizes:{auto:{width:"100%",height:"100%"},mobile:{width:"335px",height:"568px"},mobileLandscape:{width:"583px",height:"320px"},tablet:{width:"783px",height:"1024px"},tabletLandscape:{width:"1039px",height:"768px"},desktop:{width:"1024px",height:"800px"}},changeState:function(t,n){var i=this,s=this._getNavigatorStates();return n!==!1&&e.each(s,function(e,n){i.saveState("state",t)}),this.setCurrentStateName(t),this._loadCurrentState(),this.redraw(),this},changeMode:function(t,n){var i=e(".cms-container");if("split"==t)i.entwine(".ss").splitViewMode(),this.setIsPreviewEnabled(!0),this._loadCurrentState();else if("content"==t)i.entwine(".ss").contentViewMode(),this.setIsPreviewEnabled(!1);else{if("preview"!=t)throw"Invalid mode: "+t;i.entwine(".ss").previewMode(),this.setIsPreviewEnabled(!0),this._loadCurrentState()}return n!==!1&&this.saveState("mode",t),this.redraw(),this},changeSize:function(e){var t=this.getSizes();return this.setCurrentSizeName(e),this.removeClass("auto desktop tablet mobile").addClass(e),this.find(".preview-device-outer").width(t[e].width).height(t[e].height),this.find(".preview-device-inner").width(t[e].width),this.saveState("size",e),this.redraw(),this},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0));var t=this.getCurrentStateName();t&&this.find(".cms-preview-states").changeVisibleState(t);var n=e(".cms-container").entwine(".ss").getLayoutOptions();n&&e(".preview-mode-selector").changeVisibleMode(n.mode);var i=this.getCurrentSizeName();return i&&this.find(".preview-size-selector").changeVisibleSize(this.getCurrentSizeName()),this},saveState:function(e,t){this._supportsLocalStorage()&&window.localStorage.setItem("cms-preview-state-"+e,t)},loadState:function(e){return this._supportsLocalStorage()?window.localStorage.getItem("cms-preview-state-"+e):void 0},disablePreview:function(){return this.setPendingURL(null),this._loadUrl("about:blank"),this._block(),this.changeMode("content",!1),this.setIsPreviewEnabled(!1),this},enablePreview:function(){return this.getIsPreviewEnabled()||(this.setIsPreviewEnabled(!0),e.browser.msie&&e.browser.version.slice(0,3)<=7?this.changeMode("content"):this.changeMode(this.getDefaultMode(),!1)),this},getOrAppendFontFixStyleElement:function(){var t=e("#FontFixStyleElement");return t.length||(t=e('').appendTo("head")), -t},onadd:function(){var t=this,n=(this.parent(),this.find("iframe"));n.addClass("center"),n.bind("load",function(){t._adjustIframeForPreview(),t._loadCurrentPage(),e(this).removeClass("loading")}),e.browser.msie&&8===parseInt(e.browser.version,10)&&n.bind("readystatechange",function(e){"interactive"==n[0].readyState&&(t.getOrAppendFontFixStyleElement().removeAttr("disabled"),setTimeout(function(){t.getOrAppendFontFixStyleElement().attr("disabled","disabled")},0))}),this.append('
'),this.find(".cms-preview-overlay").hide(),this.disablePreview(),this._super()},_supportsLocalStorage:function(){var e,t,n=new Date;try{return(e=window.localStorage).setItem(n,n),t=e.getItem(n)==n,e.removeItem(n),t&&e}catch(i){console.warn("localStorge is not available due to current browser / system settings.")}},onenable:function(){var t=e(".preview-mode-selector");t.removeClass("split-disabled"),t.find(".disabled-tooltip").hide()},ondisable:function(){var t=e(".preview-mode-selector");t.addClass("split-disabled"),t.find(".disabled-tooltip").show()},_block:function(){return this.addClass("blocked"),this.find(".cms-preview-overlay").show(),this},_unblock:function(){return this.removeClass("blocked"),this.find(".cms-preview-overlay").hide(),this},_initialiseFromContent:function(){var t,n;return e(".cms-previewable").length?(t=this.loadState("mode"),n=this.loadState("size"),this._moveNavigator(),t&&"content"==t||(this.enablePreview(),this._loadCurrentState()),this.redraw(),t&&this.changeMode(t),n&&this.changeSize(n)):this.disablePreview(),this},"from .cms-container":{onafterstatechange:function(e,t){t.xhr.getResponseHeader("X-ControllerURL")||this._initialiseFromContent()}},PendingURL:null,oncolumnvisibilitychanged:function(){var e=this.getPendingURL();e&&!this.is(".column-hidden")&&(this.setPendingURL(null),this._loadUrl(e),this._unblock())},"from .cms-container .cms-edit-form":{onaftersubmitform:function(){this._initialiseFromContent()}},_loadUrl:function(e){return this.find("iframe").addClass("loading").attr("src",e),this},_getNavigatorStates:function(){var t=e.map(this.getAllowedStates(),function(t){var n=e(".cms-preview-states .state-name[data-name="+t+"]");return n.length?{name:t,url:n.attr("data-link"),active:n.is(":radio")?n.is(":checked"):n.is(":selected")}:null});return t},_loadCurrentState:function(){if(!this.getIsPreviewEnabled())return this;var t=this._getNavigatorStates(),n=this.getCurrentStateName(),i=null;t&&(i=e.grep(t,function(e,t){return n===e.name||!n&&e.active}));var s=null;return i[0]?s=i[0].url:t.length?(this.setCurrentStateName(t[0].name),s=t[0].url):this.setCurrentStateName(null),s+=(-1===s.indexOf("?")?"?":"&")+"CMSPreview=1",this.is(".column-hidden")?(this.setPendingURL(s),this._loadUrl("about:blank"),this._block()):(this.setPendingURL(null),s?(this._loadUrl(s),this._unblock()):this._block()),this},_moveNavigator:function(){var t=e(".cms-preview .cms-preview-controls"),n=e(".cms-edit-form .cms-navigator");n.length&&t.length?t.html(e(".cms-edit-form .cms-navigator").detach()):this._block()},_loadCurrentPage:function(){if(this.getIsPreviewEnabled()){var t;e(".cms-container");try{t=this.find("iframe")[0].contentDocument}catch(n){console.warn("Unable to access iframe, possible https mis-match")}if(t){var i=e(t).find("meta[name=x-page-id]").attr("content"),s=e(t).find("meta[name=x-cms-edit-link]").attr("content"),a=e(".cms-content");i&&a.find(":input[name=ID]").val()!=i&&e(".cms-container").entwine(".ss").loadPanel(s)}}},_adjustIframeForPreview:function(){var e,t=this.find("iframe")[0];if(t){try{e=t.contentDocument}catch(n){console.warn("Unable to access iframe, possible https mis-match")}if(e){for(var i=e.getElementsByTagName("A"),s=0;s
'):this.parent().append('')}}),e(".preview-device-outer").entwine({onclick:function(){this.toggleClass("rotate")}})})},{i18n:"i18n",jQuery:"jQuery"}],12:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss.tree",function(e){e(".cms-tree").entwine({Hints:null,IsUpdatingTree:!1,IsLoaded:!1,onadd:function(){if(this._super(),!e.isNumeric(this.data("jstree_instance_id"))){var t=this.attr("data-hints");t&&this.setHints(e.parseJSON(t));var n=this;this.jstree(this.getTreeConfig()).bind("loaded.jstree",function(t,i){n.setIsLoaded(!0),i.inst._set_settings({html_data:{ajax:{url:n.data("urlTree"),data:function(t){var i=n.data("searchparams")||[];return i=e.grep(i,function(e,t){return"ID"!=e.name&&"value"!=e.name}),i.push({name:"ID",value:e(t).data("id")?e(t).data("id"):0}),i.push({name:"ajax",value:1}),i}}}}),n.updateFromEditForm(),n.css("visibility","visible"),i.inst.hide_checkboxes()}).bind("before.jstree",function(t,i){if("start_drag"==i.func&&(!n.hasClass("draggable")||n.hasClass("multiselect")))return t.stopImmediatePropagation(),!1;if(e.inArray(i.func,["check_node","uncheck_node"])){var s=e(i.args[0]).parents("li:first"),a=s.find("li:not(.disabled)");if(s.hasClass("disabled")&&0==a)return t.stopImmediatePropagation(),!1}}).bind("move_node.jstree",function(t,i){if(!n.getIsUpdatingTree()){var s=i.rslt.o,a=i.rslt.np,o=(i.inst._get_parent(s),e(a).data("id")||0),r=e(s).data("id"),l=e.map(e(s).siblings().andSelf(),function(t){return e(t).data("id")});e.ajax({url:n.data("urlSavetreenode"),type:"POST",data:{ID:r,ParentID:o,SiblingIDs:l},success:function(){e(".cms-edit-form :input[name=ID]").val()==r&&e(".cms-edit-form :input[name=ParentID]").val(o),n.updateNodesFromServer([r])},statusCode:{403:function(){e.jstree.rollback(i.rlbk)}}})}}).bind("select_node.jstree check_node.jstree uncheck_node.jstree",function(t,n){e(document).triggerHandler(t,n)})}},onremove:function(){this.jstree("destroy"),this._super()},"from .cms-container":{onafterstatechange:function(e){this.updateFromEditForm()}},"from .cms-container form":{onaftersubmitform:function(t){var n=e(".cms-edit-form :input[name=ID]").val();this.updateNodesFromServer([n])}},getTreeConfig:function(){var t=this;return{core:{initially_open:["record-0"],animation:0,html_titles:!0},html_data:{},ui:{select_limit:1,initially_select:[this.find(".current").attr("id")]},crrm:{move:{check_move:function(n){var i=e(n.o),s=e(n.np),a=n.ot.get_container()[0]==n.np[0],o=i.getClassname(),r=s.getClassname(),l=t.getHints(),d=[],c=r?r:"Root",u=l&&"undefined"!=typeof l[c]?l[c]:null;u&&i.attr("class").match(/VirtualPage-([^\s]*)/)&&(o=RegExp.$1),u&&(d="undefined"!=typeof u.disallowedChildren?u.disallowedChildren:[]);var h=!(0===i.data("id")||i.hasClass("status-archived")||a&&"inside"!=n.p||s.hasClass("nochildren")||d.length&&-1!=e.inArray(o,d));return h}}},dnd:{drop_target:!1,drag_target:!1},checkbox:{two_state:!0},themes:{theme:"apple",url:e("body").data("frameworkpath")+"/thirdparty/jstree/themes/apple/style.css"},plugins:["html_data","ui","dnd","crrm","themes","checkbox"]}},search:function(e,t){e?this.data("searchparams",e):this.removeData("searchparams"),this.jstree("refresh",-1,t)},getNodeByID:function(e){return this.find("*[data-id="+e+"]")},createNode:function(t,n,i){var s=this,a=void 0!==n.ParentID?s.getNodeByID(n.ParentID):!1,o=e(t),r={data:""};o.hasClass("jstree-open")?r.state="open":o.hasClass("jstree-closed")&&(r.state="closed"),this.jstree("create_node",a.length?a:-1,"last",r,function(e){for(var t=e.attr("class"),n=0;n399?"bad":"good",l=["OK"];return i=window.history.state?window.history.state.path:document.URL,null===s||isSameUrl(i,s)&&isSameUrl(a,s)||_Router2["default"].show(s,{id:(new Date).getTime()+String(Math.random()).replace(/\D/g,""),pjax:t.getResponseHeader("X-Pjax")?t.getResponseHeader("X-Pjax"):n.headers["X-Pjax"]}),t.getResponseHeader("X-Reauthenticate")?void $(".cms-container").showLoginDialog():(0!==t.status&&o&&$.inArray(o,l)&&statusMessage(decodeURIComponent(o),r),void ajaxCompleteEvent(this))}),$(".cms-container").entwine({StateChangeXHR:null,FragmentXHR:{},StateChangeCount:0,LayoutOptions:{minContentWidth:940,minPreviewWidth:400,mode:"content"},onadd:function(){var e=this,t=getUrlPath($("base")[0].href);return t=t.replace(/\/$/,""),_Router2["default"].base(t),_Config2["default"].getTopLevelRoutes().forEach(function(t){(0,_Router2["default"])("/"+t+"(/*?)?",function(t,n){return"complete"!==document.readyState?n():void e.handleStateChange(null,t.state).done(n)})}),_Router2["default"].start(),$.browser.msie&&parseInt($.browser.version,10)<8?($(".ss-loading-screen").append('

Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.

').css("z-index",$(".ss-loading-screen").css("z-index")+1),$(".loading-animation").remove(),void this._super()):(this.redraw(),$(".ss-loading-screen").hide(),$("body").removeClass("loading"),$(window).unbind("resize",positionLoadingSpinner),this.restoreTabState(),void this._super())},fromWindow:{onstatechange:function(e,t){this.handleStateChange(e,t)}},onwindowresize:function(){this.redraw()},"from .cms-panel":{ontoggle:function(){this.redraw()}},"from .cms-container":{onaftersubmitform:function(){this.redraw()}},"from .cms-menu-list li a":{onclick:function(e){var t=$(e.target).attr("href");e.which>1||t==this._tabStateUrl()||this.splitViewMode()}},updateLayoutOptions:function(e){var t=this.getLayoutOptions(),n=!1;for(var i in e)t[i]!==e[i]&&(t[i]=e[i],n=!0);n&&this.redraw()},splitViewMode:function(){this.updateLayoutOptions({mode:"split"})},contentViewMode:function(){this.updateLayoutOptions({mode:"content"})},previewMode:function(){this.updateLayoutOptions({mode:"preview"})},RedrawSuppression:!1,redraw:function(){this.getRedrawSuppression()||(window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.data("jlayout",jLayout.threeColumnCompressor({menu:this.children(".cms-menu"),content:this.children(".cms-content"),preview:this.children(".cms-preview")},this.getLayoutOptions())),this.layout(),this.find(".cms-panel-layout").redraw(),this.find(".cms-content-fields[data-layout-type]").redraw(),this.find(".cms-edit-form[data-layout-type]").redraw(),this.find(".cms-preview").redraw(),this.find(".cms-content").redraw())},checkCanNavigate:function(e){var t=this._findFragments(e||["Content"]),n=t.find(":data(changetracker)").add(t.filter(":data(changetracker)")),i=!0;return n.length?(n.each(function(){$(this).confirmUnsavedChanges()||(i=!1)}),i):!0},loadPanel:function(e){var t=(arguments.length<=1||void 0===arguments[1]?"":arguments[1],arguments.length<=2||void 0===arguments[2]?{}:arguments[2]),n=arguments[3],i=arguments.length<=4||void 0===arguments[4]?window.history.state.path:arguments[4];this.checkCanNavigate(t.pjax?t.pjax.split(","):["Content"])&&(this.saveTabState(),t.__forceReferer=i,n&&(t.__forceReload=Math.random()),_Router2["default"].show(e,t))},reloadCurrentPanel:function(){this.loadPanel(window.history.state.path,null,null,!0)},submitForm:function(e,t,n,i){var s=this;t||(t=this.find(".Actions :submit[name=action_save]")),t||(t=this.find(".Actions :submit:first")),e.trigger("beforesubmitform"),this.trigger("submitform",{form:e,button:t}),$(t).addClass("loading");var a=e.validate();if("undefined"!=typeof a&&!a)return statusMessage("Validation failed.","bad"),$(t).removeClass("loading"),!1;var o=e.serializeArray();return o.push({name:$(t).attr("name"),value:"1"}),o.push({name:"BackURL",value:window.history.state.path.replace(/\/$/,"")}),this.saveTabState(),jQuery.ajax(jQuery.extend({headers:{"X-Pjax":"CurrentForm,Breadcrumbs"},url:e.attr("action"),data:o,type:"POST",complete:function(){$(t).removeClass("loading")},success:function(t,i,a){e.removeClass("changed"),n&&n(t,i,a);var r=s.handleAjaxResponse(t,i,a);r&&r.filter("form").trigger("aftersubmitform",{status:i,xhr:a,formData:o})}},i)),!1},LastState:null,PauseState:!1,handleStateChange:function(e){var t=arguments.length<=1||void 0===arguments[1]?window.history.state:arguments[1];if(!this.getPauseState()){this.getStateChangeXHR()&&this.getStateChangeXHR().abort();var n=this,i=t.pjax||"Content",s={},a=i.split(","),o=this._findFragments(a);if(this.setStateChangeCount(this.getStateChangeCount()+1),!this.checkCanNavigate()){var r=this.getLastState();return this.setPauseState(!0),null!==r?_Router2["default"].show(r.url):_Router2["default"].back(),void this.setPauseState(!1)}if(this.setLastState(t),o.length=0})),i.removeClass(r.join(" ")).addClass(d.join(" ")),s&&i.attr("style",s);var c=i.find("style").detach();c.length&&$(document).find("head").append(c),n.replaceWith(i),!a.is(".cms-container")&&o&&a.layout()});var f=u.filter("form");f.hasClass("cms-tabset")&&f.removeClass("cms-tabset").addClass("cms-tabset")}finally{this.setRedrawSuppression(!1)}return this.redraw(),this.restoreTabState(i&&"undefined"!=typeof i.tabState?i.tabState:null),u}},_findFragments:function(e){return $("[data-pjax-fragment]").filter(function(){var t,n=$(this).data("pjaxFragment").split(" ");for(t in e)if(-1!=$.inArray(e[t],n))return!0;return!1})},refresh:function(){$(window).trigger("statechange"),$(this).redraw()},saveTabState:function(){if("undefined"!=typeof window.sessionStorage&&null!==window.sessionStorage){var e=[],t=this._tabStateUrl();if(this.find(".cms-tabset,.ss-tabset").each(function(t,n){var i=$(n).attr("id");i&&$(n).data("tabs")&&($(n).data("ignoreTabState")||$(n).getIgnoreTabState()||e.push({id:i,selected:$(n).tabs("option","selected")}))}),e){var n="tabs-"+t;try{window.sessionStorage.setItem(n,JSON.stringify(e))}catch(i){if(i.code===DOMException.QUOTA_EXCEEDED_ERR&&0===window.sessionStorage.length)return;throw i}}}},restoreTabState:function(e){var t=this,n=this._tabStateUrl(),i="undefined"!=typeof window.sessionStorage&&window.sessionStorage,s=i?window.sessionStorage.getItem("tabs-"+n):null,a=s?JSON.parse(s):!1;this.find(".cms-tabset, .ss-tabset").each(function(){var n,i,s=$(this),o=s.attr("id"),r=s.find(".ss-tabs-force-active");s.data("tabs")&&(s.tabs("refresh"),r.length?n=r.index():e&&e[o]?(i=s.find(e[o].tabSelector),i.length&&(n=i.index())):a&&$.each(a,function(e,t){s.is("#"+t.id)&&(n=t.selected)}),null!==n&&(s.tabs("option","active",n),t.trigger("tabstaterestored")))})},clearTabState:function(e){if("undefined"!=typeof window.sessionStorage){var t=window.sessionStorage;if(e)t.removeItem("tabs-"+e);else for(var n=0;n
'),t.attr("id",(new Date).getTime()),t.data("url",n),$("body").append(t)}}),$(".leftandmain-logindialog").entwine({onmatch:function(){this._super(),this.ssdialog({iframeUrl:this.data("url"),dialogClass:"leftandmain-logindialog-dialog",autoOpen:!0,minWidth:500,maxWidth:500,minHeight:370,maxHeight:400,closeOnEscape:!1,open:function(){$(".ui-widget-overlay").addClass("leftandmain-logindialog-overlay")},close:function(){$(".ui-widget-overlay").removeClass("leftandmain-logindialog-overlay")}})},onunmatch:function(){this._super()},open:function(){this.ssdialog("open")},close:function(){this.ssdialog("close")},toggle:function(e){this.is(":visible")?this.close():this.open()},reauthenticate:function(e){"undefined"!=typeof e.SecurityID&&$(":input[name=SecurityID]").val(e.SecurityID),"undefined"!=typeof e.TempID&&$("body").data("member-tempid",e.TempID),this.close()}}),$("form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading").entwine({onmatch:function(){this.append('
'),this._super()},onunmatch:function(){this.find(".cms-content-loading-overlay,.cms-content-loading-spinner").remove(),this._super()}}),$('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({onadd:function(){this.addClass("ss-ui-button"),this.data("button")||this.button(),this._super()},onremove:function(){this.data("button")&&this.button("destroy"),this._super()}}),$(".cms .cms-panel-link").entwine({onclick:function(e){if($(this).hasClass("external-link"))return void e.stopPropagation();var t=this.attr("href"),n=t&&!t.match(/^#/)?t:this.data("href"),i={pjax:this.data("pjaxTarget")};$(".cms-container").loadPanel(n,null,i),e.preventDefault()}}),$(".cms .ss-ui-button-ajax").entwine({onclick:function onclick(e){$(this).removeClass("ui-button-text-only"),$(this).addClass("ss-ui-button-loading ui-button-text-icons");var loading=$(this).find(".ss-ui-loading-icon");loading.length<1&&(loading=$("").addClass("ss-ui-loading-icon ui-button-icon-primary ui-icon"),$(this).prepend(loading)),loading.show();var href=this.attr("href"),url=href?href:this.data("href");jQuery.ajax({url:url,complete:function complete(xmlhttp,status){var msg=xmlhttp.getResponseHeader("X-Status")?xmlhttp.getResponseHeader("X-Status"):xmlhttp.responseText;try{"undefined"!=typeof msg&&null!==msg&&eval(msg)}catch(e){}loading.hide(),$(".cms-container").refresh(),$(this).removeClass("ss-ui-button-loading ui-button-text-icons"),$(this).addClass("ui-button-text-only")},dataType:"html"}),e.preventDefault()}}),$(".cms .ss-ui-dialog-link").entwine({UUID:null,onmatch:function(){this._super(),this.setUUID((new Date).getTime())},onunmatch:function(){this._super()},onclick:function(){this._super();var e="ss-ui-dialog-"+this.getUUID(),t=$("#"+e);t.length||(t=$('
'),$("body").append(t));var n=this.data("popupclass")?this.data("popupclass"):"";return t.ssdialog({iframeUrl:this.attr("href"),autoOpen:!0,dialogExtraClass:n}),!1}}),$(".cms-content .Actions").entwine({onmatch:function(){this.find(".ss-ui-button").click(function(){var e=this.form;e&&(e.clickedButton=this,setTimeout(function(){e.clickedButton=null},10))}),this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.contents().filter(function(){return 3==this.nodeType&&!/\S/.test(this.nodeValue)}).remove(),this.find(".ss-ui-button").each(function(){$(this).data("button")||$(this).button()}),this.find(".ss-ui-buttonset").buttonset()}}),$(".cms .field.date input.text").entwine({onmatch:function(){var e=$(this).parents(".field.date:first"),t=e.data();return t.showcalendar?(t.showOn="button",t.locale&&$.datepicker.regional[t.locale]&&(t=$.extend(t,$.datepicker.regional[t.locale],{})),$(this).datepicker(t),void this._super()):void this._super()},onunmatch:function(){this._super()}}),$(".cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown").entwine({onmatch:function(){return this.is(".no-chosen")?void this._super():(this.data("placeholder")||this.data("placeholder"," "),this.removeClass("has-chosen").chosen("destroy"),this.siblings(".chosen-container").remove(),applyChosen(this),void this._super())},onunmatch:function(){this._super()}}),$(".cms-panel-layout").entwine({redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0))}}),$(".cms .ss-gridfield").entwine({showDetailView:function(e){var t=window.location.search.replace(/^\?/,"");t&&(e=$.path.addSearchParams(e,t)),$(".cms-container").loadPanel(e)}}),$(".cms-search-form").entwine({onsubmit:function(e){var t,n;t=this.find(":input:not(:submit)").filter(function(){var e=$.grep($(this).fieldValue(),function(e){return e});return e.length}),n=this.attr("action"),t.length&&(n=$.path.addSearchParams(n,t.serialize().replace("+","%20")));var i=this.closest(".cms-container");return i.find(".cms-edit-form").tabs("select",0),i.loadPanel(n,"",{},!0),!1}}),$(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({onclick:function(e){e.preventDefault();var t=$(this).parents("form");t.clearForm(),t.find(".dropdown select").prop("selectedIndex",0).trigger("chosen:updated"),t.submit()}}),window._panelDeferredCache={},$(".cms-panel-deferred").entwine({onadd:function(){this._super(),this.redraw()},onremove:function(){window.debug&&console.log("saving",this.data("url"),this),this.data("deferredNoCache")||(window._panelDeferredCache[this.data("url")]=this.html()),this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0));var e=this,t=this.data("url");if(!t)throw'Elements of class .cms-panel-deferred need a "data-url" attribute';this._super(),this.children().length||(this.data("deferredNoCache")||"undefined"==typeof window._panelDeferredCache[t]?(this.addClass("loading"),$.ajax({url:t,complete:function(){e.removeClass("loading")},success:function(t,n,i){e.html(t)}})):this.html(window._panelDeferredCache[t]))}}),$(".cms-tabset").entwine({onadd:function(){this.redrawTabs(),this._super()},onremove:function(){this.data("tabs")&&this.tabs("destroy"),this._super()},redrawTabs:function(){this.rewriteHashlinks();var e=(this.attr("id"),this.find("ul:first .ui-tabs-active"));this.data("uiTabs")||this.tabs({active:-1!=e.index()?e.index():0,beforeLoad:function(e,t){return!1},activate:function(e,t){var n=$(this).closest("form").find(".Actions");$(t.newTab).closest("li").hasClass("readonly")?n.fadeOut():n.show()}})},rewriteHashlinks:function(){$(this).find("ul a").each(function(){if($(this).attr("href")){var e=$(this).attr("href").match(/#.*/);e&&$(this).attr("href",document.location.href.replace(/#.*/,"")+e[0])}})}}),$("#filters-button").entwine({onmatch:function(){this._super(),this.data("collapsed",!0),this.data("animating",!1); -},onunmatch:function(){this._super()},showHide:function(){var e=this,t=$(".cms-content-filters").first(),n=this.data("collapsed");this.data("animating")||(this.toggleClass("active"),this.data("animating",!0),t[n?"slideDown":"slideUp"]({complete:function(){e.data("collapsed",!n),e.data("animating",!1)}}))},onclick:function(){this.showHide()}})});var statusMessage=function(e,t){e=jQuery("
").text(e).html(),jQuery.noticeAdd({text:e,type:t,stayTime:5e3,inEffect:{left:"0",opacity:"show"}})},errorMessage=function(e){jQuery.noticeAdd({text:e,type:"error",stayTime:5e3,inEffect:{left:"0",opacity:"show"}})}},{jQuery:"jQuery","lib/Config":15,"lib/Router":"lib/Router"}],15:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(){i(this,e)}return e.getSection=function(e){return window.ss.config.sections[e]},e.getTopLevelRoutes=function(){var e=[];return Object.keys(window.ss.config.sections).forEach(function(t){var n=window.ss.config.sections[t].route,i=n.match(/^admin\/[^\/]+(\/?)$/);if(i){n=n.replace(/\/$/,"");var s=-1===e.indexOf(n);s&&e.push(n)}}),e},e}();n["default"]=s},{}]},{},[1]); +t},onadd:function(){var t=this,n=(this.parent(),this.find("iframe"));n.addClass("center"),n.bind("load",function(){t._adjustIframeForPreview(),t._loadCurrentPage(),e(this).removeClass("loading")}),e.browser.msie&&8===parseInt(e.browser.version,10)&&n.bind("readystatechange",function(e){"interactive"==n[0].readyState&&(t.getOrAppendFontFixStyleElement().removeAttr("disabled"),setTimeout(function(){t.getOrAppendFontFixStyleElement().attr("disabled","disabled")},0))}),this.append('
'),this.find(".cms-preview-overlay").hide(),this.disablePreview(),this._super()},_supportsLocalStorage:function(){var e,t,n=new Date;try{return(e=window.localStorage).setItem(n,n),t=e.getItem(n)==n,e.removeItem(n),t&&e}catch(i){console.warn("localStorge is not available due to current browser / system settings.")}},onenable:function(){var t=e(".preview-mode-selector");t.removeClass("split-disabled"),t.find(".disabled-tooltip").hide()},ondisable:function(){var t=e(".preview-mode-selector");t.addClass("split-disabled"),t.find(".disabled-tooltip").show()},_block:function(){return this.addClass("blocked"),this.find(".cms-preview-overlay").show(),this},_unblock:function(){return this.removeClass("blocked"),this.find(".cms-preview-overlay").hide(),this},_initialiseFromContent:function(){var t,n;return e(".cms-previewable").length?(t=this.loadState("mode"),n=this.loadState("size"),this._moveNavigator(),t&&"content"==t||(this.enablePreview(),this._loadCurrentState()),this.redraw(),t&&this.changeMode(t),n&&this.changeSize(n)):this.disablePreview(),this},"from .cms-container":{onafterstatechange:function(e,t){t.xhr.getResponseHeader("X-ControllerURL")||this._initialiseFromContent()}},PendingURL:null,oncolumnvisibilitychanged:function(){var e=this.getPendingURL();e&&!this.is(".column-hidden")&&(this.setPendingURL(null),this._loadUrl(e),this._unblock())},"from .cms-container .cms-edit-form":{onaftersubmitform:function(){this._initialiseFromContent()}},_loadUrl:function(e){return this.find("iframe").addClass("loading").attr("src",e),this},_getNavigatorStates:function(){var t=e.map(this.getAllowedStates(),function(t){var n=e(".cms-preview-states .state-name[data-name="+t+"]");return n.length?{name:t,url:n.attr("data-link"),active:n.is(":radio")?n.is(":checked"):n.is(":selected")}:null});return t},_loadCurrentState:function(){if(!this.getIsPreviewEnabled())return this;var t=this._getNavigatorStates(),n=this.getCurrentStateName(),i=null;t&&(i=e.grep(t,function(e,t){return n===e.name||!n&&e.active}));var s=null;return i[0]?s=i[0].url:t.length?(this.setCurrentStateName(t[0].name),s=t[0].url):this.setCurrentStateName(null),s+=(-1===s.indexOf("?")?"?":"&")+"CMSPreview=1",this.is(".column-hidden")?(this.setPendingURL(s),this._loadUrl("about:blank"),this._block()):(this.setPendingURL(null),s?(this._loadUrl(s),this._unblock()):this._block()),this},_moveNavigator:function(){var t=e(".cms-preview .cms-preview-controls"),n=e(".cms-edit-form .cms-navigator");n.length&&t.length?t.html(e(".cms-edit-form .cms-navigator").detach()):this._block()},_loadCurrentPage:function(){if(this.getIsPreviewEnabled()){var t;e(".cms-container");try{t=this.find("iframe")[0].contentDocument}catch(n){console.warn("Unable to access iframe, possible https mis-match")}if(t){var i=e(t).find("meta[name=x-page-id]").attr("content"),s=e(t).find("meta[name=x-cms-edit-link]").attr("content"),a=e(".cms-content");i&&a.find(":input[name=ID]").val()!=i&&e(".cms-container").entwine(".ss").loadPanel(s)}}},_adjustIframeForPreview:function(){var e,t=this.find("iframe")[0];if(t){try{e=t.contentDocument}catch(n){console.warn("Unable to access iframe, possible https mis-match")}if(e){for(var i=e.getElementsByTagName("A"),s=0;s
'):this.parent().append('')}}),e(".preview-device-outer").entwine({onclick:function(){this.toggleClass("rotate")}})})},{i18n:"i18n",jQuery:"jQuery"}],12:[function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{"default":e}}var s=e("jQuery"),a=i(s);a["default"].entwine("ss.tree",function(e){e(".cms-tree").entwine({Hints:null,IsUpdatingTree:!1,IsLoaded:!1,onadd:function(){if(this._super(),!e.isNumeric(this.data("jstree_instance_id"))){var t=this.attr("data-hints");t&&this.setHints(e.parseJSON(t));var n=this;this.jstree(this.getTreeConfig()).bind("loaded.jstree",function(t,i){n.setIsLoaded(!0),i.inst._set_settings({html_data:{ajax:{url:n.data("urlTree"),data:function(t){var i=n.data("searchparams")||[];return i=e.grep(i,function(e,t){return"ID"!=e.name&&"value"!=e.name}),i.push({name:"ID",value:e(t).data("id")?e(t).data("id"):0}),i.push({name:"ajax",value:1}),i}}}}),n.updateFromEditForm(),n.css("visibility","visible"),i.inst.hide_checkboxes()}).bind("before.jstree",function(t,i){if("start_drag"==i.func&&(!n.hasClass("draggable")||n.hasClass("multiselect")))return t.stopImmediatePropagation(),!1;if(e.inArray(i.func,["check_node","uncheck_node"])){var s=e(i.args[0]).parents("li:first"),a=s.find("li:not(.disabled)");if(s.hasClass("disabled")&&0==a)return t.stopImmediatePropagation(),!1}}).bind("move_node.jstree",function(t,i){if(!n.getIsUpdatingTree()){var s=i.rslt.o,a=i.rslt.np,o=(i.inst._get_parent(s),e(a).data("id")||0),r=e(s).data("id"),l=e.map(e(s).siblings().andSelf(),function(t){return e(t).data("id")});e.ajax({url:n.data("urlSavetreenode"),type:"POST",data:{ID:r,ParentID:o,SiblingIDs:l},success:function(){e(".cms-edit-form :input[name=ID]").val()==r&&e(".cms-edit-form :input[name=ParentID]").val(o),n.updateNodesFromServer([r])},statusCode:{403:function(){e.jstree.rollback(i.rlbk)}}})}}).bind("select_node.jstree check_node.jstree uncheck_node.jstree",function(t,n){e(document).triggerHandler(t,n)})}},onremove:function(){this.jstree("destroy"),this._super()},"from .cms-container":{onafterstatechange:function(e){this.updateFromEditForm()}},"from .cms-container form":{onaftersubmitform:function(t){var n=e(".cms-edit-form :input[name=ID]").val();this.updateNodesFromServer([n])}},getTreeConfig:function(){var t=this;return{core:{initially_open:["record-0"],animation:0,html_titles:!0},html_data:{},ui:{select_limit:1,initially_select:[this.find(".current").attr("id")]},crrm:{move:{check_move:function(n){var i=e(n.o),s=e(n.np),a=n.ot.get_container()[0]==n.np[0],o=i.getClassname(),r=s.getClassname(),l=t.getHints(),d=[],c=r?r:"Root",u=l&&"undefined"!=typeof l[c]?l[c]:null;u&&i.attr("class").match(/VirtualPage-([^\s]*)/)&&(o=RegExp.$1),u&&(d="undefined"!=typeof u.disallowedChildren?u.disallowedChildren:[]);var h=!(0===i.data("id")||i.hasClass("status-archived")||a&&"inside"!=n.p||s.hasClass("nochildren")||d.length&&-1!=e.inArray(o,d));return h}}},dnd:{drop_target:!1,drag_target:!1},checkbox:{two_state:!0},themes:{theme:"apple",url:e("body").data("frameworkpath")+"/thirdparty/jstree/themes/apple/style.css"},plugins:["html_data","ui","dnd","crrm","themes","checkbox"]}},search:function(e,t){e?this.data("searchparams",e):this.removeData("searchparams"),this.jstree("refresh",-1,t)},getNodeByID:function(e){return this.find("*[data-id="+e+"]")},createNode:function(t,n,i){var s=this,a=void 0!==n.ParentID?s.getNodeByID(n.ParentID):!1,o=e(t),r={data:""};o.hasClass("jstree-open")?r.state="open":o.hasClass("jstree-closed")&&(r.state="closed"),this.jstree("create_node",a.length?a:-1,"last",r,function(e){for(var t=e.attr("class"),n=0;n399?"bad":"good",l=["OK"];return i=window.history.state?window.history.state.path:document.URL,null===s||isSameUrl(i,s)&&isSameUrl(a,s)||_Router2["default"].show(s,{id:(new Date).getTime()+String(Math.random()).replace(/\D/g,""),pjax:t.getResponseHeader("X-Pjax")?t.getResponseHeader("X-Pjax"):n.headers["X-Pjax"]}),t.getResponseHeader("X-Reauthenticate")?void $(".cms-container").showLoginDialog():(0!==t.status&&o&&$.inArray(o,l)&&statusMessage(decodeURIComponent(o),r),void ajaxCompleteEvent(this))}),$(".cms-container").entwine({StateChangeXHR:null,FragmentXHR:{},StateChangeCount:0,LayoutOptions:{minContentWidth:940,minPreviewWidth:400,mode:"content"},onadd:function(){var e=this,t=getUrlPath($("base")[0].href);return t=t.replace(/\/$/,""),t.match(/^[^\/]/)&&(t="/"+t),_Router2["default"].base(t),_Config2["default"].getTopLevelRoutes().forEach(function(t){(0,_Router2["default"])("/"+t+"(/*?)?",function(t,n){return"complete"!==document.readyState?n():void e.handleStateChange(null,t.state).done(n)})}),_Router2["default"].start(),$.browser.msie&&parseInt($.browser.version,10)<8?($(".ss-loading-screen").append('

Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.

').css("z-index",$(".ss-loading-screen").css("z-index")+1),$(".loading-animation").remove(),void this._super()):(this.redraw(),$(".ss-loading-screen").hide(),$("body").removeClass("loading"),$(window).unbind("resize",positionLoadingSpinner),this.restoreTabState(),void this._super())},fromWindow:{onstatechange:function(e,t){this.handleStateChange(e,t)}},onwindowresize:function(){this.redraw()},"from .cms-panel":{ontoggle:function(){this.redraw()}},"from .cms-container":{onaftersubmitform:function(){this.redraw()}},"from .cms-menu-list li a":{onclick:function(e){var t=$(e.target).attr("href");e.which>1||t==this._tabStateUrl()||this.splitViewMode()}},updateLayoutOptions:function(e){var t=this.getLayoutOptions(),n=!1;for(var i in e)t[i]!==e[i]&&(t[i]=e[i],n=!0);n&&this.redraw()},splitViewMode:function(){this.updateLayoutOptions({mode:"split"})},contentViewMode:function(){this.updateLayoutOptions({mode:"content"})},previewMode:function(){this.updateLayoutOptions({mode:"preview"})},RedrawSuppression:!1,redraw:function(){this.getRedrawSuppression()||(window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.data("jlayout",jLayout.threeColumnCompressor({menu:this.children(".cms-menu"),content:this.children(".cms-content"),preview:this.children(".cms-preview")},this.getLayoutOptions())),this.layout(),this.find(".cms-panel-layout").redraw(),this.find(".cms-content-fields[data-layout-type]").redraw(),this.find(".cms-edit-form[data-layout-type]").redraw(),this.find(".cms-preview").redraw(),this.find(".cms-content").redraw())},checkCanNavigate:function(e){var t=this._findFragments(e||["Content"]),n=t.find(":data(changetracker)").add(t.filter(":data(changetracker)")),i=!0;return n.length?(n.each(function(){$(this).confirmUnsavedChanges()||(i=!1)}),i):!0},loadPanel:function(e){var t=(arguments.length<=1||void 0===arguments[1]?"":arguments[1],arguments.length<=2||void 0===arguments[2]?{}:arguments[2]),n=arguments[3],i=arguments.length<=4||void 0===arguments[4]?window.history.state.path:arguments[4];this.checkCanNavigate(t.pjax?t.pjax.split(","):["Content"])&&(this.saveTabState(),t.__forceReferer=i,n&&(t.__forceReload=Math.random()),_Router2["default"].show(e,t))},reloadCurrentPanel:function(){this.loadPanel(window.history.state.path,null,null,!0)},submitForm:function(e,t,n,i){var s=this;t||(t=this.find(".Actions :submit[name=action_save]")),t||(t=this.find(".Actions :submit:first")),e.trigger("beforesubmitform"),this.trigger("submitform",{form:e,button:t}),$(t).addClass("loading");var a=e.validate();if("undefined"!=typeof a&&!a)return statusMessage("Validation failed.","bad"),$(t).removeClass("loading"),!1;var o=e.serializeArray();return o.push({name:$(t).attr("name"),value:"1"}),o.push({name:"BackURL",value:window.history.state.path.replace(/\/$/,"")}),this.saveTabState(),jQuery.ajax(jQuery.extend({headers:{"X-Pjax":"CurrentForm,Breadcrumbs"},url:e.attr("action"),data:o,type:"POST",complete:function(){$(t).removeClass("loading")},success:function(t,i,a){e.removeClass("changed"),n&&n(t,i,a);var r=s.handleAjaxResponse(t,i,a);r&&r.filter("form").trigger("aftersubmitform",{status:i,xhr:a,formData:o})}},i)),!1},LastState:null,PauseState:!1,handleStateChange:function(e){var t=arguments.length<=1||void 0===arguments[1]?window.history.state:arguments[1];if(!this.getPauseState()){this.getStateChangeXHR()&&this.getStateChangeXHR().abort();var n=this,i=t.pjax||"Content",s={},a=i.split(","),o=this._findFragments(a);if(this.setStateChangeCount(this.getStateChangeCount()+1),!this.checkCanNavigate()){var r=this.getLastState();return this.setPauseState(!0),null!==r?_Router2["default"].show(r.url):_Router2["default"].back(),void this.setPauseState(!1)}if(this.setLastState(t),o.length=0})),i.removeClass(r.join(" ")).addClass(d.join(" ")),s&&i.attr("style",s);var c=i.find("style").detach();c.length&&$(document).find("head").append(c),n.replaceWith(i),!a.is(".cms-container")&&o&&a.layout()});var f=u.filter("form");f.hasClass("cms-tabset")&&f.removeClass("cms-tabset").addClass("cms-tabset")}finally{this.setRedrawSuppression(!1)}return this.redraw(),this.restoreTabState(i&&"undefined"!=typeof i.tabState?i.tabState:null),u}},_findFragments:function(e){return $("[data-pjax-fragment]").filter(function(){var t,n=$(this).data("pjaxFragment").split(" ");for(t in e)if(-1!=$.inArray(e[t],n))return!0;return!1})},refresh:function(){$(window).trigger("statechange"),$(this).redraw()},saveTabState:function(){if("undefined"!=typeof window.sessionStorage&&null!==window.sessionStorage){var e=[],t=this._tabStateUrl();if(this.find(".cms-tabset,.ss-tabset").each(function(t,n){var i=$(n).attr("id");i&&$(n).data("tabs")&&($(n).data("ignoreTabState")||$(n).getIgnoreTabState()||e.push({id:i,selected:$(n).tabs("option","selected")}))}),e){var n="tabs-"+t;try{window.sessionStorage.setItem(n,JSON.stringify(e))}catch(i){if(i.code===DOMException.QUOTA_EXCEEDED_ERR&&0===window.sessionStorage.length)return;throw i}}}},restoreTabState:function(e){var t=this,n=this._tabStateUrl(),i="undefined"!=typeof window.sessionStorage&&window.sessionStorage,s=i?window.sessionStorage.getItem("tabs-"+n):null,a=s?JSON.parse(s):!1;this.find(".cms-tabset, .ss-tabset").each(function(){var n,i,s=$(this),o=s.attr("id"),r=s.find(".ss-tabs-force-active");s.data("tabs")&&(s.tabs("refresh"),r.length?n=r.index():e&&e[o]?(i=s.find(e[o].tabSelector),i.length&&(n=i.index())):a&&$.each(a,function(e,t){s.is("#"+t.id)&&(n=t.selected)}),null!==n&&(s.tabs("option","active",n),t.trigger("tabstaterestored")))})},clearTabState:function(e){if("undefined"!=typeof window.sessionStorage){var t=window.sessionStorage;if(e)t.removeItem("tabs-"+e);else for(var n=0;n
'),t.attr("id",(new Date).getTime()),t.data("url",n),$("body").append(t)}}),$(".leftandmain-logindialog").entwine({onmatch:function(){this._super(),this.ssdialog({iframeUrl:this.data("url"),dialogClass:"leftandmain-logindialog-dialog",autoOpen:!0,minWidth:500,maxWidth:500,minHeight:370,maxHeight:400,closeOnEscape:!1,open:function(){$(".ui-widget-overlay").addClass("leftandmain-logindialog-overlay")},close:function(){$(".ui-widget-overlay").removeClass("leftandmain-logindialog-overlay")}})},onunmatch:function(){this._super()},open:function(){this.ssdialog("open")},close:function(){this.ssdialog("close")},toggle:function(e){this.is(":visible")?this.close():this.open()},reauthenticate:function(e){"undefined"!=typeof e.SecurityID&&$(":input[name=SecurityID]").val(e.SecurityID),"undefined"!=typeof e.TempID&&$("body").data("member-tempid",e.TempID),this.close()}}),$("form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading").entwine({onmatch:function(){this.append('
'),this._super()},onunmatch:function(){this.find(".cms-content-loading-overlay,.cms-content-loading-spinner").remove(),this._super()}}),$('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({onadd:function(){this.addClass("ss-ui-button"),this.data("button")||this.button(),this._super()},onremove:function(){this.data("button")&&this.button("destroy"),this._super()}}),$(".cms .cms-panel-link").entwine({onclick:function(e){if($(this).hasClass("external-link"))return void e.stopPropagation();var t=this.attr("href"),n=t&&!t.match(/^#/)?t:this.data("href"),i={pjax:this.data("pjaxTarget")};$(".cms-container").loadPanel(n,null,i),e.preventDefault()}}),$(".cms .ss-ui-button-ajax").entwine({onclick:function onclick(e){$(this).removeClass("ui-button-text-only"),$(this).addClass("ss-ui-button-loading ui-button-text-icons");var loading=$(this).find(".ss-ui-loading-icon");loading.length<1&&(loading=$("").addClass("ss-ui-loading-icon ui-button-icon-primary ui-icon"),$(this).prepend(loading)),loading.show();var href=this.attr("href"),url=href?href:this.data("href");jQuery.ajax({url:url,complete:function complete(xmlhttp,status){var msg=xmlhttp.getResponseHeader("X-Status")?xmlhttp.getResponseHeader("X-Status"):xmlhttp.responseText;try{"undefined"!=typeof msg&&null!==msg&&eval(msg)}catch(e){}loading.hide(),$(".cms-container").refresh(),$(this).removeClass("ss-ui-button-loading ui-button-text-icons"),$(this).addClass("ui-button-text-only")},dataType:"html"}),e.preventDefault()}}),$(".cms .ss-ui-dialog-link").entwine({UUID:null,onmatch:function(){this._super(),this.setUUID((new Date).getTime())},onunmatch:function(){this._super()},onclick:function(){this._super();var e="ss-ui-dialog-"+this.getUUID(),t=$("#"+e);t.length||(t=$('
'),$("body").append(t));var n=this.data("popupclass")?this.data("popupclass"):"";return t.ssdialog({iframeUrl:this.attr("href"),autoOpen:!0,dialogExtraClass:n}),!1}}),$(".cms-content .Actions").entwine({onmatch:function(){this.find(".ss-ui-button").click(function(){var e=this.form;e&&(e.clickedButton=this,setTimeout(function(){e.clickedButton=null},10))}),this.redraw(),this._super()},onunmatch:function(){this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0)),this.contents().filter(function(){return 3==this.nodeType&&!/\S/.test(this.nodeValue)}).remove(),this.find(".ss-ui-button").each(function(){$(this).data("button")||$(this).button()}),this.find(".ss-ui-buttonset").buttonset()}}),$(".cms .field.date input.text").entwine({onmatch:function(){var e=$(this).parents(".field.date:first"),t=e.data();return t.showcalendar?(t.showOn="button",t.locale&&$.datepicker.regional[t.locale]&&(t=$.extend(t,$.datepicker.regional[t.locale],{})),$(this).datepicker(t),void this._super()):void this._super()},onunmatch:function(){this._super()}}),$(".cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown").entwine({onmatch:function(){return this.is(".no-chosen")?void this._super():(this.data("placeholder")||this.data("placeholder"," "),this.removeClass("has-chosen").chosen("destroy"),this.siblings(".chosen-container").remove(),applyChosen(this),void this._super())},onunmatch:function(){this._super()}}),$(".cms-panel-layout").entwine({redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0))}}),$(".cms .ss-gridfield").entwine({showDetailView:function(e){var t=window.location.search.replace(/^\?/,"");t&&(e=$.path.addSearchParams(e,t)),$(".cms-container").loadPanel(e)}}),$(".cms-search-form").entwine({onsubmit:function(e){var t,n;t=this.find(":input:not(:submit)").filter(function(){var e=$.grep($(this).fieldValue(),function(e){return e});return e.length}),n=this.attr("action"),t.length&&(n=$.path.addSearchParams(n,t.serialize().replace("+","%20")));var i=this.closest(".cms-container");return i.find(".cms-edit-form").tabs("select",0),i.loadPanel(n,"",{},!0),!1}}),$(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({onclick:function(e){e.preventDefault();var t=$(this).parents("form");t.clearForm(),t.find(".dropdown select").prop("selectedIndex",0).trigger("chosen:updated"),t.submit()}}),window._panelDeferredCache={},$(".cms-panel-deferred").entwine({onadd:function(){this._super(),this.redraw()},onremove:function(){window.debug&&console.log("saving",this.data("url"),this),this.data("deferredNoCache")||(window._panelDeferredCache[this.data("url")]=this.html()),this._super()},redraw:function(){window.debug&&console.log("redraw",this.attr("class"),this.get(0));var e=this,t=this.data("url");if(!t)throw'Elements of class .cms-panel-deferred need a "data-url" attribute';this._super(),this.children().length||(this.data("deferredNoCache")||"undefined"==typeof window._panelDeferredCache[t]?(this.addClass("loading"),$.ajax({url:t,complete:function(){e.removeClass("loading")},success:function(t,n,i){e.html(t)}})):this.html(window._panelDeferredCache[t]))}}),$(".cms-tabset").entwine({onadd:function(){this.redrawTabs(),this._super()},onremove:function(){this.data("tabs")&&this.tabs("destroy"),this._super()},redrawTabs:function(){this.rewriteHashlinks();var e=(this.attr("id"),this.find("ul:first .ui-tabs-active"));this.data("uiTabs")||this.tabs({active:-1!=e.index()?e.index():0,beforeLoad:function(e,t){return!1},activate:function(e,t){var n=$(this).closest("form").find(".Actions");$(t.newTab).closest("li").hasClass("readonly")?n.fadeOut():n.show()}})},rewriteHashlinks:function(){$(this).find("ul a").each(function(){if($(this).attr("href")){var e=$(this).attr("href").match(/#.*/);e&&$(this).attr("href",document.location.href.replace(/#.*/,"")+e[0])}})}}),$("#filters-button").entwine({onmatch:function(){this._super(),this.data("collapsed",!0), +this.data("animating",!1)},onunmatch:function(){this._super()},showHide:function(){var e=this,t=$(".cms-content-filters").first(),n=this.data("collapsed");this.data("animating")||(this.toggleClass("active"),this.data("animating",!0),t[n?"slideDown":"slideUp"]({complete:function(){e.data("collapsed",!n),e.data("animating",!1)}}))},onclick:function(){this.showHide()}})});var statusMessage=function(e,t){e=jQuery("
").text(e).html(),jQuery.noticeAdd({text:e,type:t,stayTime:5e3,inEffect:{left:"0",opacity:"show"}})},errorMessage=function(e){jQuery.noticeAdd({text:e,type:"error",stayTime:5e3,inEffect:{left:"0",opacity:"show"}})}},{jQuery:"jQuery","lib/Config":15,"lib/Router":"lib/Router"}],15:[function(e,t,n){"use strict";function i(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}Object.defineProperty(n,"__esModule",{value:!0});var s=function(){function e(){i(this,e)}return e.getSection=function(e){return window.ss.config.sections[e]},e.getTopLevelRoutes=function(){var e=[];return Object.keys(window.ss.config.sections).forEach(function(t){var n=window.ss.config.sections[t].route,i=n.match(/^admin\/[^\/]+(\/?)$/);if(i){n=n.replace(/\/$/,"");var s=-1===e.indexOf(n);s&&e.push(n)}}),e},e}();n["default"]=s},{}]},{},[1]); //# sourceMappingURL=bundle-legacy.js.map diff --git a/admin/client/src/legacy/LeftAndMain.js b/admin/client/src/legacy/LeftAndMain.js index 68f4d9ce0..1d0cc1629 100644 --- a/admin/client/src/legacy/LeftAndMain.js +++ b/admin/client/src/legacy/LeftAndMain.js @@ -21,26 +21,26 @@ window.ss.router = router; * @desc Returns a function that will not be called until it hasn't been invoked for `wait` seconds. */ window.ss.debounce = function (func, wait, immediate) { - var timeout, context, args; + var timeout, context, args; - var later = function() { - timeout = null; - if (!immediate) func.apply(context, args); - }; + var later = function() { + timeout = null; + if (!immediate) func.apply(context, args); + }; - return function() { - var callNow = immediate && !timeout; + return function() { + var callNow = immediate && !timeout; - context = this; - args = arguments; + context = this; + args = arguments; - clearTimeout(timeout); - timeout = setTimeout(later, wait); + clearTimeout(timeout); + timeout = setTimeout(later, wait); - if (callNow) { - func.apply(context, args); - } - }; + if (callNow) { + func.apply(context, args); + } + }; }; /** @@ -50,14 +50,14 @@ window.ss.debounce = function (func, wait, immediate) { * @return string */ function getUrlPath(url) { - var anchor = document.createElement('a'); - anchor.href = url; + var anchor = document.createElement('a'); + anchor.href = url; - return anchor.pathname + return anchor.pathname } $(window).bind('resize.leftandmain', function(e) { - $('.cms-container').trigger('windowresize'); + $('.cms-container').trigger('windowresize'); }); // setup jquery.entwine @@ -65,1437 +65,1440 @@ $.entwine.warningLevel = $.entwine.WARN_LEVEL_BESTPRACTISE; $.entwine('ss', function($) { - /* - * Handle messages sent via nested iframes - * Messages should be raised via postMessage with an object with the 'type' parameter given. - * An optional 'target' and 'data' parameter can also be specified. If no target is specified - * events will be sent to the window instead. - * type should be one of: - * - 'event' - Will trigger the given event (specified by 'event') on the target - * - 'callback' - Will call the given method (specified by 'callback') on the target - */ - $(window).on("message", function(e) { - var target, - event = e.originalEvent, - data = typeof event.data === 'object' ? event.data : JSON.parse(event.data); - - // Reject messages outside of the same origin - if($.path.parseUrl(window.location.href).domain !== $.path.parseUrl(event.origin).domain) return; - - // Get target of this action - target = typeof(data.target) === 'undefined' - ? $(window) - : $(data.target); - - // Determine action - switch(data.type) { - case 'event': - target.trigger(data.event, data.data); - break; - case 'callback': - target[data.callback].call(target, data.data); - break; - } - }); - - /** - * 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(); - }; - - // apply an select element only when it is ready, ie. when it is rendered into a template - // with css applied and got a width value. - var applyChosen = function(el) { - if(el.is(':visible')) { - el.addClass('has-chosen').chosen({ - allow_single_deselect: true, - disable_search_threshold: 20, - display_disabled_options: true - }); - } else { - setTimeout(function() { - // Make sure it's visible before applying the ui - el.show(); - applyChosen(el); - }, 500); - } - }; - - /** - * Compare URLs, but normalize trailing slashes in - * URL to work around routing weirdnesses in SS_HTTPRequest. - * Also normalizes relative URLs by prefixing them with the . - */ - var isSameUrl = function(url1, url2) { - var baseUrl = $('base').attr('href'); - url1 = $.path.isAbsoluteUrl(url1) ? url1 : $.path.makeUrlAbsolute(url1, baseUrl), - url2 = $.path.isAbsoluteUrl(url2) ? url2 : $.path.makeUrlAbsolute(url2, baseUrl); - var url1parts = $.path.parseUrl(url1), url2parts = $.path.parseUrl(url2); - return ( - url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') && - url1parts.search == url2parts.search - ); - }; - - var ajaxCompleteEvent = window.ss.debounce(function () { - $(window).trigger('ajaxComplete'); - }, 1000, true); - - $(window).bind('resize', positionLoadingSpinner).trigger('resize'); - - // global ajax handlers - $(document).ajaxComplete(function(e, xhr, settings) { - // Simulates a redirect on an ajax response. - var origUrl, - url = xhr.getResponseHeader('X-ControllerURL'), - destUrl = settings.url, - msg = xhr.getResponseHeader('X-Status') !== null ? xhr.getResponseHeader('X-Status') : xhr.statusText, // Handle custom status message headers - msgType = (xhr.status < 200 || xhr.status > 399) ? 'bad' : 'good', - ignoredMessages = ['OK']; - if(window.history.state) { - origUrl = window.history.state.path; - } else { - origUrl = document.URL; - } - - // Only redirect if controller url differs to the requested or current one - if (url !== null && (!isSameUrl(origUrl, url) || !isSameUrl(destUrl, url))) { - router.show(url, { - id: (new Date()).getTime() + String(Math.random()).replace(/\D/g,''), // Ensure that redirections are followed through by history API by handing it a unique ID - pjax: xhr.getResponseHeader('X-Pjax') ? xhr.getResponseHeader('X-Pjax') : settings.headers['X-Pjax'] - }); - } - - // Enable reauthenticate dialog if requested - if (xhr.getResponseHeader('X-Reauthenticate')) { - $('.cms-container').showLoginDialog(); - return; - } - - // Show message (but ignore aborted requests) - if (xhr.status !== 0 && msg && $.inArray(msg, ignoredMessages)) { - // Decode into UTF-8, HTTP headers don't allow multibyte - statusMessage(decodeURIComponent(msg), msgType); - } - - ajaxCompleteEvent(this); - }); - - /** - * Main LeftAndMain interface with some control panel and an edit form. - * - * Events: - * ajaxsubmit - ... - * validate - ... - * aftersubmitform - ... - */ - $('.cms-container').entwine({ - - /** - * Tracks current panel request. - */ - StateChangeXHR: null, - - /** - * Tracks current fragment-only parallel PJAX requests. - */ - FragmentXHR: {}, - - StateChangeCount: 0, - - /** - * Options for the threeColumnCompressor layout algorithm. - * - * See LeftAndMain.Layout.js for description of these options. - */ - LayoutOptions: { - minContentWidth: 940, - minPreviewWidth: 400, - mode: 'content' - }, - - /** - * Constructor: onmatch - */ - onadd: function() { - var self = this, - basePath = getUrlPath($('base')[0].href); - - // Remove trailing / from base - basePath = basePath.replace(/\/$/, ''); - router.base(basePath); - - // Register all top level routes. - Config.getTopLevelRoutes().forEach((route) => { - router(`/${route}(/*?)?`, (ctx, next) => { - - // If the page isn't ready. - if (document.readyState !== 'complete') { - return next(); - } - - // Load the panel then call the next route. - self.handleStateChange(null, ctx.state) - .done(next); - }); - }); - - router.start(); - - // Browser detection - if($.browser.msie && parseInt($.browser.version, 10) < 8) { - $('.ss-loading-screen').append( - '

' + - 'Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.' + - '

' - ).css('z-index', $('.ss-loading-screen').css('z-index')+1); - $('.loading-animation').remove(); - - this._super(); - return; - } - - // Initialize layouts - this.redraw(); - - // Remove loading screen - $('.ss-loading-screen').hide(); - $('body').removeClass('loading'); - $(window).unbind('resize', positionLoadingSpinner); - this.restoreTabState(); - this._super(); - }, - - fromWindow: { - onstatechange: function(event, historyState){ - this.handleStateChange(event, historyState); - } - }, - - 'onwindowresize': function() { - this.redraw(); - }, - - 'from .cms-panel': { - ontoggle: function(){ this.redraw(); } - }, - - 'from .cms-container': { - onaftersubmitform: function(){ this.redraw(); } - }, - - /** - * Ensure the user can see the requested section - restore the default view. - */ - 'from .cms-menu-list li a': { - onclick: function(e) { - var href = $(e.target).attr('href'); - if(e.which > 1 || href == this._tabStateUrl()) return; - this.splitViewMode(); - } - }, - - /** - * Change the options of the threeColumnCompressor layout, and trigger layouting if needed. - * You can provide any or all options. The remaining options will not be changed. - */ - updateLayoutOptions: function(newSpec) { - var spec = this.getLayoutOptions(); - - var dirty = false; - - for (var k in newSpec) { - if (spec[k] !== newSpec[k]) { - spec[k] = newSpec[k]; - dirty = true; - } - } - - if (dirty) this.redraw(); - }, - - /** - * Enable the split view - with content on the left and preview on the right. - */ - splitViewMode: function() { - this.updateLayoutOptions({ - mode: 'split' - }); - }, - - /** - * Content only. - */ - contentViewMode: function() { - this.updateLayoutOptions({ - mode: 'content' - }); - }, - - /** - * Preview only. - */ - previewMode: function() { - this.updateLayoutOptions({ - mode: 'preview' - }); - }, - - RedrawSuppression: false, - - redraw: function() { - if (this.getRedrawSuppression()) return; - - if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); - - // Reset the algorithm. - this.data('jlayout', jLayout.threeColumnCompressor( - { - menu: this.children('.cms-menu'), - content: this.children('.cms-content'), - preview: this.children('.cms-preview') - }, - this.getLayoutOptions() - )); - - // Trigger layout algorithm once at the top. This also lays out children - we move from outside to - // inside, resizing to fit the parent. - this.layout(); - - // Redraw on all the children that need it - this.find('.cms-panel-layout').redraw(); - this.find('.cms-content-fields[data-layout-type]').redraw(); - this.find('.cms-edit-form[data-layout-type]').redraw(); - this.find('.cms-preview').redraw(); - this.find('.cms-content').redraw(); - }, - - /** - * Confirm whether the current user can navigate away from this page - * - * @param {array} selectors Optional list of selectors - * @returns {boolean} True if the navigation can proceed - */ - checkCanNavigate: function(selectors) { - // Check change tracking (can't use events as we need a way to cancel the current state change) - var contentEls = this._findFragments(selectors || ['Content']), - trackedEls = contentEls - .find(':data(changetracker)') - .add(contentEls.filter(':data(changetracker)')), - safe = true; - - if(!trackedEls.length) { - return true; - } - - trackedEls.each(function() { - // See LeftAndMain.EditForm.js - if(!$(this).confirmUnsavedChanges()) { - safe = false; - } - }); - - return safe; - }, - - /** - * @param string url - * @param string title - New window title. - * @param object data - Any additional data passed through to `window.history.state`. - * @param boolean forceReload - Forces the replacement of the current history state, even if the URL is the same, i.e. allows reloading. - */ - loadPanel: function (url, title = '', data = {}, forceReload, forceReferer = window.history.state.path) { - // Check for unsaved changes - if (!this.checkCanNavigate(data.pjax ? data.pjax.split(',') : ['Content'])) { - return; - } - - this.saveTabState(); - - data.__forceReferer = forceReferer; - - if (forceReload) { - data.__forceReload = Math.random(); // Make sure the page reloads even if the URL is the same. - } - - router.show(url, data); - }, - - /** - * Nice wrapper for reloading current history state. - */ - reloadCurrentPanel: function() { - this.loadPanel(window.history.state.path, null, null, true); - }, - - /** - * Function: submitForm - * - * Parameters: - * {DOMElement} form - The form to be submitted. Needs to be passed - * in to avoid entwine methods/context being removed through replacing the node itself. - * {DOMElement} button - The pressed button (optional) - * {Function} callback - Called in complete() handler of jQuery.ajax() - * {Object} ajaxOptions - Object literal to merge into $.ajax() call - * - * Returns: - * (boolean) - */ - submitForm: function(form, button, callback, ajaxOptions) { - var self = this; - - // look for save button - if(!button) button = this.find('.Actions :submit[name=action_save]'); - // default to first button if none given - simulates browser behaviour - if(!button) button = this.find('.Actions :submit:first'); - - form.trigger('beforesubmitform'); - this.trigger('submitform', {form: form, button: button}); - - // set button to "submitting" state - $(button).addClass('loading'); - - // validate if required - var validationResult = form.validate(); - if(typeof validationResult!=='undefined' && !validationResult) { - // TODO Automatically switch to the tab/position of the first error - statusMessage("Validation failed.", "bad"); - - $(button).removeClass('loading'); - - return false; - } - - // get all data from the form - var formData = form.serializeArray(); - // add button action - formData.push({name: $(button).attr('name'), value:'1'}); - // Artificial HTTP referer, IE doesn't submit them via ajax. - // Also rewrites anchors to their page counterparts, which is important - // as automatic browser ajax response redirects seem to discard the hash/fragment. - // TODO Replaces trailing slashes added by History after locale (e.g. admin/?locale=en/) - formData.push({ name: 'BackURL', value: window.history.state.path.replace(/\/$/, '') }); - - // Save tab selections so we can restore them later - this.saveTabState(); - - // Standard Pjax behaviour is to replace the submitted form with new content. - // The returned view isn't always decided upon when the request - // is fired, so the server might decide to change it based on its own logic, - // sending back different `X-Pjax` headers and content - jQuery.ajax(jQuery.extend({ - headers: {"X-Pjax" : "CurrentForm,Breadcrumbs"}, - url: form.attr('action'), - data: formData, - type: 'POST', - complete: function() { - $(button).removeClass('loading'); - }, - success: function(data, status, xhr) { - form.removeClass('changed'); // TODO This should be using the plugin API - if(callback) callback(data, status, xhr); - - var newContentEls = self.handleAjaxResponse(data, status, xhr); - if(!newContentEls) return; - - newContentEls.filter('form').trigger('aftersubmitform', {status: status, xhr: xhr, formData: formData}); - } - }, ajaxOptions)); - - return false; - }, - - /** - * Last html5 history state - */ - LastState: null, - - /** - * Flag to pause handleStateChange - */ - PauseState: false, - - /** - * Handles ajax loading of new panels through the window.history object. - * To trigger loading, pass a new URL to router.show(). - * Use loadPanel() as a router.show() wrapper as it provides some additional functionality - * like global changetracking and user aborts. - * - * 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 router.show(url, data), 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 router.show() - * - 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. - */ - handleStateChange: function (event, historyState = window.history.state) { - if (this.getPauseState()) { - return; - } - - // Don't allow parallel loading to avoid edge cases - if (this.getStateChangeXHR()) { - this.getStateChangeXHR().abort(); - } - - var self = this, - fragments = historyState.pjax || 'Content', - headers = {}, - fragmentsArr = fragments.split(','), - contentEls = this._findFragments(fragmentsArr); - - this.setStateChangeCount(this.getStateChangeCount() + 1); - - if (!this.checkCanNavigate()) { - var lastState = this.getLastState(); - - // Suppress panel loading while resetting state - this.setPauseState(true); - - // Restore best last state - if (lastState !== null) { - router.show(lastState.url); - } else { - router.back(); - } - - this.setPauseState(false); - - // Abort loading of this panel - return; - } - - this.setLastState(historyState); - - // If any of the requested Pjax fragments don't exist in the current view, - // fetch the "Content" view instead, which is the "outermost" fragment - // that can be reloaded without reloading the whole window. - if (contentEls.length < fragmentsArr.length) { - fragments = 'Content', fragmentsArr = ['Content']; - contentEls = this._findFragments(fragmentsArr); - } - - this.trigger('beforestatechange', { state: historyState, element: contentEls }); - - // Set Pjax headers, which can declare a preference for the returned view. - // The actually returned view isn't always decided upon when the request - // is fired, so the server might decide to change it based on its own logic. - headers['X-Pjax'] = fragments; - - if (typeof historyState.__forceReferer !== 'undefined') { - // Ensure query string is properly encoded if present - let url = historyState.__forceReferer; - - try { - // Prevent double-encoding by attempting to decode - url = decodeURI(url); - } catch(e) { - // URL not encoded, or was encoded incorrectly, so do nothing - } finally { - // Set our referer header to the encoded URL - headers['X-Backurl'] = encodeURI(url); - } - } - - contentEls.addClass('loading'); - - let promise = $.ajax({ - headers: headers, - url: historyState.path - }) - .done((data, status, xhr) => { - var els = self.handleAjaxResponse(data, status, xhr, historyState); - self.trigger('afterstatechange', {data: data, status: status, xhr: xhr, element: els, state: historyState}); - }) - .always(() => { - self.setStateChangeXHR(null); - // Remove loading indication from old content els (regardless of which are replaced) - contentEls.removeClass('loading'); - }); - - this.setStateChangeXHR(promise); - - return promise; - }, - - /** - * ALternative to loadPanel/submitForm. - * - * Triggers a parallel-fetch of a PJAX fragment, which is a separate request to the - * state change requests. There could be any amount of these fetches going on in the background, - * and they don't register as a HTML5 history states. - * - * This is meant for updating a PJAX areas that are not complete panel/form reloads. These you'd - * normally do via submitForm or loadPanel which have a lot of automation built in. - * - * On receiving successful response, the framework will update the element tagged with appropriate - * data-pjax-fragment attribute (e.g. data-pjax-fragment=""). Make sure this element - * is available. - * - * Example usage: - * $('.cms-container').loadFragment('admin/foobar/', 'FragmentName'); - * - * @param url string Relative or absolute url of the controller. - * @param pjaxFragments string PJAX fragment(s), comma separated. - */ - loadFragment: function(url, pjaxFragments) { - - var self = this, - xhr, - headers = {}, - baseUrl = $('base').attr('href'), - fragmentXHR = this.getFragmentXHR(); - - // Make sure only one XHR for a specific fragment is currently in progress. - if( - typeof fragmentXHR[pjaxFragments]!=='undefined' && - fragmentXHR[pjaxFragments]!==null - ) { - fragmentXHR[pjaxFragments].abort(); - fragmentXHR[pjaxFragments] = null; - } - - url = $.path.isAbsoluteUrl(url) ? url : $.path.makeUrlAbsolute(url, baseUrl); - headers['X-Pjax'] = pjaxFragments; - - xhr = $.ajax({ - headers: headers, - url: url, - success: function(data, status, xhr) { - var elements = self.handleAjaxResponse(data, status, xhr, null); - - // We are fully done now, make it possible for others to hook in here. - self.trigger('afterloadfragment', { data: data, status: status, xhr: xhr, elements: elements }); - }, - error: function(xhr, status, error) { - self.trigger('loadfragmenterror', { xhr: xhr, status: status, error: error }); - }, - complete: function() { - // Reset the current XHR in tracking object. - var fragmentXHR = self.getFragmentXHR(); - if( - typeof fragmentXHR[pjaxFragments]!=='undefined' && - fragmentXHR[pjaxFragments]!==null - ) { - fragmentXHR[pjaxFragments] = null; - } - } - }); - - // Store the fragment request so we can abort later, should we get a duplicate request. - fragmentXHR[pjaxFragments] = xhr; - - return xhr; - }, - - /** - * Handles ajax responses containing plain HTML, or mulitple - * PJAX fragments wrapped in JSON (see PjaxResponseNegotiator PHP class). - * Can be hooked into an ajax 'success' callback. - * - * Parameters: - * (Object) data - * (String) status - * (XMLHTTPRequest) xhr - * (Object) state The original history state which the request was initiated with - */ - handleAjaxResponse: function(data, status, xhr, state) { - var self = this, url, selectedTabs, guessFragment, fragment, $data; - - // Support a full reload - if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) { - var baseUrl = $('base').attr('href'), - rawURL = xhr.getResponseHeader('X-ControllerURL'), - url = $.path.isAbsoluteUrl(rawURL) ? rawURL : $.path.makeUrlAbsolute(rawURL, baseUrl); - - document.location.href = url; - return; - } - - // Pseudo-redirects via X-ControllerURL might return empty data, in which - // case we'll ignore the response - if(!data) return; - - // Update title - var title = xhr.getResponseHeader('X-Title'); - if(title) document.title = decodeURIComponent(title.replace(/\+/g, ' ')); - - var newFragments = {}, newContentEls; - // If content type is text/json (ignoring charset and other parameters) - if(xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) { - newFragments = data; - } else { - - // Fall back to replacing the content fragment if HTML is returned - fragment = document.createDocumentFragment(); - - jQuery.clean( [ data ], document, fragment, [] ); - $data = $(jQuery.merge( [], fragment.childNodes )); - - // Try and guess the fragment if none is provided - // TODO: data-pjax-fragment might actually give us the fragment. For now we just check most common case - guessFragment = 'Content'; - if ($data.is('form') && !$data.is('[data-pjax-fragment~=Content]')) guessFragment = 'CurrentForm'; - - newFragments[guessFragment] = $data; - } - - this.setRedrawSuppression(true); - try { - // Replace each fragment individually - $.each(newFragments, function(newFragment, html) { - var contentEl = $('[data-pjax-fragment]').filter(function() { - return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1; - }), newContentEl = $(html); - - // Add to result collection - if(newContentEls) newContentEls.add(newContentEl); - else newContentEls = newContentEl; - - // Update panels - if(newContentEl.find('.cms-container').length) { - throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops'; - } - - // Set loading state and store element state - var origStyle = contentEl.attr('style'); - var origParent = contentEl.parent(); - var origParentLayoutApplied = (typeof origParent.data('jlayout')!=='undefined'); - var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden']; - var elemClasses = contentEl.attr('class'); - var origLayoutClasses = []; - if(elemClasses) { - origLayoutClasses = $.grep( - elemClasses.split(' '), - function(val) { return ($.inArray(val, layoutClasses) >= 0);} - ); - } - - newContentEl - .removeClass(layoutClasses.join(' ')) - .addClass(origLayoutClasses.join(' ')); - if(origStyle) newContentEl.attr('style', origStyle); - - // Allow injection of inline styles, as they're not allowed in the document body. - // Not handling this through jQuery.ondemand to avoid parsing the DOM twice. - var styles = newContentEl.find('style').detach(); - if(styles.length) $(document).find('head').append(styles); - - // Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead) - contentEl.replaceWith(newContentEl); - - // Force jlayout to rebuild internal hierarchy to point to the new elements. - // This is only necessary for elements that are at least 3 levels deep. 2nd level elements will - // be taken care of when we lay out the top level element (.cms-container). - if (!origParent.is('.cms-container') && origParentLayoutApplied) { - origParent.layout(); - } - }); - - // Re-init tabs (in case the form tag itself is a tabset) - var newForm = newContentEls.filter('form'); - if(newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset'); - } - finally { - this.setRedrawSuppression(false); - } - - this.redraw(); - this.restoreTabState((state && typeof state.tabState !== 'undefined') ? state.tabState : null); - - return newContentEls; - }, - - /** - * - * - * Parameters: - * - fragments {Array} - * Returns: jQuery collection - */ - _findFragments: function(fragments) { - return $('[data-pjax-fragment]').filter(function() { - // Allows for more than one fragment per node - var i, nodeFragments = $(this).data('pjaxFragment').split(' '); - for(i in fragments) { - if($.inArray(fragments[i], nodeFragments) != -1) return true; - } - return false; - }); - }, - - /** - * Function: refresh - * - * Updates the container based on the current url - * - * Returns: void - */ - refresh: function() { - $(window).trigger('statechange'); - - $(this).redraw(); - }, - - /** - * Save tab selections in order to reconstruct them later. - * Requires HTML5 sessionStorage support. - */ - saveTabState: function() { - if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return; - - var selectedTabs = [], url = this._tabStateUrl(); - this.find('.cms-tabset,.ss-tabset').each(function(i, el) { - var id = $(el).attr('id'); - if(!id) return; // we need a unique reference - if(!$(el).data('tabs')) return; // don't act on uninit'ed controls - - // Allow opt-out via data element or entwine property. - if($(el).data('ignoreTabState') || $(el).getIgnoreTabState()) return; - - selectedTabs.push({id:id, selected:$(el).tabs('option', 'selected')}); - }); - - if(selectedTabs) { - var tabsUrl = 'tabs-' + url; - try { - window.sessionStorage.setItem(tabsUrl, JSON.stringify(selectedTabs)); - } catch(err) { - if (err.code === DOMException.QUOTA_EXCEEDED_ERR && window.sessionStorage.length === 0) { - // If this fails we ignore the error as the only issue is that it - // does not remember the tab state. - // This is a Safari bug which happens when private browsing is enabled. - return; - } else { - throw err; - } - } - } - }, - - /** - * Re-select previously saved tabs. - * Requires HTML5 sessionStorage support. - * - * Parameters: - * (Object) Map of tab container selectors to tab selectors. - * Used to mark a specific tab as active regardless of the previously saved options. - */ - restoreTabState: function(overrideStates) { - var self = this, url = this._tabStateUrl(), - hasSessionStorage = (typeof(window.sessionStorage)!=="undefined" && window.sessionStorage), - sessionData = hasSessionStorage ? window.sessionStorage.getItem('tabs-' + url) : null, - sessionStates = sessionData ? JSON.parse(sessionData) : false; - - this.find('.cms-tabset, .ss-tabset').each(function() { - var index, tabset = $(this), tabsetId = tabset.attr('id'), tab, - forcedTab = tabset.find('.ss-tabs-force-active'); - - if(!tabset.data('tabs')){ - return; // don't act on uninit'ed controls - } - - // The tabs may have changed, notify the widget that it should update its internal state. - tabset.tabs('refresh'); - - // Make sure the intended tab is selected. - if(forcedTab.length) { - index = forcedTab.index(); - } else if(overrideStates && overrideStates[tabsetId]) { - tab = tabset.find(overrideStates[tabsetId].tabSelector); - if(tab.length){ - index = tab.index(); - } - } else if(sessionStates) { - $.each(sessionStates, function(i, sessionState) { - if(tabset.is('#' + sessionState.id)){ - index = sessionState.selected; - } - }); - } - if(index !== null){ - tabset.tabs('option', 'active', index); - self.trigger('tabstaterestored'); - } - }); - }, - - /** - * Remove any previously saved state. - * - * Parameters: - * (String) url Optional (sanitized) URL to clear a specific state. - */ - clearTabState: function(url) { - if(typeof(window.sessionStorage)=="undefined") return; - - var s = window.sessionStorage; - if(url) { - s.removeItem('tabs-' + url); - } else { - for(var i=0;i
'); - dialog.attr('id', new Date().getTime()); - dialog.data('url', url); - $('body').append(dialog); - } - }); - - // Login dialog page - $('.leftandmain-logindialog').entwine({ - onmatch: function() { - this._super(); - - // Create jQuery dialog - this.ssdialog({ - iframeUrl: this.data('url'), - dialogClass: "leftandmain-logindialog-dialog", - autoOpen: true, - minWidth: 500, - maxWidth: 500, - minHeight: 370, - maxHeight: 400, - closeOnEscape: false, - open: function() { - $('.ui-widget-overlay').addClass('leftandmain-logindialog-overlay'); - }, - close: function() { - $('.ui-widget-overlay').removeClass('leftandmain-logindialog-overlay'); - } - }); - }, - onunmatch: function() { - this._super(); - }, - open: function() { - this.ssdialog('open'); - }, - close: function() { - this.ssdialog('close'); - }, - toggle: function(bool) { - if(this.is(':visible')) this.close(); - else this.open(); - }, - /** - * Callback activated by CMSSecurity_success.ss - */ - reauthenticate: function(data) { - // Replace all SecurityID fields with the given value - if(typeof(data.SecurityID) !== 'undefined') { - $(':input[name=SecurityID]').val(data.SecurityID); - } - // Update TempID for current user - if(typeof(data.TempID) !== 'undefined') { - $('body').data('member-tempid', data.TempID); - } - this.close(); - } - }); - - /** - * Add loading overlay to selected regions in the CMS automatically. - * Not applied to all "*.loading" elements to avoid secondary regions - * like the breadcrumbs showing unnecessary loading status. - */ - $('form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ - onmatch: function() { - this.append('
'); - this._super(); - }, - onunmatch: function() { - this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); - this._super(); - } - }); - - /** Make all buttons "hoverable" with jQuery theming. */ - $('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({ - onadd: function() { - this.addClass('ss-ui-button'); - if(!this.data('button')) this.button(); - this._super(); - }, - onremove: function() { - if(this.data('button')) this.button('destroy'); - this._super(); - } - }); - - /** - * Loads the link's 'href' attribute into a panel via ajax, - * as opposed to triggering a full page reload. - * Little helper to avoid repetition, and make it easy to - * "opt in" to panel loading, while by default links still exhibit their default behaviour. - * The PJAX target can be specified via a 'data-pjax-target' attribute. - */ - $('.cms .cms-panel-link').entwine({ - onclick: function(e) { - if($(this).hasClass('external-link')) { - e.stopPropagation(); - - return; - } - - var href = this.attr('href'), - url = (href && !href.match(/^#/)) ? href : this.data('href'), - data = {pjax: this.data('pjaxTarget')}; - - $('.cms-container').loadPanel(url, null, data); - e.preventDefault(); - } - }); - - /** - * Does an ajax loads of the link's 'href' attribute via ajax and displays any FormResponse messages from the CMS. - * Little helper to avoid repetition, and make it easy to trigger actions via a link, - * without reloading the page, changing the URL, or loading in any new panel content. - */ - $('.cms .ss-ui-button-ajax').entwine({ - onclick: function(e) { - $(this).removeClass('ui-button-text-only'); - $(this).addClass('ss-ui-button-loading ui-button-text-icons'); - - var loading = $(this).find(".ss-ui-loading-icon"); - - if(loading.length < 1) { - loading = $("").addClass('ss-ui-loading-icon ui-button-icon-primary ui-icon'); - - $(this).prepend(loading); - } - - loading.show(); - - var href = this.attr('href'), url = href ? href : this.data('href'); - - jQuery.ajax({ - url: url, - // Ensure that form view is loaded (rather than whole "Content" template) - complete: function(xmlhttp, status) { - var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText; - - try { - if (typeof msg != "undefined" && msg !== null) eval(msg); - } - catch(e) {} - - loading.hide(); - - $(".cms-container").refresh(); - - $(this).removeClass('ss-ui-button-loading ui-button-text-icons'); - $(this).addClass('ui-button-text-only'); - }, - dataType: 'html' - }); - e.preventDefault(); - } - }); - - /** - * Trigger dialogs with iframe based on the links href attribute (see ssui-core.js). - */ - $('.cms .ss-ui-dialog-link').entwine({ - UUID: null, - onmatch: function() { - this._super(); - this.setUUID(new Date().getTime()); - }, - onunmatch: function() { - this._super(); - }, - onclick: function() { - this._super(); - - var self = this, id = 'ss-ui-dialog-' + this.getUUID(); - var dialog = $('#' + id); - if(!dialog.length) { - dialog = $('
'); - $('body').append(dialog); - } - - var extraClass = this.data('popupclass')?this.data('popupclass'):''; - - dialog.ssdialog({iframeUrl: this.attr('href'), autoOpen: true, dialogExtraClass: extraClass}); - return false; - } - }); - - /** - * Add styling to all contained buttons, and create buttonsets if required. - */ - $('.cms-content .Actions').entwine({ - onmatch: function() { - this.find('.ss-ui-button').click(function() { - var form = this.form; - - // forms don't natively store the button they've been triggered with - if(form) { - form.clickedButton = this; - // Reset the clicked button shortly after the onsubmit handlers - // have fired on the form - setTimeout(function() { - form.clickedButton = null; - }, 10); - } - }); - - this.redraw(); - this._super(); - }, - onunmatch: function() { - this._super(); - }, - redraw: function() { - if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); - - // Remove whitespace to avoid gaps with inline elements - this.contents().filter(function() { - return (this.nodeType == 3 && !/\S/.test(this.nodeValue)); - }).remove(); - - // Init buttons if required - this.find('.ss-ui-button').each(function() { - if(!$(this).data('button')) $(this).button(); - }); - - // Mark up buttonsets - this.find('.ss-ui-buttonset').buttonset(); - } - }); - - /** - * 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 .field.date input.text').entwine({ - onmatch: function() { - var holder = $(this).parents('.field.date:first'), config = holder.data(); - if(!config.showcalendar) { - this._super(); - 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(); - }, - onunmatch: function() { - 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 it has a select so - * we can fix the height cropping. - */ - $('.cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown').entwine({ - onmatch: function() { - if(this.is('.no-chosen')) { - this._super(); - return; - } - - // Explicitly disable default placeholder if no custom one is defined - if(!this.data('placeholder')) this.data('placeholder', ' '); - - // We could've gotten stale classes and DOM elements from deferred cache. - this.removeClass('has-chosen').chosen("destroy"); - this.siblings('.chosen-container').remove(); - - // Apply Chosen - applyChosen(this); - - this._super(); - }, - onunmatch: function() { - this._super(); - } - }); - - $(".cms-panel-layout").entwine({ - redraw: function() { - if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); - } - }); - - /** - * Overload the default GridField behaviour (open a new URL in the browser) - * with the CMS-specific ajax loading. - */ - $('.cms .ss-gridfield').entwine({ - showDetailView: function(url) { - // Include any GET parameters from the current URL, as the view state might depend on it. - // For example, a list prefiltered through external search criteria might be passed to GridField. - var params = window.location.search.replace(/^\?/, ''); - if(params) url = $.path.addSearchParams(url, params); - $('.cms-container').loadPanel(url); - } - }); - - - /** - * Generic search form in the CMS, often hooked up to a GridField results display. - */ - $('.cms-search-form').entwine({ - onsubmit: function(e) { - // Remove empty elements and make the URL prettier - var nonEmptyInputs, - url; - - nonEmptyInputs = this.find(':input:not(:submit)').filter(function() { - // Use fieldValue() from jQuery.form plugin rather than jQuery.val(), - // as it handles checkbox values more consistently - var vals = $.grep($(this).fieldValue(), function(val) { return (val);}); - return (vals.length); - }); - - url = this.attr('action'); - - if(nonEmptyInputs.length) { - url = $.path.addSearchParams( - url, - // Undo jQuery's non-standard serialisation - // See https://github.com/jquery/jquery/blob/1.7.2/src/ajax.js#L797 - nonEmptyInputs.serialize().replace('+', '%20') - ); - } - - var container = this.closest('.cms-container'); - container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching - container.loadPanel(url, "", {}, true); - - return false; - } - }); - - /** - * Reset button handler. IE8 does not bubble reset events to - */ - $(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({ - onclick: function(e) { - e.preventDefault(); - - var form = $(this).parents('form'); - - form.clearForm(); - form.find(".dropdown select").prop('selectedIndex', 0).trigger("chosen:updated"); // Reset chosen.js - form.submit(); - } - }); - - /** - * Allows to lazy load a panel, by leaving it empty - * and declaring a URL to load its content via a 'url' HTML5 data attribute. - * The loaded HTML is cached, with cache key being the 'url' attribute. - * In order for this to work consistently, we assume that the responses are stateless. - * To avoid caching, add a 'deferred-no-cache' to the node. - */ - window._panelDeferredCache = {}; - $('.cms-panel-deferred').entwine({ - onadd: function() { - this._super(); - this.redraw(); - }, - onremove: function() { - if(window.debug) console.log('saving', this.data('url'), this); - - // Save the HTML state at the last possible moment. - // Don't store the DOM to avoid memory leaks. - if(!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); - this._super(); - }, - redraw: function() { - if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); - - var self = this, url = this.data('url'); - if(!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; - - this._super(); - - // If the node is empty, try to either load it from cache or via ajax. - if(!this.children().length) { - if(!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { - this.html(window._panelDeferredCache[url]); - } else { - this.addClass('loading'); - $.ajax({ - url: url, - complete: function() { - self.removeClass('loading'); - }, - success: function(data, status, xhr) { - self.html(data); - } - }); - } - } - } - }); - - /** - * Lightweight wrapper around jQuery UI tabs. - * Ensures that anchor links are set properly, - * and any nested tabs are scrolled if they have - * their height explicitly set. This is important - * for forms inside the CMS layout. - */ - $('.cms-tabset').entwine({ - onadd: function() { - // Can't name redraw() as it clashes with other CMS entwine classes - this.redrawTabs(); - this._super(); - }, - onremove: function() { - if (this.data('tabs')) this.tabs('destroy'); - this._super(); - }, - redrawTabs: function() { - this.rewriteHashlinks(); - - var id = this.attr('id'), activeTab = this.find('ul:first .ui-tabs-active'); - - if(!this.data('uiTabs')) this.tabs({ - active: (activeTab.index() != -1) ? activeTab.index() : 0, - beforeLoad: function(e, ui) { - // Disable automatic ajax loading of tabs without matching DOM elements, - // determining if the current URL differs from the tab URL is too error prone. - return false; - }, - activate: function(e, ui) { - // Usability: Hide actions for "readonly" tabs (which don't contain any editable fields) - var actions = $(this).closest('form').find('.Actions'); - if($(ui.newTab).closest('li').hasClass('readonly')) { - actions.fadeOut(); - } else { - actions.show(); - } - } - }); - }, - - /** - * Ensure hash links are prefixed with the current page URL, - * otherwise jQuery interprets them as being external. - */ - rewriteHashlinks: function() { - $(this).find('ul a').each(function() { - if (!$(this).attr('href')) return; - var matches = $(this).attr('href').match(/#.*/); - if(!matches) return; - $(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]); - }); - } - }); - - /** - * CMS content filters - */ - $('#filters-button').entwine({ - onmatch: function () { - this._super(); - - this.data('collapsed', true); // The current collapsed state of the element. - this.data('animating', false); // True if the element is currently animating. - }, - onunmatch: function () { - this._super(); - }, - showHide: function () { - var self = this, - $filters = $('.cms-content-filters').first(), - collapsed = this.data('collapsed'); - - // Prevent the user from spamming the UI with animation requests. - if (this.data('animating')) { - return; - } - - this.toggleClass('active'); - this.data('animating', true); - - // Slide the element down / up based on it's current collapsed state. - $filters[collapsed ? 'slideDown' : 'slideUp']({ - complete: function () { - // Update the element's state. - self.data('collapsed', !collapsed); - self.data('animating', false); - } - }); - }, - onclick: function () { - this.showHide(); - } - }); + /* + * Handle messages sent via nested iframes + * Messages should be raised via postMessage with an object with the 'type' parameter given. + * An optional 'target' and 'data' parameter can also be specified. If no target is specified + * events will be sent to the window instead. + * type should be one of: + * - 'event' - Will trigger the given event (specified by 'event') on the target + * - 'callback' - Will call the given method (specified by 'callback') on the target + */ + $(window).on("message", function(e) { + var target, + event = e.originalEvent, + data = typeof event.data === 'object' ? event.data : JSON.parse(event.data); + + // Reject messages outside of the same origin + if($.path.parseUrl(window.location.href).domain !== $.path.parseUrl(event.origin).domain) return; + + // Get target of this action + target = typeof(data.target) === 'undefined' + ? $(window) + : $(data.target); + + // Determine action + switch(data.type) { + case 'event': + target.trigger(data.event, data.data); + break; + case 'callback': + target[data.callback].call(target, data.data); + break; + } + }); + + /** + * 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(); + }; + + // apply an select element only when it is ready, ie. when it is rendered into a template + // with css applied and got a width value. + var applyChosen = function(el) { + if(el.is(':visible')) { + el.addClass('has-chosen').chosen({ + allow_single_deselect: true, + disable_search_threshold: 20, + display_disabled_options: true + }); + } else { + setTimeout(function() { + // Make sure it's visible before applying the ui + el.show(); + applyChosen(el); + }, 500); + } + }; + + /** + * Compare URLs, but normalize trailing slashes in + * URL to work around routing weirdnesses in SS_HTTPRequest. + * Also normalizes relative URLs by prefixing them with the . + */ + var isSameUrl = function(url1, url2) { + var baseUrl = $('base').attr('href'); + url1 = $.path.isAbsoluteUrl(url1) ? url1 : $.path.makeUrlAbsolute(url1, baseUrl), + url2 = $.path.isAbsoluteUrl(url2) ? url2 : $.path.makeUrlAbsolute(url2, baseUrl); + var url1parts = $.path.parseUrl(url1), url2parts = $.path.parseUrl(url2); + return ( + url1parts.pathname.replace(/\/*$/, '') == url2parts.pathname.replace(/\/*$/, '') && + url1parts.search == url2parts.search + ); + }; + + var ajaxCompleteEvent = window.ss.debounce(function () { + $(window).trigger('ajaxComplete'); + }, 1000, true); + + $(window).bind('resize', positionLoadingSpinner).trigger('resize'); + + // global ajax handlers + $(document).ajaxComplete(function(e, xhr, settings) { + // Simulates a redirect on an ajax response. + var origUrl, + url = xhr.getResponseHeader('X-ControllerURL'), + destUrl = settings.url, + msg = xhr.getResponseHeader('X-Status') !== null ? xhr.getResponseHeader('X-Status') : xhr.statusText, // Handle custom status message headers + msgType = (xhr.status < 200 || xhr.status > 399) ? 'bad' : 'good', + ignoredMessages = ['OK']; + if(window.history.state) { + origUrl = window.history.state.path; + } else { + origUrl = document.URL; + } + + // Only redirect if controller url differs to the requested or current one + if (url !== null && (!isSameUrl(origUrl, url) || !isSameUrl(destUrl, url))) { + router.show(url, { + id: (new Date()).getTime() + String(Math.random()).replace(/\D/g,''), // Ensure that redirections are followed through by history API by handing it a unique ID + pjax: xhr.getResponseHeader('X-Pjax') ? xhr.getResponseHeader('X-Pjax') : settings.headers['X-Pjax'] + }); + } + + // Enable reauthenticate dialog if requested + if (xhr.getResponseHeader('X-Reauthenticate')) { + $('.cms-container').showLoginDialog(); + return; + } + + // Show message (but ignore aborted requests) + if (xhr.status !== 0 && msg && $.inArray(msg, ignoredMessages)) { + // Decode into UTF-8, HTTP headers don't allow multibyte + statusMessage(decodeURIComponent(msg), msgType); + } + + ajaxCompleteEvent(this); + }); + + /** + * Main LeftAndMain interface with some control panel and an edit form. + * + * Events: + * ajaxsubmit - ... + * validate - ... + * aftersubmitform - ... + */ + $('.cms-container').entwine({ + + /** + * Tracks current panel request. + */ + StateChangeXHR: null, + + /** + * Tracks current fragment-only parallel PJAX requests. + */ + FragmentXHR: {}, + + StateChangeCount: 0, + + /** + * Options for the threeColumnCompressor layout algorithm. + * + * See LeftAndMain.Layout.js for description of these options. + */ + LayoutOptions: { + minContentWidth: 940, + minPreviewWidth: 400, + mode: 'content' + }, + + /** + * Constructor: onmatch + */ + onadd: function() { + var self = this, + basePath = getUrlPath($('base')[0].href); + + // Cleanup baseurl + basePath = basePath.replace(/\/$/, ''); // No trailing slash + if(basePath.match(/^[^\/]/)) { + basePath = '/' + basePath; // Mandatory leading slash + } + router.base(basePath); + + // Register all top level routes. + Config.getTopLevelRoutes().forEach((route) => { + router(`/${route}(/*?)?`, (ctx, next) => { + + // If the page isn't ready. + if (document.readyState !== 'complete') { + return next(); + } + + // Load the panel then call the next route. + self.handleStateChange(null, ctx.state) + .done(next); + }); + }); + + router.start(); + + // Browser detection + if($.browser.msie && parseInt($.browser.version, 10) < 8) { + $('.ss-loading-screen').append( + '

' + + 'Your browser is not compatible with the CMS interface. Please use Internet Explorer 8+, Google Chrome or Mozilla Firefox.' + + '

' + ).css('z-index', $('.ss-loading-screen').css('z-index')+1); + $('.loading-animation').remove(); + + this._super(); + return; + } + + // Initialize layouts + this.redraw(); + + // Remove loading screen + $('.ss-loading-screen').hide(); + $('body').removeClass('loading'); + $(window).unbind('resize', positionLoadingSpinner); + this.restoreTabState(); + this._super(); + }, + + fromWindow: { + onstatechange: function(event, historyState){ + this.handleStateChange(event, historyState); + } + }, + + 'onwindowresize': function() { + this.redraw(); + }, + + 'from .cms-panel': { + ontoggle: function(){ this.redraw(); } + }, + + 'from .cms-container': { + onaftersubmitform: function(){ this.redraw(); } + }, + + /** + * Ensure the user can see the requested section - restore the default view. + */ + 'from .cms-menu-list li a': { + onclick: function(e) { + var href = $(e.target).attr('href'); + if(e.which > 1 || href == this._tabStateUrl()) return; + this.splitViewMode(); + } + }, + + /** + * Change the options of the threeColumnCompressor layout, and trigger layouting if needed. + * You can provide any or all options. The remaining options will not be changed. + */ + updateLayoutOptions: function(newSpec) { + var spec = this.getLayoutOptions(); + + var dirty = false; + + for (var k in newSpec) { + if (spec[k] !== newSpec[k]) { + spec[k] = newSpec[k]; + dirty = true; + } + } + + if (dirty) this.redraw(); + }, + + /** + * Enable the split view - with content on the left and preview on the right. + */ + splitViewMode: function() { + this.updateLayoutOptions({ + mode: 'split' + }); + }, + + /** + * Content only. + */ + contentViewMode: function() { + this.updateLayoutOptions({ + mode: 'content' + }); + }, + + /** + * Preview only. + */ + previewMode: function() { + this.updateLayoutOptions({ + mode: 'preview' + }); + }, + + RedrawSuppression: false, + + redraw: function() { + if (this.getRedrawSuppression()) return; + + if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); + + // Reset the algorithm. + this.data('jlayout', jLayout.threeColumnCompressor( + { + menu: this.children('.cms-menu'), + content: this.children('.cms-content'), + preview: this.children('.cms-preview') + }, + this.getLayoutOptions() + )); + + // Trigger layout algorithm once at the top. This also lays out children - we move from outside to + // inside, resizing to fit the parent. + this.layout(); + + // Redraw on all the children that need it + this.find('.cms-panel-layout').redraw(); + this.find('.cms-content-fields[data-layout-type]').redraw(); + this.find('.cms-edit-form[data-layout-type]').redraw(); + this.find('.cms-preview').redraw(); + this.find('.cms-content').redraw(); + }, + + /** + * Confirm whether the current user can navigate away from this page + * + * @param {array} selectors Optional list of selectors + * @returns {boolean} True if the navigation can proceed + */ + checkCanNavigate: function(selectors) { + // Check change tracking (can't use events as we need a way to cancel the current state change) + var contentEls = this._findFragments(selectors || ['Content']), + trackedEls = contentEls + .find(':data(changetracker)') + .add(contentEls.filter(':data(changetracker)')), + safe = true; + + if(!trackedEls.length) { + return true; + } + + trackedEls.each(function() { + // See LeftAndMain.EditForm.js + if(!$(this).confirmUnsavedChanges()) { + safe = false; + } + }); + + return safe; + }, + + /** + * @param string url + * @param string title - New window title. + * @param object data - Any additional data passed through to `window.history.state`. + * @param boolean forceReload - Forces the replacement of the current history state, even if the URL is the same, i.e. allows reloading. + */ + loadPanel: function (url, title = '', data = {}, forceReload, forceReferer = window.history.state.path) { + // Check for unsaved changes + if (!this.checkCanNavigate(data.pjax ? data.pjax.split(',') : ['Content'])) { + return; + } + + this.saveTabState(); + + data.__forceReferer = forceReferer; + + if (forceReload) { + data.__forceReload = Math.random(); // Make sure the page reloads even if the URL is the same. + } + + router.show(url, data); + }, + + /** + * Nice wrapper for reloading current history state. + */ + reloadCurrentPanel: function() { + this.loadPanel(window.history.state.path, null, null, true); + }, + + /** + * Function: submitForm + * + * Parameters: + * {DOMElement} form - The form to be submitted. Needs to be passed + * in to avoid entwine methods/context being removed through replacing the node itself. + * {DOMElement} button - The pressed button (optional) + * {Function} callback - Called in complete() handler of jQuery.ajax() + * {Object} ajaxOptions - Object literal to merge into $.ajax() call + * + * Returns: + * (boolean) + */ + submitForm: function(form, button, callback, ajaxOptions) { + var self = this; + + // look for save button + if(!button) button = this.find('.Actions :submit[name=action_save]'); + // default to first button if none given - simulates browser behaviour + if(!button) button = this.find('.Actions :submit:first'); + + form.trigger('beforesubmitform'); + this.trigger('submitform', {form: form, button: button}); + + // set button to "submitting" state + $(button).addClass('loading'); + + // validate if required + var validationResult = form.validate(); + if(typeof validationResult!=='undefined' && !validationResult) { + // TODO Automatically switch to the tab/position of the first error + statusMessage("Validation failed.", "bad"); + + $(button).removeClass('loading'); + + return false; + } + + // get all data from the form + var formData = form.serializeArray(); + // add button action + formData.push({name: $(button).attr('name'), value:'1'}); + // Artificial HTTP referer, IE doesn't submit them via ajax. + // Also rewrites anchors to their page counterparts, which is important + // as automatic browser ajax response redirects seem to discard the hash/fragment. + // TODO Replaces trailing slashes added by History after locale (e.g. admin/?locale=en/) + formData.push({ name: 'BackURL', value: window.history.state.path.replace(/\/$/, '') }); + + // Save tab selections so we can restore them later + this.saveTabState(); + + // Standard Pjax behaviour is to replace the submitted form with new content. + // The returned view isn't always decided upon when the request + // is fired, so the server might decide to change it based on its own logic, + // sending back different `X-Pjax` headers and content + jQuery.ajax(jQuery.extend({ + headers: {"X-Pjax" : "CurrentForm,Breadcrumbs"}, + url: form.attr('action'), + data: formData, + type: 'POST', + complete: function() { + $(button).removeClass('loading'); + }, + success: function(data, status, xhr) { + form.removeClass('changed'); // TODO This should be using the plugin API + if(callback) callback(data, status, xhr); + + var newContentEls = self.handleAjaxResponse(data, status, xhr); + if(!newContentEls) return; + + newContentEls.filter('form').trigger('aftersubmitform', {status: status, xhr: xhr, formData: formData}); + } + }, ajaxOptions)); + + return false; + }, + + /** + * Last html5 history state + */ + LastState: null, + + /** + * Flag to pause handleStateChange + */ + PauseState: false, + + /** + * Handles ajax loading of new panels through the window.history object. + * To trigger loading, pass a new URL to router.show(). + * Use loadPanel() as a router.show() wrapper as it provides some additional functionality + * like global changetracking and user aborts. + * + * 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 router.show(url, data), 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 router.show() + * - 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. + */ + handleStateChange: function (event, historyState = window.history.state) { + if (this.getPauseState()) { + return; + } + + // Don't allow parallel loading to avoid edge cases + if (this.getStateChangeXHR()) { + this.getStateChangeXHR().abort(); + } + + var self = this, + fragments = historyState.pjax || 'Content', + headers = {}, + fragmentsArr = fragments.split(','), + contentEls = this._findFragments(fragmentsArr); + + this.setStateChangeCount(this.getStateChangeCount() + 1); + + if (!this.checkCanNavigate()) { + var lastState = this.getLastState(); + + // Suppress panel loading while resetting state + this.setPauseState(true); + + // Restore best last state + if (lastState !== null) { + router.show(lastState.url); + } else { + router.back(); + } + + this.setPauseState(false); + + // Abort loading of this panel + return; + } + + this.setLastState(historyState); + + // If any of the requested Pjax fragments don't exist in the current view, + // fetch the "Content" view instead, which is the "outermost" fragment + // that can be reloaded without reloading the whole window. + if (contentEls.length < fragmentsArr.length) { + fragments = 'Content', fragmentsArr = ['Content']; + contentEls = this._findFragments(fragmentsArr); + } + + this.trigger('beforestatechange', { state: historyState, element: contentEls }); + + // Set Pjax headers, which can declare a preference for the returned view. + // The actually returned view isn't always decided upon when the request + // is fired, so the server might decide to change it based on its own logic. + headers['X-Pjax'] = fragments; + + if (typeof historyState.__forceReferer !== 'undefined') { + // Ensure query string is properly encoded if present + let url = historyState.__forceReferer; + + try { + // Prevent double-encoding by attempting to decode + url = decodeURI(url); + } catch(e) { + // URL not encoded, or was encoded incorrectly, so do nothing + } finally { + // Set our referer header to the encoded URL + headers['X-Backurl'] = encodeURI(url); + } + } + + contentEls.addClass('loading'); + + let promise = $.ajax({ + headers: headers, + url: historyState.path + }) + .done((data, status, xhr) => { + var els = self.handleAjaxResponse(data, status, xhr, historyState); + self.trigger('afterstatechange', {data: data, status: status, xhr: xhr, element: els, state: historyState}); + }) + .always(() => { + self.setStateChangeXHR(null); + // Remove loading indication from old content els (regardless of which are replaced) + contentEls.removeClass('loading'); + }); + + this.setStateChangeXHR(promise); + + return promise; + }, + + /** + * ALternative to loadPanel/submitForm. + * + * Triggers a parallel-fetch of a PJAX fragment, which is a separate request to the + * state change requests. There could be any amount of these fetches going on in the background, + * and they don't register as a HTML5 history states. + * + * This is meant for updating a PJAX areas that are not complete panel/form reloads. These you'd + * normally do via submitForm or loadPanel which have a lot of automation built in. + * + * On receiving successful response, the framework will update the element tagged with appropriate + * data-pjax-fragment attribute (e.g. data-pjax-fragment=""). Make sure this element + * is available. + * + * Example usage: + * $('.cms-container').loadFragment('admin/foobar/', 'FragmentName'); + * + * @param url string Relative or absolute url of the controller. + * @param pjaxFragments string PJAX fragment(s), comma separated. + */ + loadFragment: function(url, pjaxFragments) { + + var self = this, + xhr, + headers = {}, + baseUrl = $('base').attr('href'), + fragmentXHR = this.getFragmentXHR(); + + // Make sure only one XHR for a specific fragment is currently in progress. + if( + typeof fragmentXHR[pjaxFragments]!=='undefined' && + fragmentXHR[pjaxFragments]!==null + ) { + fragmentXHR[pjaxFragments].abort(); + fragmentXHR[pjaxFragments] = null; + } + + url = $.path.isAbsoluteUrl(url) ? url : $.path.makeUrlAbsolute(url, baseUrl); + headers['X-Pjax'] = pjaxFragments; + + xhr = $.ajax({ + headers: headers, + url: url, + success: function(data, status, xhr) { + var elements = self.handleAjaxResponse(data, status, xhr, null); + + // We are fully done now, make it possible for others to hook in here. + self.trigger('afterloadfragment', { data: data, status: status, xhr: xhr, elements: elements }); + }, + error: function(xhr, status, error) { + self.trigger('loadfragmenterror', { xhr: xhr, status: status, error: error }); + }, + complete: function() { + // Reset the current XHR in tracking object. + var fragmentXHR = self.getFragmentXHR(); + if( + typeof fragmentXHR[pjaxFragments]!=='undefined' && + fragmentXHR[pjaxFragments]!==null + ) { + fragmentXHR[pjaxFragments] = null; + } + } + }); + + // Store the fragment request so we can abort later, should we get a duplicate request. + fragmentXHR[pjaxFragments] = xhr; + + return xhr; + }, + + /** + * Handles ajax responses containing plain HTML, or mulitple + * PJAX fragments wrapped in JSON (see PjaxResponseNegotiator PHP class). + * Can be hooked into an ajax 'success' callback. + * + * Parameters: + * (Object) data + * (String) status + * (XMLHTTPRequest) xhr + * (Object) state The original history state which the request was initiated with + */ + handleAjaxResponse: function(data, status, xhr, state) { + var self = this, url, selectedTabs, guessFragment, fragment, $data; + + // Support a full reload + if(xhr.getResponseHeader('X-Reload') && xhr.getResponseHeader('X-ControllerURL')) { + var baseUrl = $('base').attr('href'), + rawURL = xhr.getResponseHeader('X-ControllerURL'), + url = $.path.isAbsoluteUrl(rawURL) ? rawURL : $.path.makeUrlAbsolute(rawURL, baseUrl); + + document.location.href = url; + return; + } + + // Pseudo-redirects via X-ControllerURL might return empty data, in which + // case we'll ignore the response + if(!data) return; + + // Update title + var title = xhr.getResponseHeader('X-Title'); + if(title) document.title = decodeURIComponent(title.replace(/\+/g, ' ')); + + var newFragments = {}, newContentEls; + // If content type is text/json (ignoring charset and other parameters) + if(xhr.getResponseHeader('Content-Type').match(/^((text)|(application))\/json[ \t]*;?/i)) { + newFragments = data; + } else { + + // Fall back to replacing the content fragment if HTML is returned + fragment = document.createDocumentFragment(); + + jQuery.clean( [ data ], document, fragment, [] ); + $data = $(jQuery.merge( [], fragment.childNodes )); + + // Try and guess the fragment if none is provided + // TODO: data-pjax-fragment might actually give us the fragment. For now we just check most common case + guessFragment = 'Content'; + if ($data.is('form') && !$data.is('[data-pjax-fragment~=Content]')) guessFragment = 'CurrentForm'; + + newFragments[guessFragment] = $data; + } + + this.setRedrawSuppression(true); + try { + // Replace each fragment individually + $.each(newFragments, function(newFragment, html) { + var contentEl = $('[data-pjax-fragment]').filter(function() { + return $.inArray(newFragment, $(this).data('pjaxFragment').split(' ')) != -1; + }), newContentEl = $(html); + + // Add to result collection + if(newContentEls) newContentEls.add(newContentEl); + else newContentEls = newContentEl; + + // Update panels + if(newContentEl.find('.cms-container').length) { + throw 'Content loaded via ajax is not allowed to contain tags matching the ".cms-container" selector to avoid infinite loops'; + } + + // Set loading state and store element state + var origStyle = contentEl.attr('style'); + var origParent = contentEl.parent(); + var origParentLayoutApplied = (typeof origParent.data('jlayout')!=='undefined'); + var layoutClasses = ['east', 'west', 'center', 'north', 'south', 'column-hidden']; + var elemClasses = contentEl.attr('class'); + var origLayoutClasses = []; + if(elemClasses) { + origLayoutClasses = $.grep( + elemClasses.split(' '), + function(val) { return ($.inArray(val, layoutClasses) >= 0);} + ); + } + + newContentEl + .removeClass(layoutClasses.join(' ')) + .addClass(origLayoutClasses.join(' ')); + if(origStyle) newContentEl.attr('style', origStyle); + + // Allow injection of inline styles, as they're not allowed in the document body. + // Not handling this through jQuery.ondemand to avoid parsing the DOM twice. + var styles = newContentEl.find('style').detach(); + if(styles.length) $(document).find('head').append(styles); + + // Replace panel completely (we need to override the "layout" attribute, so can't replace the child instead) + contentEl.replaceWith(newContentEl); + + // Force jlayout to rebuild internal hierarchy to point to the new elements. + // This is only necessary for elements that are at least 3 levels deep. 2nd level elements will + // be taken care of when we lay out the top level element (.cms-container). + if (!origParent.is('.cms-container') && origParentLayoutApplied) { + origParent.layout(); + } + }); + + // Re-init tabs (in case the form tag itself is a tabset) + var newForm = newContentEls.filter('form'); + if(newForm.hasClass('cms-tabset')) newForm.removeClass('cms-tabset').addClass('cms-tabset'); + } + finally { + this.setRedrawSuppression(false); + } + + this.redraw(); + this.restoreTabState((state && typeof state.tabState !== 'undefined') ? state.tabState : null); + + return newContentEls; + }, + + /** + * + * + * Parameters: + * - fragments {Array} + * Returns: jQuery collection + */ + _findFragments: function(fragments) { + return $('[data-pjax-fragment]').filter(function() { + // Allows for more than one fragment per node + var i, nodeFragments = $(this).data('pjaxFragment').split(' '); + for(i in fragments) { + if($.inArray(fragments[i], nodeFragments) != -1) return true; + } + return false; + }); + }, + + /** + * Function: refresh + * + * Updates the container based on the current url + * + * Returns: void + */ + refresh: function() { + $(window).trigger('statechange'); + + $(this).redraw(); + }, + + /** + * Save tab selections in order to reconstruct them later. + * Requires HTML5 sessionStorage support. + */ + saveTabState: function() { + if(typeof(window.sessionStorage)=="undefined" || window.sessionStorage === null) return; + + var selectedTabs = [], url = this._tabStateUrl(); + this.find('.cms-tabset,.ss-tabset').each(function(i, el) { + var id = $(el).attr('id'); + if(!id) return; // we need a unique reference + if(!$(el).data('tabs')) return; // don't act on uninit'ed controls + + // Allow opt-out via data element or entwine property. + if($(el).data('ignoreTabState') || $(el).getIgnoreTabState()) return; + + selectedTabs.push({id:id, selected:$(el).tabs('option', 'selected')}); + }); + + if(selectedTabs) { + var tabsUrl = 'tabs-' + url; + try { + window.sessionStorage.setItem(tabsUrl, JSON.stringify(selectedTabs)); + } catch(err) { + if (err.code === DOMException.QUOTA_EXCEEDED_ERR && window.sessionStorage.length === 0) { + // If this fails we ignore the error as the only issue is that it + // does not remember the tab state. + // This is a Safari bug which happens when private browsing is enabled. + return; + } else { + throw err; + } + } + } + }, + + /** + * Re-select previously saved tabs. + * Requires HTML5 sessionStorage support. + * + * Parameters: + * (Object) Map of tab container selectors to tab selectors. + * Used to mark a specific tab as active regardless of the previously saved options. + */ + restoreTabState: function(overrideStates) { + var self = this, url = this._tabStateUrl(), + hasSessionStorage = (typeof(window.sessionStorage)!=="undefined" && window.sessionStorage), + sessionData = hasSessionStorage ? window.sessionStorage.getItem('tabs-' + url) : null, + sessionStates = sessionData ? JSON.parse(sessionData) : false; + + this.find('.cms-tabset, .ss-tabset').each(function() { + var index, tabset = $(this), tabsetId = tabset.attr('id'), tab, + forcedTab = tabset.find('.ss-tabs-force-active'); + + if(!tabset.data('tabs')){ + return; // don't act on uninit'ed controls + } + + // The tabs may have changed, notify the widget that it should update its internal state. + tabset.tabs('refresh'); + + // Make sure the intended tab is selected. + if(forcedTab.length) { + index = forcedTab.index(); + } else if(overrideStates && overrideStates[tabsetId]) { + tab = tabset.find(overrideStates[tabsetId].tabSelector); + if(tab.length){ + index = tab.index(); + } + } else if(sessionStates) { + $.each(sessionStates, function(i, sessionState) { + if(tabset.is('#' + sessionState.id)){ + index = sessionState.selected; + } + }); + } + if(index !== null){ + tabset.tabs('option', 'active', index); + self.trigger('tabstaterestored'); + } + }); + }, + + /** + * Remove any previously saved state. + * + * Parameters: + * (String) url Optional (sanitized) URL to clear a specific state. + */ + clearTabState: function(url) { + if(typeof(window.sessionStorage)=="undefined") return; + + var s = window.sessionStorage; + if(url) { + s.removeItem('tabs-' + url); + } else { + for(var i=0;i
'); + dialog.attr('id', new Date().getTime()); + dialog.data('url', url); + $('body').append(dialog); + } + }); + + // Login dialog page + $('.leftandmain-logindialog').entwine({ + onmatch: function() { + this._super(); + + // Create jQuery dialog + this.ssdialog({ + iframeUrl: this.data('url'), + dialogClass: "leftandmain-logindialog-dialog", + autoOpen: true, + minWidth: 500, + maxWidth: 500, + minHeight: 370, + maxHeight: 400, + closeOnEscape: false, + open: function() { + $('.ui-widget-overlay').addClass('leftandmain-logindialog-overlay'); + }, + close: function() { + $('.ui-widget-overlay').removeClass('leftandmain-logindialog-overlay'); + } + }); + }, + onunmatch: function() { + this._super(); + }, + open: function() { + this.ssdialog('open'); + }, + close: function() { + this.ssdialog('close'); + }, + toggle: function(bool) { + if(this.is(':visible')) this.close(); + else this.open(); + }, + /** + * Callback activated by CMSSecurity_success.ss + */ + reauthenticate: function(data) { + // Replace all SecurityID fields with the given value + if(typeof(data.SecurityID) !== 'undefined') { + $(':input[name=SecurityID]').val(data.SecurityID); + } + // Update TempID for current user + if(typeof(data.TempID) !== 'undefined') { + $('body').data('member-tempid', data.TempID); + } + this.close(); + } + }); + + /** + * Add loading overlay to selected regions in the CMS automatically. + * Not applied to all "*.loading" elements to avoid secondary regions + * like the breadcrumbs showing unnecessary loading status. + */ + $('form.loading,.cms-content.loading,.cms-content-fields.loading,.cms-content-view.loading').entwine({ + onmatch: function() { + this.append('
'); + this._super(); + }, + onunmatch: function() { + this.find('.cms-content-loading-overlay,.cms-content-loading-spinner').remove(); + this._super(); + } + }); + + /** Make all buttons "hoverable" with jQuery theming. */ + $('.cms input[type="submit"], .cms button, .cms input[type="reset"], .cms .ss-ui-button').entwine({ + onadd: function() { + this.addClass('ss-ui-button'); + if(!this.data('button')) this.button(); + this._super(); + }, + onremove: function() { + if(this.data('button')) this.button('destroy'); + this._super(); + } + }); + + /** + * Loads the link's 'href' attribute into a panel via ajax, + * as opposed to triggering a full page reload. + * Little helper to avoid repetition, and make it easy to + * "opt in" to panel loading, while by default links still exhibit their default behaviour. + * The PJAX target can be specified via a 'data-pjax-target' attribute. + */ + $('.cms .cms-panel-link').entwine({ + onclick: function(e) { + if($(this).hasClass('external-link')) { + e.stopPropagation(); + + return; + } + + var href = this.attr('href'), + url = (href && !href.match(/^#/)) ? href : this.data('href'), + data = {pjax: this.data('pjaxTarget')}; + + $('.cms-container').loadPanel(url, null, data); + e.preventDefault(); + } + }); + + /** + * Does an ajax loads of the link's 'href' attribute via ajax and displays any FormResponse messages from the CMS. + * Little helper to avoid repetition, and make it easy to trigger actions via a link, + * without reloading the page, changing the URL, or loading in any new panel content. + */ + $('.cms .ss-ui-button-ajax').entwine({ + onclick: function(e) { + $(this).removeClass('ui-button-text-only'); + $(this).addClass('ss-ui-button-loading ui-button-text-icons'); + + var loading = $(this).find(".ss-ui-loading-icon"); + + if(loading.length < 1) { + loading = $("").addClass('ss-ui-loading-icon ui-button-icon-primary ui-icon'); + + $(this).prepend(loading); + } + + loading.show(); + + var href = this.attr('href'), url = href ? href : this.data('href'); + + jQuery.ajax({ + url: url, + // Ensure that form view is loaded (rather than whole "Content" template) + complete: function(xmlhttp, status) { + var msg = (xmlhttp.getResponseHeader('X-Status')) ? xmlhttp.getResponseHeader('X-Status') : xmlhttp.responseText; + + try { + if (typeof msg != "undefined" && msg !== null) eval(msg); + } + catch(e) {} + + loading.hide(); + + $(".cms-container").refresh(); + + $(this).removeClass('ss-ui-button-loading ui-button-text-icons'); + $(this).addClass('ui-button-text-only'); + }, + dataType: 'html' + }); + e.preventDefault(); + } + }); + + /** + * Trigger dialogs with iframe based on the links href attribute (see ssui-core.js). + */ + $('.cms .ss-ui-dialog-link').entwine({ + UUID: null, + onmatch: function() { + this._super(); + this.setUUID(new Date().getTime()); + }, + onunmatch: function() { + this._super(); + }, + onclick: function() { + this._super(); + + var self = this, id = 'ss-ui-dialog-' + this.getUUID(); + var dialog = $('#' + id); + if(!dialog.length) { + dialog = $('
'); + $('body').append(dialog); + } + + var extraClass = this.data('popupclass')?this.data('popupclass'):''; + + dialog.ssdialog({iframeUrl: this.attr('href'), autoOpen: true, dialogExtraClass: extraClass}); + return false; + } + }); + + /** + * Add styling to all contained buttons, and create buttonsets if required. + */ + $('.cms-content .Actions').entwine({ + onmatch: function() { + this.find('.ss-ui-button').click(function() { + var form = this.form; + + // forms don't natively store the button they've been triggered with + if(form) { + form.clickedButton = this; + // Reset the clicked button shortly after the onsubmit handlers + // have fired on the form + setTimeout(function() { + form.clickedButton = null; + }, 10); + } + }); + + this.redraw(); + this._super(); + }, + onunmatch: function() { + this._super(); + }, + redraw: function() { + if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); + + // Remove whitespace to avoid gaps with inline elements + this.contents().filter(function() { + return (this.nodeType == 3 && !/\S/.test(this.nodeValue)); + }).remove(); + + // Init buttons if required + this.find('.ss-ui-button').each(function() { + if(!$(this).data('button')) $(this).button(); + }); + + // Mark up buttonsets + this.find('.ss-ui-buttonset').buttonset(); + } + }); + + /** + * 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 .field.date input.text').entwine({ + onmatch: function() { + var holder = $(this).parents('.field.date:first'), config = holder.data(); + if(!config.showcalendar) { + this._super(); + 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(); + }, + onunmatch: function() { + 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 it has a select so + * we can fix the height cropping. + */ + $('.cms .field.dropdown select, .cms .field select[multiple], .fieldholder-small select.dropdown').entwine({ + onmatch: function() { + if(this.is('.no-chosen')) { + this._super(); + return; + } + + // Explicitly disable default placeholder if no custom one is defined + if(!this.data('placeholder')) this.data('placeholder', ' '); + + // We could've gotten stale classes and DOM elements from deferred cache. + this.removeClass('has-chosen').chosen("destroy"); + this.siblings('.chosen-container').remove(); + + // Apply Chosen + applyChosen(this); + + this._super(); + }, + onunmatch: function() { + this._super(); + } + }); + + $(".cms-panel-layout").entwine({ + redraw: function() { + if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); + } + }); + + /** + * Overload the default GridField behaviour (open a new URL in the browser) + * with the CMS-specific ajax loading. + */ + $('.cms .ss-gridfield').entwine({ + showDetailView: function(url) { + // Include any GET parameters from the current URL, as the view state might depend on it. + // For example, a list prefiltered through external search criteria might be passed to GridField. + var params = window.location.search.replace(/^\?/, ''); + if(params) url = $.path.addSearchParams(url, params); + $('.cms-container').loadPanel(url); + } + }); + + + /** + * Generic search form in the CMS, often hooked up to a GridField results display. + */ + $('.cms-search-form').entwine({ + onsubmit: function(e) { + // Remove empty elements and make the URL prettier + var nonEmptyInputs, + url; + + nonEmptyInputs = this.find(':input:not(:submit)').filter(function() { + // Use fieldValue() from jQuery.form plugin rather than jQuery.val(), + // as it handles checkbox values more consistently + var vals = $.grep($(this).fieldValue(), function(val) { return (val);}); + return (vals.length); + }); + + url = this.attr('action'); + + if(nonEmptyInputs.length) { + url = $.path.addSearchParams( + url, + // Undo jQuery's non-standard serialisation + // See https://github.com/jquery/jquery/blob/1.7.2/src/ajax.js#L797 + nonEmptyInputs.serialize().replace('+', '%20') + ); + } + + var container = this.closest('.cms-container'); + container.find('.cms-edit-form').tabs('select',0); //always switch to the first tab (list view) when searching + container.loadPanel(url, "", {}, true); + + return false; + } + }); + + /** + * Reset button handler. IE8 does not bubble reset events to + */ + $(".cms-search-form button[type=reset], .cms-search-form input[type=reset]").entwine({ + onclick: function(e) { + e.preventDefault(); + + var form = $(this).parents('form'); + + form.clearForm(); + form.find(".dropdown select").prop('selectedIndex', 0).trigger("chosen:updated"); // Reset chosen.js + form.submit(); + } + }); + + /** + * Allows to lazy load a panel, by leaving it empty + * and declaring a URL to load its content via a 'url' HTML5 data attribute. + * The loaded HTML is cached, with cache key being the 'url' attribute. + * In order for this to work consistently, we assume that the responses are stateless. + * To avoid caching, add a 'deferred-no-cache' to the node. + */ + window._panelDeferredCache = {}; + $('.cms-panel-deferred').entwine({ + onadd: function() { + this._super(); + this.redraw(); + }, + onremove: function() { + if(window.debug) console.log('saving', this.data('url'), this); + + // Save the HTML state at the last possible moment. + // Don't store the DOM to avoid memory leaks. + if(!this.data('deferredNoCache')) window._panelDeferredCache[this.data('url')] = this.html(); + this._super(); + }, + redraw: function() { + if(window.debug) console.log('redraw', this.attr('class'), this.get(0)); + + var self = this, url = this.data('url'); + if(!url) throw 'Elements of class .cms-panel-deferred need a "data-url" attribute'; + + this._super(); + + // If the node is empty, try to either load it from cache or via ajax. + if(!this.children().length) { + if(!this.data('deferredNoCache') && typeof window._panelDeferredCache[url] !== 'undefined') { + this.html(window._panelDeferredCache[url]); + } else { + this.addClass('loading'); + $.ajax({ + url: url, + complete: function() { + self.removeClass('loading'); + }, + success: function(data, status, xhr) { + self.html(data); + } + }); + } + } + } + }); + + /** + * Lightweight wrapper around jQuery UI tabs. + * Ensures that anchor links are set properly, + * and any nested tabs are scrolled if they have + * their height explicitly set. This is important + * for forms inside the CMS layout. + */ + $('.cms-tabset').entwine({ + onadd: function() { + // Can't name redraw() as it clashes with other CMS entwine classes + this.redrawTabs(); + this._super(); + }, + onremove: function() { + if (this.data('tabs')) this.tabs('destroy'); + this._super(); + }, + redrawTabs: function() { + this.rewriteHashlinks(); + + var id = this.attr('id'), activeTab = this.find('ul:first .ui-tabs-active'); + + if(!this.data('uiTabs')) this.tabs({ + active: (activeTab.index() != -1) ? activeTab.index() : 0, + beforeLoad: function(e, ui) { + // Disable automatic ajax loading of tabs without matching DOM elements, + // determining if the current URL differs from the tab URL is too error prone. + return false; + }, + activate: function(e, ui) { + // Usability: Hide actions for "readonly" tabs (which don't contain any editable fields) + var actions = $(this).closest('form').find('.Actions'); + if($(ui.newTab).closest('li').hasClass('readonly')) { + actions.fadeOut(); + } else { + actions.show(); + } + } + }); + }, + + /** + * Ensure hash links are prefixed with the current page URL, + * otherwise jQuery interprets them as being external. + */ + rewriteHashlinks: function() { + $(this).find('ul a').each(function() { + if (!$(this).attr('href')) return; + var matches = $(this).attr('href').match(/#.*/); + if(!matches) return; + $(this).attr('href', document.location.href.replace(/#.*/, '') + matches[0]); + }); + } + }); + + /** + * CMS content filters + */ + $('#filters-button').entwine({ + onmatch: function () { + this._super(); + + this.data('collapsed', true); // The current collapsed state of the element. + this.data('animating', false); // True if the element is currently animating. + }, + onunmatch: function () { + this._super(); + }, + showHide: function () { + var self = this, + $filters = $('.cms-content-filters').first(), + collapsed = this.data('collapsed'); + + // Prevent the user from spamming the UI with animation requests. + if (this.data('animating')) { + return; + } + + this.toggleClass('active'); + this.data('animating', true); + + // Slide the element down / up based on it's current collapsed state. + $filters[collapsed ? 'slideDown' : 'slideUp']({ + complete: function () { + // Update the element's state. + self.data('collapsed', !collapsed); + self.data('animating', false); + } + }); + }, + onclick: function () { + this.showHide(); + } + }); }); var statusMessage = function(text, type) { - text = jQuery('
').text(text).html(); // Escape HTML entities in text - jQuery.noticeAdd({text: text, type: type, stayTime: 5000, inEffect: {left: '0', opacity: 'show'}}); + text = jQuery('
').text(text).html(); // Escape HTML entities in text + jQuery.noticeAdd({text: text, type: type, stayTime: 5000, inEffect: {left: '0', opacity: 'show'}}); }; var errorMessage = function(text) { - jQuery.noticeAdd({text: text, type: 'error', stayTime: 5000, inEffect: {left: '0', opacity: 'show'}}); + jQuery.noticeAdd({text: text, type: 'error', stayTime: 5000, inEffect: {left: '0', opacity: 'show'}}); };