The jsmin filter fails to produce the correct output on a complex script (included below), a line break is inserted in the wrong place. The same javascript is compressed correctly with the older jsmin filter included in the django-compressor 0.5.3 (mintchaos?).
$ LC_ALL=en_US diff -c widget-working.js widget-broken.js
*** widget-working.js 2011-05-03 11:09:41.553279754 +0200
--- widget-broken.js 2011-05-03 10:25:59.120443254 +0200
***************
*** 46,50 ****
var addToolbarHtmlClickEventListener=function(){jQuery(document.html).click(function(event){jQuery('#kth-toolbar ul li ul').hide();event.stopPropagation();});}
var mouseLeaveTimer;var isApiDataLoaded;function renderKthToolbar(){renderInitialToolbar();initializeToolbar("#kth-toolbar","#showkth-toolbar");addToolbarDeactivateClickEventListener("#hidekth-toolbar","#kth-toolbar","#showkth-toolbar");addToolbarActivateClickEventListener("#kth-toolbar","#showkth-toolbar");addToolbarHtmlClickEventListener();jQuery(".kth-toolbar-menu").live('mouseleave',function(){mouseLeaveTimer=window.setTimeout(function(){hideAllMenus();mouseLeaveTimer=null;},500);});jQuery(".kth-toolbar-menu").live('mouseenter',function(){if(mouseLeaveTimer){window.clearTimeout(mouseLeaveTimer);}});isApiDataLoaded=false;window.setTimeout(function(){if(!isApiDataLoaded){renderErrorToolbar();}},10000);jQuery.ajax({url:"http://fjo.ite.kth.se:8081/toolbar/api/1.0/"+KthToolbarConfig.language,dataType:"jsonp",jsonpCallback:"KthToolbarJSONPLoader",success:function(toolbarapi_data){isApiDataLoaded=true;if(toolbarapi_data.status=="OK"){renderPersonalizedToolbar(toolbarapi_data);}else if(toolbarapi_data.status=="anonymous"){renderAnonymousToolbar(toolbarapi_data);}else{renderErrorToolbar();}}
});}
! jQuery(document).ready(function(){renderKthToolbar();});}
! KthToolbar();
\ No newline at end of file
--- 46,49 ----
var addToolbarHtmlClickEventListener=function(){jQuery(document.html).click(function(event){jQuery('#kth-toolbar ul li ul').hide();event.stopPropagation();});}
var mouseLeaveTimer;var isApiDataLoaded;function renderKthToolbar(){renderInitialToolbar();initializeToolbar("#kth-toolbar","#showkth-toolbar");addToolbarDeactivateClickEventListener("#hidekth-toolbar","#kth-toolbar","#showkth-toolbar");addToolbarActivateClickEventListener("#kth-toolbar","#showkth-toolbar");addToolbarHtmlClickEventListener();jQuery(".kth-toolbar-menu").live('mouseleave',function(){mouseLeaveTimer=window.setTimeout(function(){hideAllMenus();mouseLeaveTimer=null;},500);});jQuery(".kth-toolbar-menu").live('mouseenter',function(){if(mouseLeaveTimer){window.clearTimeout(mouseLeaveTimer);}});isApiDataLoaded=false;window.setTimeout(function(){if(!isApiDataLoaded){renderErrorToolbar();}},10000);jQuery.ajax({url:"http://fjo.ite.kth.se:8081/toolbar/api/1.0/"+KthToolbarConfig.language,dataType:"jsonp",jsonpCallback:"KthToolbarJSONPLoader",success:function(toolbarapi_data){isApiDataLoaded=true;if(toolbarapi_data.status=="OK"){renderPersonalizedToolbar(toolbarapi_data);}else if(toolbarapi_data.status=="anonymous"){renderAnonymousToolbar(toolbarapi_data);}else{renderErrorToolbar();}}
});}
! jQuery(document).ready(function(){renderKthToolbar();});}KthToolbar();
\ No newline at end of file
if (!KthToolbarConfig) {
var KthToolbarConfig = {};
}
if (!KthToolbarConfig.frontUrl) {
KthToolbarConfig.frontUrl = "http://fjo.ite.kth.se:8081";
}
if (!KthToolbarConfig.loginUrl) {
KthToolbarConfig.loginUrl = "http://fjo.ite.kth.se:8081/accounts/login/?next=" + escape(location.href);
} else {
if (KthToolbarConfig.loginUrl.charAt(0) == '?') {
KthToolbarConfig.loginUrl = "http://fjo.ite.kth.se:8081/accounts/login/?next=" + escape(location.href + KthToolbarConfig.loginUrl);
} else {
KthToolbarConfig.loginUrl = "http://fjo.ite.kth.se:8081/accounts/login/?next=" + escape(KthToolbarConfig.loginUrl);
}
}
if (!KthToolbarConfig.loginMethod) {
KthToolbarConfig.loginMethod = 'POST';
}
if (!KthToolbarConfig.locale) {
KthToolbarConfig.locale = "sv_SE";
}
KthToolbarConfig.language = KthToolbarConfig.locale.split('_')[0];
if (KthToolbar && console && console.warn) {
console.warn("KthToolbar already defined on page!");
}
var KthToolbar = function() {
jQuery("head").append("<link rel='stylesheet' type='text/css' href='" + KthToolbarConfig.frontUrl + "/static/toolbar/toolbar-screen.css'>" +
"<link rel='stylesheet' type='text/css' href='" + KthToolbarConfig.frontUrl + "/static/toolbar/reset-context-min.css'>" +
"<!--[if IE 7]><link rel='stylesheet' type='text/css' href='" + KthToolbarConfig.frontUrl + "/static/toolbar/ie7-toolbar-screen.css'><![endif]-->");
var lang = {
"Startsida för www.kth.se": { en: "Start page for www.kth.se" },
"Dölj": { en: "Hide" },
"Dölj toolbaren": { en: "Hide toolbar" },
"Mina sidor": { en: "My pages" },
"Visa": { en: "Show" },
"Logga in": { en: "Log in" },
"Laddar personlig information": { en: "Loading personalized information" },
"Kunde inte ladda personlig information (klicka för att försöka igen)": { en: "Failed to load customized information (click to retry)" }
};
function translate(text) {
function trans(sv) {
var translations = lang[sv];
if (!translations) {
return sv;
}
var translation = translations[KthToolbarConfig.language];
if (!translation) {
return sv;
}
return translation;
}
return text.replace(/#\{(.*?)\}/g, function(str, p1) {
return trans(p1);
});
}
/**
* Create the initial toolbar that is displayed until more information i known
*/
function renderInitialToolbar() {
var html = "<div class='yui3-cssreset'><div id='kth-toolbar' class='yui3-cssreset'><ul class='kth-toolbar-items'><li class='kth-toolbar-left kth-toolbar-home'><a href='http://www.kth.se' title='#{Startsida för www.kth.se}'><img src='" + KthToolbarConfig.frontUrl + "/static/toolbar/kth-logo-24-24.png' width='24' height='24'></a></li><li id='kth-toolbar-profile' class='kth-toolbar-left'><span style='color: #cccccc'>#{Laddar personlig information}...</span></li><li id='kth-toolbar-linksets-insertion-point' style='display: none'></li><li id='hidekth-toolbar' title='#{Dölj toolbaren}'>#{Dölj}</li><li id='kth-toolbar-webmail' class='kth-toolbar-right kth-toolbar-button'><a href='https://webmail.kth.se'>#{Webmail}</a></li><li id='kth-toolbar-my-pages' class='kth-toolbar-right kth-toolbar-button'><a id='kth-toolbar-mypages-link'>#{Mina sidor}</a></li></ul></div><div id='showkth-toolbar'>#{Visa}</div></div>";
jQuery('body').append(translate(html));
jQuery('#kth-toolbar-mypages-link').attr('href', 'https://www.kth.se/student/minasidor/?l=' + KthToolbarConfig.locale);
}
/**
* adjust toolbar for (async deduced) error situation so personal/loggedin cannot be known
*/
function renderErrorToolbar() {
jQuery('#kth-toolbar #kth-toolbar-profile').html(translate("<a href='http://fjo.ite.kth.se:8081/accounts/login/?next=" + escape(location.href) + "'>#{Kunde inte ladda personlig information (klicka för att försöka igen)}</a>"));
}
/**
* adjust toolbar for (async deduced) not logged in
*/
function renderAnonymousToolbar(toolbarapi_data) {
jQuery('#kth-toolbar #kth-toolbar-profile').html(translate("<form action='" + KthToolbarConfig.loginUrl + "' method='" + KthToolbarConfig.loginMethod + "'><input type='submit' name='kth-toolbar-login-button' value='#{Logga in}' class='kth-toolbar-login' /></form>"));
}
/**
* adjust toolbar for (async fetched) personal information
*/
function renderPersonalizedToolbar(toolbarapi_data) {
/* NOTE! name and avatar_html is leaked into the global name space on purpose. */
name = '';
avatar_html = '';
if (toolbarapi_data.avatar) {
avatar_html = '<span class="kth-toolbar-small-profile-picture"><img src="' + toolbarapi_data.avatar.url + '" alt="' + toolbarapi_data.avatar.alt + '" width="' + toolbarapi_data.avatar.width + '" height="' + toolbarapi_data.avatar.height + '"></span>';
}
name = toolbarapi_data.name.firstname + ' ' + toolbarapi_data.name.lastname;
jQuery('#kth-toolbar #kth-toolbar-profile').html('<a href="' + toolbarapi_data.homeurl + '" class="kth-toolbar-profile-link">'
+ avatar_html +
'<span class="kth-toolbar-label">' + name + '</span></a>');
jQuery("#kth-toolbar-insert-username").html(name);
jQuery.each(toolbarapi_data.linksets, function(index, linkset) {
if (linkset.links.length > 0) {
var html = "<li id='" + index + "' class='kth-toolbar-left kth-toolbar-menu " + linkset.type + "'><ul id='kth-toolbar-toolbar-menu" + index + "' class='kth-toolbar-menuitems'>";
jQuery.each(linkset.links, function(index, link) {
html += "<li class='kth-toolbar-item'>";
html += "<a href='" + link.url + "' class='kth-toolbar-label'>" + link.name + "</a>";
if (link.extra) {
html += "<a class='kth-toolbar-extra' href='" + link.url + "'>" + link.extra + "</a>";
}
html += "</li>";
});
html += "</ul><span class='kth-toolbar-button title'>" + linkset.name + "</span></li>";
jQuery("#kth-toolbar-linksets-insertion-point").before(html);
addMenuActivatorClickEventListener("#kth-toolbar-toolbar-menu" + index, "#" + index);
}
});
}
/**
* Toolbar functions
*/
function slideElementVertically(element, from, to, animationSpeed) {
jQuery(element).css({height: from}).animate({ height: to }, animationSpeed);
}
/**
* Shows the toolbar by sliding it up.
*
* @param toolbarId the actual toolbar element.
*/
function showToolbar(toolbarId, animationSpeed) {
slideElementVertically(toolbarId, 0, 34, animationSpeed);
}
/**
* Hides the toolbar by sliding it down.
*
* @param toolbarId the actual toolbar element.
*/
function hideToolbar(toolbarId) {
slideElementVertically(toolbarId, 34, 0, 'slow');
}
/**
* Shows the "Show toolbar" tab by sliding it up.
*
* @param tabId the element for the "Show toolbar".
*/
function showTab(tabId) {
slideElementVertically(tabId, -5, 24, 'slow');
}
/**
* Hides the "Show toolbar" tab by sliding it down.
*
* @param tabId the element for the "Show toolbar".
*/
function hideTab(tabId) {
slideElementVertically(tabId, 24, -5, 'slow');
}
/**
* Checks if the toolbar is activated (visible).
*
* @param toolbarId the actual toolbar element.
* @return true if the toolbar is activated, else false.
*/
function isActivated(toolbarId) {
if(jQuery(toolbarId).height() > 0) {
return true;
} else {
return false;
}
}
/**
* Hides all the menus in the toolbar.
*/
function hideAllMenus() {
jQuery('#kth-toolbar ul li ul').hide();
jQuery('#kth-toolbar .kth-toolbar-items .kth-toolbar-left.kth-toolbar-menu.kth-toolbar-active').removeClass('kth-toolbar-active');
}
/**
* Gets the toolbar visibility state value from the kthToolbar cookie.
*
* @param name the name of the cookie to retrieve the toolbar state from.
* @return the visibility state of the toolbar (true/false) .
*/
function getToolbarCookie(name) {
if (document.cookie.length > 0) {
start = document.cookie.indexOf(name + "=");
if (start != -1) {
start = start + name.length+1;
end = document.cookie.indexOf(";",start);
if (end == -1) {
end = document.cookie.length;
}
return unescape(document.cookie.substring(start,end));
}
}
return "";
}
/**
* Sets a kthToolbar cookie and prepare it with the toolbar state
* and expire time.
*
* @param name the name of the cookie
* @param value the value/state (true or false)
* @param expiredays number of days until the cookie should expire.
*/
function setToolbarCookies(name, value, expiredays) {
var exdate=new Date();
exdate.setDate(exdate.getDate() + expiredays);
var domain="kth.se";
var path="/";
document.cookie = name + "=" +escape( value ) +
( ( expiredays ) ? ";expires=" + exdate.toUTCString() : "" ) +
( ( path ) ? ";path=" + path : "" ) +
( ( domain ) ? ";domain=" + domain : "" );
}
/**
* Activates the toolbar and deactivates the "Show toolbar" tab.
*
* @param toolbarId the actual toolbar element.
* @param tabId the element for the "Show toolbar".
*/
function activateToolbar(toolbarId, tabId) {
hideTab(tabId);
showToolbar(toolbarId, 600);
}
/**
* Deactivates the toolbar, hide all menus, and activates
* the "Show toolbar" tab.
*
* @param toolbarId the actual toolbar element.
* @param tabId the element for the "Show toolbar".
*/
function deActivateToolbar(toolbarId, tabId) {
hideAllMenus();
hideToolbar(toolbarId);
showTab(tabId);
}
/**
* Calculates the menu position based on the number of
* elements in the menu and the height of the toolbar.
*
* The browser specific code sucks, but is the best I
* can figure out thus far. /fjo 20110103
*
* @param menuId element
* @return
*/
function calculateMenuPosition(menuId) {
var maxValue = 404;
var items = jQuery(menuId).children().size();
var itemHeight = 25;
var adjustment = 4;
if (jQuery.browser.msie) {
var pos = (items * itemHeight) + adjustment;
} else {
var pos = jQuery(menuId).height() + adjustment;
}
if (pos < maxValue) {
return pos;
} else {
return maxValue;
}
}
/**
* Initializes the toolbar by checking the cookie for the toolar state.
* If no cookie is set, show the toolar and hide the "Show toolbar" tab.
*
* @param toolbarId the actual toolbar element.
* @param tabId the element for the "Show toolbar".
*/
function initializeToolbar(toolbarId, tabId) {
var isVisible = getToolbarCookie("kthToolbar");
if (isVisible=="") {
jQuery(tabId).toggle();
showToolbar(toolbarId, 0);
} else if (isVisible == "true") {
jQuery(toolbarId).css({height: 34});
jQuery(tabId).toggle();
} else if (isVisible == "false") {
jQuery(tabId).css({height: 20});
}
}
/**
* Deactivates the toolbar and shows the "Show toolbar" tab.
* This function also sets a cookie to remember the toolbar state.
*
* @param activatorId the activator element for the toolbar.
* @param toolbarId the actual toolbar element.
* @param tabId the element for the "Show toolbar".
*/
function addToolbarDeactivateClickEventListener(activatorId, toolbarId, tabId) {
jQuery(activatorId).click(function (event) {
if(isActivated(toolbarId)) {
deActivateToolbar(toolbarId, tabId);
setToolbarCookies("kthToolbar", "false", 7);
}
event.stopPropagation();
});
}
/**
* Activates the toolbar and hides the "Show toolbar" tab.
* This function also sets a cookie to remember the toolbar state.
*
* @param toolbarId the actual toolbar element.
* @param tabId the element for the "Show toolbar".
*/
function addToolbarActivateClickEventListener(toolbarId, tabId) {
jQuery(tabId).click(function (event) {
activateToolbar(toolbarId, tabId);
setToolbarCookies("kthToolbar", "true", 7);
event.stopPropagation();
});
}
/**
* Check if the given menu is showing.
*
* @param menuId the element containing the menu.
* @return true if the menu is showing, else false.
*/
function showingMenu(menuId) {
var display = jQuery(menuId).css('display');
if (display != 'none') {
return true;
} else {
return false;
}
}
/**
* Toggle the given menu element and calculates
* the position of the expanded menu.
*
* @param menuId the element of the active menu.
*/
function toggleMenu(menuId) {
var pos = calculateMenuPosition(menuId);
jQuery(menuId).css('top', -pos);
jQuery(menuId).toggle();
}
/**
* Toggle the menus depending on their states.
*
* @param menuId the element that contains the menu.
* @param event the click event on the menu activator element.
*/
function toggleMenus(menuId, event, activator) {
var toggle = true;
if(showingMenu(menuId)) {
toggle = false;
}
hideAllMenus();
if(toggle) {
toggleMenu(menuId);
jQuery(activator).addClass('kth-toolbar-active');
}
event.stopPropagation();
}
/**
* Adds a click event listener for the given menu button on the toolbar.
*/
function addMenuActivatorClickEventListener(menuId, activator) {
jQuery(activator).click(function (event) {
toggleMenus(menuId, event, activator);
});
}
/**
* Close the menus if the HTML element is clicked.
*/
var addToolbarHtmlClickEventListener = function() {
jQuery(document.html).click(function (event) {
jQuery('#kth-toolbar ul li ul').hide();
event.stopPropagation();
});
}
var mouseLeaveTimer;
var isApiDataLoaded;
/**
* Main bootstrapt function that creates the toolbar and fills it,
* intented to be called once the page is loaded
*/
function renderKthToolbar() {
renderInitialToolbar();
initializeToolbar("#kth-toolbar", "#showkth-toolbar");
addToolbarDeactivateClickEventListener("#hidekth-toolbar", "#kth-toolbar", "#showkth-toolbar");
addToolbarActivateClickEventListener("#kth-toolbar", "#showkth-toolbar");
addToolbarHtmlClickEventListener();
jQuery(".kth-toolbar-menu").live('mouseleave', function() {
mouseLeaveTimer = window.setTimeout(function() {
hideAllMenus();
mouseLeaveTimer = null;
}, 500);
});
jQuery(".kth-toolbar-menu").live('mouseenter', function() {
if (mouseLeaveTimer) {
window.clearTimeout(mouseLeaveTimer);
}
});
isApiDataLoaded = false;
window.setTimeout(function() {
if (!isApiDataLoaded) {
renderErrorToolbar();
}
}, 10000);
jQuery.ajax({
url: "http://fjo.ite.kth.se:8081/toolbar/api/1.0/" + KthToolbarConfig.language,
dataType: "jsonp",
jsonpCallback: "KthToolbarJSONPLoader",
success: function(toolbarapi_data) {
isApiDataLoaded = true;
if (toolbarapi_data.status == "OK") {
renderPersonalizedToolbar(toolbarapi_data);
} else if (toolbarapi_data.status == "anonymous") {
renderAnonymousToolbar(toolbarapi_data);
} else {
renderErrorToolbar();
}
}
/*timeout, error and complete do not work when we use jsonp in 1.4 */
});
}
/**
* Activate when document is loaded.
*/
jQuery(document).ready(function () {
renderKthToolbar();
});
}; /*end KthToolbar*/
KthToolbar();