Seditio Source
Root |
./othercms/ips_4.3.4/applications/cms/data/javascript.xml
<?xml version="1.0" encoding="UTF-8"?>
<javascript app="cms">
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/blocks" javascript_name="ips.blocks.form.js" javascript_type="controller" javascript_version="103021" javascript_position="1000100"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.blocks.form.js
 *
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.blocks.form', {

initialize: function () {
this.on( 'change', '#elSelect_block_template_use_how', this.toggle );
this.on( 'click', '[data-role=viewTemplate]', this.viewTemplate );

if ( this.scope.find('input[name=block_type]:hidden').val() == 'plugin' )
{
this.on( 'click', 'a[id$=form_tab__content]', this.refreshPreview );
this.on( 'click', 'span[data-role=refreshPreview]', this.refreshPreview );
}

$('#elCodemirror_block_content-input').attr('loaded', 'false');
},

/**
* Refreshes block preview
*
* @param {event} e Event object
* @returns {void}
*/
refreshPreview: function () {
if ( ! $('#elPages_block_preview_form').length )
{
$('<form />').attr('method', 'post').attr('id', 'elPages_block_preview_form').attr('target', 'elpages_block_preview').addClass('ipsHide').appendTo( $('body') );
}

var iframe  = $('#elpages_block_preview');
var form    = $('#elPages_block_preview_form');

form.find('input[type=hidden]').remove();

var newSrc  = iframe.attr('data-base-url');

if ( ! iframe.hasClass('ipsLoading') ){
iframe.addClass('ipsLoading');
}

var fields = '';
$.each( this.scope.find('form').serializeArray(), function( i, item )
{
if ( item.name == 'block_content' )
{
item.value = $('#elCodemirror_block_content-input').data('CodeMirrorInstance').getValue();
}

$('<input type="hidden" />').attr("name", item.name).attr("value", item.value).appendTo( form );

fields += item.name + ",";

Debug.log( "Adding " + item.name + " = " + item.value );
} );

form.attr('action', newSrc + '&__nocache=' + Math.random().toString(36).substr(2, 9) + "&_sending=" + fields );

Debug.log( form );

form.submit();

/* Stop links loading stuff when clicked */
iframe.load(function()
{
iframe.removeClass('ipsLoading');
iframe.removeClass('loading');
iframe.contents().find("a").each( function(index)
{
$( this ).on("click", function(event)
{
event.preventDefault();
event.stopPropagation();
} );
    } );
} );
},

/**
* View a template. As the title of the method suggests.
*
* @param {event} e Event object
* @returns {void}
*/
viewTemplate: function (e) {
var span = $( e.currentTarget );

if ( !_.isUndefined( this.scope.find('input[name=block_plugin_app]').val() ) )
{
var appOrPlugin = '&block_app=' + this.scope.find('input[name=block_plugin_app]').val();
}
else
{
var appOrPlugin = '&block_plugin=' + this.scope.find('input[name=block_plugin_plugin]').val();
}

var dialogRef = ips.ui.dialog.create({
title: this.scope.find('input[name=block_plugin]').val(),
url: '?app=cms&module=pages&controller=ajax&do=loadTemplate&show=modal&t_location=block' + appOrPlugin + '&block_key=' + this.scope.find('input[name=block_plugin]').val() + '&t_key=' + this.scope.find('#elSelect_block_template_id').val(),
forceReload: true,
remoteSubmit: true
});

dialogRef.show();
},

/**
* Toggle event handler
*
* @param {event} e Event object
* @returns {void}
*/
toggle: function (e) {
var thisToggle = $( e.currentTarget );

if ( thisToggle.val() === 'copy' && $('#elCodemirror_block_content-input').attr('loaded') == 'false' )
{
var save = { 't_location': 'block', 't_key': this.scope.find('#elSelect_block_template_id').val(), 'block_key': this.scope.find('input[name=block_plugin]').val(), 'block_app': this.scope.find('input[name=block_plugin_app]').val() };
var self = this;

ips.getAjax()( '?app=cms&module=pages&controller=ajax&do=loadTemplate&show=json&noencode=1', {
dataType: 'json',
data: save,
type: 'post'
} )
.done( function (response) {
/* Update codemirror */
$('#elCodemirror_block_content-input').data('CodeMirrorInstance').setValue( response.template_content );
$('input[name=template_params]').val( response.template_params );
$('#elCodemirror_block_content-input').attr('loaded', 'true');
});
}
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/databases" javascript_name="ips.databases.download.js" javascript_type="controller" javascript_version="103021" javascript_position="1000150">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.databases.download.js - Present an alert to the user asking them if they want to build the app first
 *
 * Author: Rikki Tissier (Good bits), Matt Mecham (bad bits)
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.admin.databases.download', {

initialize: function () {
this.on( 'click', this.launchAlert );
},

/**
* Displays a dialog to the user with 'build and download' and 'download' options
*
* @param {event} e Event object
* @returns {void}
*/
launchAlert: function (e) {
var self = this;

e.preventDefault();

ips.ui.alert.show({
type: 'confirm',
message: ips.getString('cms_download_db_explain'),
icon: 'fa fa-download',
buttons: {
ok: ips.getString('cms_download_db'),
cancel: ips.getString('cancel')
},
callbacks: {
ok: function () {
window.location = self.scope.attr('data-downloadURL');
},
}
});
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/databases" javascript_name="ips.databases.form.js" javascript_type="controller" javascript_version="103021" javascript_position="1000150">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.databases.form.js
 *
 * Author: Matt Mecham
 */

;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.admin.databases.form', {

initialize: function () {
this.on( 'change', '[name=database_create_page]', this.toggle );
this.on( 'change', '[name=database_use_categories]', this.toggleUseCategories );
this.toggle();
this.toggleUseCategories();
},

/**
* Toggle use categories
*
* @param {event} e Event object
* @returns {void}
*/
toggleUseCategories: function (e) {
var label = this.scope.find('[data-lang=index_as_categories]');

if ( $('[name=database_use_categories]:checked').val() == 1 )
{
label.html( ips.getString('index_as_categories') );
}
else
{
label.html( ips.getString('index_as_records') );
}
},

/**
* Toggle event handler
*
* @param {event} e Event object
* @returns {void}
*/
toggle: function (e) {
var thisToggle = $('[name=database_create_page]:checked');

$.each( ['details', 'meta'], function( i, row)
{
if ( thisToggle.val() == 'existing' )
{
$('#form_header_content_page_form_tab__' + row ).hide();
}
else
{
$('#form_header_content_page_form_tab__' + row ).show();
}
} );
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="front" javascript_path="controllers/external" javascript_name="ips.external.communication.js" javascript_type="controller" javascript_version="103021" javascript_position="1000150"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.external.communication.js - External communication widget - sends messages to the parent window
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.front.external.communication', {

_blockID: '',
_widgetID: '',

initialize: function () {
this.on( window, 'message', this.windowMessage );

this.on( 'click', 'a', this.clickLink );
this.setup();
},

/**
* setup method
*
* @returns {void}
*/
setup: function () {
var self = this;
var url = ips.utils.url.getURIObject();
this._blockID = url.queryKey.blockid;
this._widgetID = url.queryKey.widgetid;

this._send( 'iframeReady' );
this._sendHeight();

// We'll run a loop to track the height of the document and let the parent know
setInterval( _.bind( this._sendHeight, this ), 500 );
},

/**
* Handles messages received from the parent window
*
* @param {event} e Event object
* @returns {void}
*/
windowMessage: function (e) {
try {
var pmData = JSON.parse( e.originalEvent.data );
var method = pmData.method;
} catch (err) {
return;
}

// Check we have a widget ID and it matches ours
if( _.isUndefined( pmData.widgetID ) || pmData.widgetID !== this._widgetID ){
return;
}

if( method && !_.isUndefined( this[ method ] ) ){
this[ method ].call( this, pmData );
}
},

/**
* The parent has sent us some styles to use
*
* @param {object} data Data object containing the styles
* @returns {void}
*/
probedStyles: function (data) {
// Create a style element to insert into the head
var elem = $('<style/>').attr('type', 'text/css').appendTo('head');
var stylesheet = "\
body {\
font-family: " + data.font + "; \
color: " + data.text + ";\
}\
\
a {\
color: " + data.link + ";\
}\
";

elem.text( stylesheet );
},

/**
* Event handler for clicking a link; we'll send it to the parent to handle
*
* @param {event} e Event object
* @returns {void}
*/
clickLink: function (e) {
var link = $( e.currentTarget );
var href = link.attr('href');

if( href.startsWith('#') ){
return;
}

e.preventDefault();

this._send( 'goToLink', { link: href } );
},

/**
* Shortcut for calculating and sending the height to the parent
*
* @returns {void}
*/
_sendHeight: function () {
var currentSize = $('body').outerHeight();
this._send( 'iframeSize', { size: currentSize } );
},

/**
* Sends a message to the parent
*
* @param {string} method Method name to call on the parent
* @param {object} data Data object to send
* @returns {void}
*/
_send: function (method, data) {
var output = JSON.stringify( _.extend( data || {}, {
method: method,
widgetID: this._widgetID,
blockID: this._blockID
}));

window.top.postMessage( output, '*' );
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/fields" javascript_name="ips.fields.form.js" javascript_type="controller" javascript_version="103021" javascript_position="1000200">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.fields.form.js
 *
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.admin.fields.form', {
currentValue: null,
initialize: function () {
$('#elTextarea_field_default_value').on( 'keypress', this.checkChange );

if ( $('#field_default_update_existing').length )
{
this.currentValue = $('#elTextarea_field_default_value').val();
$('#field_default_update_existing').hide();
}
},

/**
* checkChange handler
*
* @param {event} e Event object
* @returns {void}
*/
checkChange: function (e) {

if ( $('#elTextarea_field_default_value').val() != this.currentValue )
{
$('#field_default_update_existing').show();
}
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/media" javascript_name="ips.media.main.js" javascript_type="controller" javascript_version="103021" javascript_position="1000300"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.media.main.js - Main media controller
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.media.main', {

_fileListing: null,
_sidebar: null,
_folderListing: null,
_cachedFileInfo: {},
_cachedFolders: {},
_folderAjax: null,
_searchTimer: null,
_uploadURL: '',

initialize: function () {
this.on( 'click', '[data-role="mediaItem"]', this.clickItem );
this.on( 'click', '[data-role="fileListing"]', this.clickWrapper );
this.on( 'click', '[data-role="mediaFolder"] > a', this.clickMediaFolder );
this.on( 'click', '[data-action="deleteSelected"]', this.deleteSelected );
this.on( 'keydown', '[data-role="mediaSearch"]', this.searchKeyPress );
this.on( 'submitDialog', '[data-role="uploadButton"]', this.dialogSubmitted );
this.on( 'submitDialog', '[data-role="replaceFile"]', this.replaceDialogSubmitted );
this.on( 'click', '[data-role="replaceFile"]', this.uploadNewFile );
this.on( window, 'resize', this.resizePanels );
this.setup();
},

/**
* Setup method
*
* @returns {void}
*/
setup: function () {
this._fileListing = this.scope.find('[data-role="fileListing"]');
this._searchResults = this.scope.find('[data-role="searchResults"]');
this._sidebar = this.scope.find('[data-role="mediaSidebar"]');
this._folderListing = this.scope.find('[data-role="folderList"]');
this._uploadURL = this.scope.find('[data-role="uploadButton"]').attr('href');
this._newFolderURL = this.scope.find('[data-role="folderButton"]').attr('href');
this._deleteFolderURL = this.scope.find('[data-action="deleteFolder"]').find('a').attr('href');

this.scope.find('[data-role="uploadButton"]').attr('href', this._uploadURL + '&media_parent=0' );
this.scope.find('[data-role="folderButton"]').attr('href', this._newFolderURL + '&media_parent=0' );

this.resizePanels();
},

/**
* Resize panels
*
* @returns {void}
*/
resizePanels: function () {
var windowHeight = $( window ).height();

this.scope.find('#elMedia_sidebar, #elMedia_fileList, #elMedia_searchResults').each( function () {
var top = $( this ).offset().top;

$( this ).css({
height: ( windowHeight - top ) + 'px'
});
});
},

/**
* Dialog submit handler from uploading process
* The dialog response will include an array of media items in this folder. We'll use
* this data to rebuild the file listing, thereby showing the new files immediately.
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
dialogSubmitted: function (e, data) {
var newHTML = [];
var folderID = data.response.folderID;

_.each( data.response.rows, function (file, key) {
newHTML.push( file );
});

this._cachedFolders[ folderID ] = newHTML;
this._buildFileListing( folderID, newHTML );

ips.ui.flashMsg.show( ips.pluralize( ips.getString('mediaUploadedCount'), data.response.count ) );
},

/**
* Dialog submit handler from uploading process
* The dialog response will include an array of media items in this folder. We'll use
* this data to rebuild the file listing, thereby showing the new files immediately.
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
replaceDialogSubmitted: function (e, data) {
var newHTML = [];
var folderID = data.response.folderID;

_.each( data.response.rows, function (file, key) {
newHTML.push( file );
});

this._cachedFolders[ folderID ] = newHTML;
this._buildFileListing( folderID, newHTML );
delete this._cachedFileInfo[ data.response.fileID ];

ips.ui.flashMsg.show( ips.getString('mediaUploadedReplace') );

this._fileListing.find('[data-fileid="' + data.response.fileID + '"]').click();
},

/**
* keypress event handler for the search text box
*
* @param {event} e Event object
* @returns {void}
*/
searchKeyPress: function (e) {
clearTimeout( this._searchTimer );
this._searchTimer = setTimeout( _.bind( this._doSearch, this ), 500 );
},

/**
* Event handler for clicking a folder in the list
*
* @param {event} e Event object
* @returns {void}
*/
clickMediaFolder: function (e) {
e.preventDefault();

var self = this;
var allRows = this._folderListing.find('[data-role="mediaFolder"]');
var row = $( e.currentTarget ).closest('[data-role="mediaFolder"]');
var rowURL = row.find('> a').attr('href');
var rowID = row.attr('data-folderID');

this._resetSearch();

if( row.hasClass('ipsTreeList_activeBranch') ){
row
.removeClass('ipsTreeList_activeBranch')
.addClass('ipsTreeList_inactiveBranch');
} else {
row
.removeClass('ipsTreeList_inactiveBranch')
.addClass('ipsTreeList_activeBranch');
}

allRows.removeClass('ipsTreeList_activeNode');
row.addClass('ipsTreeList_activeNode');

// Update the upload/new folder buttons
this.scope.find('[data-role="uploadButton"]').attr('href', this._uploadURL + '&media_parent=' + rowID );
this.scope.find('[data-role="folderButton"]').attr('href', this._newFolderURL + '&media_parent=' + rowID );
this.scope.find('[data-role="folderButton"]').attr('href', this._newFolderURL + '&media_parent=' + rowID );
this.scope.find('[data-action="deleteFolder"]').find('a').attr('href', this._deleteFolderURL + '&id=' + rowID );

if ( rowID > 0 ) {
this.scope.find('[data-action="deleteFolder"]').removeClass('ipsHide');
} else {
this.scope.find('[data-action="deleteFolder"]').addClass('ipsHide');
}

// Load subfolders
this._loadFolders( rowID, rowURL );

// And folder contents
this._loadFiles( rowID, rowURL );

this._unselectAll();
this._updatePreview();
this._checkDeleteButton();
},

/**
* Prompts and deletes selected files
*
* @param {event} e Event object
* @returns {void}
*/
deleteSelected: function (e) {
var self = this;
var selected = this._getSelected().closest('[data-role="mediaItem"]');
var selectedIDs = [];

$.each( selected, function () {
selectedIDs.push( $( this ).attr('data-fileid') );
});

// Confirm with user
ips.ui.alert.show({
type: 'confirm',
message: ips.pluralize( ips.getString('mediaConfirmDelete'), selected.length ),
icon: 'warn',
callbacks: {
ok: function () {
selected.find('.cMedia_itemSelected').removeClass('cMedia_itemSelected')

// Hide and remove them
ips.utils.anim.go( 'fadeOutDown', selected )
.done( function () {
selected.remove();
self._updatePreview();
self._checkDeleteButton();
});

// Delete them on the server
ips.getAjax()( '?app=cms&module=pages&controller=media&do=deleteByFileIds', {
data: {
fileIds: selectedIDs
}
})
.fail( function () {
ips.ui.alert.show({
type: 'alert',
message: ips.pluralize( ips.getString('mediaErrorDeleting'), selected.length )
});
})
}
}
});
},

/**
* Event handler for clicking in the whitepace of the file listing
* Unselects any selected files
*
* @param {event} e Event object
* @returns {void}
*/
clickWrapper: function (e) {
if( !$( e.target ).closest('[data-role="mediaItem"]').length ) {
this._unselectAll();
this._updatePreview();
this._checkDeleteButton();
}
},

/**
* Event handler for clicking a file in the file listing
* Modifier keys change how we handle the selection in order to mimic file managers
*
* @param {event} e Event object
* @returns {void}
*/
clickItem: function (e) {
var item = $( e.currentTarget ).find('> .cMedia_item');
var metaPressed = e.metaKey;
var shiftPressed = e.shiftKey;

if( !metaPressed && !shiftPressed ){
// If no special keys are pressed, unhighlight all and highlight this one
this._unselectAll();
item.addClass('cMedia_itemSelected');
this._switchToTab('overview');
} else if( metaPressed ) {
// If meta key is pressed, toggle the state of the clicked image
item.toggleClass( 'cMedia_itemSelected', ( !item.hasClass('cMedia_itemSelected') ) );
} else if( shiftPressed ) {
// If shift is pressed, select the whole run

// Find the first selected file
var all = this._fileListing.find('.cMedia_item');
var indexOfFirst = all.index( all.filter('.cMedia_itemSelected').first() );
var indexOfClicked = all.index( item );
var itemsToSelect = null;

// Unselect all to start with
this._unselectAll();

// Select the run
if( indexOfFirst == indexOfClicked ){
itemsToSelect = item;
} else if( indexOfFirst < indexOfClicked ){
itemsToSelect = all.slice( indexOfFirst, indexOfClicked ).addBack( item );
} else if( indexOfFirst > indexOfClicked ){
itemsToSelect = all.slice( indexOfClicked, indexOfFirst + 1 );
}

itemsToSelect.addClass('cMedia_itemSelected');
}

this._updatePreview();
this._checkDeleteButton();
},

/**
* Show the dialog to replace the current file we are viewing
*
* @returns {void}
*/
uploadNewFile: function( e ) {
if( e ){
e.preventDefault();
}

var itemId = this._getSelected().closest('[data-role="mediaItem"]').attr('data-fileid');

$( e.currentTarget ).ipsDialog( {
remoteSubmit: true,
forceReload: true,
url: $( e.currentTarget ).attr('data-baseUrl') + itemId,
                destructOnClose : true,
title: ips.getString('replaceMediaFile')
});
},

/**
* Checks whether any files are selected and displays the delete button if so
*
* @returns {void}
*/
_checkDeleteButton: function () {
var selected = this._getSelected().closest('[data-role="mediaItem"]');

if( selected.length == 0 ){
this.scope.find('[data-action="deleteSelected"]').addClass('ipsHide');
} else {
this.scope.find('[data-action="deleteSelected"]').removeClass('ipsHide');
}
},

/**
* Loads sub-folders of the given folder id
*
* @param {number} rowID Folder row ID to load
* @param {string} url Url for this folder
* @returns {void}
*/
_loadFolders: function (rowID, url) {
var self = this;
var row = this._folderListing.find('[data-folderID="' + rowID + '"]');

if( this._folderAjax && _.isFunction( this._folderAjax.abort ) ){
this._folderAjax.abort();
}

// If this row is loaded, we can just show it
if( row.attr('data-loaded') ){
return;
}

this._folderAjax = ips.getAjax()( url, {
data: {
get: 'folders'
}
} )
.done( function (response) {
var subFolder = row.find('ul');
var newHTML = [];

_.each( response, function (folder, key) {
newHTML.push( folder );
});

subFolder.html( newHTML.join('') );
row.attr('data-loaded', true);
});
},

/**
* Loads files inside the given folder ID
*
* @param {number} folderID Folder row ID to load
* @param {string} url Url for this folder
* @returns {void}
*/
_loadFiles: function (folderID, url) {
var self = this;

if( this._filesAjax && _.isFunction( this._filesAjax.abort ) ){
this._filesAjax.abort();
}

// Are we already viewing this folder?
if( this._fileListing.attr('data-showing') == folderID ){
return;
}

// Do we have a cache already?
if( !_.isUndefined( this._cachedFolders[ folderID ] ) ){
this._buildFileListing( folderID, this._cachedFolders[ folderID ] );
return;
}

// No cache, so load the file listing
this._fileListing.addClass('ipsLoading').html('');

ips.getAjax()( url, {
data: {
get: 'files'
}
} )
.done( function (response) {
var newHTML = [];

_.each( response, function (file, key) {
newHTML.push( file );
});

self._cachedFolders[ folderID ] = newHTML;
self._buildFileListing( folderID, newHTML );
})
.always( function () {
self._fileListing.removeClass('ipsLoading');
});
},

/**
* Builds the file listing for the given folder ID from the given data
*
* @param {number} folderID Folder row ID to build
* @param {array} data Array of file item HTML fragments
* @returns {void}
*/
_buildFileListing: function (folderID, data) {
var output;

if( !data.length ){
output = ips.templates.render('templates.media.noItems');
} else {
output = ips.templates.render( 'templates.media.grid', {
contents: data.join('')
});
}

this._fileListing.attr( 'data-showing', folderID ).html( output );
$( document ).trigger( 'contentChange', [ this._fileListing ] );
},

/**
* Updates the preview panel in the sidebar by finding the selected item and extracting
* relevant information about it to display
*
* @returns {void}
*/
_updatePreview: function () {
// How many selected?
var self = this;
var selected = this._getSelected().closest('[data-role="mediaItem"]');

if( selected.length == 0 || selected.length > 1 ){
// Build the string
var language = ( selected.length == 0 ) ? ips.getString('mediaNoneSelected') : ips.pluralize( ips.getString('mediaMultipleSelected'), selected.length );
// Insert it
this._sidebar.find('[data-role="multipleItemsMessage"]').html( language );
// Show the message box
this._sidebar.find('[data-role="itemInformation"]').hide().end().find('[data-role="multipleItems"]').show();
this._sidebar.find('[data-role="replaceFile"]').hide();
} else {

// Build the file info
var info = {
itemFilename: selected.attr('data-filename'),
itemUploaded: selected.attr('data-uploaded'),
itemTag: '{media="' + selected.attr('data-fileid') + '"}',
itemID: selected.attr('data-fileid'),
itemIsImage: ( selected.attr('[data-fileType]') == 'image' ),
itemFilesize: null,
itemDimensions: null
};

// Dimensions & filesize
var cache = this._cachedFileInfo[ info.itemID ];

if( !_.isUndefined( cache ) ){
info.itemFilesize = cache['itemFilesize'];
info.itemDimensions = cache['itemDimensions'];
} else {
// Do ajax
this._infoAjax = ips.getAjax()( '?app=cms&module=pages&controller=media&do=getFileInfo&id=' + info.itemID )
.done( function (response) {
self._cachedFileInfo[ info.itemID ] = {
itemFilesize: response.fileSize,
itemDimensions: response.dimensions
};

self._sidebar.find('[data-role="itemFilesize"]').text( response.fileSize );
self._sidebar.find('[data-role="itemDimensions"]').text( response.dimensions );
});
}

// Build thumbnail
if( info.itemIsImage ){
info.itemPreview = $('<img/>').attr( 'src', selected.attr('data-url') ).attr('data-ipsLightbox', true );
} else {
info.itemPreview = $('<div/>').addClass('ipsNoThumb');
}

// Update the easy info
_.each( info, function (value, key) {
var elem = self._sidebar.find('[data-role="' + key + '"]');

if( !elem.length ){
return;
}

if( elem.is('input') ){
elem.val( value );
} else if( key == 'itemPreview' ) {
elem.html( value );
} else if( value === null ){
elem.html( $('<span/>').addClass('ipsType_light').text('Loading...') );
} else {
elem.text( value );
}
});

// If this is an image, show the dimensions row
this._sidebar.find('[data-role="itemDimensionsRow"]').toggle( info.itemIsImage );

// Pre-select the tag box
this._sidebar.find('[data-role="itemTag"]').get(0).select();

// Hide the 'multiple items selected' div and show our info div
this._sidebar.find('[data-role="itemInformation"]').show().end().find('[data-role="multipleItems"]').hide();
this._sidebar.find('[data-role="replaceFile"]').show();

$( document ).trigger( 'contentChange', [ this._sidebar ] );
}
},

/**
* Performs a tree search and displays results
*
* @returns {void}
*/
_doSearch: function () {
if( this._searchAjax && _.isFunction( this._searchAjax.abort ) ){
this._searchAjax.abort();
}

var self = this;
var value = this.scope.find('[data-role="mediaSearch"]').val();

if( !_.isEmpty( value ) ){
this._searchResults.show().addClass('ipsLoading');
this._fileListing.hide();
this._folderListing.addClass('cMedia_treeDisabled');

this._searchAjax = ips.getAjax()( '?app=cms&module=pages&controller=media&do=search', {
data: {
input: value
}
})
.done( function (response) {
var newHTML = [];
var output = '';

_.each( response, function (file, key) {
newHTML.push( file );
});

if( !newHTML.length ){
output = ips.templates.render('templates.media.noSearchResults');
} else {
output = ips.templates.render( 'templates.media.grid', {
contents: newHTML.join('')
});
}

self._searchResults.removeClass('ipsLoading').html( output );
$( document ).trigger( 'contentChange', [ self._searchResults ] );

self._unselectAll();
self._updatePreview();
self._checkDeleteButton();
})
} else {
this._searchResults.hide().html('');
this._fileListing.show();
this._folderListing.removeClass('cMedia_treeDisabled');
this._updatePreview();
this._checkDeleteButton();
}
},

/**
* Returns selected files, from the file listing or search results panels depending
* on which is active
*
* @returns {object} jQuery collection of selected files
*/
_getSelected: function () {
if( this._fileListing.is(':visible') ){
return this._fileListing.find('.cMedia_itemSelected');
} else {
return this._searchResults.find('.cMedia_itemSelected');
}
},

/**
* Resets the search box, hiding the results in the process
*
* @returns {void}
*/
_resetSearch: function () {
this.scope.find('[data-role="mediaSearch"]').val('');
this._searchResults.hide();
this._fileListing.show();
this._unselectAll();
this._updatePreview();
this._checkDeleteButton();
},

/**
* Unselects all files
*
* @returns {void}
*/
_unselectAll: function () {
this._fileListing.find('.cMedia_item').removeClass('cMedia_itemSelected');
this._searchResults.find('.cMedia_item').removeClass('cMedia_itemSelected');
},

/**
* Switches the sidebar to the given tab
*
* @param {string} tab Tab to switch to
* @returns {void}
*/
_switchToTab: function (tab) {
if( tab == 'overview' ){
this.scope.find('#elTab_overview').click();
}
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/pages" javascript_name="ips.pages.embed.js" javascript_type="controller" javascript_version="103021" javascript_position="1000250"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.pages.embed.js - Pages embed dialog
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.pages.embed', {

initialize: function () {
this.on( 'change', 'input[type="checkbox"]', this.toggleInherit );
this.on( 'mouseenter', 'textarea', this.mouseEnterTextarea );
},

/**
* Event handler for mousing over the textareas - we'll select hem to make them easy to copy and paste
*
* @param {event} e Event object
* @returns {void}
*/
mouseEnterTextarea: function (e) {
$( e.currentTarget ).select();
},

/**
* Event handler for toggling the 'inherit styles' check
*
* @param {event} e Event object
* @returns {void}
*/
toggleInherit: function (e) {
var toggle = this.scope.find('input[type="checkbox"]');
var textbox = this.scope.find('[data-role="blockCode"]');

if( toggle.is(':checked') ){
textbox.val( textbox.val().replace("></div>", " data-inheritStyle='true'></div>") );
} else {
textbox.val( textbox.val().replace(" data-inheritStyle='true'></div>", "></div>") );
}
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/pages" javascript_name="ips.pages.form.js" javascript_type="controller" javascript_version="103021" javascript_position="1000250"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.pages.form.js - Page Form Stuff That I Can't Be Bothered To Type Out Here
 *
 * Author: Matt Mecham
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.pages.form', {

initialize: function () {
this.on( 'change', '#check_page_ipb_wrapper', this.toggleIPSWrapper );
this.on( 'change', '#elSelect_page_wrapper_template', this.toggleTemplate );
this.on( 'click', '[data-role=viewTemplate]', this.viewTemplate );

/* View template */
if ( this.scope.find('#elSelect_page_wrapper_template').val() == '_none_' )
{
this.scope.find('[data-role=viewTemplate]').hide();
}

/* Includes warning */
if ( $('#check_page_ipb_wrapper').prop('checked') )
{
$('.ipsCmsIncludesMessage').hide();
}
else
{
$('.ipsCmsIncludesMessage').show();
}
},

/**
* View a template. As the title of the method suggests.
*
* @param {event} e Event object
* @returns {void}
*/
viewTemplate: function (e) {
var span = $( e.currentTarget );
var template = this.scope.find('#elSelect_page_wrapper_template').val().split('__');
var dialogRef = ips.ui.dialog.create({
title: this.scope.find('#elSelect_page_wrapper_template option:selected').text(),
url: '?app=cms&module=pages&controller=ajax&do=loadTemplate&show=modal&t_location=page&t_key=' + template[2],
forceReload: true,
remoteSubmit: true
});

dialogRef.show();
},

/**
* Toggle event handler
*
* @param {event} e Event object
* @returns {void}
*/
toggleTemplate: function (e) {
var thisToggle = $( e.currentTarget );

if ( thisToggle.val() == '_none_' )
{
this.scope.find('[data-role=viewTemplate]').hide();
}
else
{
this.scope.find('[data-role=viewTemplate]').show();
}
},

/**
* Toggle event handler
*
* @param {event} e Event object
* @returns {void}
*/
toggleIPSWrapper: function (e) {
var thisToggle = $( e.currentTarget );

if ( thisToggle.prop('checked') )
{
$('.ipsCmsIncludesMessage').hide();
}
else
{
$('.ipsCmsIncludesMessage').show();
}
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="front" javascript_path="controllers/records" javascript_name="ips.records.form.js" javascript_type="controller" javascript_version="103021" javascript_position="1000100"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.databases.form.js
 *
 * Author: Matt Mecham
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.front.records.form', {
lastChecked: '',

initialize: function () {
$( '#elInput_' + this.scope.attr('data-ipsTitleField') ).on( 'blur', $.proxy( this.updateSlug, this ) );
this.scope.find('button[data-ipsChange]').on( 'click', $.proxy( this.manualToggle, this ) );
this.scope.find('button[data-ipsCancel]').on( 'click', $.proxy( this.manualCancel, this ) );

/* Stuff already populated? */
if ( this.scope.find('input[name=record_static_furl_set_checkbox]').prop('checked') )
{
this.scope.removeClass('ipsHide');
this._show();
}
},

/**
* Hide options
*
* @param {event} e Event object
* @returns {void}
*/
_hide: function() {
this.scope.find('span[data-ipsSlugManual]').addClass('ipsHide');
this.scope.find('span[data-ipsSlugSlug]').removeClass('ipsHide');
this.scope.find('span[data-ipsSlugExt]').removeClass('ipsHide');
this.scope.find('button[data-ipsCancel]').addClass('ipsHide');
this.scope.find('button[data-ipsChange]').removeClass('ipsHide');

this.scope.find('input[name=record_static_furl_set_checkbox]').prop('checked', false);
this.scope.find('input[name=record_static_furl_set]').val(0);
},

/**
* Show options
*
* @param {event} e Event object
* @returns {void}
*/
_show: function() {
this.scope.find('button[data-ipsCancel]').removeClass('ipsHide');
this.scope.find('button[data-ipsChange]').addClass('ipsHide');
this.scope.find('span[data-ipsSlugManual]').removeClass('ipsHide');
this.scope.find('span[data-ipsSlugSlug]').addClass('ipsHide');
this.scope.find('span[data-ipsSlugExt]').addClass('ipsHide');

this.scope.find('input[name=record_static_furl_set_checkbox]').prop('checked', true);
this.scope.find('input[name=record_static_furl_set]').val(1);
},

/**
* Changed ones mind
*
* @param {event} e Event object
* @returns {void}
*/
manualCancel: function (e) {
e.preventDefault();

this._hide();
},

/**
* Enter a manual URL
*
* @param {event} e Event object
* @returns {void}
*/
manualToggle: function (e) {
e.preventDefault();

this._show();
},

/**
* Slug is being updated?
*
* @param {event} e Event object
* @returns {void}
*/
updateSlug: function (e) {
var value = $( '#elInput_' + this.scope.attr('data-ipsTitleField') ).val();
var self  = this;

if ( value != this.lastChecked )
{
ips.getAjax()( ips.getSetting('baseURL') + 'index.php?app=cms&module=database&controller=ajax&do=makeFurl', {
data: {
slug: value
}
})
.done( function (response) {
self.scope.removeClass('ipsHide');
self.scope.find('span[data-ipsSlugSlug]').html( '/' + response.slug );
});

this.lastChecked = value;
}
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="front" javascript_path="controllers/records" javascript_name="ips.records.revisions.js" javascript_type="controller" javascript_version="103021" javascript_position="1000100">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.databases.revisions.js
 *
 * Author: Brandon Farber
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.front.records.revisions', {
initialize: function () {
var self = this;

ips.loader.get( ['core/interface/codemirror/diff_match_patch.js','core/interface/codemirror/codemirror.js'] ).then( function () {
self._initCodeMirror();
});
},

/**
* Initializes CodeMirror on a textarea with the provided key
*
* @returns {void}
*/
_initCodeMirror: function () {
var self = this;
var count = 0;

_.each( self.scope.find(&quot;[data-key]&quot;), function(elem){
if( $(elem).attr('data-method') == 'merge' )
{
CodeMirror.MergeView( document.getElementById( $(elem).identify().attr('id') ), {
value: self.scope.find( '[data-original=&quot;' + $(elem).attr('data-key') + '&quot;]' ).val(),
origLeft: self.scope.find( '[data-current=&quot;' + $(elem).attr('data-key') + '&quot;]' ).val(),
lineWrapping: true,
lineNumbers: false,
mode: 'htmlmixed',
revertButtons: false
} );
}
else if( _.isUndefined( $(elem).attr('data-complete') ) )
{
var diff = new diff_match_patch();
var differences = diff.diff_main( self.scope.find( '[data-original=&quot;' + $(elem).attr('data-key') + '&quot;]' ).html(), self.scope.find( '[data-current=&quot;' + $(elem).attr('data-key') + '&quot;]' ).html() );
var currentDiff = diff.diff_prettyHtml(differences);

self.scope.find( '[data-current=&quot;' + $(elem).attr('data-key') + '&quot;]' ).html( currentDiff );

$(elem).attr( 'data-complete', 1 );
}
});
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.addForm.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.addForm.js - Controller for the 'add' form in the template editor
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('core.admin.templates.addForm', {

initialize: function () {
this.on( 'submit', 'form.ipsForm', this.submitForm );
this.on( document, 'fileListRefreshed.templates', this.closeDialog );
},

submitForm: function (e) {
e.preventDefault();

var self = this;

if( !$( e.currentTarget ).attr('data-bypassValidation') ){
// The form hasn't been validated by the genericDialog controller yet, so bail for now
return;
}

// Gather form values and send them
ips.getAjax()( $( e.currentTarget ).attr('action'), {
dataType: 'json',
data: $( e.currentTarget ).serialize(),
type: 'post'
})
.done( function (response) {

self.trigger( 'addedFile.templates', {
type: self.scope.attr('data-type'),
fileID: response.id,
name: response.name
});

});
},

closeDialog: function (e, data) {
this.trigger('closeDialog');
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.conflict.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.conflict.js - Templates: Parent controller for the template conflict manager
 *
 * Author: Matt Mecham
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('core.admin.templates.conflict', {

initialize: function () {
this.on( 'click', '.ipsButton', this.makeSelection );
this.setup();
},

setup: function () {
$('span[data-conflict-name] input[type=radio]').hide();
},

/**
* &quot;Use this version&quot; button is clicked
*
* @param {event} e Event object
* @returns {void}
*/
makeSelection: function (e) {
var span  = $( e.target ).closest('span');
var radio = $( span ).find('input[type=radio]');
var name  = $( span ).attr('data-conflict-name');
var id    = $( radio ).attr('name').replace( /conflict_/, '' );

// Button disabled
if ( span.hasClass('ipsButton_disabled') ){
return false;
}

// Undo selection
else if ( span.hasClass('ipsButton_alternate') ){
radio.removeAttr('checked');

span.removeClass('ipsButton_alternate')
  .addClass('ipsButton_primary')
  .find('strong')
  .html( ips.getString('sc_use_this_version') );
 
$('input[type=radio][name=conflict_' + id + ']').closest('span.ipsButton[data-conflict-name=' + ( name == 'new' ? 'old' : 'new' ) +']').removeClass('ipsButton_disabled');

$('th span[data-conflict-id=' + id + '][data-conflict-name=' + name + ']').removeClass('ipsPos_left ipsBadge ipsBadge_positive');

ips.utils.anim.go( 'blindDown', this.scope.find('div[data-conflict-id=' + id + ']') );
}
// Make selection
else
{
radio.attr('checked', 'checked');
span.removeClass('ipsButton_primary')
  .addClass('ipsButton_alternate')
  .find('strong')
  .html( ips.getString('sc_remove_selection') );
 
$('input[type=radio][name=conflict_' + id + ']').closest('span.ipsButton[data-conflict-name=' + ( name == 'new' ? 'old' : 'new' ) +']').addClass('ipsButton_disabled');

$('th span[data-conflict-id=' + id + '][data-conflict-name=' + name + ']').addClass('ipsPos_left ipsBadge ipsBadge_positive');

ips.utils.anim.go( 'fadeOut', this.scope.find('div[data-conflict-id=' + id + ']') );
}
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.fileEditor.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.fileEditor.js - Templates: controller for the tabbed file editor
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.templates.fileEditor', {

_tabBar: null,
_tabContent: null,
_fileStore: {},
_ajaxURL: '',
_cmInstances: {},
_currentHeight: 0,
_editorPreferences: {
wrap: true,
lines: false
},

initialize: function () {
// Events from elsewhere
this.on( document, 'openFile.templates', this.openFile );
this.on( document, 'variablesUpdated.templates', this.updateVariables );

// Events from within
this.on( 'tabChanged', this.changedTab );
this.on( 'click', '[data-action="closeTab"]', this.closeTab );
this.on( 'click', '[data-action="save"]', this.save );
this.on( 'click', '[data-action="revert"]:not( .ipsButton_disabled )', this.revert );
this.on( 'savedFile.templates', this.updateFile );
this.on( 'revertedFile.templates', this.updateFile );
this.on( 'openDialog', this.openedDialog );
this.on( 'menuItemSelected', this.menuSelected );

var debounce = _.debounce( _.bind( this._recalculatePanelWrapper, this ), 100 );
this.on( window, 'resize', debounce );

// Other setup
this.setup();
},

/**
* Setup method
*
* @returns {void}
*/
setup: function () {
var self = this;

// Set element references
this._tabBar = this.scope.find('#elTemplateEditor_tabbar');
this._tabContent = this.scope.find('#elTemplateEditor_panels');

// Get the current height of the tab bar
this._currentHeight = this._tabBar.outerHeight();

// Set URLs
this._ajaxURL = this.scope.closest('[data-ajaxURL]').attr('data-ajaxURL');
this._normalURL = this.scope.closest('[data-normalURL]').attr('data-normalURL');

// Get the template editor preferences
this._editorPreferences['wrap'] = ips.utils.db.get('templateEditor', 'wrap');
this._editorPreferences['lines'] = ips.utils.db.get('templateEditor', 'lines');

if( this._editorPreferences['wrap'] ){
$('#elTemplateEditor_preferences_menu').find('[data-ipsMenuValue="wrap"]').addClass('ipsMenu_itemChecked');
}

if( this._editorPreferences['lines'] ){
$('#elTemplateEditor_preferences_menu').find('[data-ipsMenuValue="lines"]').addClass('ipsMenu_itemChecked');
}

// Initialize the initial content
this._tabContent.find('[data-group]').each( function (i, item) {
// We need to turn the variables/attributes text input into a hidden field
// We can't simply change the type because IE8 throws a hissy fit, so we'll make a copy
// then remove the original
var original = self._tabContent.find('[data-fileid="' + $( item ).attr('data-fileid') +
'"] input[data-role="variables"]');

if( original.length ){
original.after(
$('<input/>')
.attr( 'type', 'hidden' )
.attr( 'name', original.attr('name') )
.attr( 'value', original.attr('value') )
.attr( 'data-role', 'variables' )
)

original.remove();
}

ips.loader.get( ['core/interface/codemirror/diff_match_patch.js','core/interface/codemirror/codemirror.js'] ).then( function () {
self._initCodeMirror( $( item ).attr('data-fileid'), $( item ).attr('data-type') );
});
});
},

/**
* Event handler for the editor preference menu
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
menuSelected: function (e, data) {
if( data.originalEvent ){
data.originalEvent.preventDefault();
}

if( data.triggerID == 'elTemplateEditor_preferences' ){
if( data.selectedItemID == 'wrap' ){
this._changeEditorPreference( !ips.utils.db.get('templateEditor', 'wrap'), 'wrap', 'lineWrapping' );
} else {
this._changeEditorPreference( !ips.utils.db.get('templateEditor', 'lines'), 'lines', 'lineNumbers' );
}
}
},

/**
* Method that updates an editor preference
*
* @param {object} data Event data object from this.menuSelected
* @returns {void}
*/
_changeEditorPreference: function (toValue, type, cmName) {
// Set the menu appropriately
if( toValue ){
$('#elTemplateEditor_preferences_menu')
.find('[data-ipsMenuValue="' + type + '"]').addClass('ipsMenu_itemChecked');
} else {
$('#elTemplateEditor_preferences_menu')
.find('[data-ipsMenuValue="' + type + '"]').removeClass('ipsMenu_itemChecked');
}

// Update controller variable
this._editorPreferences[ type ] = toValue;

// Update local DB
ips.utils.db.set( 'templateEditor', type, toValue );

// Update all CM instances
for( var i in this._cmInstances ){
this._cmInstances[ i ].setOption( cmName, toValue );
}
},

/**
* A dialog has been opened
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
openedDialog: function (e, data) {
if( data.elemID == 'elTemplateEditor_variables' || data.elemID == 'elTemplateEditor_attributes' ){
this._insertVariablesIntoDialog( data );
}
},

/**
* Inserts the current variables value into the dialog
*
* @param {object} data Event data object from the dialog
* @returns {void}
*/
_insertVariablesIntoDialog: function (data) {
// First get the variables
var active = this._getActiveTab();

if( !active.tabPanel ){
return;
}

var variablesValue   = $.trim( active.tabPanel.find('[data-role="variables"]').val() );
var descriptionValue = $.trim( active.tabPanel.find('[data-role="description"]').val() );
var titleValue       = $.trim( active.tabPanel.find('[data-role="title"]').val() );
var groupValue       = $.trim( active.tabPanel.find('[data-role="group"]').val() );

data.dialog
.find('[data-role="variables"]')
.val( variablesValue )
.end()
.find('[data-role="description"]')
.val( descriptionValue )
.end()
.find('[name="_variables_fileid"]')
.val( active.tabPanel.attr('data-fileid') )
.end()
.find('[name="_variables_location"]')
.val( active.tabPanel.attr('data-location') )
.end()
.find('[data-role="title"]')
.val( titleValue );

/* Show the correct select box */
data.dialog.find('select[data-container-type]').addClass('ipsHide');
data.dialog.find('select[data-container-type=' + active.tabPanel.attr('data-location') + ']').removeClass('ipsHide');
data.dialog.find('select[data-container-type=' + active.tabPanel.attr('data-location') + ']').val( groupValue );

            /* Reset */
            data.dialog.find('#elTemplateEditor_container_title').show();
            data.dialog.find('[data-role="container"]').show();
            data.dialog.find('[data-role="variables"]').show();
            data.dialog.find('#elTemplateEditor_attributes_title').show();

if ( active.tabPanel.attr('data-type') != 'template' )
{
data.dialog.find('#elTemplateEditor_group_title').hide();

data.dialog.find('[data-role="variables"]').hide();
data.dialog.find('#elTemplateEditor_attributes_title').hide();
}
else if ( active.tabPanel.attr('data-location') == 'block')
{
data.dialog.find('#elTemplateEditor_group_title').hide();
data.dialog.find('[data-role="group"]').hide();
}
else if ( active.tabPanel.attr('data-location') == 'database' )
{
/* Can't move */
data.dialog.find('#elTemplateEditor_group_title').hide();
data.dialog.find('[data-role="group"]').hide();

/* Can't rename */
data.dialog.find('#elTemplateEditor_title_title').hide();
data.dialog.find('[data-role="title"]').hide();
}
else if ( active.tabPanel.attr('data-location') == 'page' )
{
/* Hide params */
data.dialog.find('[data-role="variables"]').show();
data.dialog.find('#elTemplateEditor_attributes_title').show();
data.dialog.find('select[data-container-type=' + active.tabPanel.attr('data-location') + ']').show();
}
},

/**
* Updates the value of the variables field in the tab with the given file ID
*
* @param {event} e Event object
* @param {event} e Event data object from templates.variablesDialog
* @returns {void}
*/
updateVariables: function (e, data) {
// Find the panel with the correct fileID, and update it's variables value

this._tabContent
.find('[data-fileid="' + data.fileID + '"]')
.find('[data-role="variables"]')
.val( data.value )
.end()
.find('[data-role="title"]')
.val( data.title )
.end()
.find('[data-role="description"]')
.val( data.description )
.end()
.find('[data-role="group"]')
.val( data.group );

this._updateTabLabel( data.fileID, data.title );
},

/**
* Saves the contents of the editor
*
* @param {event} e Event object
* @returns {void}
*/
save: function (e) {
e.preventDefault();
var self = this;
var active = this._getActiveTab();
var panel = active.tabPanel;
var key = panel.attr('data-fileid');

if( !active.tab || !active.tabPanel ){
Debug.warn('No active tab or tab panel');
return;
}

var save = this._getParametersFromPanel( panel );

// We call .save() on the CodeMirror instance, which will cause it to update the
// contents of the original textbox
this._cmInstances[ key ].save();

// Add it to the save object
save[ 'editor_' + key ] = this.scope.find( '#editor_' + key ).val();

// Name
_.extend( save, {
't_name': panel.find('[data-role="title"]').val()
});

// Description
_.extend( save, {
't_description': panel.find('[data-role="description"]').val()
});

// Group
_.extend( save, {
't_group': panel.find('input[data-role="group"]').val()
});

// If this is a template, add its variables
if( panel.attr('data-location') == 'block' || panel.attr('data-location') == 'database' || panel.attr('data-location') == 'page' ){
_.extend( save, {
't_variables': panel.find('[data-role="variables"]').val()
});
}

// Show loading
this.trigger( 'savingFile.templates' );

// Send it
ips.getAjax()( this._normalURL + '&do=save', {
dataType: 'json',
data: save,
type: 'post'
})
.done( function (response) {
if ( response.msg )
{
ips.ui.alert.show( {
type: 'alert',
message: response.msg,
icon: 'warn'
});
}
else
{
// Let everyone know
self.trigger( 'savedFile.templates', {
key: key,
oldID: parseInt( panel.attr('data-itemID') ),
newID: parseInt( response.template_id ),
newTitle: response.template_title,
oldContainer: parseInt( panel.attr('data-container') ),
newContainer: parseInt( response.template_container ),
status: ( response.template_user_added == 1 ) ? 'custom' : 'changed'
});

// Remove the unsaved status from the tab
self._setChanged( false, key );

// Update the toolbar
self._updateToolbar( active.tab );

// Update the tab name
self._updateTabLabel( key, response.template_title );
}
})
.fail( function () {
ips.ui.alert.show( {
type: 'alert',
message: ips.getString('saveThemeError'),
icon: 'warn'
});
})
.always( function () {
self.trigger( 'saveFileFinished.templates' );
});
},

/**
* Reverts or deletes a file
* If the bypass parameter is false, this method will confirm the action with the user first
*
* @param {event} e Event object
* @param {boolean} bypass Bypass the user confirmation?
* @returns {void}
*/
revert: function (e, bypass) {
e.preventDefault();
var self = this;
var active = this._getActiveTab();
var panel = active.tabPanel;
var key = panel.attr('data-fileid');

var message = ( $( e.currentTarget ).attr('data-actionType') == 'revert' ) ?
ips.getString('skin_revert_confirm') : ips.getString('skin_delete_confirm');

if( bypass !== true ){
ips.ui.alert.show({
type: 'confirm',
message: message,
icon: 'warn',
callbacks: {
ok: function () {
self.revert( e, true );
}
}
});

return;
}

var save = this._getParametersFromPanel( panel );

// Send it
ips.getAjax()( this._normalURL + '&do=delete&wasConfirmed=1', {
dataType: 'json',
data: save,
type: 'post'
})
.done( function (response) {
if( response.template_id ){
self._revertedFile( response, key, active );
} else {
self._deletedFile( key, active );
}
});
},

/**
* Handles updating the editor when a file is reverted
*
* @param {object} response JSON response object from ajax request
* @param {string} key Key of the file that's been reverted
* @param {object} active Object containing keys 'tab' and 'tabPanel' referencing active items
* @returns {void}
*/
_revertedFile: function (response, key, active) {
// Let the document know
this.trigger( 'revertedFile.templates', {
key: key,
oldID: parseInt( active.tabPanel.attr('data-itemID') ),
newID: parseInt( response.template_id ),
status: response.InheritedValue
});

// Update the raw textarea
$( '#editor_' + key ).val( response.template_content );

// Update codemirror
this._cmInstances[ key ].setValue( response.template_content );

// Remove the unsaved status from the tab
this._setChanged( false, key );

// Update the toolbar
this._updateToolbar( active.tab );
},

/**
* Handles updating the editor when a file is deleted
*
* @param {string} key Key of the file that's been reverted
* @param {object} active Object containing keys 'tab' and 'tabPanel' referencing active items
* @returns {void}
*/
_deletedFile: function (key, active) {
this.trigger( 'deletedFile.templates', {
key: key,
fileID: active.tabPanel.attr('data-itemID'),
location: active.tabPanel.attr('data-location'),
type: active.tabPanel.attr('data-type')
});

// Find close link in the tab
active.tab.find('[data-action="closeTab"]').click();
},

/**
* Returns an object of parameters used by the ajax requests
*
* @param {element} panel The panel being used as the source
* @returns {object}
*/
_getParametersFromPanel: function (panel) {
return {
t_type: panel.attr('data-type'),
t_location: panel.attr('data-location'),
t_item_id: panel.attr('data-itemID'),
t_container: panel.find('input[data-role=container]').val(),
t_group: panel.attr('data-group'),
t_name: panel.attr('data-name'),
t_key: panel.attr('data-fileid')
};
},

/**
* A file has been saved or reverted
* Updates the ID of any element with the old ID, and changes the state
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
updateFile: function (e, data) {
this.scope
.find('[data-itemID="' + data.oldID + '"]')
.attr( 'data-itemID', data.newID )
.attr( 'data-container', data.newContainer )
.attr( 'data-inherited-value', data.status );
},

/**
* Event handler for clicking a close tab button
*
* @param {event} e Event object
* @returns {void}
*/
closeTab: function (e) {
var tab = $( e.currentTarget ).closest('.ipsTabs_item');
this._doCloseTab( tab );
},

/**
* Handles closing a tab.
* We first check if the tab is in an 'unsaved' state, and if so, prompt the user to confirm losing changes.
* We then destroy the codemirror instance, remove the tab and panel, and switch to another open tab.
*
* @param {element} tab The tab to be closed
* @param {boolean} bypass Whether to bypass the unsaved check
* @returns {void}
*/
_doCloseTab: function (tab, bypass) {
var self = this;
var tabParent = tab.closest('[data-fileid]');
var key = tabParent.attr('data-fileid');
var allTabs = this._tabBar.find('.ipsTabs_item').closest('[data-fileid]');
var newTab = null;

// Check if there's unsaved content
if( tabParent.attr('data-state') == 'unsaved' && bypass != true ){
ips.ui.alert.show({
type: 'confirm',
message: ips.getString('themeUnsavedContent'),
icon: 'warn',
callbacks: {
ok: function () {
self._doCloseTab( tab, true );
}
}
});

return;
}

// Is this tab active?
var active = tab.hasClass('ipsTabs_activeItem');

// Let the document know what we're up to
this.trigger( 'closedTab.templates', {
fileID: key
});

// Remove the codemirrrrr element & instance
$( this._cmInstances[ key ].getWrapperElement() ).remove();
delete( this._cmInstances[ key ] );

// Find the next or prev tab, if this tab is active, and switch to it
if( active && allTabs.length > 1 ){
if( allTabs.first().attr('data-fileid') == tabParent.attr('data-fileid') ){
newTab = tabParent.next();
} else {
newTab = tabParent.prev();
}
}

if( newTab ){
newTab.find('> a').click();
}

// Close the tab
ips.utils.anim.go('fadeOutDown fast', tabParent)
.done( function () {
tabParent.remove();
self._recalculatePanelWrapper();
});

// Remove the panel
this._tabContent.find('[data-fileid="' + key + '"]').remove();
},

/**
* Tab widget has indicated that the user has changed tab
* If there's a file ID, trigger a new event with it, to enable the file listing to highlight it
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
changedTab: function (e, data) {
var tab = data.tab;

if( !_.isUndefined( tab.closest('[data-fileid]').attr('data-fileid') ) ){
this.trigger( 'fileSelected.templates', {
fileID: tab.closest('[data-fileid]').attr('data-fileid')
});
}

this._updateToolbar( tab );
},

/**
* Updates the toolbar buttons
*
* @param {element} tab The current tab
* @returns {void}
*/
_updateToolbar: function (tab) {
var tabParent = tab.closest('[data-fileid]').attr('data-fileid');
var tabPanel = this._tabContent.find('[data-fileid="' + tabParent + '"]');
var status = tabPanel.attr('data-inherited-value');
var type   = tabPanel.attr('data-type');
var revert = this.scope.find('[data-action="revert"]');

switch( status ){
case 'original':
case 'inherit':
revert
.addClass('ipsButton_disabled')
break;
case 'custom':
revert
.html( ips.getString('skin_delete') )
.removeClass('ipsButton_disabled')
.attr('data-actionType', 'delete')
.show();
break;
case 'changed':
revert
.html( ips.getString('skin_revert') )
.removeClass('ipsButton_disabled')
.attr('data-actionType', 'revert')
.show();
break;
}

$('#elTemplateEditor_variables').show();
$('#elTemplateEditor_attributes').hide();
},

/**
* Reponds to the openFile event, to open a file
* Either switch to it if already open, or hand off to _buildTab to load it
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
openFile: function (e, data) {
// Is this file already open?
if( !this._tabBar.find('[data-fileid="' + data.meta.key + '"]').length ){
this._buildTab( data.meta );
} else {
this._tabBar.find('[data-fileid="' + data.meta.key + '"] > a').click();
}
},

/**
* Builds a tab for the file with the given metadata
*
* @param {object} meta Object of file metadata
* @returns {void}
*/
_buildTab: function (meta) {
var self = this;
this._tabContent.attr('data-haseditor', 'true');
this.scope.find('#elTemplateEditor_panels_empty').hide();

// Build the actual tab
this._tabBar.append( ips.templates.render('templates.editor.newTab', {
title: meta.title,
fileid: meta.key,
location: meta.location,
                group: meta.group,
container: meta.container,
id: 'tab_' + meta.key
}));

// Build the content container
this._tabContent.append( ips.templates.render('templates.editor.tabPanel', {
fileid: meta.key,
name: meta.name,
type: meta.type,
location: meta.location,
container: meta.container,
group: meta.group,
id: meta.id,
inherited: meta.inherited
}));

// We may need to rejig the tab pane wrap to account for wrapped tabs,
// so do that now both tab and panel have been added
this._recalculatePanelWrapper();

// Toggle the new tab
this._tabBar.find('[data-fileid="' + meta.key + '"] > a').click();

// Manually set the content area to loading since we aren't using ui.tabbar's load methods
this._tabContent.addClass('ipsLoading ipsTabs_loadingContent');

// Load the content
ips.getAjax()( this._ajaxURL + '&do=loadTemplate&show=json', {
dataType: 'json',
data: {
't_container': meta.container,
't_group':     meta.group,
't_name':      meta.name,
't_key':       meta.key,
't_location':  meta.location,
't_type':   meta.type
}
})
.done( function (response) {
self._postProcessNewTab( response, meta );
})
.always( function () {
self._tabContent.removeClass('ipsLoading ipsTabs_loadingContent');
});
},

/**
* Update the tab label
*
* @param {string} key File key (block__foo)
* @param {string} title Label Title (Foo)
* @return {void}
*/
_updateTabLabel: function( key, title )
{
var span = this._tabBar.find('[data-fileid="' + key + '"] > a span').clone();

this._tabBar.find('[data-fileid="' + key + '"] > a').html( title ).append( span );
},

/**
* Once tab content has been returned by ajax, this method builds the content of a tab,
* and initializes codemirrior for syntax highlighting
*
* @param {object} response   Response JSON object from ajax request
* @param {object} meta Object of meta data for the tab being created
* @returns {void}
*/
_postProcessNewTab: function (response, meta) {
var content = ips.templates.render('templates.editor.tabContent', {
fileid: meta.key,
content: response.template_content,
variables: response.template_params,
description: response.template_desc,
title: response.template_title,
container: response.template_container,
                group: response.template_group
});

this._tabContent.find('[data-fileid="' + meta.key + '"]').html( content );
this._initCodeMirror( meta.key, meta.type );
},

/**
* Initializes CodeMirror on a textarea with the provided key
*
* @param {string} key Key of the textarea to be turned into codemirrior
* @returns {void}
*/
_initCodeMirror: function (key, type) {
var self = this;

this._cmInstances[ key ] = CodeMirror.fromTextArea( document.getElementById('editor_' + key ), {
mode: (type == 'template' ? 'htmlmixed' : 'css'),
lineWrapping: this._editorPreferences['wrap'],
lineNumbers: this._editorPreferences['lines']
} );
this._cmInstances[ key ].setSize( null, this._getTabContentHeight() );

this._cmInstances[ key ].on( 'change', function (doc, cm) {
self._setChanged( true, key );
});
},

/**
* Sets a tab to 'unsaved' state
*
* @returns {void}
*/
_setChanged: function (state, key) {

if( state == true ){
// Update 'x' in tab to an unsaved version, then set state on the tab
this._tabBar
.find('[data-fileid="' + key + '"]')
.attr('data-state', 'unsaved')
.find('[data-action="closeTab"]')
.html( ips.templates.render('templates.editor.unsaved') );
} else {
this._tabBar
.find('[data-fileid="' + key + '"]')
.attr('data-state', 'saved')
.find('[data-action="closeTab"]')
.html( ips.templates.render('templates.editor.saved') );
}
},

/**
* Calculates whether the tab bar has wrapped, and if so, resizes the panel wrapper and updates
* CodeMirror instances with the new height
*
* @returns {void}
*/
_recalculatePanelWrapper: function () {
// Get height of the tab bar
var tabHeight = this._tabBar.outerHeight();

// Set the top value of the panel
this._tabContent.css( { top: tabHeight + 'px' } );

// Get the height of it
var contentHeight = this._getTabContentHeight();

// Find all codemirror instances and resize those
this._tabContent.find('.CodeMirror').css( { height: contentHeight + 'px' } );

this._currentHeight = tabHeight;
},

/**
* Returns references to both the active tab and the active tab panel
*
* @returns {object} Contains keys 'tab' and 'tabPanel', which are jQuery objects
*/
_getActiveTab: function () {
var toReturn = {
tab: null,
tabPanel: null
};

var tab = this._tabBar.find('.ipsTabs_item.ipsTabs_activeItem').first().parent();

if( !tab.length ){
return toReturn;
}

// Get the associated panel
toReturn = {
tab: tab,
tabPanel: this._tabContent.find('[data-fileid="' + tab.attr('data-fileid') + '"]')
};

return toReturn;
},

/**
* Returns the current height of the tab panel wrapper
*
* @returns {number}
*/
_getTabContentHeight: function () {
var tabContentTop = this._tabContent.offset().top;
var windowHeight = $( window ).height();
var infoHeight = this.scope.find('#elTemplateEditor_info').outerHeight();

this._panelHeight = windowHeight - tabContentTop - infoHeight - 31;
return this._panelHeight;
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.fileList.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.fileList.js - Templates: controller for the file listing component of the template manager
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
"use strict";

ips.controller.register('cms.admin.templates.fileList', {

_tabBar: null,
_tabContent: null,

initialize: function () {
// Events started here
this.on( 'click', '[data-action="openFile"]', this.openFile );
this.on( 'click', '[data-action="toggleBranch"]', this.toggleBranch );
this.on( 'modifiedFile.templates', this.refreshFileList );

// Events coming from elsewhere
this.on( document, 'fileSelected.templates', this.selectFile );
this.on( document, 'savedFile.templates revertedFile.templates', this.updateItemMeta );
this.on( document, 'savedFile.templates revertedFile.templates', this.fileChangedStatus );

this.on( document, 'addedFile.templates', this.refreshFileList );
this.on( document, 'deletedFile.templates', this.refreshFileList );

var debounce = _.debounce( _.bind( this.resizeFileList, this ), 100 );
this.on( window, 'resize', debounce );

// Other setup
this.setup();
},

/**
* Setup method
*
* @returns {void}
*/
setup: function () {
this._tabBar = this.scope.find('#elTemplateEditor_typeTabs');
this._tabContent = this.scope.find('#elTemplateEditor_fileList');
this.resizeFileList();
},

/**
* Resizes the file list to full height
*
* @returns {void}
*/
resizeFileList: function () {
// Get height of parts we want to exclude
var fileListTop = this._tabContent.offset().top;
var infoHeight = this.scope.find('#elTemplateEditor_info').height();
var browserHeight = $( window ).height();

var fileListNewTop = browserHeight - fileListTop - infoHeight - 30;

this._tabContent.css({
height: fileListNewTop + 'px'
});
},

/**
* Something has changed in the file list, so we refresh it and try and remember
* the position we were at. If a new file ID is provided, we'll also select it.
*
* @param {event} e Event object
* @returns {void}
*/
refreshFileList: function (e, data) {
var self = this;
var type = data.type;
var panel = this._tabContent.find('.cTemplateList[data-type="' + type + '"]');
var activeItem = panel.find('.cTemplateList_activeNode > a').attr('data-key');

// If the panel isn't actually open, we'll just show it
if( !panel.length ){
this._tabBar.find('[data-type="' + type + '"]').click();
} else {
var open = this._getOpenNodes( panel );

// Now fetch the new list
var url = this._tabBar.find('[data-type]').attr('data-tabURL');

ips.getAjax()( url )
.done( function (response) {
panel.html( response );

// Now reopen all the nodes
self._openNodes( open, panel, activeItem );

if( data.fileID ){
// Click it
panel.find('[data-itemid="' + data.fileID + '"]').click();
}

// Let everyone know
self.trigger('fileListRefreshed.templates');
});
}
},

/**
* Opens the nodes provided in the toOpen param
*
* @param {object} toOpen Object of nodes to open, containing three keys: apps, locations, groups
* @returns {void}
*/
_openNodes: function (toOpen, panel, activeItem) {

var selector = [];

// Get the locations
if( toOpen.locations.length ){
for( var i = 0; i < toOpen.locations.length; i++ ){
selector.push('[data-location="' + toOpen.locations[i] + '"]');
}
}

// Get groups
if( toOpen.groups.length ){
for( var i = 0; i < toOpen.groups.length; i++ ){
var str = '[data-location="' + toOpen.groups[i][0] + '"] ';
str += '[data-group="' + toOpen.groups[i][1] + '"]';

selector.push( str );
}
}

// Now close all branches, then reopen the ones matching our selector
panel
.find('.cTemplateList_activeBranch')
.removeClass('cTemplateList_activeBranch')
.addClass('cTemplateList_inactiveBranch')
.end()
.find( selector.join(',') )
.removeClass('cTemplateList_inactiveBranch')
.addClass('cTemplateList_activeBranch');

// Anything to make active?
if( activeItem ){
panel
.find('[data-key="' + activeItem + '"]')
.click();
}
},

/**
* Returns an object containing the open nodes in the provided panel
*
* @param {element} panel Panel element to fetch from
* @returns {object} Three array keys: apps, locations, groups
*/
_getOpenNodes: function (panel) {
var locations = [];
var groups = [];

// Fetch all open nodes
panel.find('.cTemplateList_activeBranch').each( function (i, item) {
var el = $( item );

if( el.attr('data-location') ){
locations.push( el.attr('data-location') );
}

if( el.attr('data-group') ){
groups.push( [
el.closest('[data-location]').attr('data-location'),
el.attr('data-group')
]);
}
});

return {
locations: locations,
groups: groups
};
},

/**
* The editor controller has indicated that a file tab has been selected
* We respond to this event by highlighting the file in the list
*
* @param {event} e Event object
* @returns {void}
*/
selectFile: function (e, data) {
if( data.fileID ){
this._makeActive( data.fileID );
}
},

/**
* Event handler for clicking a file node in the listing.
* Gather metadata from the file, then trigger an event so that the editor controller
* can load it.
*
* @param {event} e Event object
* @returns {void}
*/
openFile: function (e) {
e.preventDefault();
var elem = $( e.currentTarget );

// Get meta data for this file
var meta = {
name: elem.attr('data-name'),
key: elem.attr('data-key'),
type: elem.closest('[data-type]').attr('data-type'),
title: elem.text(),
group: elem.closest('[data-group]').attr('data-group'),
location: elem.closest('[data-location]').attr('data-location'),
id: elem.closest('[data-itemID]').attr('data-itemID'),
inherited: elem.closest('[data-inherited-value]').attr('data-inherited-value')
};

Debug.log( meta );

this.trigger( 'openFile.templates', {
meta: meta
});
},

/**
* Event handler for clicking a branch in the listing.
* Expends or collapses the branch
*
* @param {event} e Event object
* @returns {void}
*/
toggleBranch: function (e) {
e.preventDefault();
var branchTrigger = $( e.currentTarget );
var branchItem = branchTrigger.parent();

if( branchItem.hasClass('cTemplateList_inactiveBranch') ){
ips.utils.anim.go( 'fadeInDown', branchItem.find(' > ul') );

branchItem
.removeClass('cTemplateList_inactiveBranch')
.addClass('cTemplateList_activeBranch');
} else {
branchItem.find(' > ul').hide();

branchItem
.removeClass('cTemplateList_activeBranch')
.addClass('cTemplateList_inactiveBranch');
}
},

/**
* Updates the ID of any element with the old ID
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
updateItemMeta: function (e, data) {
if( data.oldID != data.newID ){
this.scope
.find('[data-itemID="' + data.oldID + '"]')
.attr( 'data-itemID', data.newID );
}

/* Update name */
this.scope
.find('[data-itemID="' + data.newID + '"]')
.attr( 'data-name', data.newTitle )
.html( data.newTitle );

/* Update container */
if ( data.oldContainer != data.newContainer )
{
data.fileID = data.newID;

// Let everyone know
this.trigger( 'modifiedFile.templates', data );
}
},

/**
* A file's status has changed, so we update it with the new status
*
* @param {event} e Event object
* @param {object} data Event data object
* @returns {void}
*/
fileChangedStatus: function (e, data) {
this.scope
.find('[data-key="' + data.key + '"]')
.attr( 'data-inherited-value', data.status );
},

/**
* Finds the provided fileID in the list, highlights it and opens all branches to it
*
* @param {string} fileID fileID of node to higlight
* @returns {void}
*/
_makeActive: function (fileID) {
// Find the file entry
var file = this.scope.find('[data-key="' + fileID +'"]');

// Make all others inactive
this.scope.find('[data-key]').parent().removeClass('cTemplateList_activeNode');

// Make this one active
file.parent().addClass('cTemplateList_activeNode');

// Get all parent nodes, and show them
file.parents('li[data-group], li[data-location]').each( function (idx, parent) {
if( $( parent ).hasClass('cTemplateList_inactiveBranch') ){
$( parent )
.removeClass('cTemplateList_inactiveBranch')
.addClass('cTemplateList_activeBranch')
.find('> ul')
.show();
}
});
},

/**
* Returns the currently-selected type being shown (templates or css)
*
* @returns {string}
*/
_currentType: function () {
if( this._tabBar.find('[data-type="template"]').hasClass('ipsTabs_activeItem') ){
return 'template';
}
if( this._tabBar.find('[data-type="js"]').hasClass('ipsTabs_activeItem') ){
return 'js';
}

return 'css';
}
});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.main.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.main.js - Templates: Parent controller for the template editor
 * Simply manages showing the loading thingy based on events coming from within
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.admin.templates.main', {

initialize: function () {
this.on( 'savingFile.templates', this.showLoading );
this.on( 'saveFileFinished.templates', this.hideLoading );
},

/**
* Shows the loading thingy
*
* @param {event} e Event object
* @returns {void}
*/
showLoading: function (e) {
ips.utils.anim.go( 'fadeIn', this.scope.find('[data-role=&quot;loading&quot;]') );
},

/**
* Hides the loading thingy
*
* @param {event} e Event object
* @returns {void}
*/
hideLoading: function (e) {
ips.utils.anim.go( 'fadeOut', this.scope.find('[data-role=&quot;loading&quot;]') );
}

});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="controllers/templates" javascript_name="ips.templates.variablesDialog.js" javascript_type="controller" javascript_version="103021" javascript_position="1000050">/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.templates.variablesDialog.js - Controller for the variables dialog
 *
 * Author: Rikki Tissier
 */
;( function($, _, undefined){
&quot;use strict&quot;;

ips.controller.register('cms.admin.templates.variablesDialog', {

initialize: function () {
this.on( 'click', 'input[type=&quot;submit&quot;]', this.submitChange );
},

/**
* Event handler called when the submit button within the dialog is clicked
* Fires an event that the editor controller can respond to
*
* @param {event} e Event object
* @returns {void}
*/
submitChange: function (e) {
this.trigger( 'variablesUpdated.templates', {
fileID: this.scope.find('[name=&quot;_variables_fileid&quot;]').val(),
value: this.scope.find('[data-role=&quot;variables&quot;]').val(),
title: this.scope.find('[data-role=&quot;title&quot;]').val(),
description: this.scope.find('[data-role=&quot;description&quot;]').val(),
container: this.scope.find('[data-role=&quot;container&quot;]').val(),
group: this.scope.find('[data-role=&quot;group&quot;]:not(.ipsHide)').val()
});

this.trigger( 'closeDialog' );
}
});
}(jQuery, _));</file>
 <file javascript_app="cms" javascript_location="front" javascript_path="mixins" javascript_name="ips.cms.builder.area.js" javascript_type="mixins" javascript_version="103021" javascript_position="1000050"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.cms.builder.area.js - Front-end mixin for tables
 *
 * Author: Matt Mecham
 */
;( function($, _, undefined){
"use strict";

ips.controller.mixin('builder.area', 'core.front.widgets.area', true, function () {

_pageID: null;

/**
* After init, init
*
* @returns {void}
*/
this.after('setup', function () {
if( ! $('.cWidgetContainer').length ) {
$('[data-action="openSidebar"]').hide();
}
});

/**
* Updates the ordering of widgets in this area
*
* @returns {void}
*/
this.updateOrdering = function (without) {
var body = $('body');
var order = this.scope.find('> ul').sortable('toArray', {
attribute: 'data-blockID'
});

order = ( without ) ? _.without( _.uniq( order ), without ) : _.uniq( order );

// Remove hidden blocks as these should not be stored
var self = this;
_.each( order, function( value, key )
{
if ( self.scope.find('li[data-blockID=' + value + ']').attr('data-hidden') == 'true' )
{
order = _.without( order, value );
}
} );

ips.getAjax()( ips.getSetting('baseURL') + 'index.php?app=cms&module=pages&controller=builder&do=saveOrder&orientation='  + this._orientation, {
data: {
order: order,
pageID: $('#elCmsPageWrap').attr('data-pageid'),
area: this._areaID,
exclude: _.isString(without) ? without : ''
}
})
.fail( function () {
ips.ui.alert.show( {
type: 'alert',
icon: 'warn',
message: ips.getString('sidebarError'),
callbacks: {}
});
});
};

});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="front" javascript_path="mixins" javascript_name="ips.cms.builder.block.js" javascript_type="mixins" javascript_version="103021" javascript_position="1000050"><![CDATA[/**
 * Invision Community
 * (c) Invision Power Services, Inc. - https://www.invisioncommunity.com
 *
 * ips.cms.builder.block.js - Front-end mixin for CMS sidebar
 *
 * Author: Matt Mecham
 */
;( function($, _, undefined){
"use strict";

ips.controller.mixin('builder.block', 'core.front.widgets.block', true, function () {

/**
* When the menu is opened, we need to load the form into it
*
* @returns {void}
*/
this.menuOpened = function (e, data) {
/* Don't override menus inside the block */
if ( ! this.scope.closest('[data-widgetArea]').hasClass('cWidgetContainer_managing') )
{
return;
}

var body = $('body');
var area = this.scope.closest('[data-widgetArea]').attr('data-widgetArea');
var block = this._blockID;
var self = this;
var managerBlock = $('[data-role="availableBlocks"] [data-blockID="' + this._getBlockIDWithoutUniqueKey( this._blockID ) + '"]');
var menuStyle = managerBlock.attr('data-menuStyle');

if ( menuStyle == 'modal' )
{
var dialogRef = ips.ui.dialog.create({
title: managerBlock.find('h4').html(),
url: ips.getSetting('baseURL') + 'index.php?app=cms&module=pages&controller=builder&do=getConfiguration&block=' + block + '&pageID=' + $('#elCmsPageWrap').attr('data-pageid') + '&pageArea=' + area,
forceReload: true,
remoteSubmit: true
});

dialogRef.show();
Debug.log( dialogRef );
$('#' + dialogRef.dialogID ).css( "height", "1000px");
this._modalOpen = block;
}
else
{
data.menu.html( $('<div/>').addClass('ipsLoading').css({ height: '100px' }) );

setTimeout( function () {
ips.getAjax()( ips.getSetting('baseURL') + 'index.php?app=cms&module=pages&controller=builder&do=getConfiguration', {
data: {
block: block,
pageID: $('#elCmsPageWrap').attr('data-pageid'),
pageArea: area
}
} )
.done( function (response) {
data.menu
.html( response )
.find('form')
.on( 'submit', _.bind( self._configurationForm, self, data.menu ) );

$( document ).trigger('contentChange', [ data.menu ] );
});
}, 1000);
}
};

/**
* Event handler/method that reloads the entire contents of this widget
*
* @returns {void}
*/
this.reloadContent = function () {
var self = this;

this._setLoading( true );

// Get content
if( this._ajaxObj && this._ajaxObj.abort ){
this._ajaxObj.abort();
}

var body = $('body');
var url = ips.getSetting('baseURL') + 'index.php?app=cms&module=pages&controller=builder&do=getBlock&blockID=' + this._blockID + '&pageID=' + $('#elCmsPageWrap').attr('data-pageid') + '&orientation=' + this._orientation;

this._ajaxObj = ips.getAjax()( url )
.done( function (response) {
self.scope.hide().html( response );

ips.utils.anim.go('fadeIn', self.scope);

self.trigger('loadedWidget.widgets', {
blockID: self._blockID
});
})
.fail( function () {
self.scope.html('Error');
})
.always( function () {
self._setLoading( false );
});
};

});
}(jQuery, _));]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="templates" javascript_name="ips.templates.media.js" javascript_type="template" javascript_version="103021" javascript_position="1000050"><![CDATA[ips.templates.set( 'templates.media.grid', "\
<ul class='ipsGrid' data-ipsGrid data-ipsGrid-minItemSize='100' data-ipsGrid-maxItemSize='200'>\
{{{contents}}}\
</ul>\
");

ips.templates.set( 'templates.media.noItems', "\
<div class='ipsType_center ipsType_large ipsType_light ipsPad_double'>\
{{#lang}}mediaEmptyFolder{{/lang}}\
</div>\
");

ips.templates.set( 'templates.media.noSearchResults', "\
<div class='ipsType_center ipsType_large ipsType_light ipsPad_double'>\
{{#lang}}mediaNoResults{{/lang}}\
</div>\
");]]></file>
 <file javascript_app="cms" javascript_location="admin" javascript_path="templates" javascript_name="ips.templates.templates.js" javascript_type="template" javascript_version="103021" javascript_position="1000050"><![CDATA[/* TEMPLATE EDITOR TEMPLATES */
/* TEMPLATECEPTION */
ips.templates.set('templates.editor.newTab', " \
<li data-fileid='{{fileid}}' data-location='{{location}}'>\
<a href='#' class='ipsTabs_item' id='{{id}}'>{{title}} <span data-action='closeTab'><i class='fa fa-times'></i></span></a>\
</li>\
");

ips.templates.set('templates.editor.tabPanel', " \
<div data-fileid='{{fileid}}' id='ipsTabs_elTemplateEditor_tabbar_tab_{{fileid}}_panel' class='ipsTabs_panel' style='display: none' data-location='{{location}}' data-type='{{type}}' data-group='{{group}}' data-name='{{name}}' data-type='{{type}}' data-itemID='{{id}}' data-inherited-value='{{inherited}}'>\
{{{content}}}\
</div>\
");

ips.templates.set('templates.editor.tabContent', " \
<input data-role='group' type='hidden' name='group_{{fileid}}' value=\"{{{group}}}\">\
<input data-role='variables' type='hidden' name='variables_{{fileid}}' value=\"{{{variables}}}\">\
<input data-role='title' type='hidden' name='title_{{fileid}}' value=\"{{{title}}}\">\
<input data-role='description' type='hidden' name='description_{{fileid}}' value=\"{{{description}}}\">\
<textarea data-fileid='{{fileid}}' id='editor_{{fileid}}'>{{{content}}}</textarea>\
");

ips.templates.set('templates.editor.unsaved', " \
<i class='fa fa-circle'></i>\
");

ips.templates.set('templates.editor.saved', " \
<i class='fa fa-times'></i>\
");]]></file>
 <order app="global" path="/dev/js//framework/">templates
common/ips.loader.js
common/ui
common/utils
common
controllers</order>
 <order app="global" path="/dev/js//library/">underscore
jquery
mustache
jstz
Debug.js
app.js</order>
 <order app="global" path="/dev/js//library//jquery">jquery.js
jquery.history.js
jquery.transform.js</order>
 <order app="global" path="/dev/js//library//linkify">linkify.min.js
linkify-jquery.min.js</order>
</javascript>