diff --git a/modules/cms/assets/js/october.cmspage.js b/modules/cms/assets/js/october.cmspage.js index da2ae5528..99787561a 100644 --- a/modules/cms/assets/js/october.cmspage.js +++ b/modules/cms/assets/js/october.cmspage.js @@ -2,649 +2,683 @@ * Scripts for the CMS page. */ +function ($) { "use strict"; - $(document).ready(function(){ + + var Base = $.oc.foundation.base, + BaseProto = Base.prototype + + var CmsPage = function() { + + Base.call(this) + + // + // Initialization + // + + this.init() + } + + CmsPage.prototype = Object.create(BaseProto) + CmsPage.prototype.constructor = CmsPage + + CmsPage.prototype.init = function() { + $(document).ready(this.proxy(this.registerHandlers)) + } + + CmsPage.prototype.updateTemplateList = function(type) { + var $form = $('#cms-side-panel form[data-template-type='+type+']'), + templateList = type + 'List' + + $form.request(templateList + '::onUpdate', { + complete: function() { + $('button[data-control=delete-template]', $form).trigger('oc.triggerOn.update') + } + }) + } + + CmsPage.prototype.registerHandlers = function() { + var $document = $(document), + $masterTabs = $('#cms-master-tabs'), + ajaxSuccessBound = this.proxy(this.onAjaxSuccess) + + $masterTabs.on('closed.oc.tab', this.proxy(this.onTabClosed)) + $masterTabs.on('beforeClose.oc.tab', this.proxy(this.onBeforeTabClose)) + $masterTabs.on('oc.beforeRequest', this.proxy(this.onBeforeRequest)) + $masterTabs.on('shown.bs.tab', this.proxy(this.onTabShown)) + $masterTabs.on('initTab.oc.tab', this.proxy(this.onInitTab)) + $masterTabs.on('afterAllClosed.oc.tab', this.proxy(this.onAfterAllTabsClosed)) + + $(window).on('ajaxInvalidField', this.proxy(this.ajaxInvalidField)) + $document.on('open.oc.list', '#cms-side-panel', this.proxy(this.onOpenDocument)) + $document.on('ajaxUpdate', '[data-control=filelist], [data-control=assetlist]', this.proxy(this.onAjaxUpdate)) + $document.on('ajaxSuccess', '#cms-master-tabs form', function(event, context, data){ + $document.on('ajaxError', '#cms-master-tabs form', this.proxy(this.onAjaxError)) + $document.on('click', '#cms-side-panel form button[data-control=create-template], #cms-side-panel form li a[data-control=create-template]', this.proxy(this.onCreateTemplateClick)) + $document.on('click', '#cms-side-panel form button[data-control=delete-template]', this.proxy(this.onDeleteTemplateClick)) + $document.on('showing.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorShowing)) + $document.on('hidden.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHidden)) + $document.on('hiding.oc.inspector', '[data-inspectable]', this.proxy(this.onInspectorHiding)) + $document.on('click', '#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist a.remove', this.proxy(this.onComponentRemove)) + $document.on('click', '#cms-component-list [data-component]', this.proxy(this.onComponentClick)) + ajaxSuccessBound(this, event, context, data) + }) + } + + // EVENT HANDLERS + // ============================ + + CmsPage.prototype.onOpenDocument = function(e) { /* - * Listen for the "open.oc.list" event in the template lists + * Open a document when it's clicked in the sidebar */ - $(document).on('open.oc.list', '#cms-side-panel', function(e) { - var - $item = $(e.relatedTarget), - $form = $item.closest('[data-template-type]'), - data = { - type: $form.data('template-type'), - theme: $item.data('item-theme'), - path: $item.data('item-path') - }, - tabId = data.type + '-' + data.theme + '-' + data.path - if (data.type == 'asset' && $item.data('editable') === undefined) - return true + var $item = $(e.relatedTarget), + $form = $item.closest('[data-template-type]'), + data = { + type: $form.data('template-type'), + theme: $item.data('item-theme'), + path: $item.data('item-path') + }, + tabId = data.type + '-' + data.theme + '-' + data.path - if ($form.length == 0) - return false - - /* - * Find if the tab is already opened - */ - if ($('#cms-master-tabs').data('oc.tab').goTo(tabId)) - return false - - /* - * Open a new tab - */ - $.oc.stripeLoadIndicator.show() - $form.request('onOpenTemplate', { - data: data, - success: function(data) { - this.success(data).done(function(){ - $.oc.stripeLoadIndicator.hide() - $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon')) - }).always(function(){ - $.oc.stripeLoadIndicator.hide() - }) - }, - error: function(jqXHR, textStatus, errorThrown) { - alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) - $.oc.stripeLoadIndicator.hide() - } - }) + if (data.type == 'asset' && $item.data('editable') === undefined) + return true + if ($form.length == 0) return false + + /* + * Find if the tab is already opened + */ + if ($('#cms-master-tabs').data('oc.tab').goTo(tabId)) + return false + + /* + * Open a new tab + */ + $.oc.stripeLoadIndicator.show() + $form.request('onOpenTemplate', { + data: data, + success: function(data) { + this.success(data).done(function(){ + $.oc.stripeLoadIndicator.hide() + $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon')) + }).always(function(){ + $.oc.stripeLoadIndicator.hide() + }) + }, + error: function(jqXHR, textStatus, errorThrown) { + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + $.oc.stripeLoadIndicator.hide() + } }) + return false + } + + CmsPage.prototype.ajaxInvalidField = function(event, element, name, messages, isFirst) { /* * Detect invalid fields, uncollapse the panel */ - $(window).on('ajaxInvalidField', function(event, element, name, messages, isFirst){ - if (!isFirst) return - event.preventDefault() - var $panel = element.closest('.form-tabless-fields.collapsed'), - $primaryPanel = element.closest('.control-tabs.primary.collapsed') + if (!isFirst) return + event.preventDefault() - if ($panel.length > 0) - $panel.removeClass('collapsed') + var $panel = element.closest('.form-tabless-fields.collapsed'), + $primaryPanel = element.closest('.control-tabs.primary.collapsed') - if ($primaryPanel.length > 0) { - $primaryPanel.removeClass('collapsed') + if ($panel.length > 0) + $panel.removeClass('collapsed') - var pane = $primaryPanel.closest('.tab-pane'), - $secondaryPanel = $('.control-tabs.secondary', pane) + if ($primaryPanel.length > 0) { + $primaryPanel.removeClass('collapsed') - $secondaryPanel.removeClass('primary-collapsed') - } + var pane = $primaryPanel.closest('.tab-pane'), + $secondaryPanel = $('.control-tabs.secondary', pane) - element.focus() - }) + $secondaryPanel.removeClass('primary-collapsed') + } - /* - * Listen for the closing events - */ - $('#cms-master-tabs').on('closed.oc.tab', function(event){ - updateModifiedCounter() + element.focus() + } - if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0) - setPageTitle('') - }) + CmsPage.prototype.onTabClosed = function(event) { + this.updateModifiedCounter() - $('#cms-master-tabs').on('beforeClose.oc.tab', function(event){ - // Dispose data table widgets + if ($('> div.tab-content > div.tab-pane', '#cms-master-tabs').length == 0) + this.setPageTitle('') + } - if ($.fn.table !== undefined) - $('[data-control=table]', event.relatedTarget).table('dispose') - }) + CmsPage.prototype.onBeforeTabClose = function(event) { + if ($.fn.table !== undefined) + $('[data-control=table]', event.relatedTarget).table('dispose') + } - /* - * Listen for the onBeforeRequest event - */ - $('#cms-master-tabs').on('oc.beforeRequest', function(event) { - var $form = $(event.target) + CmsPage.prototype.onBeforeRequest = function(event) { + var $form = $(event.target) - if ( $('.components .layout-cell.error-component', $form).length > 0) { - if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?')) - event.preventDefault() - } - }) + if ($('.components .layout-cell.error-component', $form).length > 0) { + if (!confirm('The form contains unknown components. Their properties will be lost on save. Do you want to save the form?')) + event.preventDefault() + } + } + CmsPage.prototype.onTabShown = function(event) { /* * Listen for the tabs "shown" event to track the current template in the list */ - $('#cms-master-tabs').on('shown.bs.tab', function(event){ - if ($(event.target).closest('[data-control=tab]').attr('id') != 'cms-master-tabs') - return - var dataId = $(event.target).closest('li').attr('data-tab-id') + if ($(event.target).closest('[data-control=tab]').attr('id') != 'cms-master-tabs') + return - var title = $(event.target).attr('title') - if (title) - setPageTitle(title) + var dataId = $(event.target).closest('li').attr('data-tab-id') - $('#cms-side-panel [data-control=filelist]').fileList('markActive', dataId) - $('#cms-side-panel form').trigger('oc.list.setActiveItem', [dataId]) - }) + var title = $(event.target).attr('title') + if (title) + this.setPageTitle(title) + $('#cms-side-panel [data-control=filelist]').fileList('markActive', dataId) + $('#cms-side-panel form').trigger('oc.list.setActiveItem', [dataId]) + } + + CmsPage.prototype.onInitTab = function(event, data) { /* * Listen for the tabs "initTab" event to inject extra controls to the tab */ - $('#cms-master-tabs').on('initTab.oc.tab', function(event, data){ - if ($(event.target).attr('id') != 'cms-master-tabs') - return - var $collapseIcon = $('') - var $panel = $('.form-tabless-fields', data.pane) - $panel.append($collapseIcon); + if ($(event.target).attr('id') != 'cms-master-tabs') + return - $collapseIcon.click(function(){ - $panel.toggleClass('collapsed') + var $collapseIcon = $('') + var $panel = $('.form-tabless-fields', data.pane) + $panel.append($collapseIcon); - if (typeof(localStorage) !== 'undefined') - localStorage.ocCmsTablessCollapsed = $panel.hasClass('collapsed') ? 1 : 0 + $collapseIcon.click(function(){ + $panel.toggleClass('collapsed') - window.setTimeout(function(){ - $(window).trigger('oc.updateUi') - }, 500) + if (typeof(localStorage) !== 'undefined') + localStorage.ocCmsTablessCollapsed = $panel.hasClass('collapsed') ? 1 : 0 - return false - }) + window.setTimeout(function(){ + $(window).trigger('oc.updateUi') + }, 500) - var $primaryCollapseIcon = $('') - var $primaryPanel = $('.control-tabs.primary', data.pane) - var $secondaryPanel = $('.control-tabs.secondary', data.pane) - var $primaryTabContainer = $('.nav-tabs', $primaryPanel) - - $primaryTabContainer.addClass('master-area') - - if ($primaryPanel.length > 0) { - $secondaryPanel.append($primaryCollapseIcon); - - $primaryCollapseIcon.click(function(){ - $primaryPanel.toggleClass('collapsed') - $secondaryPanel.toggleClass('primary-collapsed') - $(window).trigger('oc.updateUi') - if (typeof(localStorage) !== 'undefined') - localStorage.ocCmsPrimaryCollapsed = $primaryPanel.hasClass('collapsed') ? 1 : 0 - return false - }) - } - - if (typeof(localStorage) !== 'undefined') { - if (!$('a', data.tab).hasClass('new-template') && localStorage.ocCmsTablessCollapsed == 1) - $panel.addClass('collapsed') - - if (localStorage.ocCmsPrimaryCollapsed == 1) { - $primaryPanel.addClass('collapsed') - $secondaryPanel.addClass('primary-collapsed') - } - } - - var $componentListFormGroup = $('.control-componentlist', data.pane).closest('.form-group') - if ($primaryPanel.length > 0) - $primaryPanel.before($componentListFormGroup) - else - $secondaryPanel.parent().before($componentListFormGroup) - - $componentListFormGroup.removeClass() - $componentListFormGroup.addClass('layout-row min-size') - updateComponentListClass(data.pane) - updateFormEditorMode(data.pane, true) - - var $form = $('form', data.pane) - - $form.on('changed.oc.changeMonitor', function() { - $panel.trigger('modified.oc.tab') - updateModifiedCounter() - }) - - $form.on('unchanged.oc.changeMonitor', function() { - $panel.trigger('unmodified.oc.tab') - updateModifiedCounter() - }) - - addTokenExpanderToEditor(data.pane, $form) - }) - - /* - * Listen for the "afterAllClosed.oc.tab" event in the master tabs widget - */ - $('#cms-master-tabs').on('afterAllClosed.oc.tab', function(event){ - $('#cms-side-panel [data-control=filelist]').fileList('markActive', null) - $('#cms-side-panel form').trigger('oc.list.setActiveItem', [null]) - }) - - /* - * Listen for AJAX updates of the list views - */ - $(document).on('ajaxUpdate', '[data-control=filelist], [data-control=assetlist]', function(event){ - var dataId = $('#cms-master-tabs .nav-tabs li.active').attr('data-tab-id') - $('#cms-side-panel [data-control=filelist]').fileList('markActive', dataId) - $('#cms-side-panel form').trigger('oc.list.setActiveItem', [dataId]) - }) - - /* - * Listen for the template forms AJAX success event - */ - $(document).on('ajaxSuccess', '#cms-master-tabs form', function(event, context, data){ - if (data.templatePath !== undefined) { - $('input[name=templatePath]', this).val(data.templatePath) - $('input[name=templateMtime]', this).val(data.templateMtime) - $('[data-control=delete-button]', this).removeClass('hide') - $('[data-control=preview-button]', this).removeClass('hide') - - if (data.pageUrl !== undefined) - $('[data-control=preview-button]', this).attr('href', data.pageUrl) - } - - if (data.tabTitle !== undefined) { - $('#cms-master-tabs').ocTab('updateTitle', $(this).closest('.tab-pane'), data.tabTitle) - setPageTitle(data.tabTitle) - } - - var tabId = $('input[name=templateType]', this).val() + '-' - + $('input[name=theme]', this).val() + '-' - + $('input[name=templatePath]', this).val(); - - $('#cms-master-tabs').ocTab('updateIdentifier', $(this).closest('.tab-pane'), tabId) - - var templateType = $('input[name=templateType]', this).val() - if (templateType.length > 0) { - $.oc.cmsPage.updateTemplateList(templateType) - - if (templateType == 'layout') - updateLayouts(this) - } - - updateFormEditorMode($(this).closest('.tab-pane'), false) - - if (context.handler == 'onSave' && (!data['X_OCTOBER_ERROR_FIELDS'] && !data['X_OCTOBER_ERROR_MESSAGE'])) { - $(this).trigger('unchange.oc.changeMonitor') - } - }) - - $(document).on('ajaxError', '#cms-master-tabs form', function(event, context, data, jqXHR){ - if (context.handler == 'onSave') { - if (jqXHR.responseText == 'mtime-mismatch') { - event.preventDefault() - handleMtimeMismatch(this) - } - } - }) - - /* - * Listen for the oc.createTemplate event - */ - $(document).on('click', '#cms-side-panel form button[data-control=create-template], #cms-side-panel form li a[data-control=create-template]', function(event){ - var - $form = $(this).closest('[data-template-type]'), - type = $form.data('template-type'), - tabId = type + Math.random() - - $.oc.stripeLoadIndicator.show() - $form.request('onCreateTemplate', { - data: { - type: type - }, - success: function(data) { - this.success(data).done(function(){ - $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template') - $('#layout-side-panel').trigger('close.oc.sidePanel') - setPageTitle(data.tabTitle) - }).always(function(){ - $.oc.stripeLoadIndicator.hide() - }) - } - }) - }) - - /* - * Listen for the oc.deleteTemplate event - */ - $(document).on('click', '#cms-side-panel form button[data-control=delete-template]', function(event){ - var - $el = $(this), - $form = $el.closest('form'), - templateType = $form.data('template-type') - - if (!confirm($el.data('confirmation'))) - return - - $form.request('onDeleteTemplates', { - data: { - type: templateType - }, - success: function(data) { - var tabs = $('#cms-master-tabs').data('oc.tab'); - $.each(data.deleted, function(index, path){ - var - tabId = templateType + '-' + data.theme + '-' + path, - tab = tabs.findByIdentifier(tabId) - - $('#cms-master-tabs').ocTab('closeTab', tab, true) - }) - - if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length) - $.oc.flashMsg({text: data.error, 'class': 'error'}) - }, - complete: function() { - $.oc.cmsPage.updateTemplateList(templateType) - } - }) - }) - - /* - * Listen for the showing.oc.inspector event on the components - */ - $(document).on('showing.oc.inspector', '[data-inspectable]', function(ev, data) { - $(ev.currentTarget).closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement(ev.currentTarget, data.callback) - - ev.stopPropagation() - }) - - /* - * Listen for the hidden.oc.inspector event on the components - */ - $(document).on('hidden.oc.inspector', '[data-inspectable]', function() { - var values = $.parseJSON($('[data-inspector-values]', this).val()) - - $('[name="component_aliases[]"]', this).val(values['oc.alias']) - $('span.alias', this).text(values['oc.alias']) - }) - - /* - * Listen for the hiding.oc.inspector event on the components - */ - $(document).on('hiding.oc.inspector', '[data-inspectable]', function(e, values) { - var values = $.parseJSON($('[data-inspector-values]', this).val()), - alias = values['oc.alias'], - $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout'), - $cell = $(e.target).parent() - - $('div.layout-cell', $componentList).each(function(){ - if ($cell.get(0) == this) - return true - - var $input = $('input[name="component_aliases[]"]', this) - - if ($input.val() == alias) { - e.preventDefault() - alert('The component alias "'+alias+'" is already used.') - return false - } - }) - }) - - function updateComponentListClass(pane) { - var $componentList = $('.control-componentlist', pane), - $primaryPanel = $('.control-tabs.primary', pane), - $primaryTabContainer = $('.nav-tabs', $primaryPanel), - hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0 - - $primaryTabContainer.toggleClass('component-area', hasComponents) - $componentList.toggleClass('has-components', hasComponents) - } - - function updateFormEditorMode(pane, initialization) { - var $contentTypeElement = $('[data-toolbar-type]', pane) - if ($contentTypeElement.length == 0) - return - - if ($contentTypeElement.data('toolbar-type') != 'content') - return - - var fileName = $('input[name=fileName]', pane).val(), - parts = fileName.split('.'), - extension = 'txt', - mode = 'plain_text', - modes = $.oc.codeEditorExtensionModes, - editor = $('[data-control=codeeditor]', pane) - - if (parts.length >= 2) - extension = parts.pop().toLowerCase() - - if (modes[extension] !== undefined) - mode = modes[extension]; - - var setEditorMode = function() { - window.setTimeout(function(){ - editor.data('oc.codeEditor').editor.getSession().setMode({path: 'ace/mode/'+mode}) - }, 200) - } - - if (initialization) - editor.on('oc.codeEditorReady', setEditorMode) - else - setEditorMode() - } - - function updateModifiedCounter() { - var counters = { - page: {menu: 'pages', count: 0}, - partial: {menu: 'partials', count: 0}, - layout: {menu: 'layouts', count: 0}, - content: {menu: 'content', count: 0}, - asset:{menu: 'assets', count: 0} - } - - $('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function(){ - var inputType = $('> form > input[name=templateType]', this).val() - counters[inputType].count++ - }) - - $.each(counters, function(type, data){ - $.oc.sideNav.setCounter('cms/' + data.menu, data.count); - }) - } - - function addTokenExpanderToEditor(pane, $form) { - var group = $('[data-field-name=markup]', pane), - editor = $('[data-control=codeeditor]', group), - canExpand = false - - if (!editor.length || editor.data('oc.tokenexpander')) - return - - var toolbar = editor.codeEditor('getToolbar') - - editor.tokenExpander() - - var breakButton = $('
  • ').prop({ 'class': 'tokenexpander-button' }).append( - $('').prop({ 'href': 'javascript:; '}).append( - $('').prop({ 'class': 'icon-code-fork' }) - ) - ) - - breakButton.hide().on('click', function(){ - handleExpandToken(editor, $form) - return false - }) - - $('ul:first', toolbar).prepend(breakButton) - - editor - .on('show.oc.tokenexpander', function(){ - canExpand = true - breakButton.show() - }) - .on('hide.oc.tokenexpander', function(){ - canExpand = false - breakButton.hide() - }) - .on('dblclick', function(e){ - if ((e.metaKey || e.ctrlKey) && canExpand) { - handleExpandToken(editor, $form) - } - }) - } - - function handleExpandToken(editor, $form) { - editor.tokenExpander('expandToken', function(token, value){ - return $form.request('onExpandMarkupToken', { - data: { tokenType: token, tokenName: value } - }) - }) - } - - function handleMtimeMismatch(form) { - var $form = $(form) - $form.popup({ handler: 'onOpenConcurrencyResolveForm' }) - - var popup = $form.data('oc.popup') - - $(popup.$content).on('click', 'button[data-action=reload]', function(){ - popup.hide() - - reloadForm(form) - }) - - $(popup.$content).on('click', 'button[data-action=save]', function(){ - popup.hide() - - $('input[name=templateForceSave]', $form).val(1) - $('a[data-request=onSave]', $form).trigger('click') - $('input[name=templateForceSave]', $form).val(0) - }) - } - - function reloadForm(form) { - var - $form = $(form), - data = { - type: $('[name=templateType]', $form).val(), - theme: $('[name=theme]', $form).val(), - path: $('[name=templatePath]', $form).val(), - }, - tabId = data.type + '-' + data.theme + '-' + data.path, - tabs = $('#cms-master-tabs').data('oc.tab'), - tab = tabs.findByIdentifier(tabId) - - /* - * Update tab - */ - - $.oc.stripeLoadIndicator.show() - $form.request('onOpenTemplate', { - data: data, - success: function(data) { - this.success(data).done(function(){ - $.oc.stripeLoadIndicator.hide() - $('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab) - $('#cms-master-tabs').ocTab('unmodifyTab', tab) - updateModifiedCounter() - }).always(function(){ - $.oc.stripeLoadIndicator.hide() - }) - }, - error: function(jqXHR, textStatus, errorThrown) { - alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) - $.oc.stripeLoadIndicator.hide() - } - }) - } - - function setPageTitle(title) { - if (title.length) - $.oc.layout.setPageTitle(title + ' | ') - else - $.oc.layout.setPageTitle(title) - } - - /* - * Listen for the click event on the components' remove link - */ - $(document).on('click', '#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist a.remove', function(e) { - $(this).trigger('change') - var pane = $(this).closest('.tab-pane'), - component = $(this).closest('div.layout-cell') - - /* - * Remove any {% component %} tags in the editor for this component - */ - var editor = $('[data-control=codeeditor]', pane) - if (editor.length) { - var alias = $('input[name="component_aliases[]"]', component).val(), - codeEditor = editor.codeEditor('getEditorObject') - - codeEditor.replace('', { - needle: "{% component '" + alias + "' %}" - }) - } - - component.remove() - $(window).trigger('oc.updateUi') - - updateComponentListClass(pane) return false }) - /* - * Listen for the click event in the component list - */ - $(document).on('click', '#cms-component-list [data-component]', function(e) { - /* - * Determine if a page or layout is open in the master tabs - */ + var $primaryCollapseIcon = $('') + var $primaryPanel = $('.control-tabs.primary', data.pane) + var $secondaryPanel = $('.control-tabs.secondary', data.pane) + var $primaryTabContainer = $('.nav-tabs', $primaryPanel) - var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout') - if ($componentList.length == 0) { - alert('Components can be added only to pages, partials and layouts.') - return; - } + $primaryTabContainer.addClass('master-area') - var $component = $(this).clone(), - $iconInput = $component.find('[data-component-icon]'), - $componentContainer = $('.layout-relative', $component), - $configInput = $component.find('[data-inspector-config]'), - $aliasInput = $component.find('[data-component-default-alias]'), - $valuesInput = $component.find('[data-inspector-values]'), - $nameInput = $component.find('[data-component-name]'), - $classInput = $component.find('[data-inspector-class]'), - alias = $aliasInput.val(), - originalAlias = alias, - counter = 2, - existingAliases = [] + if ($primaryPanel.length > 0) { + $secondaryPanel.append($primaryCollapseIcon); - $('div.layout-cell input[name="component_aliases[]"]', $componentList).each(function(){ - existingAliases.push($(this).val()) + $primaryCollapseIcon.click(function(){ + $primaryPanel.toggleClass('collapsed') + $secondaryPanel.toggleClass('primary-collapsed') + $(window).trigger('oc.updateUi') + if (typeof(localStorage) !== 'undefined') + localStorage.ocCmsPrimaryCollapsed = $primaryPanel.hasClass('collapsed') ? 1 : 0 + return false }) + } - while($.inArray(alias, existingAliases) !== -1) { - alias = originalAlias + counter - counter++ + if (typeof(localStorage) !== 'undefined') { + if (!$('a', data.tab).hasClass('new-template') && localStorage.ocCmsTablessCollapsed == 1) + $panel.addClass('collapsed') + + if (localStorage.ocCmsPrimaryCollapsed == 1) { + $primaryPanel.addClass('collapsed') + $secondaryPanel.addClass('primary-collapsed') } + } - // Set the last alias used so dragComponents can use it - $('input[name="component_aliases[]"]', $(this)).val(alias) + var $componentListFormGroup = $('.control-componentlist', data.pane).closest('.form-group') + if ($primaryPanel.length > 0) + $primaryPanel.before($componentListFormGroup) + else + $secondaryPanel.parent().before($componentListFormGroup) - $component.attr('data-component-attached', true) - $componentContainer.addClass($iconInput.val()) - $iconInput.remove() - $componentContainer.attr('data-inspectable', '') - $componentContainer.attr('data-inspector-title', $component.find('span.name').text()) - $componentContainer.attr('data-inspector-description', $component.find('span.description').text()) - $componentContainer.attr('data-inspector-config', $configInput.val()) - $componentContainer.attr('data-inspector-class', $classInput.val()) + $componentListFormGroup.removeClass() + $componentListFormGroup.addClass('layout-row min-size') + this.updateComponentListClass(data.pane) + this.updateFormEditorMode(data.pane, true) - $configInput.remove() - $('input[name="component_names[]"]', $component).val($nameInput.val()) - $nameInput.remove() - $('input[name="component_aliases[]"]', $component).val(alias) - $component.find('span.alias').text(alias) - $component.find('span.alias').text(alias) - $valuesInput.val($valuesInput.val().replace('--alias--', alias)) - $aliasInput.remove() + var $form = $('form', data.pane), + self = this - $component.addClass('adding') - $componentList.append($component) - $componentList.closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement($component) - $component.removeClass('adding') - $component.trigger('change') - - updateComponentListClass($component.closest('.tab-pane')) - - $(window).trigger('oc.updateUi') + $form.on('changed.oc.changeMonitor', function() { + $panel.trigger('modified.oc.tab') + self.updateModifiedCounter() }) - }) - function updateLayouts(form) { + $form.on('unchanged.oc.changeMonitor', function() { + $panel.trigger('unmodified.oc.tab') + self.updateModifiedCounter() + }) + + this.addTokenExpanderToEditor(data.pane, $form) + } + + CmsPage.prototype.onAfterAllTabsClosed = function(event) { + $('#cms-side-panel [data-control=filelist]').fileList('markActive', null) + $('#cms-side-panel form').trigger('oc.list.setActiveItem', [null]) + } + + CmsPage.prototype.onAjaxUpdate = function(event) { + var dataId = $('#cms-master-tabs .nav-tabs li.active').attr('data-tab-id') + $('#cms-side-panel [data-control=filelist]').fileList('markActive', dataId) + $('#cms-side-panel form').trigger('oc.list.setActiveItem', [dataId]) + } + + CmsPage.prototype.onAjaxSuccess = function(element, event, context, data) { + if (data.templatePath !== undefined) { + $('input[name=templatePath]', this).val(data.templatePath) + $('input[name=templateMtime]', this).val(data.templateMtime) + $('[data-control=delete-button]', this).removeClass('hide') + $('[data-control=preview-button]', this).removeClass('hide') + + if (data.pageUrl !== undefined) + $('[data-control=preview-button]', this).attr('href', data.pageUrl) + } + + if (data.tabTitle !== undefined) { + $('#cms-master-tabs').ocTab('updateTitle', $(this).closest('.tab-pane'), data.tabTitle) + this.setPageTitle(data.tabTitle) + } + + var tabId = $('input[name=templateType]', this).val() + '-' + + $('input[name=theme]', this).val() + '-' + + $('input[name=templatePath]', this).val(); + + $('#cms-master-tabs').ocTab('updateIdentifier', $(this).closest('.tab-pane'), tabId) + + var templateType = $('input[name=templateType]', this).val() + if (templateType.length > 0) { + $.oc.cmsPage.updateTemplateList(templateType) + + if (templateType == 'layout') + this.updateLayouts(this) + } + + this.updateFormEditorMode($(element).closest('.tab-pane'), false) + + if (context.handler == 'onSave' && (!data['X_OCTOBER_ERROR_FIELDS'] && !data['X_OCTOBER_ERROR_MESSAGE'])) { + $(this).trigger('unchange.oc.changeMonitor') + } + } + + CmsPage.prototype.onAjaxError = function(event, context, data, jqXHR) { + if (context.handler == 'onSave') { + if (jqXHR.responseText == 'mtime-mismatch') { + event.preventDefault() + this.handleMtimeMismatch(this) + } + } + } + + CmsPage.prototype.onCreateTemplateClick = function(event) { + var + $form = $(this).closest('[data-template-type]'), + type = $form.data('template-type'), + tabId = type + Math.random(), + self = this + + $.oc.stripeLoadIndicator.show() + $form.request('onCreateTemplate', { + data: { + type: type + }, + success: function(data) { + this.success(data).done(function(){ + $('#cms-master-tabs').ocTab('addTab', data.tabTitle, data.tab, tabId, $form.data('type-icon') + ' new-template') + $('#layout-side-panel').trigger('close.oc.sidePanel') + self.setPageTitle(data.tabTitle) + }).always(function(){ + $.oc.stripeLoadIndicator.hide() + }) + } + }) + } + + CmsPage.prototype.onDeleteTemplateClick = function(event) { + var + $el = $(this), + $form = $el.closest('form'), + templateType = $form.data('template-type') + + if (!confirm($el.data('confirmation'))) + return + + $form.request('onDeleteTemplates', { + data: { + type: templateType + }, + success: function(data) { + var tabs = $('#cms-master-tabs').data('oc.tab'); + $.each(data.deleted, function(index, path){ + var + tabId = templateType + '-' + data.theme + '-' + path, + tab = tabs.findByIdentifier(tabId) + + $('#cms-master-tabs').ocTab('closeTab', tab, true) + }) + + if (data.error !== undefined && $.type(data.error) === 'string' && data.error.length) + $.oc.flashMsg({text: data.error, 'class': 'error'}) + }, + complete: function() { + $.oc.cmsPage.updateTemplateList(templateType) + } + }) + } + + CmsPage.prototype.onInspectorShowing = function(ev, data) { + $(ev.currentTarget).closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement(ev.currentTarget, data.callback) + + ev.stopPropagation() + } + + CmsPage.prototype.onInspectorHidden = function() { + var values = $.parseJSON($('[data-inspector-values]', this).val()) + + $('[name="component_aliases[]"]', this).val(values['oc.alias']) + $('span.alias', this).text(values['oc.alias']) + } + + CmsPage.prototype.onInspectorHiding = function(e, values) { + var values = $.parseJSON($('[data-inspector-values]', this).val()), + alias = values['oc.alias'], + $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout'), + $cell = $(e.target).parent() + + $('div.layout-cell', $componentList).each(function(){ + if ($cell.get(0) == this) + return true + + var $input = $('input[name="component_aliases[]"]', this) + + if ($input.val() == alias) { + e.preventDefault() + alert('The component alias "'+alias+'" is already used.') + return false + } + }) + } + + CmsPage.prototype.onComponentRemove = function(e) { + $(this).trigger('change') + var pane = $(this).closest('.tab-pane'), + component = $(this).closest('div.layout-cell') + + /* + * Remove any {% component %} tags in the editor for this component + */ + var editor = $('[data-control=codeeditor]', pane) + if (editor.length) { + var alias = $('input[name="component_aliases[]"]', component).val(), + codeEditor = editor.codeEditor('getEditorObject') + + codeEditor.replace('', { + needle: "{% component '" + alias + "' %}" + }) + } + + component.remove() + $(window).trigger('oc.updateUi') + + this.updateComponentListClass(pane) + return false + } + + CmsPage.prototype.onComponentClick = function(e) { + /* + * Determine if a page or layout is open in the master tabs + */ + + var $componentList = $('#cms-master-tabs > div.tab-content > .tab-pane.active .control-componentlist .layout') + if ($componentList.length == 0) { + alert('Components can be added only to pages, partials and layouts.') + return; + } + + var $component = $(this).clone(), + $iconInput = $component.find('[data-component-icon]'), + $componentContainer = $('.layout-relative', $component), + $configInput = $component.find('[data-inspector-config]'), + $aliasInput = $component.find('[data-component-default-alias]'), + $valuesInput = $component.find('[data-inspector-values]'), + $nameInput = $component.find('[data-component-name]'), + $classInput = $component.find('[data-inspector-class]'), + alias = $aliasInput.val(), + originalAlias = alias, + counter = 2, + existingAliases = [] + + $('div.layout-cell input[name="component_aliases[]"]', $componentList).each(function(){ + existingAliases.push($(this).val()) + }) + + while($.inArray(alias, existingAliases) !== -1) { + alias = originalAlias + counter + counter++ + } + + // Set the last alias used so dragComponents can use it + $('input[name="component_aliases[]"]', $(this)).val(alias) + + $component.attr('data-component-attached', true) + $componentContainer.addClass($iconInput.val()) + $iconInput.remove() + $componentContainer.attr('data-inspectable', '') + $componentContainer.attr('data-inspector-title', $component.find('span.name').text()) + $componentContainer.attr('data-inspector-description', $component.find('span.description').text()) + $componentContainer.attr('data-inspector-config', $configInput.val()) + $componentContainer.attr('data-inspector-class', $classInput.val()) + + $configInput.remove() + $('input[name="component_names[]"]', $component).val($nameInput.val()) + $nameInput.remove() + $('input[name="component_aliases[]"]', $component).val(alias) + $component.find('span.alias').text(alias) + $component.find('span.alias').text(alias) + $valuesInput.val($valuesInput.val().replace('--alias--', alias)) + $aliasInput.remove() + + $component.addClass('adding') + $componentList.append($component) + $componentList.closest('[data-control="toolbar"]').data('oc.dragScroll').goToElement($component) + $component.removeClass('adding') + $component.trigger('change') + + this.updateComponentListClass($component.closest('.tab-pane')) + + $(window).trigger('oc.updateUi') + } + + // INTERNAL METHODS + // ============================ + + CmsPage.prototype.updateComponentListClass = function(pane) { + var $componentList = $('.control-componentlist', pane), + $primaryPanel = $('.control-tabs.primary', pane), + $primaryTabContainer = $('.nav-tabs', $primaryPanel), + hasComponents = $('.layout', $componentList).children(':not(.hidden)').length > 0 + + $primaryTabContainer.toggleClass('component-area', hasComponents) + $componentList.toggleClass('has-components', hasComponents) + } + + CmsPage.prototype.updateFormEditorMode = function(pane, initialization) { + var $contentTypeElement = $('[data-toolbar-type]', pane) + if ($contentTypeElement.length == 0) + return + + if ($contentTypeElement.data('toolbar-type') != 'content') + return + + var fileName = $('input[name=fileName]', pane).val(), + parts = fileName.split('.'), + extension = 'txt', + mode = 'plain_text', + modes = $.oc.codeEditorExtensionModes, + editor = $('[data-control=codeeditor]', pane) + + if (parts.length >= 2) + extension = parts.pop().toLowerCase() + + if (modes[extension] !== undefined) + mode = modes[extension]; + + var setEditorMode = function() { + window.setTimeout(function(){ + editor.data('oc.codeEditor').editor.getSession().setMode({path: 'ace/mode/'+mode}) + }, 200) + } + + if (initialization) + editor.on('oc.codeEditorReady', setEditorMode) + else + setEditorMode() + } + + CmsPage.prototype.updateModifiedCounter = function() { + var counters = { + page: {menu: 'pages', count: 0}, + partial: {menu: 'partials', count: 0}, + layout: {menu: 'layouts', count: 0}, + content: {menu: 'content', count: 0}, + asset:{menu: 'assets', count: 0} + } + + $('> div.tab-content > div.tab-pane[data-modified]', '#cms-master-tabs').each(function(){ + var inputType = $('> form > input[name=templateType]', this).val() + counters[inputType].count++ + }) + + $.each(counters, function(type, data){ + $.oc.sideNav.setCounter('cms/' + data.menu, data.count); + }) + } + + CmsPage.prototype.addTokenExpanderToEditor = function(pane, $form) { + var group = $('[data-field-name=markup]', pane), + editor = $('[data-control=codeeditor]', group), + canExpand = false, + self = this + + if (!editor.length || editor.data('oc.tokenexpander')) + return + + var toolbar = editor.codeEditor('getToolbar') + + editor.tokenExpander() + + var breakButton = $('
  • ').prop({ 'class': 'tokenexpander-button' }).append( + $('').prop({ 'href': 'javascript:; '}).append( + $('').prop({ 'class': 'icon-code-fork' }) + ) + ) + + breakButton.hide().on('click', function(){ + self.handleExpandToken(editor, $form) + return false + }) + + $('ul:first', toolbar).prepend(breakButton) + + editor + .on('show.oc.tokenexpander', function(){ + canExpand = true + breakButton.show() + }) + .on('hide.oc.tokenexpander', function(){ + canExpand = false + breakButton.hide() + }) + .on('dblclick', function(e){ + if ((e.metaKey || e.ctrlKey) && canExpand) { + self.handleExpandToken(editor, $form) + } + }) + } + + CmsPage.prototype.handleExpandToken = function(editor, $form) { + editor.tokenExpander('expandToken', function(token, value){ + return $form.request('onExpandMarkupToken', { + data: { tokenType: token, tokenName: value } + }) + }) + } + + CmsPage.prototype.handleMtimeMismatch = function(form) { + var $form = $(form) + $form.popup({ handler: 'onOpenConcurrencyResolveForm' }) + + var popup = $form.data('oc.popup'), + self = this + + $(popup.$content).on('click', 'button[data-action=reload]', function(){ + popup.hide() + self.reloadForm(form) + }) + + $(popup.$content).on('click', 'button[data-action=save]', function(){ + popup.hide() + + $('input[name=templateForceSave]', $form).val(1) + $('a[data-request=onSave]', $form).trigger('click') + $('input[name=templateForceSave]', $form).val(0) + }) + } + + CmsPage.prototype.reloadForm = function(form) { + var + $form = $(form), + data = { + type: $('[name=templateType]', $form).val(), + theme: $('[name=theme]', $form).val(), + path: $('[name=templatePath]', $form).val(), + }, + tabId = data.type + '-' + data.theme + '-' + data.path, + tabs = $('#cms-master-tabs').data('oc.tab'), + tab = tabs.findByIdentifier(tabId), + self = this + + /* + * Update tab + */ + + $.oc.stripeLoadIndicator.show() + $form.request('onOpenTemplate', { + data: data, + success: function(data) { + this.success(data).done(function(){ + $.oc.stripeLoadIndicator.hide() + $('#cms-master-tabs').ocTab('updateTab', tab, data.tabTitle, data.tab) + $('#cms-master-tabs').ocTab('unmodifyTab', tab) + self.updateModifiedCounter() + }).always(function(){ + $.oc.stripeLoadIndicator.hide() + }) + }, + error: function(jqXHR, textStatus, errorThrown) { + alert(jqXHR.responseText.length ? jqXHR.responseText : jqXHR.statusText) + $.oc.stripeLoadIndicator.hide() + } + }) + } + + CmsPage.prototype.setPageTitle = function(title) { + if (title.length) + $.oc.layout.setPageTitle(title + ' | ') + else + $.oc.layout.setPageTitle(title) + } + + CmsPage.prototype.updateLayouts = function(form) { $(form).request('onGetTemplateList', { success: function(data) { $('#cms-master-tabs > .tab-content select[name="settings[layout]"]').each(function(){ @@ -665,19 +699,5 @@ }) } - var CmsPage = function() { - this.updateTemplateList = function(type) { - var - $form = $('#cms-side-panel form[data-template-type='+type+']'), - templateList = type + 'List' - - $form.request(templateList + '::onUpdate', { - complete: function() { - $('button[data-control=delete-template]', $form).trigger('oc.triggerOn.update') - } - }) - } - } - - $.oc.cmsPage = new CmsPage; + $.oc.cmsPage = new CmsPage(); }(window.jQuery); \ No newline at end of file