[server] Port BFSS to py3k
The BFSS API changes in python 3:
* 'defaultdir' MUST be a unicode object
* 'fsencoding' MUST NOT be set
In python 2, fsencoding handles both the encoding of file paths on the
file system (utf-8 by default, but the system may actually be using
something else) and the encoding of file paths that will be stored in
the database.
So in python 3, we wipe the slate clean:
* rely on sys.getfilesystemencoding() to convert unicode objects to
bytes
* always encode paths to utf-8 for storage in the database
Caveat emptor / here be dragons:
* sys.getfilesystemencoding() depends on the current locale, which
therefore MUST be set properly
* when migrating an existing instance from py2 to py3, one MAY need
to reencode file paths stored in the database
/*
* Treeview 1.5pre - jQuery plugin to hide and show branches of a tree
*
* http://bassistance.de/jquery-plugins/jquery-plugin-treeview/
* http://docs.jquery.com/Plugins/Treeview
*
* Copyright 2010 Jörn Zaefferer
* Released under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
;(function($) {
// TODO rewrite as a widget, removing all the extra plugins
$.extend($.fn, {
swapClass: function(c1, c2) {
var c1Elements = this.filter('.' + c1);
this.filter('.' + c2).removeClass(c2).addClass(c1);
c1Elements.removeClass(c1).addClass(c2);
return this;
},
replaceClass: function(c1, c2) {
return this.filter('.' + c1).removeClass(c1).addClass(c2).end();
},
hoverClass: function(className) {
className = className || "hover";
return this.hover(function() {
$(this).addClass(className);
}, function() {
$(this).removeClass(className);
});
},
heightToggle: function(animated, callback) {
animated ?
this.animate({ height: "toggle" }, animated, callback) :
this.each(function(){
jQuery(this)[ jQuery(this).is(":hidden") ? "show" : "hide" ]();
if(callback)
callback.apply(this, arguments);
});
},
heightHide: function(animated, callback) {
if (animated) {
this.animate({ height: "hide" }, animated, callback);
} else {
this.hide();
if (callback)
this.each(callback);
}
},
prepareBranches: function(settings) {
if (!settings.prerendered) {
// mark last tree items
this.filter(":last-child:not(ul)").addClass(CLASSES.last);
// collapse whole tree, or only those marked as closed, anyway except those marked as open
this.filter((settings.collapsed ? "" : "." + CLASSES.closed) + ":not(." + CLASSES.open + ")").find(">ul").hide();
}
// return all items with sublists
return this.filter(":has(>ul)");
},
applyClasses: function(settings, toggler) {
// TODO use event delegation
this.filter(":has(>ul):not(:has(>a))").find(">span").unbind("click.treeview").bind("click.treeview", function(event) {
// don't handle click events on children, eg. checkboxes
if ( this == event.target )
toggler.apply($(this).next());
}).add( $("a", this) ).hoverClass();
if (!settings.prerendered) {
// handle closed ones first
this.filter(":has(>ul:hidden)")
.addClass(CLASSES.expandable)
.replaceClass(CLASSES.last, CLASSES.lastExpandable);
// handle open ones
this.not(":has(>ul:hidden)")
.addClass(CLASSES.collapsable)
.replaceClass(CLASSES.last, CLASSES.lastCollapsable);
// create hitarea if not present
var hitarea = this.find("div." + CLASSES.hitarea);
if (!hitarea.length)
hitarea = this.prepend("<div class=\"" + CLASSES.hitarea + "\"/>").find("div." + CLASSES.hitarea);
hitarea.removeClass().addClass(CLASSES.hitarea).each(function() {
var classes = "";
$.each($(this).parent().attr("class").split(" "), function() {
classes += this + "-hitarea ";
});
$(this).addClass( classes );
})
}
// apply event to hitarea
this.find("div." + CLASSES.hitarea).click( toggler );
},
treeview: function(settings) {
settings = $.extend({
cookieId: "treeview"
}, settings);
if ( settings.toggle ) {
var callback = settings.toggle;
settings.toggle = function() {
return callback.apply($(this).parent()[0], arguments);
};
}
// factory for treecontroller
function treeController(tree, control) {
// factory for click handlers
function handler(filter) {
return function() {
// reuse toggle event handler, applying the elements to toggle
// start searching for all hitareas
toggler.apply( $("div." + CLASSES.hitarea, tree).filter(function() {
// for plain toggle, no filter is provided, otherwise we need to check the parent element
return filter ? $(this).parent("." + filter).length : true;
}) );
return false;
};
}
// click on first element to collapse tree
$("a:eq(0)", control).click( handler(CLASSES.collapsable) );
// click on second to expand tree
$("a:eq(1)", control).click( handler(CLASSES.expandable) );
// click on third to toggle tree
$("a:eq(2)", control).click( handler() );
}
// handle toggle event
function toggler() {
$(this)
.parent()
// swap classes for hitarea
.find(">.hitarea")
.swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
.end()
// swap classes for parent li
.swapClass( CLASSES.collapsable, CLASSES.expandable )
.swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
// find child lists
.find( ">ul" )
// toggle them
.heightToggle( settings.animated, settings.toggle );
if ( settings.unique ) {
$(this).parent()
.siblings()
// swap classes for hitarea
.find(">.hitarea")
.replaceClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.replaceClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea )
.end()
.replaceClass( CLASSES.collapsable, CLASSES.expandable )
.replaceClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
.find( ">ul" )
.heightHide( settings.animated, settings.toggle );
}
}
this.data("toggler", toggler);
function serialize() {
function binary(arg) {
return arg ? 1 : 0;
}
var data = [];
branches.each(function(i, e) {
data[i] = $(e).is(":has(>ul:visible)") ? 1 : 0;
});
$.cookie(settings.cookieId, data.join(""), settings.cookieOptions );
}
function deserialize() {
var stored = $.cookie(settings.cookieId);
if ( stored ) {
var data = stored.split("");
branches.each(function(i, e) {
$(e).find(">ul")[ parseInt(data[i]) ? "show" : "hide" ]();
});
}
}
// add treeview class to activate styles
this.addClass("treeview");
// prepare branches and find all tree items with child lists
var branches = this.find("li").prepareBranches(settings);
switch(settings.persist) {
case "cookie":
var toggleCallback = settings.toggle;
settings.toggle = function() {
serialize();
if (toggleCallback) {
toggleCallback.apply(this, arguments);
}
};
deserialize();
break;
case "location":
var current = this.find("a").filter(function() {
return location.href.toLowerCase().indexOf(this.href.toLowerCase()) == 0;
});
if ( current.length ) {
// TODO update the open/closed classes
var items = current.addClass("selected").parents("ul, li").add( current.next() ).show();
if (settings.prerendered) {
// if prerendered is on, replicate the basic class swapping
items.filter("li")
.swapClass( CLASSES.collapsable, CLASSES.expandable )
.swapClass( CLASSES.lastCollapsable, CLASSES.lastExpandable )
.find(">.hitarea")
.swapClass( CLASSES.collapsableHitarea, CLASSES.expandableHitarea )
.swapClass( CLASSES.lastCollapsableHitarea, CLASSES.lastExpandableHitarea );
}
}
break;
}
branches.applyClasses(settings, toggler);
// if control option is set, create the treecontroller and show it
if ( settings.control ) {
treeController(this, settings.control);
$(settings.control).show();
}
return this;
}
});
// classes used by the plugin
// need to be styled via external stylesheet, see first example
$.treeview = {};
var CLASSES = ($.treeview.classes = {
open: "open",
closed: "closed",
expandable: "expandable",
expandableHitarea: "expandable-hitarea",
lastExpandableHitarea: "lastExpandable-hitarea",
collapsable: "collapsable",
collapsableHitarea: "collapsable-hitarea",
lastCollapsableHitarea: "lastCollapsable-hitarea",
lastCollapsable: "lastCollapsable",
lastExpandable: "lastExpandable",
last: "last",
hitarea: "hitarea"
});
})(jQuery);