/**
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
Element.addMethods({
pngHack: function(element) {
element = $(element);
var transparentGifPath = 'clear.gif';
// If there is valid element, it is an image and the image file ends with png:
if (Object.isElement(element) && element.tagName === 'IMG' && element.src.endsWith('.png')) {
var alphaImgSrc = element.src;
var sizingMethod = 'scale';
element.src = transparentGifPath;
}
if (alphaImgSrc) {
element.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="#{alphaImgSrc}",sizingMethod="#{sizingMethod}")'.interpolate(
{
alphaImgSrc: alphaImgSrc,
sizingMethod: sizingMethod
});
}
return element;
}
});
var IECompatibility = Class.create({
/**
* initializes the compatibility class
*/
initialize: function() {
Event.observe(document, 'dom:loaded', function() {
$$('input[type="checkbox"]').invoke('addClassName', 'checkbox');
}.bind(this));
Event.observe(window, 'load', function() {
if (Prototype.Browser.IE) {
var version = parseFloat(navigator.appVersion.split(';')[1].strip().split(' ')[1]);
if (version === 6) {
$$('img').each(function(img) {
img.pngHack();
});
$$('#typo3-menu li ul li').each(function(li) {
li.setStyle({height: '21px'});
});
}
}
});
}
});
if (Prototype.Browser.IE) {
var TYPO3IECompatibilty = new IECompatibility();
}
/**
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* javascript functions regarding the TYPO3 wrapper
* for the javascript library "prototype"
*
* Please make sure that prototype.js is loaded before loading this
* file in your script, the responder is only added if prototype was loaded
*/
if (Prototype) {
// adding generic a responder to use when a AJAX request is done
Ajax.Responders.register({
onCreate: function(request, transport) {
// if the TYPO3 AJAX backend is used,
// the onSuccess & onComplete callbacks are hooked
if (request.url.indexOf("ajax.php") === -1) {
return;
}
var origSuccess = request.options.onSuccess, origComplete = request.options.onComplete;
// hooking "onSuccess"
if (origSuccess) {
request.options.onSuccess = function(xhr, json) {
if (!json) {
T3AJAX.showError(xhr);
} else {
origSuccess(xhr, json);
}
}
}
// hooking "onComplete", using T3Error handler if available
if (origComplete) {
request.options.onComplete = function(xhr, json) {
if (!json && request.options.onT3Error) {
request.options.onT3Error(xhr, json);
} else if (!json) {
T3AJAX.showError(xhr);
} else {
origComplete(xhr, json);
}
};
}
}
});
}
var T3AJAX = {};
T3AJAX.showError = function(xhr, json) {
if (typeof xhr.responseText !== undefined && xhr.responseText) {
if (typeof Ext.MessageBox !== undefined) {
Ext.MessageBox.alert('TYPO3', xhr.responseText);
}
else {
alert(xhr.responseText);
}
}
};
// common storage and global object, could later hold more information about the current user etc.
if (typeof TYPO3 === undefined) {
// window definition required, otherwise IE8 clears TYPO3 variable completely
window['TYPO3'] = {};
}
TYPO3 = Ext.apply(TYPO3, {
// store instances that only should be running once
_instances: {},
getInstance: function(className) {
return TYPO3._instances[className] || false;
},
addInstance: function(className, instance) {
TYPO3._instances[className] = instance;
return instance;
},
helpers: {
// creates an array by splitting a string into parts, taking a delimiter
split: function(str, delim) {
var res = [];
while (str.indexOf(delim) > 0) {
res.push(str.substr(0, str.indexOf(delim)));
str = str.substr(str.indexOf(delim) + delim.length);
}
return res;
}
}
});
/**
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* javascript functions regarding the page & folder tree
* relies on the javascript library "prototype"
*
* @author Benjamin Mack
*/
// new object-oriented drag and drop - code,
// tested in IE 6, Firefox 2, Opera 9
var DragDrop = {
dragID: null,
// options needed for doing the changes when dropping
table: null, // can be "pages" or "folders"
changeURL: null,
backPath: null,
dragElement: function(event, elementID) {
Event.stop(event); // stop bubbling
this.dragID = this.getIdFromEvent(event);
if (!this.dragID) {
return false;
}
if (!elementID) {
elementID = this.dragID;
}
if (!$('dragIcon')) {
this.addDragIcon();
}
$('dragIcon').innerHTML = $('dragIconID_'+elementID).innerHTML +
$('dragTitleID_'+elementID).firstChild.innerHTML;
document.onmouseup = function(event) { DragDrop.cancelDragEvent(event); };
document.onmousemove = function(event) { DragDrop.mouseMoveEvent(event); };
return false;
},
dropElement: function(event) {
var dropID = this.getIdFromEvent(event);
if ((this.dragID) && (this.dragID !== dropID)) {
var url = this.changeURL +
'?dragDrop=' + this.table +
'&srcId=' + this.dragID +
'&dstId=' + dropID +
'&backPath=' + this.backPath;
showClickmenu_raw(url);
}
this.cancelDragEvent();
return false;
},
cancelDragEvent: function(event) {
this.dragID = null;
if ($('dragIcon') && $('dragIcon').style.visibility === 'visible') {
$('dragIcon').style.visibility = 'hidden';
}
document.onmouseup = null;
document.onmousemove = null;
},
mouseMoveEvent: function(event) {
if (!event) {
event = window.event;
}
$('dragIcon').style.left = (Event.pointerX(event) + 5) + 'px';
$('dragIcon').style.top = (Event.pointerY(event) - 5) + 'px';
$('dragIcon').style.visibility = 'visible';
return false;
},
// -- helper functions --
getIdFromEvent: function(event) {
var obj = Event.element(event);
while (obj.id == false && obj.parentNode) {
obj = obj.parentNode;
}
return obj.id.substring(obj.id.indexOf('_') + 1);
},
// dynamically manipulates the DOM to add the div needed for drag&drop at the bottom of the
-tag
addDragIcon: function() {
var code = '
';
var insert = new Insertion.Bottom(document.getElementsByTagName('body')[0], code);
}
};
var Tree = {
ajaxID: 'SC_alt_db_navframe::expandCollapse', // has to be either "SC_alt_db_navframe::expandCollapse" or "SC_alt_file_navframe::expandCollapse"
frameSetModule: null,
activateDragDrop: true,
highlightClass: 'active',
pageID: 0,
// reloads a part of the page tree (useful when "expand" / "collapse")
load: function(params, isExpand, obj, scopeData, scopeHash) {
var scope = '';
if (scopeData && scopeHash) {
scope = '&scopeData=' + encodeURIComponent(scopeData) + '&scopeHash=' + encodeURIComponent(scopeHash);
}
// fallback if AJAX is not possible (e.g. IE < 6)
if (typeof Ajax.getTransport() !== 'object') {
window.location.href = TYPO3.settings.ajaxUrls[this.ajaxID] + '&PM=' + encodeURIComponent(params) + scope;
return;
}
// immediately collapse the subtree and change the plus to a minus when collapsing
// without waiting for the response
if (!isExpand) {
var ul = obj.parentNode.parentNode.getElementsByTagName('ul')[0];
if (ul) {
obj.parentNode.parentNode.removeChild(ul); // no remove() directly because of IE 5.5
}
var pm = Selector.findChildElements(obj.parentNode, ['.pm'])[0]; // Getting pm object by CSS selector (because document.getElementsByClassName() doesn't seem to work on Konqueror)
if (pm) {
pm.onclick = null;
Element.cleanWhitespace(pm);
pm.firstChild.src = pm.firstChild.src.replace('minus', 'plus');
}
} else {
obj.style.cursor = 'wait';
}
var call = new Ajax.Request(TYPO3.settings.ajaxUrls[this.ajaxID], {
method: 'get',
parameters: 'PM=' + encodeURIComponent(params) + scope,
onComplete: function(xhr) {
// the parent node needs to be overwritten, not the object
$(obj.parentNode.parentNode).replace(xhr.responseText);
TYPO3PageTreeFilter.filter();
this.registerDragDropHandlers();
this.reSelectActiveItem();
}.bind(this),
onT3Error: function(xhr) {
// if this is not a valid ajax response, the whole page gets refreshed
this.refresh();
}.bind(this)
});
},
// does the complete page refresh (previously known as "_refresh_nav()")
refresh: function() {
var r = new Date();
// randNum is useful so pagetree does not get cached in browser cache when refreshing
var loc = window.location.href.replace(/&randNum=\d+|#.*/g, '');
var addSign = loc.indexOf('?') > 0 ? '&' : '?';
window.location = loc + addSign + 'randNum=' + r.getTime();
},
// attaches the events to the elements needed for the drag and drop (for the titles and the icons)
registerDragDropHandlers: function() {
if (!this.activateDragDrop) {
return;
}
this._registerDragDropHandlers('dragTitle');
this._registerDragDropHandlers('dragIcon');
},
_registerDragDropHandlers: function(className) {
var elements = Selector.findChildElements($('tree'), ['.'+className]); // using Selector because document.getElementsByClassName() doesn't seem to work on Konqueror
for (var i = 0; i < elements.length; i++) {
Event.observe(elements[i], 'mousedown', function(event) { DragDrop.dragElement(event); }, true);
Event.observe(elements[i], 'dragstart', function(event) { DragDrop.dragElement(event); }, false);
Event.observe(elements[i], 'mouseup', function(event) { DragDrop.dropElement(event); }, false);
}
},
// selects the activated item again, in case it collapsed and got expanded again
reSelectActiveItem: function() {
var obj = $(top.fsMod.navFrameHighlightedID[this.frameSetModule]);
if (obj) {
Element.addClassName(obj, this.highlightClass);
this.extractPageIdFromTreeItem(obj.id);
}
},
// highlights an active list item in the page tree and registers it to the top-frame
// used when loading the page for the first time
highlightActiveItem: function(frameSetModule, highlightID) {
this.frameSetModule = frameSetModule;
this.extractPageIdFromTreeItem(highlightID);
// Remove all items that are already highlighted
var obj = $(top.fsMod.navFrameHighlightedID[frameSetModule]);
if (obj) {
var classes = $w(this.highlightClass);
for (var i = 0; i < classes.length; i++)
Element.removeClassName(obj, classes[i]);
}
// Set the new item
top.fsMod.navFrameHighlightedID[frameSetModule] = highlightID;
if ($(highlightID)) {
Element.addClassName(highlightID, this.highlightClass);
}
},
//extract pageID from the given id (pagesxxx_y_z where xxx is the ID)
extractPageIdFromTreeItem: function(highlightID) {
if(highlightID) {
this.pageID = highlightID.split('_')[0].substring(5);
}
}
};
/**
*
* @author Ingo Renner
**/
var PageTreeFilter = Class.create({
field: 'treeFilter',
resetfield: 'treeFilterReset',
/**
* constructor with event listener
*/
initialize: function() {
// event listener
Event.observe(document, 'dom:loaded', function(){
Event.observe(this.field, "keyup", this.filter.bindAsEventListener(this));
Event.observe(this.resetfield, "click", this.resetSearchField.bindAsEventListener(this) );
}.bind(this));
},
/**
* Filters the tree by setting a class on items not matching search input string
* tested in FF2, S3, IE6, IE7
*/
filter: function() {
var searchString = $F(this.field).toLowerCase();
var pages = $$('#treeRoot .dragTitle a');
pages.each(function(el) {
if (el.innerHTML.toLowerCase().include(searchString)) {
el.up().removeClassName('not-found');
} else {
el.up().addClassName('not-found');
}
});
this.toggleReset();
},
/**
* toggles the visibility of the reset button
*/
toggleReset: function() {
var searchFieldContent = $F(this.field);
var resetButton = $(this.resetfield);
if (searchFieldContent != '' && resetButton.getStyle('visibility') == 'hidden') {
resetButton.setStyle({ visibility: 'visible'} );
} else if (searchFieldContent == '' && resetButton.getStyle('visibility') == 'visible') {
resetButton.setStyle( {visibility: 'hidden'} );
}
},
/**
* resets the search field
*/
resetSearchField: function() {
if ($(this.resetfield).getStyle('visibility') == 'visible') {
$(this.field).value = '';
this.filter('');
}
}
});
// Call this function, refresh_nav(), from another script in the backend if you want
// to refresh the navigation frame (eg. after having changed a page title or moved pages etc.)
// please use the function in the "Tree" object for future implementations
function refresh_nav() {
window.setTimeout('Tree.refresh();',0);
}
/**
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* Javascript functions regarding the clickmenu
* relies on the javascript library "prototype"
*/
/**
* new clickmenu code to make an AJAX call and render the
* AJAX result in a layer next to the mouse cursor
*/
var Clickmenu = {
clickURL: 'alt_clickmenu.php',
ajax: true, // \TYPO3\CMS\Backend\Template\DocumentTemplate::isCMLayers
mousePos: { X: null, Y: null },
delayClickMenuHide: false,
/**
* main function, called from most clickmenu links
* @param table table from where info should be fetched
* @param uid the UID of the item
* @param listFr list Frame?
* @param enDisItems Items to disable / enable
* @param backPath TYPO3 backPath
* @param addParams additional params
* @return nothing
*/
show: function(table, uid, listFr, enDisItems, backPath, addParams) {
var params = 'table=' + encodeURIComponent(table) +
'&uid=' + uid +
'&listFr=' + listFr +
'&enDisItems=' + enDisItems +
'&backPath=' + backPath +
'&addParams=' + addParams;
this.callURL(params);
},
/**
* switch function that either makes an AJAX call
* or loads the request in the top frame
*
* @param params parameters added to the URL
* @return nothing
*/
callURL: function(params) {
if (this.ajax && Ajax.getTransport()) { // run with AJAX
params += '&ajax=1';
var call = new Ajax.Request(this.clickURL, {
method: 'get',
parameters: params,
onComplete: function(xhr) {
var response = xhr.responseXML;
if (!response.getElementsByTagName('data')[0]) {
var res = params.match(/&reloadListFrame=(0|1|2)(&|$)/);
var reloadListFrame = parseInt(res[1], 0);
if (reloadListFrame) {
var doc = reloadListFrame != 2 ? top.content.list_frame : top.content;
doc.location.reload(true);
}
return;
}
var menu = response.getElementsByTagName('data')[0].getElementsByTagName('clickmenu')[0];
var data = menu.getElementsByTagName('htmltable')[0].firstChild.data;
var level = menu.getElementsByTagName('cmlevel')[0].firstChild.data;
this.populateData(data, level);
}.bind(this)
});
}
},
/**
* fills the clickmenu with content and displays it correctly
* depending on the mouse position
* @param data the data that will be put in the menu
* @param level the depth of the clickmenu
*/
populateData: function(data, level) {
if (!$('contentMenu0')) {
this.addClickmenuItem();
}
level = parseInt(level, 10) || 0;
var obj = $('contentMenu' + level);
if (obj && (level === 0 || Element.visible('contentMenu' + (level-1)))) {
obj.innerHTML = data;
var x = this.mousePos.X;
var y = this.mousePos.Y;
var dimsWindow = document.viewport.getDimensions();
dimsWindow.width = dimsWindow.width-20; // saving margin for scrollbars
var dims = Element.getDimensions(obj); // dimensions for the clickmenu
var offset = document.viewport.getScrollOffsets();
var relative = { X: this.mousePos.X - offset.left, Y: this.mousePos.Y - offset.top };
// adjusting the Y position of the layer to fit it into the window frame
// if there is enough space above then put it upwards,
// otherwise adjust it to the bottom of the window
if (dimsWindow.height - dims.height < relative.Y) {
if (relative.Y > dims.height) {
y -= (dims.height - 10);
} else {
y += (dimsWindow.height - dims.height - relative.Y);
}
}
// adjusting the X position like Y above, but align it to the left side of the viewport if it does not fit completely
if (dimsWindow.width - dims.width < relative.X) {
if (relative.X > dims.width) {
x -= (dims.width - 10);
} else if ((dimsWindow.width - dims.width - relative.X) < offset.left) {
x = offset.left;
} else {
x += (dimsWindow.width - dims.width - relative.X);
}
}
obj.style.left = x + 'px';
obj.style.top = y + 'px';
Element.show(obj);
}
if (/MSIE5/.test(navigator.userAgent)) {
this._toggleSelectorBoxes('hidden');
}
},
/**
* event handler function that saves the actual position of the mouse
* in the Clickmenu object
* @param event the event object
*/
calcMousePosEvent: function(event) {
if (!event) {
event = window.event;
}
this.mousePos.X = Event.pointerX(event);
this.mousePos.Y = Event.pointerY(event);
this.mouseOutFromMenu('contentMenu0');
this.mouseOutFromMenu('contentMenu1');
},
/**
* hides a visible menu if the mouse has moved outside
* of the object
* @param obj the object to hide
* @result nothing
*/
mouseOutFromMenu: function(obj) {
obj = $(obj);
if (obj && Element.visible(obj) && !Position.within(obj, this.mousePos.X, this.mousePos.Y)) {
this.hide(obj);
if (/MSIE5/.test(navigator.userAgent) && obj.id === 'contentMenu0') {
this._toggleSelectorBoxes('visible');
}
} else if (obj && Element.visible(obj)) {
this.delayClickMenuHide = true;
}
},
/**
* hides a clickmenu
*
* @param obj the clickmenu object to hide
* @result nothing
*/
hide: function(obj) {
this.delayClickMenuHide = false;
window.setTimeout(function() {
if (!Clickmenu.delayClickMenuHide) {
Element.hide(obj);
}
}, 500);
},
/**
* hides all clickmenus
*/
hideAll: function() {
this.hide('contentMenu0');
this.hide('contentMenu1');
},
/**
* hides / displays all selector boxes in a page, fixes an IE 5 selector problem
* originally by Michiel van Leening
*
* @param action hide (= "hidden") or show (= "visible")
* @result nothing
*/
_toggleSelectorBoxes: function(action) {
for (var i = 0; i < document.forms.length; i++) {
for (var j = 0; j < document.forms[i].elements.length; j++) {
if (document.forms[i].elements[j].type == 'select-one') {
document.forms[i].elements[j].style.visibility = action;
}
}
}
},
/**
* manipulates the DOM to add the divs needed for clickmenu at the bottom of the -tag
*
* @return nothing
*/
addClickmenuItem: function() {
var code = '';
var insert = new Insertion.Bottom(document.getElementsByTagName('body')[0], code);
}
}
// register mouse movement inside the document
Event.observe(document, 'mousemove', Clickmenu.calcMousePosEvent.bindAsEventListener(Clickmenu), true);
// @deprecated: Deprecated functions since 4.2, here for compatibility, remove in 4.4+
// ## BEGIN ##
// Still used in Core: typo3/alt_clickmenu.php::linkItem()
function showClickmenu_raw(url) {
var parts = url.split('?');
if (parts.length === 2) {
Clickmenu.clickURL = parts[0];
Clickmenu.callURL(parts[1]);
} else {
Clickmenu.callURL(url);
}
}
function showClickmenu_noajax(url) {
Clickmenu.ajax = false; showClickmenu_raw(url);
}
function setLayerObj(tableData, cmLevel) {
Clickmenu.populateData(tableData, cmLevel);
}
function hideEmpty() {
Clickmenu.hideAll();
return false;
}
function hideSpecific(level) {
if (level === 0 || level === 1) {
Clickmenu.hide('contentMenu'+level);
}
}
function showHideSelectorBoxes(action) {
toggleSelectorBoxes(action);
}
// ## END ##
/*
* This code has been copied from Project_CMS
* Copyright (c) 2005 by Phillip Berndt (www.pberndt.com)
*
* Extended Textarea for IE and Firefox browsers
* Features:
* - Possibility to place tabs in