Browse Source

Forgot to merge these changes too >.<

Yes, this was actually the first time I did this.
sh4nks 9 years ago
parent
commit
025712ec90

+ 50 - 15
flaskbb/forum/models.py

@@ -888,22 +888,41 @@ class Category(db.Model, CRUDMixin):
         :param user: The user object is needed to check if we also need their
         :param user: The user object is needed to check if we also need their
                      forumsread object.
                      forumsread object.
         """
         """
+        # import Group model locally to avoid cicular imports
+        from flaskbb.user.models import Group
         if user.is_authenticated():
         if user.is_authenticated():
+            # get list of user group ids
+            user_groups = [gr.id for gr in user.groups]
+            # filter forums by user groups
+            user_forums = Forum.query.\
+                filter(Forum.groups.any(Group.id.in_(user_groups))).\
+                subquery()
+
+            forum_alias = aliased(Forum, user_forums)
+            # get all
             forums = cls.query.\
             forums = cls.query.\
-                join(Forum, cls.id == Forum.category_id).\
+                join(forum_alias, cls.id == forum_alias.category_id).\
                 outerjoin(ForumsRead,
                 outerjoin(ForumsRead,
-                          db.and_(ForumsRead.forum_id == Forum.id,
+                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                   ForumsRead.user_id == user.id)).\
                                   ForumsRead.user_id == user.id)).\
-                add_entity(Forum).\
+                add_entity(forum_alias).\
                 add_entity(ForumsRead).\
                 add_entity(ForumsRead).\
-                order_by(Category.id, Category.position, Forum.position).\
+                order_by(Category.position, Category.id,
+                         forum_alias.position).\
                 all()
                 all()
         else:
         else:
-            # Get all the forums
+            guest_group = Group.get_guest_group()
+            # filter forums by guest groups
+            guest_forums = Forum.query.\
+                filter(Forum.groups.any(Group.id == guest_group.id)).\
+                subquery()
+
+            forum_alias = aliased(Forum, guest_forums)
             forums = cls.query.\
             forums = cls.query.\
-                join(Forum, cls.id == Forum.category_id).\
-                add_entity(Forum).\
-                order_by(Category.id, Category.position, Forum.position).\
+                join(forum_alias, cls.id == forum_alias.category_id).\
+                add_entity(forum_alias).\
+                order_by(Category.position, Category.id,
+                         forum_alias.position).\
                 all()
                 all()
 
 
         return get_categories_and_forums(forums, user)
         return get_categories_and_forums(forums, user)
@@ -922,23 +941,39 @@ class Category(db.Model, CRUDMixin):
         :param user: The user object is needed to check if we also need their
         :param user: The user object is needed to check if we also need their
                      forumsread object.
                      forumsread object.
         """
         """
+        from flaskbb.user.models import Group
         if user.is_authenticated():
         if user.is_authenticated():
+            # get list of user group ids
+            user_groups = [gr.id for gr in user.groups]
+            # filter forums by user groups
+            user_forums = Forum.query.\
+                filter(Forum.groups.any(Group.id.in_(user_groups))).\
+                subquery()
+
+            forum_alias = aliased(Forum, user_forums)
             forums = cls.query.\
             forums = cls.query.\
                 filter(cls.id == category_id).\
                 filter(cls.id == category_id).\
-                join(Forum, cls.id == Forum.category_id).\
+                join(forum_alias, cls.id == forum_alias.category_id).\
                 outerjoin(ForumsRead,
                 outerjoin(ForumsRead,
-                          db.and_(ForumsRead.forum_id == Forum.id,
+                          db.and_(ForumsRead.forum_id == forum_alias.id,
                                   ForumsRead.user_id == user.id)).\
                                   ForumsRead.user_id == user.id)).\
-                add_entity(Forum).\
+                add_entity(forum_alias).\
                 add_entity(ForumsRead).\
                 add_entity(ForumsRead).\
-                order_by(Forum.position).\
+                order_by(forum_alias.position).\
                 all()
                 all()
         else:
         else:
+            guest_group = Group.get_guest_group()
+            # filter forums by guest groups
+            guest_forums = Forum.query.\
+                filter(Forum.groups.any(Group.id == guest_group.id)).\
+                subquery()
+
+            forum_alias = aliased(Forum, guest_forums)
             forums = cls.query.\
             forums = cls.query.\
                 filter(cls.id == category_id).\
                 filter(cls.id == category_id).\
-                join(Forum, cls.id == Forum.category_id).\
-                add_entity(Forum).\
-                order_by(Forum.position).\
+                join(forum_alias, cls.id == forum_alias.category_id).\
+                add_entity(forum_alias).\
+                order_by(forum_alias.position).\
                 all()
                 all()
 
 
         if not forums:
         if not forums:

+ 0 - 12
flaskbb/management/forms.py

@@ -320,7 +320,6 @@ class ForumForm(Form):
         _("Group Access to Forum"),
         _("Group Access to Forum"),
         query_factory=selectable_groups,
         query_factory=selectable_groups,
         get_label="name",
         get_label="name",
-        widget=MultiSelect(),
         description=_("Select user groups that can access this forum.")
         description=_("Select user groups that can access this forum.")
     )
     )
 
 
@@ -368,17 +367,6 @@ class ForumForm(Form):
         else:
         else:
             field.data = approved_moderators
             field.data = approved_moderators
 
 
-    def validate_groups(self, field):
-
-        if field.data:
-            pass
-        elif field.raw_data:
-            ids = field.raw_data.pop().split(",")
-            groups = Group.query.filter(Group.id.in_(ids)).all()
-            field.data = groups
-        else:
-            field.data = []
-
     def save(self):
     def save(self):
         data = self.data
         data = self.data
         # remove the button
         # remove the button

+ 0 - 114
flaskbb/static/css/megalist-multiselect.css

@@ -1,114 +0,0 @@
-.megalist-mutliselect {
-  -webkit-user-select: none;
-  -moz-user-select: none;
-  -ms-user-select: none;
-  user-select: none;
-  overflow: hidden;
-}
-.megalist-mutliselect .megalist {
-  float: left;
-  width: 285px;
-}
-.megalist-mutliselect .megalist input[type=text] {
-  -webkit-box-sizing: border-box;
-  -moz-box-sizing: border-box;
-  -ms-box-sizing: border-box;
-  box-sizing: border-box;
-  width: 100% !important;
-  padding: 5px 4px;
-  margin-bottom: 5px;
-}
-.megalist-mutliselect .megalist .megalist-inner {
-  -webkit-box-sizing: border-box;
-  -moz-box-sizing: border-box;
-  -ms-box-sizing: border-box;
-  box-sizing: border-box;
-  position: relative;
-  height: 205px;
-  width: 100%;
-  overflow: hidden;
-  border: 1px solid silver;
-}
-.megalist-mutliselect .megalist .megalist-inner ul {
-  position: absolute;
-  padding: 0;
-  display: block;
-  margin-top: 0px;
-  width: 100%;
-  top: 0px;
-}
-.megalist-mutliselect .megalist .megalist-inner ul li {
-  margin: 0;
-  border: none;
-  white-space: nowrap;
-  overflow: hidden;
-  padding: 6px 0px 6px 10px !important;
-  display: block;
-  position: absolute;
-  width: 100%;
-  border-top: 1px solid #EDEDED;
-  line-height: 1em !important;
-  cursor: pointer;
-  color: #555;
-}
-.megalist-mutliselect .megalist .megalist-inner ul li:hover {
-  background-color: #08c;
-}
-.megalist-mutliselect .megalist .scrollbar {
-  -webkit-border-radius: 3px;
-  -moz-border-radius: 3px;
-  border-radius: 3px;
-  position: absolute;
-  right: 1px;
-  width: 11px;
-  height: 25px;
-  background-color: #bebebe;
-  z-index: 2;
-}
-.megalist-mutliselect .megalist .scrollbar:hover {
-  background-color: #afafaf;
-}
-.megalist-mutliselect .megalist .scrollbar-background {
-  position: absolute;
-  top: 0px;
-  right: 0px;
-  width: 14px;
-  height: 100%;
-  z-index: 1;
-  background-color: #ececec;
-}
-.megalist-mutliselect .move-buttons {
-  margin: 90px 0px;
-  float: left;
-}
-.megalist-mutliselect .move-buttons .move-button {
-  height: 30px;
-  padding: 0px 50px;
-  margin-bottom: 10px;
-  cursor: pointer;
-}
-.megalist-mutliselect .move-buttons .move-button svg {
-  fill: #0374bb;
-}
-.megalist-mutliselect .move-buttons .move-button:hover svg {
-  fill: #024570;
-}
-.megalist-mutliselect .move-buttons .move-button:hover.arrow-left.no-svg .svg {
-  background-position: 0% 100%;
-}
-.megalist-mutliselect .move-buttons .move-button:hover.arrow-right.no-svg .svg {
-  background-position: 100% 100%;
-}
-.megalist-mutliselect .move-buttons .move-button.no-svg .svg {
-  background-repeat: no-repeat;
-  background-position: center;
-  background: url('../icons/megalist-icons.png');
-  width: 32px;
-  height: 32px;
-}
-.megalist-mutliselect .move-buttons .move-button.arrow-left.no-svg .svg {
-  background-position: 0% 0%;
-}
-.megalist-mutliselect .move-buttons .move-button.arrow-right.no-svg .svg {
-  background-position: 100% 0%;
-}

+ 0 - 1174
flaskbb/static/js/megalist-multiselect.js

@@ -1,1174 +0,0 @@
-!function($){
-  'use strict';
-
- /* LIST CLASS DEFINITION
-  * ========================= */
-
-  var Megalist = function(element, $parent) {
-    var html;
-
-    if ($parent === undefined){
-    //if there's no $parent then we are creating one
-        this.$el = element;
-        this.setOptions(this.$el.options);
-        // build HTML
-        html = this.buildParentDOM();
-        //source list - data to choose from
-        this.$el.sourceList = new Megalist(html.srcElement, this.$el);
-        //destination list - data chosen by user
-        this.$el.destinationList = new Megalist(html.dstElement, this.$el);
-
-    } else {
-    //else just init one of the megalistSide children
-        this.init(element, $parent);
-    }
-    return this;
-  };
-
-  Megalist.prototype = {
-
-    constructor: Megalist,
-
-    /**
-     * megalistSide constructor - initializes one side of megalist
-     *
-     * @param {object} element - jQuery object on witch megalist is initialized
-     * @param {object} $parent - optional jQuery object with parent for
-     *                           megalistSide initialization only
-     * @return {object} - returns self
-     */
-    init: function(element, $parent) {
-        this.$el = element;
-        this.$parent = $parent;
-
-        //defaults
-        this.processedItems = {};
-        this.totalItems = [];
-        this.itemHeight = -1;
-        this.listItems = $();
-        this.suffix = undefined;
-        this.yPosition = 0;
-        this.filteredData = [];
-        this.pageHeight = 0;
-        this.scrollingActive = false;
-
-        //init widget
-        this.setOptions(this.$parent.options);
-        this.getSuffix();
-        this.buildDOM();
-        this.bindEvents();
-        this.bindData();
-        this.updateLayout();
-        this.generatePOST(this.conf.BUILD_FULL_POST);
-
-        return this;
-    },
-
-    /**
-     * Sets default options and extends them if configuration was provided on
-     * megalist initialization
-     *
-     * @param {object} options - object containing options for megalist
-     */
-    setOptions: function(options){
-        var conf = {};
-
-        // mimimum scrollbar height in pixels
-        conf.SCROLLBAR_MIN_SIZE = 12;
-        // inertial delay for megalist ui update after resize event occurs
-        conf.RESIZE_TIMEOUT_DELAY = 100;
-        // minimum characters to trigger quicksearch filtering
-        conf.MINIMUM_SEARCH_QUERY_SIZE = 3;
-        // build full or simple (comma separated ids) post
-        conf.BUILD_FULL_POST = false;
-        // move action event name to trigger
-        conf.MOVE_ACTION_NAME = 'move';
-        //functional suffixes for multiselect: destination list suffix
-        conf.DESTINATION_SUFFIX = 'dst';
-        // functional suffixes for multiselect: source list suffix
-        conf.SOURCE_SUFFIX = 'src';
-        // text to display as search input placeholder
-        conf.PLACEHOLDER_TEXT = 'Search';
-        // time to wait for first continous scrolling
-        conf.CONTINOUS_SCROLLING_FIRST_INTERVAL = 500;
-        // time to wait for every next continous scrolling
-        conf.CONTINOUS_SCROLLING_INTERVAL = 60;
-
-        if (typeof options === 'object'){
-            conf = $.extend(conf, options);
-        }
-        this.conf = conf;
-    },
-
-    /**
-     * Builds required html elements for both source and destination
-     * megalistSide and append them to parent element
-     */
-    buildParentDOM: function() {
-        var srcElement, dstElement;
-
-        this.$el.html('');
-        this.$el.addClass('megalist-mutliselect');
-
-        //create 2 containers for megalists and buttons between then append
-        srcElement = $( '<div/>', {
-            'id': this.$el.attr('id') + '_' + this.conf.SOURCE_SUFFIX,
-            'class': 'megalist-inner'
-        });
-        dstElement = $( '<div/>', {
-            'id': this.$el.attr('id') + '_' + this.conf.DESTINATION_SUFFIX,
-            'class': 'megalist-inner'
-        });
-        this.$el.$moveButtons = $( '<div/>', {
-            'class': 'move-buttons'
-        });
-
-        this.$el.append(srcElement, this.$el.$moveButtons, dstElement);
-
-        return {srcElement:srcElement, dstElement:dstElement};
-    },
-
-    /**
-     * Builds required html elements for megalistSide:
-     * searchbox, scrollbar, move button and hidden result input
-     */
-    buildDOM: function() {
-        var arrowIcon = 'arrow-left';
-
-        if (this.suffix === this.conf.SOURCE_SUFFIX) {
-            arrowIcon = 'arrow-right';
-        }
-
-        this.$el.wrap('<div class="megalist"></div>"');
-
-        this.$search = $('<input/>', {
-            'id': this.$el.attr('id') + '_search',
-            'placeholder': this.conf.PLACEHOLDER_TEXT,
-            'type': 'text'
-        });
-        this.$scrollbar = $('<div/>', {
-            'id': this.$el.attr('id') + '_scrollbar',
-            'class': 'scrollbar'
-        });
-        this.$scrollbarBackground = $('<div/>', {
-            'class': 'scrollbar-background'
-        });
-        this.$moveall = $('<div/>', {
-            'class': 'move-button ' + arrowIcon
-        });
-
-        if (Modernizr.svg) {
-            this.$moveall.append($(
-                '<svg width="32" height="32" viewBox="0 0 64 64">' +
-                '<use xlink:href="#' + arrowIcon + '"></svg>'
-            ));
-        } else {
-            this.$moveall.addClass('no-svg');
-            this.$moveall.append($('<div class="svg" />'));
-        }
-
-        //attach to container in parent
-        this.$parent.$moveButtons.append(this.$moveall);
-
-        this.$input = $('<input/>', {
-            'name': this.name,
-            'type': 'hidden'
-        });
-        this.$ul = $('<ul />');
-
-        this.$el.before(this.$search);
-
-        // Set tabindex, so the element can be in focus
-        this.$el.attr('tabindex', '-1');
-    },
-
-    /**
-     * Resolves suffix for megalistSide so that it know if it's source or
-     * destination side. Resolving is based on id of the container
-     */
-    getSuffix: function() {
-        var id_tokens, lastToken;
-
-        id_tokens = this.$el.attr('id').split('_');
-        lastToken = id_tokens[id_tokens.length - 1];
-        this.name = id_tokens.splice(id_tokens, id_tokens.length-1).join('_');
-
-        if (lastToken === this.conf.SOURCE_SUFFIX) {
-            this.suffix = this.conf.SOURCE_SUFFIX;
-        } else if (lastToken === this.conf.DESTINATION_SUFFIX) {
-            this.suffix = this.conf.DESTINATION_SUFFIX;
-        }
-    },
-
-    /**
-     * Returns targetList for current megalistSide. In not defined, gets proper
-     * one from $parent first and stores it for later use
-     *
-     * @return {object} - returns target list
-     */
-    getTargetList: function() {
-        if (!(this.targetList instanceof Object)){
-            if ( this.suffix === this.conf.SOURCE_SUFFIX) {
-                this.targetList = this.$parent.destinationList;
-            } else if ( this.suffix === this.conf.DESTINATION_SUFFIX) {
-                this.targetList = this.$parent.sourceList;
-            }
-        }
-        return this.targetList;
-    },
-
-    /**
-     * Binds all events need by the widget
-     */
-    bindEvents: function() {
-        var self = this,
-            filterEvent;
-
-       $(window).resize(function(event){
-            return self.onResize(event);
-        });
-
-        $(window).bind('keydown', function(event) {
-            return self.onKeydown(event);
-        });
-
-        this.$el.mousedown(function() {
-            setTimeout(function(){
-                this.focus();
-            }, 1);
-        });
-
-        this.$el.bind('mousewheel DOMMouseScroll', function(event) {
-            event.preventDefault();
-            return self.onMouseWheel(event);
-        });
-
-        this.$el.click(function(event) {
-            self.processListClick(event);
-        });
-
-        this.$scrollbar.bind('mousedown', function(event) {
-             self.onScrollbarStart(event);
-        });
-
-        this.$scrollbarBackground.mousedown(function(event) {
-            self.scrollingActive = true;
-            self.scrollDir = undefined;
-            self.onScrollbarBackgroundClick(event);
-        });
-
-        this.$scrollbarBackground.mouseleave(function(event) {
-            self.scrollingActive = false;
-        });
-
-        this.$scrollbarBackground.mouseup(function(event) {
-            self.scrollingActive = false;
-        });
-
-        this.$moveall.click(function(event) {
-            self.onMoveAll(event);
-        });
-
-        if (Modernizr.hasEvent('input', this.$search)) {
-            filterEvent = 'input';
-        } else {
-            filterEvent = 'keyup';
-        }
-
-        this.$search.on(filterEvent, function() {
-            self.yPosition = 0;
-            self.filterList();
-        });
-    },
-
-    /**
-     * Extracts the supplied data for megalistSide from data-provider-src or
-     * data-provider-dst attributes depending which side is being loaded.
-     * The attributes must be set on megalist container.
-     */
-    bindData: function() {
-        this.origData = this.$parent.attr('data-provider-' + this.suffix);
-        if (this.origData.length){
-            this.dataProviderOrig =  this.parseData(this.origData);
-            this.$parent.attr('data-provider-' + this.suffix, '');
-        } else {
-            this.dataProviderOrig = {};
-        }
-
-        this.dataProvider = this.dataProviderOrig;
-
-        this.clearSelectedIndex();
-
-        this.$ul.find('li').each(function() {
-            $(this).remove();
-        });
-
-        this.yPosition = 0;
-    },
-
-    /**
-     * Parses the data extracted from container attribues. Currently two
-     * formats are supported: JSON and passing old <select> element that
-     * is being replaced by this widget
-     *
-     * @param {string} origData - string extracted from attribute
-     *                            (JSON or old select html)
-     * @return {Array} parsed - parsed data array
-     */
-    parseData: function(origData){
-        var parsed = [], item = {};
-        var selected = ':not(:selected)';
-
-        //first see if it's JSON
-        try {
-          parsed = $.parseJSON(origData);
-        } catch(e) {
-          //not JSON
-        }
-        //ok, maybe it's being fed <option>s from an old select?
-        if (origData.substr(0, 7) == '<select'){
-          if (this.suffix === this.conf.DESTINATION_SUFFIX) {
-              selected = ':selected';
-          }
-           $.map($('option', origData).filter(selected), function(opt){
-               item.listValue = opt.value;
-               item.label = opt.text;
-               parsed.push(item);
-               item = {};
-           });
-        } else if ((origData.indexOf('<select') > -1)){
-            console.log('ERROR: the supplied string MUST start with <select');
-        }
-
-        return parsed;
-    },
-
-    /**
-     * Updates responsive mutliselect on window resize by recalculating new
-     * sizing and redrawing megalistSide widgets. Updating has some inertia
-     * added resizing only after RESIZE_TIMEOUT_DELAY is reached
-     */
-    onResize: function() {
-        clearTimeout(this.reizeTimeout);
-        var self = this,
-            totalHeight = this.dataProvider.length * this.itemHeight,
-            maxPosition = totalHeight - this.$el.height();
-
-        maxPosition = Math.max(0, maxPosition);
-        this.yPosition = Math.min(this.yPosition, maxPosition);
-        this.reizeTimeout = setTimeout(function() {
-            self.updateLayout();
-        }, this.conf.RESIZE_TIMEOUT_DELAY);
-    },
-
-    /**
-    * @TODO - @FIXME
-    * @param {event} event - user key press event
-    */
-    onKeydown: function (event) {
-        var delta = 0,
-            action = this.conf.MOVE_ACTION_NAME,
-            self = this,
-            oldindex = this.getSelectedIndex(),
-            index = oldindex + delta;
-
-        if (!this.$el.is(':focus')) {
-            return;
-        }
-
-        switch (event.which) {
-            case 33:  // Page up
-                delta = -1 * Math.floor(this.$el.height() / this.itemHeight);
-                break;
-
-            case 34:  // Page down
-                delta = Math.floor(this.$el.height() / this.itemHeight);
-                break;
-
-            case 38:  // Up
-                delta = -1;
-                break;
-
-            case 40:  // Down
-                delta = 1;
-                break;
-
-            default:
-                return;
-        }
-
-        if (index > this.dataProvider.length -1) {
-            index = this.dataProvider.length;
-        }
-        if (index < 0) {
-            index = 0;
-        }
-
-        if (index === oldindex) {
-            return false;
-        }
-
-        this.setSelectedIndex(index);
-
-        if (this.yPosition > (index * this.itemHeight)) {
-            this.yPosition = (index*this.itemHeight);
-        }
-        if (this.yPosition < ((index+1) * this.itemHeight) - this.$el.height()) {
-            this.yPosition = ((index+1)*this.itemHeight) - this.$el.height();
-        }
-
-        this.updateLayout();
-        this.cleanupTimeout = setTimeout(function() {
-            self.cleanupListItems();
-        }, 100);
-
-        var target = this.$ul.find('.megalistSelected');
-
-        setTimeout(function() {
-            var event = $.Event(action, data),
-                data = {
-                    selectedIndex: index,
-                    srcElement: $(target),
-                    item: self.dataProvider[index],
-                    destination: self.$el.attr('id')
-                };
-            self.$el.trigger(event);
-        }, 150);
-
-        return false;
-    },
-
-    /**
-     * Updates megalistSide widget on mouse scroll event
-     * only concerned about vertical scroll
-     *
-     * @param {event} event - mouse wheel event
-     */
-    onMouseWheel: function (event) {
-        clearTimeout(this.cleanupTimeout);
-
-        var self = this,
-            orgEvent = event.originalEvent,
-            delta = 0,
-            totalHeight = this.dataProvider.length * this.itemHeight,
-            maxPosition = totalHeight - this.$el.height();
-
-        // Old school scrollwheel delta
-        if (orgEvent.wheelDelta) {
-            delta = orgEvent.wheelDelta / 120;
-        }
-        if (orgEvent.detail) {
-            delta = -orgEvent.detail / 3;
-        }
-
-        // Webkit
-        if ( orgEvent.wheelDeltaY !== undefined ) {
-            delta = orgEvent.wheelDeltaY / 120;
-        }
-
-        this.yPosition -= delta * this.itemHeight;
-
-        //limit the mouse wheel scroll area
-        if (this.yPosition > maxPosition) {
-            this.yPosition = maxPosition;
-        }
-        if (this.yPosition < 0) {
-            this.yPosition = 0;
-        }
-
-        this.updateLayout();
-        this.cleanupTimeout = setTimeout(function() {
-            self.cleanupListItems();
-        }, 100);
-
-        return false;
-    },
-
-    /**
-     * Handles click event on megalist element
-     *
-     * @param {event} event - mouse wheel event
-     */
-    processListClick: function(event) {
-        var self = this,
-            target = event.target,
-            index = $(target).attr('list-index'),
-            out_data = this.dataProvider[index],
-            clicked_value = this.dataProvider[index];
-
-        while (target.parentNode !== null) {
-            if (target.nodeName === 'LI') {
-                break;
-            }
-            target = target.parentNode;
-        }
-
-        if (target.nodeName !== 'LI') {
-            return false;
-        }
-
-        if (index === this.selectedIndex) {
-            return false;
-        }
-
-        this.setSelectedIndex(index);
-
-        this.getTargetList().updateDataProvider(out_data);
-
-        self.clearSelectedIndex();
-
-        self.dataProviderOrig.splice(
-            self.dataProviderOrig.indexOf(clicked_value), 1
-        );
-
-        if (this.yPosition > this.getMaxPosition()) {
-            this.yPosition -= this.itemHeight;
-        }
-
-        self.filterList();
-        this.$parent.destinationList.generatePOST(this.conf.BUILD_FULL_POST);
-
-        return true;
-    },
-
-    /**
-     * Handles click on "move all" button, move all items from one
-     * megalistSide to the other, renders POST result into hidden input field
-     * after the action is performed
-     *
-     */
-    onMoveAll: function(){
-        var out_data = this.dataProvider,
-            i;
-
-        this.getTargetList().updateDataProvider(out_data);
-
-        this.clearSelectedIndex();
-        this.dataProvider = [];
-        if (this.filteredData.length > 0) {
-            for (i = this.filteredData.length - 1; i >= 0; i--) {
-                this.dataProviderOrig.splice(this.filteredData[i], 1);
-            }
-        } else if (!this.searchingIsActive()) {
-            this.dataProviderOrig = [];
-        }
-        this.$parent.destinationList.generatePOST(this.conf.BUILD_FULL_POST);
-        this.updateLayout();
-    },
-
-    /**
-     * Handles drag event on scrollbar - binds events appropriate to user
-     * action and delgates event to correct function
-     *
-     * @param {event} event - mouse event on scrollbar
-     */
-    onScrollbarStart: function(event) {
-        var self = this;
-
-        this.unbindScrollbarEvents();
-        this.scrollbarInputCoordinates = this.getInputCoordinates(event);
-
-        $(document).bind('mousemove', function(event) {
-             self.onScrollbarMove(event);
-        });
-
-        $(document).bind('mouseup', function() {
-             self.unbindScrollbarEvents();
-        });
-
-        event.preventDefault();
-        return false;
-    },
-
-    /**
-     * Handles drag event on scroll bar and recalculates what items should be
-     * rendered in the viewport
-     *
-     * @param {event} event - scrollbar drag event to get coordinates from
-     */
-    onScrollbarMove: function(event) {
-        var newCoordinates = this.getInputCoordinates(event),
-            height = this.$el.height(),
-            totalHeight = this.dataProvider.length * this.itemHeight,
-            scrollbarHeight = this.$scrollbar.height(),
-            yDelta = this.scrollbarInputCoordinates.y - newCoordinates.y,
-            yPosition = parseInt(this.$scrollbar.css('top'), 10),
-            usingMinSize = scrollbarHeight === this.conf.SCROLLBAR_MIN_SIZE,
-            heightOffset = usingMinSize ? scrollbarHeight : 0,
-            newYPosition;
-
-        // valid move occurs only when pressing left mouse button
-        if (event.which !== 1) {
-            this.unbindScrollbarEvents();
-            return;
-        }
-
-        yPosition -= yDelta;
-
-        yPosition = Math.max(yPosition, 0);
-        yPosition = Math.min(yPosition, height - scrollbarHeight);
-        yPosition = Math.min(yPosition, height);
-
-        this.$scrollbar.css('top', yPosition);
-        this.scrollbarInputCoordinates = newCoordinates;
-
-        newYPosition = (
-            yPosition / (height - heightOffset) *
-            (this.itemHeight * this.dataProvider.length - 1)
-        );
-        newYPosition = Math.max(0, newYPosition);
-        newYPosition = Math.min(
-            newYPosition, totalHeight - height
-        );
-
-        this.yPosition = newYPosition;
-        this.updateLayout(true);
-
-        event.preventDefault();
-        return false;
-    },
-
-    /**
-     * Utility function to remove events bound to the scrollbar
-     *
-     */
-    unbindScrollbarEvents: function() {
-        $(document).unbind('mousemove');
-        $(document).unbind('mouseup');
-    },
-
-    /**
-     * Handles click event on scrollbar background - a click on scrollbar
-     * background should cause pageUp/PageDown action on the viewport
-     *
-     * @param {event} event - scrollbar click event to get coordinates from
-     */
-    onScrollbarBackgroundClick: function(event, repeatTimeout) {
-        var self = this,
-            // firefox uses originalEvent.layerY instead of offsetY
-            yOffset = event.offsetY !== undefined ? event.offsetY : event.originalEvent.layerY,
-            scrollbarBackgroundHeight = $(event.target).height(),
-            clickPos = yOffset / scrollbarBackgroundHeight,
-            listTotalHeight = this.dataProvider.length * this.itemHeight,
-            scrollbarHeightFraction = this.$scrollbar.height() / scrollbarBackgroundHeight,
-            currentPos = this.yPosition / listTotalHeight,
-            offsetToMove = this.pageHeight,
-            shouldMoveUp = clickPos > currentPos + scrollbarHeightFraction,
-            shouldMoveDown = clickPos < currentPos;
-
-        if (!this.scrollingActive) {
-            return;
-        }
-
-        if (this.scrollDir == undefined) {
-            if (shouldMoveUp) {
-                this.scrollDir = 1;
-            } else if (shouldMoveDown) {
-                this.scrollDir = -1;
-            } else {
-                return;
-            }
-        }
-
-        if (shouldMoveUp && this.scrollDir === 1) {
-            this.yPosition += offsetToMove;
-        } else if (shouldMoveDown && this.scrollDir === -1) {
-            this.yPosition -= offsetToMove;
-        } else {
-            return;
-        }
-
-        if (this.yPosition > listTotalHeight - this.pageHeight) {
-            this.yPosition = listTotalHeight - this.pageHeight;
-        } else if (this.yPosition < 0) {
-            this.yPosition = 0;
-        }
-
-        this.updateLayout();
-
-        if (this.scrollingActive) {
-            if (repeatTimeout === undefined) {
-                repeatTimeout = this.conf.CONTINOUS_SCROLLING_FIRST_INTERVAL;
-            }
-            setTimeout(function() {
-                self.onScrollbarBackgroundClick(
-                    event, self.conf.CONTINOUS_SCROLLING_INTERVAL
-                );
-            }, repeatTimeout);
-        }
-    },
-
-    /**
-     * Removes items rendered in megalist that no longer fit into the viewport
-     * and removes them from processed items cache
-     */
-    cleanupListItems: function() {
-        //remove any remaining LI elements hanging out on the dom
-        var temp = [],
-            item, index, x;
-
-        for (x = 0; x < this.totalItems.length; x++ ) {
-            item = this.totalItems[x];
-            index = item.attr('list-index');
-            if (this.processedItems[index] === undefined) {
-                item.remove();
-            }
-        }
-        //cleanup processedItems array
-        if (this.processedItems) {
-            for (index in this.processedItems) {
-                temp.push(this.processedItems[index]);
-            }
-        }
-        this.totalItems = temp;
-    },
-
-    /**
-     * Extracts input coordinates from the event
-     *
-     * @param {event} event - event to get coordinates from
-     * @return {object} result - object containge x and y coordinates
-     */
-    getInputCoordinates: function (event) {
-        var targetEvent = event,
-            result = {
-                x: Math.round(targetEvent.pageX),
-                y: Math.round(targetEvent.pageY)
-            };
-        return result;
-    },
-
-    /**
-     * Main rendering function for megalist: redraws the list based on data
-     * fed to megalist and scrollbar position. Iterates over visible items
-     * and renders them then calls update on the scrollbar if ignoreScrollbar
-     * not set to true
-     *
-     * @param {boolean} ignoreScrollbar - a flag allowing scrollbar to not be
-     * redrawn if not necessary
-     */
-    updateLayout: function(ignoreScrollbar) {
-        var height = this.$el.height(),
-            i = -1,
-            startPosition = Math.ceil(this.yPosition / this.itemHeight),
-            maxHeight = 2 * (height + (2 * this.itemHeight)),
-            index, item, currentPosition, parentLength;
-
-        if (this.dataProvider.length > 0) {
-            this.$ul.detach();
-            this.processedItems = {};
-
-            while (i * this.itemHeight < maxHeight) {
-                index = Math.min(
-                    Math.max(startPosition + i, 0),
-                    this.dataProvider.length
-                );
-
-                item = this.getItemAtIndex(index);
-                this.totalItems.push(item);
-
-                this.processedItems[index.toString()] = item;
-                currentPosition = i * this.itemHeight;
-                this.setItemPosition(item, 0, currentPosition);
-
-                if (item.parent().length <= 0) {
-                    this.$ul.append(item);
-
-                    if (this.itemHeight <= 0) {
-                        this.prepareLayout(item);
-                        this.updateLayout();
-                        return;
-                    }
-                }
-                i++;
-            }
-
-            this.cleanupListItems();
-            if (ignoreScrollbar !== true) {
-                this.updateScrollBar();
-            }
-            if (this.$scrollbar.parent().length > 0){
-                this.$scrollbar.before(this.$ul);
-            } else {
-                 this.$el.append(this.$ul);
-            }
-        } else {
-            if (this.$ul.children().length > 0) {
-                this.$ul.empty();
-                this.cleanupListItems();
-                parentLength = this.$scrollbar.parent().length > 0;
-                if (ignoreScrollbar !== true && parentLength > 0) {
-                    this.updateScrollBar();
-                }
-            } else {
-                this.hideScrollbar();
-            }
-        }
-    },
-
-    /**
-     * Prepares layout by appending list to DOM, and calculating site of
-     * element and size of single page
-     *
-     * @param  {object} item    jQuery object representing single item
-     */
-    prepareLayout: function(item) {
-        var itemsPerPage;
-
-        // make sure item have proper height by filling it with content
-        item.html('&nsbp;');
-        this.$el.append(this.$ul);
-
-        // calculate height of item and height of single page
-        this.itemHeight = item.outerHeight();
-        itemsPerPage = Math.floor(
-            this.$ul.parent().height() / this.itemHeight
-        );
-        this.pageHeight = this.itemHeight * itemsPerPage;
-    },
-
-    /**
-     * Shows scrollbar
-     */
-    showScrollbar: function() {
-        this.$el.append(this.$scrollbar, this.$scrollbarBackground);
-    },
-
-    /**
-     * Hides scrollbar
-     */
-    hideScrollbar: function() {
-        this.$scrollbar.detach();
-        this.$scrollbarBackground.detach();
-    },
-
-    /**
-     * Renders the scrollbar as a part of UI update when list is scrolled or
-     * modified
-     */
-    updateScrollBar: function() {
-        var height = this.$el.height(),
-            maxScrollbarHeight = height,
-            maxItemsHeight = this.dataProvider.length * this.itemHeight,
-            targetHeight = maxScrollbarHeight * Math.min(
-                maxScrollbarHeight / maxItemsHeight, 1
-            ),
-            actualHeight = Math.floor(
-                Math.max(targetHeight, this.conf.SCROLLBAR_MIN_SIZE)
-            ),
-            scrollPosition = (
-                this.yPosition / (maxItemsHeight - height) *
-                (maxScrollbarHeight - actualHeight)
-            ),
-            parent = this.$scrollbar.parent();
-
-        if (scrollPosition < 0) {
-            actualHeight = Math.max(actualHeight + scrollPosition, 0);
-            scrollPosition = 0;
-        } else if (scrollPosition > (height - actualHeight)) {
-            actualHeight = Math.min(actualHeight, height - scrollPosition);
-        }
-
-        this.$scrollbar.height(actualHeight);
-
-        if ((this.dataProvider.length * this.itemHeight) <= height) {
-            if (parent.length > 0) {
-                this.hideScrollbar();
-            }
-        } else {
-            if (parent.length <= 0) {
-                this.showScrollbar();
-            }
-            this.$scrollbar.css('top', scrollPosition);
-        }
-    },
-
-    /**
-     * Utility function to set offset css on an item
-     *
-     * @param {object} item - megalist element
-     * @param {int} x - x offset in pixels
-     * @param {int} y - y offset in pixels
-     */
-    setItemPosition: function(item, x, y) {
-        item.css('left', x);
-        item.css('top', y);
-    },
-
-    /**
-     * Gets megalist item at given index. Parses it to <li> item if necessary
-     *
-     * @param {int} i - object index
-     * @return {object} - jQuery object containing selected <li> element
-     */
-    getItemAtIndex: function(i) {
-        var item, iString, data;
-        if (this.dataProvider === this.listItems) {
-            item = $(this.listItems[i]);
-        }
-        else if (i !== undefined){
-            iString = i.toString();
-
-            if (this.listItems[iString] === null ||
-                this.listItems[iString] === undefined
-            ) {
-                item = $('<li />');
-                this.listItems[iString] = item;
-            } else {
-                item = $(this.listItems[i]);
-            }
-
-            if (i >= 0 && i < this.dataProvider.length){
-                data = this.dataProvider[i];
-                item.html(data.label);
-                item.attr('list-value', data.listValue);
-            }
-        }
-        if (item !== null && item !== undefined) {
-            item.attr('list-index', i);
-        }
-        return item;
-    },
-
-    /**
-     * Returns index of currently selected item
-     *
-     * @return {int} - index of item that was selected
-     */
-    getSelectedIndex: function() {
-        return parseInt(this.selectedIndex, 10);
-    },
-
-    /**
-     * Sets item at given index as selected and adds appropriate styling to it
-     *
-     * @param {int} index = index of item that was selected
-     */
-    setSelectedIndex: function(index) {
-        var item = this.getItemAtIndex(this.selectedIndex);
-
-        if (item !== undefined) {
-            item.removeClass('megalistSelected');
-        }
-
-        this.selectedIndex = index;
-        this.getItemAtIndex(index).addClass('megalistSelected');
-    },
-
-    /**
-     * Clears currently selected object by removing styling and setting
-     * internal variable pointing to currently selected item to -1
-     *
-     */
-    clearSelectedIndex: function() {
-        var item = this.getItemAtIndex(this.selectedIndex);
-
-        if (item !== undefined) {
-            item.removeClass('megalistSelected');
-        }
-        this.selectedIndex = -1;
-    },
-
-    /**
-     * Sets initial data for megalist and updates layout with it
-     *
-     * @param {Array} dataProvider - object array to initially feed megalist
-     */
-    setDataProvider: function(dataProvider) {
-        this.clearSelectedIndex();
-        this.dataProviderOrig = dataProvider;
-        this.dataProvider = dataProvider;
-
-        this.$ul.find('li').each(function() {
-            $(this).remove();
-        });
-
-        this.yPosition = 0;
-        this.updateLayout();
-    },
-
-    /**
-     * Updates megalist with new data. Accepts either a single object or
-     * an Array of objects and updates layout with new data
-     *
-     * @param {object|Array} newElement - new object / array of objects
-     *                                    to be inserted into the list
-     */
-    updateDataProvider: function(newElement) {
-        this.clearSelectedIndex();
-
-        if ($.isArray(newElement)) {
-            $.merge(this.dataProviderOrig, newElement);
-        } else {
-            this.dataProviderOrig.push(newElement);
-        }
-        this.filterList();
-
-        this.$ul.find('li').each(function() {
-            $(this).remove();
-        });
-
-        this.yPosition = this.getMaxPosition();
-        this.updateLayout();
-    },
-
-    /**
-     * Returns current objects in megalist
-     *
-     * @return {Array} - list of objects in megalist
-     *
-     */
-    getDataProvider: function() {
-        return this.dataProvider;
-    },
-
-    /**
-     * Get maximum value of yPosition
-     *
-     * @return {int} maximum value of yPosition
-     */
-    getMaxPosition: function() {
-        var height = this.$el.height(),
-            totalHeight = this.dataProvider.length * this.itemHeight;
-
-        return totalHeight > height ? totalHeight - height : 0;
-    },
-
-    /**
-     * Checks if search input has minimal length and therefor searching  is
-     * active.
-     * @return {bool} true when searching is active, false otherwise
-     */
-    searchingIsActive: function() {
-        var querySize = $.trim(this.$search.val()).length;
-        return querySize >= this.conf.MINIMUM_SEARCH_QUERY_SIZE;
-    },
-
-    /**
-     * Parses search input and performs filtering of list. The algorithm
-     * splits the search query to tokens and seeks for all subsequent
-     * tokens in the data. If not all tokens are found in the data then this
-     * record is excluded from the results.
-     *
-     */
-    filterList: function() {
-        var self = this,
-            searchQuery = $.trim(this.$search.val().toLowerCase()),
-            searchTokens = searchQuery.split(' '),
-            i;
-
-        this.filteredData = [];
-
-        for (i = searchTokens.length - 1; i >= 0; i--) {
-            searchTokens[i] = $.trim(searchTokens[i]);
-        }
-
-        if (!this.searchingIsActive()) {
-            this.dataProvider = this.dataProviderOrig;
-
-        } else {
-            this.dataProvider = $.grep(
-                this.dataProviderOrig,
-                function(val, index) {
-                    return self.testListElement(val, searchTokens, index);
-                }
-            );
-        }
-
-        this.updateLayout();
-    },
-
-    /**
-     * Tests if element of list data meets the query search criterias.
-     *
-     * @param  {string} val          value of the element to test
-     * @param  {array}  searchTokens query search tokens
-     * @param  {int}    index        the index of element in original data
-     *
-     * @return {boolean}             whether element meets the criteria on not
-     */
-    testListElement: function(val, searchTokens, index) {
-        var tokenIndex = 0,
-            valI = 0,
-            tokenDetected = true,
-            i;
-        val = val.label.toLowerCase();
-        while (valI < val.length) {
-            if (val[valI++] === searchTokens[tokenIndex][0]) {
-                tokenDetected = true;
-                for (i = 1; i < searchTokens[tokenIndex].length; i++) {
-                    if (val[valI] === searchTokens[tokenIndex][i]) {
-                        valI++;
-                    } else {
-                        tokenDetected = false;
-                        break;
-                    }
-                }
-                if (tokenDetected && ++tokenIndex === searchTokens.length) {
-                    this.filteredData[this.filteredData.length] = index;
-                    return true;
-                }
-            }
-        }
-        return false;
-    },
-
-    /**
-     * Generates string result of what is currently selected and populates
-     * this.$input value, adding it to DOM id necessary. Only does it for
-     * destination list. Result can be in 2 formats: POST-like (full) or comma
-     * separated
-     *
-     * @param {boolean} full - wherever to generate full POST-like data
-     * @return {string} result - string result of what is currently selected
-     */
-    generatePOST: function(full) {
-      var i,
-          postData = [],
-          result = {},
-          name = this.name;
-
-      if (this.suffix === this.conf.DESTINATION_SUFFIX){
-          for (i = 0; i < this.dataProviderOrig.length; i++) {
-              postData[i] = this.dataProviderOrig[i].listValue;
-          }
-          if (full === true){
-              result[name] = postData;
-              result = decodeURIComponent($.param(result, true ));
-              //cut out first name so that the post will not contain repetition
-              result = result.slice(this.name.length + 1, result.length);
-              this.$input.val(result);
-          } else {
-              result = postData.join(',');
-              this.$input.val(result);
-          }
-
-          if (this.$el.has(this.$input).length < 1) {
-              this.$el.append(this.$input);
-          }
-          return result;
-      } else {
-          return '';
-      }
-    }
-
-  };
-
-  /* LIST PLUGIN DEFINITION
-   * ========================== */
-
-  $.fn.megalist = function (option, params) {
-    if (typeof option === 'object') { this.options = option;}
-    var multiselect = new Megalist(this);
-    if (typeof option === 'string') { this.result = multiselect[option](params); }
-    return this;
-  };
-
-  // injects svg arrow icons into dom
-  $(document).ready(function(){
-    $('body').append('<svg style="display: none" xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 32 32"> <path id="arrow-left" d="M48 10.667q1.104 0 1.885 0.781t0.781 1.885-0.792 1.896l-16.771 16.771 16.771 16.771q0.792 0.792 0.792 1.896t-0.781 1.885-1.885 0.781q-1.125 0-1.896-0.771l-18.667-18.667q-0.771-0.771-0.771-1.896t0.771-1.896l18.667-18.667q0.771-0.771 1.896-0.771zM32 10.667q1.104 0 1.885 0.781t0.781 1.885-0.792 1.896l-16.771 16.771 16.771 16.771q0.792 0.792 0.792 1.896t-0.781 1.885-1.885 0.781q-1.125 0-1.896-0.771l-18.667-18.667q-0.771-0.771-0.771-1.896t0.771-1.896l18.667-18.667q0.771-0.771 1.896-0.771z"></path> <path id="arrow-right" d="M29.333 10.667q1.104 0 1.875 0.771l18.667 18.667q0.792 0.792 0.792 1.896t-0.792 1.896l-18.667 18.667q-0.771 0.771-1.875 0.771t-1.885-0.781-0.781-1.885q0-1.125 0.771-1.896l16.771-16.771-16.771-16.771q-0.771-0.771-0.771-1.896 0-1.146 0.76-1.906t1.906-0.76zM13.333 10.667q1.104 0 1.875 0.771l18.667 18.667q0.792 0.792 0.792 1.896t-0.792 1.896l-18.667 18.667q-0.771 0.771-1.875 0.771t-1.885-0.781-0.781-1.885q0-1.125 0.771-1.896l16.771-16.771-16.771-16.771q-0.771-0.771-0.771-1.896 0-1.146 0.76-1.906t1.906-0.76z"></path></svg>');});
-
-  //adds indexOf to arry prototype for ie8
-  if(!Array.prototype.indexOf){Array.prototype.indexOf=function(e){var t=this.length>>>0;var n=Number(arguments[1])||0;n=n<0?Math.ceil(n):Math.floor(n);if(n<0)n+=t;for(;n<t;n++){if(n in this&&this[n]===e)return n}return-1}}
-
-} (window.jQuery);

+ 0 - 828
flaskbb/static/js/modernizr.custom.js

@@ -1,828 +0,0 @@
-/* Modernizr 2.8.3 (Custom Build) | MIT & BSD
- * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load
- */
-;
-
-
-
-window.Modernizr = (function( window, document, undefined ) {
-
-    var version = '2.8.3',
-
-    Modernizr = {},
-
-    enableClasses = true,
-
-    docElement = document.documentElement,
-
-    mod = 'modernizr',
-    modElem = document.createElement(mod),
-    mStyle = modElem.style,
-
-    inputElem  = document.createElement('input')  ,
-
-    smile = ':)',
-
-    toString = {}.toString,
-
-    prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
-
-
-
-    omPrefixes = 'Webkit Moz O ms',
-
-    cssomPrefixes = omPrefixes.split(' '),
-
-    domPrefixes = omPrefixes.toLowerCase().split(' '),
-
-    ns = {'svg': 'http://www.w3.org/2000/svg'},
-
-    tests = {},
-    inputs = {},
-    attrs = {},
-
-    classes = [],
-
-    slice = classes.slice,
-
-    featureName, 
-
-
-    injectElementWithStyles = function( rule, callback, nodes, testnames ) {
-
-      var style, ret, node, docOverflow,
-          div = document.createElement('div'),
-                body = document.body,
-                fakeBody = body || document.createElement('body');
-
-      if ( parseInt(nodes, 10) ) {
-                      while ( nodes-- ) {
-              node = document.createElement('div');
-              node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
-              div.appendChild(node);
-          }
-      }
-
-                style = ['&#173;','<style id="s', mod, '">', rule, '</style>'].join('');
-      div.id = mod;
-          (body ? div : fakeBody).innerHTML += style;
-      fakeBody.appendChild(div);
-      if ( !body ) {
-                fakeBody.style.background = '';
-                fakeBody.style.overflow = 'hidden';
-          docOverflow = docElement.style.overflow;
-          docElement.style.overflow = 'hidden';
-          docElement.appendChild(fakeBody);
-      }
-
-      ret = callback(div, rule);
-        if ( !body ) {
-          fakeBody.parentNode.removeChild(fakeBody);
-          docElement.style.overflow = docOverflow;
-      } else {
-          div.parentNode.removeChild(div);
-      }
-
-      return !!ret;
-
-    },
-
-
-
-    isEventSupported = (function() {
-
-      var TAGNAMES = {
-        'select': 'input', 'change': 'input',
-        'submit': 'form', 'reset': 'form',
-        'error': 'img', 'load': 'img', 'abort': 'img'
-      };
-
-      function isEventSupported( eventName, element ) {
-
-        element = element || document.createElement(TAGNAMES[eventName] || 'div');
-        eventName = 'on' + eventName;
-
-            var isSupported = eventName in element;
-
-        if ( !isSupported ) {
-                if ( !element.setAttribute ) {
-            element = document.createElement('div');
-          }
-          if ( element.setAttribute && element.removeAttribute ) {
-            element.setAttribute(eventName, '');
-            isSupported = is(element[eventName], 'function');
-
-                    if ( !is(element[eventName], 'undefined') ) {
-              element[eventName] = undefined;
-            }
-            element.removeAttribute(eventName);
-          }
-        }
-
-        element = null;
-        return isSupported;
-      }
-      return isEventSupported;
-    })(),
-
-
-    _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
-
-    if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) {
-      hasOwnProp = function (object, property) {
-        return _hasOwnProperty.call(object, property);
-      };
-    }
-    else {
-      hasOwnProp = function (object, property) { 
-        return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
-      };
-    }
-
-
-    if (!Function.prototype.bind) {
-      Function.prototype.bind = function bind(that) {
-
-        var target = this;
-
-        if (typeof target != "function") {
-            throw new TypeError();
-        }
-
-        var args = slice.call(arguments, 1),
-            bound = function () {
-
-            if (this instanceof bound) {
-
-              var F = function(){};
-              F.prototype = target.prototype;
-              var self = new F();
-
-              var result = target.apply(
-                  self,
-                  args.concat(slice.call(arguments))
-              );
-              if (Object(result) === result) {
-                  return result;
-              }
-              return self;
-
-            } else {
-
-              return target.apply(
-                  that,
-                  args.concat(slice.call(arguments))
-              );
-
-            }
-
-        };
-
-        return bound;
-      };
-    }
-
-    function setCss( str ) {
-        mStyle.cssText = str;
-    }
-
-    function setCssAll( str1, str2 ) {
-        return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
-    }
-
-    function is( obj, type ) {
-        return typeof obj === type;
-    }
-
-    function contains( str, substr ) {
-        return !!~('' + str).indexOf(substr);
-    }
-
-    function testProps( props, prefixed ) {
-        for ( var i in props ) {
-            var prop = props[i];
-            if ( !contains(prop, "-") && mStyle[prop] !== undefined ) {
-                return prefixed == 'pfx' ? prop : true;
-            }
-        }
-        return false;
-    }
-
-    function testDOMProps( props, obj, elem ) {
-        for ( var i in props ) {
-            var item = obj[props[i]];
-            if ( item !== undefined) {
-
-                            if (elem === false) return props[i];
-
-                            if (is(item, 'function')){
-                                return item.bind(elem || obj);
-                }
-
-                            return item;
-            }
-        }
-        return false;
-    }
-
-    function testPropsAll( prop, prefixed, elem ) {
-
-        var ucProp  = prop.charAt(0).toUpperCase() + prop.slice(1),
-            props   = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
-
-            if(is(prefixed, "string") || is(prefixed, "undefined")) {
-          return testProps(props, prefixed);
-
-            } else {
-          props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
-          return testDOMProps(props, prefixed, elem);
-        }
-    }    tests['flexbox'] = function() {
-      return testPropsAll('flexWrap');
-    };    tests['canvas'] = function() {
-        var elem = document.createElement('canvas');
-        return !!(elem.getContext && elem.getContext('2d'));
-    };
-
-    tests['canvastext'] = function() {
-        return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function'));
-    };
-
-
-
-    tests['webgl'] = function() {
-        return !!window.WebGLRenderingContext;
-    };
-
-
-    tests['touch'] = function() {
-        var bool;
-
-        if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
-          bool = true;
-        } else {
-          injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) {
-            bool = node.offsetTop === 9;
-          });
-        }
-
-        return bool;
-    };
-
-
-
-    tests['geolocation'] = function() {
-        return 'geolocation' in navigator;
-    };
-
-
-    tests['postmessage'] = function() {
-      return !!window.postMessage;
-    };
-
-
-    tests['websqldatabase'] = function() {
-      return !!window.openDatabase;
-    };
-
-    tests['indexedDB'] = function() {
-      return !!testPropsAll("indexedDB", window);
-    };
-
-    tests['hashchange'] = function() {
-      return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7);
-    };
-
-    tests['history'] = function() {
-      return !!(window.history && history.pushState);
-    };
-
-    tests['draganddrop'] = function() {
-        var div = document.createElement('div');
-        return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div);
-    };
-
-    tests['websockets'] = function() {
-        return 'WebSocket' in window || 'MozWebSocket' in window;
-    };
-
-
-    tests['rgba'] = function() {
-        setCss('background-color:rgba(150,255,150,.5)');
-
-        return contains(mStyle.backgroundColor, 'rgba');
-    };
-
-    tests['hsla'] = function() {
-            setCss('background-color:hsla(120,40%,100%,.5)');
-
-        return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla');
-    };
-
-    tests['multiplebgs'] = function() {
-                setCss('background:url(https://),url(https://),red url(https://)');
-
-            return (/(url\s*\(.*?){3}/).test(mStyle.background);
-    };    tests['backgroundsize'] = function() {
-        return testPropsAll('backgroundSize');
-    };
-
-    tests['borderimage'] = function() {
-        return testPropsAll('borderImage');
-    };
-
-
-
-    tests['borderradius'] = function() {
-        return testPropsAll('borderRadius');
-    };
-
-    tests['boxshadow'] = function() {
-        return testPropsAll('boxShadow');
-    };
-
-    tests['textshadow'] = function() {
-        return document.createElement('div').style.textShadow === '';
-    };
-
-
-    tests['opacity'] = function() {
-                setCssAll('opacity:.55');
-
-                    return (/^0.55$/).test(mStyle.opacity);
-    };
-
-
-    tests['cssanimations'] = function() {
-        return testPropsAll('animationName');
-    };
-
-
-    tests['csscolumns'] = function() {
-        return testPropsAll('columnCount');
-    };
-
-
-    tests['cssgradients'] = function() {
-        var str1 = 'background-image:',
-            str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));',
-            str3 = 'linear-gradient(left top,#9f9, white);';
-
-        setCss(
-                       (str1 + '-webkit- '.split(' ').join(str2 + str1) +
-                       prefixes.join(str3 + str1)).slice(0, -str1.length)
-        );
-
-        return contains(mStyle.backgroundImage, 'gradient');
-    };
-
-
-    tests['cssreflections'] = function() {
-        return testPropsAll('boxReflect');
-    };
-
-
-    tests['csstransforms'] = function() {
-        return !!testPropsAll('transform');
-    };
-
-
-    tests['csstransforms3d'] = function() {
-
-        var ret = !!testPropsAll('perspective');
-
-                        if ( ret && 'webkitPerspective' in docElement.style ) {
-
-                      injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) {
-            ret = node.offsetLeft === 9 && node.offsetHeight === 3;
-          });
-        }
-        return ret;
-    };
-
-
-    tests['csstransitions'] = function() {
-        return testPropsAll('transition');
-    };
-
-
-
-    tests['fontface'] = function() {
-        var bool;
-
-        injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) {
-          var style = document.getElementById('smodernizr'),
-              sheet = style.sheet || style.styleSheet,
-              cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : '';
-
-          bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0;
-        });
-
-        return bool;
-    };
-
-    tests['generatedcontent'] = function() {
-        var bool;
-
-        injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) {
-          bool = node.offsetHeight >= 3;
-        });
-
-        return bool;
-    };
-    tests['video'] = function() {
-        var elem = document.createElement('video'),
-            bool = false;
-
-            try {
-            if ( bool = !!elem.canPlayType ) {
-                bool      = new Boolean(bool);
-                bool.ogg  = elem.canPlayType('video/ogg; codecs="theora"')      .replace(/^no$/,'');
-
-                            bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,'');
-
-                bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,'');
-            }
-
-        } catch(e) { }
-
-        return bool;
-    };
-
-    tests['audio'] = function() {
-        var elem = document.createElement('audio'),
-            bool = false;
-
-        try {
-            if ( bool = !!elem.canPlayType ) {
-                bool      = new Boolean(bool);
-                bool.ogg  = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,'');
-                bool.mp3  = elem.canPlayType('audio/mpeg;')               .replace(/^no$/,'');
-
-                                                    bool.wav  = elem.canPlayType('audio/wav; codecs="1"')     .replace(/^no$/,'');
-                bool.m4a  = ( elem.canPlayType('audio/x-m4a;')            ||
-                              elem.canPlayType('audio/aac;'))             .replace(/^no$/,'');
-            }
-        } catch(e) { }
-
-        return bool;
-    };
-
-
-    tests['localstorage'] = function() {
-        try {
-            localStorage.setItem(mod, mod);
-            localStorage.removeItem(mod);
-            return true;
-        } catch(e) {
-            return false;
-        }
-    };
-
-    tests['sessionstorage'] = function() {
-        try {
-            sessionStorage.setItem(mod, mod);
-            sessionStorage.removeItem(mod);
-            return true;
-        } catch(e) {
-            return false;
-        }
-    };
-
-
-    tests['webworkers'] = function() {
-        return !!window.Worker;
-    };
-
-
-    tests['applicationcache'] = function() {
-        return !!window.applicationCache;
-    };
-
-
-    tests['svg'] = function() {
-        return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect;
-    };
-
-    tests['inlinesvg'] = function() {
-      var div = document.createElement('div');
-      div.innerHTML = '<svg/>';
-      return (div.firstChild && div.firstChild.namespaceURI) == ns.svg;
-    };
-
-    tests['smil'] = function() {
-        return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate')));
-    };
-
-
-    tests['svgclippaths'] = function() {
-        return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath')));
-    };
-
-    function webforms() {
-                                            Modernizr['input'] = (function( props ) {
-            for ( var i = 0, len = props.length; i < len; i++ ) {
-                attrs[ props[i] ] = !!(props[i] in inputElem);
-            }
-            if (attrs.list){
-                                  attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement);
-            }
-            return attrs;
-        })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' '));
-                            Modernizr['inputtypes'] = (function(props) {
-
-            for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) {
-
-                inputElem.setAttribute('type', inputElemType = props[i]);
-                bool = inputElem.type !== 'text';
-
-                                                    if ( bool ) {
-
-                    inputElem.value         = smile;
-                    inputElem.style.cssText = 'position:absolute;visibility:hidden;';
-
-                    if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) {
-
-                      docElement.appendChild(inputElem);
-                      defaultView = document.defaultView;
-
-                                        bool =  defaultView.getComputedStyle &&
-                              defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' &&
-                                                                                  (inputElem.offsetHeight !== 0);
-
-                      docElement.removeChild(inputElem);
-
-                    } else if ( /^(search|tel)$/.test(inputElemType) ){
-                                                                                    } else if ( /^(url|email)$/.test(inputElemType) ) {
-                                        bool = inputElem.checkValidity && inputElem.checkValidity() === false;
-
-                    } else {
-                                        bool = inputElem.value != smile;
-                    }
-                }
-
-                inputs[ props[i] ] = !!bool;
-            }
-            return inputs;
-        })('search tel url email datetime date month week time datetime-local number range color'.split(' '));
-        }
-    for ( var feature in tests ) {
-        if ( hasOwnProp(tests, feature) ) {
-                                    featureName  = feature.toLowerCase();
-            Modernizr[featureName] = tests[feature]();
-
-            classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
-        }
-    }
-
-    Modernizr.input || webforms();
-
-
-     Modernizr.addTest = function ( feature, test ) {
-       if ( typeof feature == 'object' ) {
-         for ( var key in feature ) {
-           if ( hasOwnProp( feature, key ) ) {
-             Modernizr.addTest( key, feature[ key ] );
-           }
-         }
-       } else {
-
-         feature = feature.toLowerCase();
-
-         if ( Modernizr[feature] !== undefined ) {
-                                              return Modernizr;
-         }
-
-         test = typeof test == 'function' ? test() : test;
-
-         if (typeof enableClasses !== "undefined" && enableClasses) {
-           docElement.className += ' ' + (test ? '' : 'no-') + feature;
-         }
-         Modernizr[feature] = test;
-
-       }
-
-       return Modernizr; 
-     };
-
-
-    setCss('');
-    modElem = inputElem = null;
-
-    ;(function(window, document) {
-                var version = '3.7.0';
-
-            var options = window.html5 || {};
-
-            var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i;
-
-            var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i;
-
-            var supportsHtml5Styles;
-
-            var expando = '_html5shiv';
-
-            var expanID = 0;
-
-            var expandoData = {};
-
-            var supportsUnknownElements;
-
-        (function() {
-          try {
-            var a = document.createElement('a');
-            a.innerHTML = '<xyz></xyz>';
-                    supportsHtml5Styles = ('hidden' in a);
-
-            supportsUnknownElements = a.childNodes.length == 1 || (function() {
-                        (document.createElement)('a');
-              var frag = document.createDocumentFragment();
-              return (
-                typeof frag.cloneNode == 'undefined' ||
-                typeof frag.createDocumentFragment == 'undefined' ||
-                typeof frag.createElement == 'undefined'
-              );
-            }());
-          } catch(e) {
-                    supportsHtml5Styles = true;
-            supportsUnknownElements = true;
-          }
-
-        }());
-
-            function addStyleSheet(ownerDocument, cssText) {
-          var p = ownerDocument.createElement('p'),
-          parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement;
-
-          p.innerHTML = 'x<style>' + cssText + '</style>';
-          return parent.insertBefore(p.lastChild, parent.firstChild);
-        }
-
-            function getElements() {
-          var elements = html5.elements;
-          return typeof elements == 'string' ? elements.split(' ') : elements;
-        }
-
-            function getExpandoData(ownerDocument) {
-          var data = expandoData[ownerDocument[expando]];
-          if (!data) {
-            data = {};
-            expanID++;
-            ownerDocument[expando] = expanID;
-            expandoData[expanID] = data;
-          }
-          return data;
-        }
-
-            function createElement(nodeName, ownerDocument, data){
-          if (!ownerDocument) {
-            ownerDocument = document;
-          }
-          if(supportsUnknownElements){
-            return ownerDocument.createElement(nodeName);
-          }
-          if (!data) {
-            data = getExpandoData(ownerDocument);
-          }
-          var node;
-
-          if (data.cache[nodeName]) {
-            node = data.cache[nodeName].cloneNode();
-          } else if (saveClones.test(nodeName)) {
-            node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode();
-          } else {
-            node = data.createElem(nodeName);
-          }
-
-                                                    return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node;
-        }
-
-            function createDocumentFragment(ownerDocument, data){
-          if (!ownerDocument) {
-            ownerDocument = document;
-          }
-          if(supportsUnknownElements){
-            return ownerDocument.createDocumentFragment();
-          }
-          data = data || getExpandoData(ownerDocument);
-          var clone = data.frag.cloneNode(),
-          i = 0,
-          elems = getElements(),
-          l = elems.length;
-          for(;i<l;i++){
-            clone.createElement(elems[i]);
-          }
-          return clone;
-        }
-
-            function shivMethods(ownerDocument, data) {
-          if (!data.cache) {
-            data.cache = {};
-            data.createElem = ownerDocument.createElement;
-            data.createFrag = ownerDocument.createDocumentFragment;
-            data.frag = data.createFrag();
-          }
-
-
-          ownerDocument.createElement = function(nodeName) {
-                    if (!html5.shivMethods) {
-              return data.createElem(nodeName);
-            }
-            return createElement(nodeName, ownerDocument, data);
-          };
-
-          ownerDocument.createDocumentFragment = Function('h,f', 'return function(){' +
-                                                          'var n=f.cloneNode(),c=n.createElement;' +
-                                                          'h.shivMethods&&(' +
-                                                                                                                getElements().join().replace(/[\w\-]+/g, function(nodeName) {
-            data.createElem(nodeName);
-            data.frag.createElement(nodeName);
-            return 'c("' + nodeName + '")';
-          }) +
-            ');return n}'
-                                                         )(html5, data.frag);
-        }
-
-            function shivDocument(ownerDocument) {
-          if (!ownerDocument) {
-            ownerDocument = document;
-          }
-          var data = getExpandoData(ownerDocument);
-
-          if (html5.shivCSS && !supportsHtml5Styles && !data.hasCSS) {
-            data.hasCSS = !!addStyleSheet(ownerDocument,
-                                                                                'article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}' +
-                                                                                    'mark{background:#FF0;color:#000}' +
-                                                                                    'template{display:none}'
-                                         );
-          }
-          if (!supportsUnknownElements) {
-            shivMethods(ownerDocument, data);
-          }
-          return ownerDocument;
-        }
-
-            var html5 = {
-
-                'elements': options.elements || 'abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video',
-
-                'version': version,
-
-                'shivCSS': (options.shivCSS !== false),
-
-                'supportsUnknownElements': supportsUnknownElements,
-
-                'shivMethods': (options.shivMethods !== false),
-
-                'type': 'default',
-
-                'shivDocument': shivDocument,
-
-                createElement: createElement,
-
-                createDocumentFragment: createDocumentFragment
-        };
-
-            window.html5 = html5;
-
-            shivDocument(document);
-
-    }(this, document));
-
-    Modernizr._version      = version;
-
-    Modernizr._prefixes     = prefixes;
-    Modernizr._domPrefixes  = domPrefixes;
-    Modernizr._cssomPrefixes  = cssomPrefixes;
-
-
-    Modernizr.hasEvent      = isEventSupported;
-
-    Modernizr.testProp      = function(prop){
-        return testProps([prop]);
-    };
-
-    Modernizr.testAllProps  = testPropsAll;
-
-
-    Modernizr.testStyles    = injectElementWithStyles;
-    Modernizr.prefixed      = function(prop, obj, elem){
-      if(!obj) {
-        return testPropsAll(prop, 'pfx');
-      } else {
-            return testPropsAll(prop, obj, elem);
-      }
-    };
-
-
-    docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') +
-
-                                                    (enableClasses ? ' js ' + classes.join(' ') : '');
-
-    return Modernizr;
-
-})(this, this.document);
-/*yepnope1.5.4|WTFPL*/
-(function(a,b,c){function d(a){return"[object Function]"==o.call(a)}function e(a){return"string"==typeof a}function f(){}function g(a){return!a||"loaded"==a||"complete"==a||"uninitialized"==a}function h(){var a=p.shift();q=1,a?a.t?m(function(){("c"==a.t?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){"img"!=a&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l=b.createElement(a),o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};1===y[c]&&(r=1,y[c]=[]),"object"==a?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),"img"!=a&&(r||2===y[c]?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i("c"==b?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),1==p.length&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&"[object Opera]"==o.call(a.opera),l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return"[object Array]"==o.call(a)},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f<d;f++)g=a[f].split("="),(e=z[g.shift()])&&(c=e(c,g));for(f=0;f<b;f++)c=x[f](c);return c}function g(a,e,f,g,h){var i=b(a),j=i.autoCallback;i.url.split(".").pop().split("?").shift(),i.bypass||(e&&(e=d(e)?e:e[a]||e[g]||e[a.split("/").pop().split("?")[0]]),i.instead?i.instead(a,e,f,g,h):(y[i.url]?i.noexec=!0:y[i.url]=1,f.load(i.url,i.forceCSS||!i.forceJS&&"css"==i.url.split(".").pop().split("?").shift()?"c":c,i.noexec,i.attrs,i.timeout),(d(e)||d(j))&&f.load(function(){k(),e&&e(i.origUrl,h,g),j&&j(i.origUrl,h,g),y[i.url]=2})))}function h(a,b){function c(a,c){if(a){if(e(a))c||(j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}),g(a,j,b,0,h);else if(Object(a)===a)for(n in m=function(){var b=0,c;for(c in a)a.hasOwnProperty(c)&&b++;return b}(),a)a.hasOwnProperty(n)&&(!c&&!--m&&(d(j)?j=function(){var a=[].slice.call(arguments);k.apply(this,a),l()}:j[n]=function(a){return function(){var b=[].slice.call(arguments);a&&a.apply(this,b),l()}}(k[n])),g(a[n],j,b,n,h))}else!c&&l()}var h=!!a.test,i=a.load||a.both,j=a.callback||f,k=j,l=a.complete||f,m,n;c(h?a.yep:a.nope,!!i),i&&c(i)}var i,j,l=this.yepnope.loader;if(e(a))g(a,0,l,0);else if(w(a))for(i=0;i<a.length;i++)j=a[i],e(j)?g(j,0,l,0):w(j)?B(j):Object(j)===j&&h(j,l);else Object(a)===a&&h(a,l)},B.addPrefix=function(a,b){z[a]=b},B.addFilter=function(a){x.push(a)},B.errorTimeout=1e4,null==b.readyState&&b.addEventListener&&(b.readyState="loading",b.addEventListener("DOMContentLoaded",A=function(){b.removeEventListener("DOMContentLoaded",A,0),b.readyState="complete"},0)),a.yepnope=k(),a.yepnope.executeStack=h,a.yepnope.injectJs=function(a,c,d,e,i,j){var k=b.createElement("script"),l,o,e=e||B.errorTimeout;k.src=a;for(o in d)k.setAttribute(o,d[o]);c=j?h:c||f,k.onreadystatechange=k.onload=function(){!l&&g(k.readyState)&&(l=1,c(),k.onload=k.onreadystatechange=null)},m(function(){l||(l=1,c(1))},e),i?k.onload():n.parentNode.insertBefore(k,n)},a.yepnope.injectCss=function(a,c,d,e,g,i){var e=b.createElement("link"),j,c=i?h:c||f;e.href=a,e.rel="stylesheet",e.type="text/css";for(j in d)e.setAttribute(j,d[j]);g||(n.parentNode.insertBefore(e,n),m(c,0))}})(this,document);
-Modernizr.load=function(){yepnope.apply(window,[].slice.call(arguments,0));};
-;

+ 0 - 125
flaskbb/static/less/megalist-multiselect.less

@@ -1,125 +0,0 @@
-.box-sizing(@boxmodel) {
-  -webkit-box-sizing: @boxmodel;
-     -moz-box-sizing: @boxmodel;
-      -ms-box-sizing: @boxmodel;
-          box-sizing: @boxmodel;
-}
-.border-radius(@radius) {
-    -webkit-border-radius: @radius;
-       -moz-border-radius: @radius;
-            border-radius: @radius;
-}
-.user-select(@select) {
-    -webkit-user-select: @select;
-       -moz-user-select: @select;
-        -ms-user-select: @select;
-            user-select: @select;
-}
-
-@icon-move-color: rgb(3, 116, 187);
-.megalist-mutliselect {
-    .user-select(none);
-    overflow: hidden;
-    .megalist {
-        float: left;
-        width: 285px;
-        input[type=text] {
-            .box-sizing(border-box);
-            width: 100% !important;
-            padding: 5px 4px;
-            margin-bottom: 5px;
-        }
-        .megalist-inner {
-            .box-sizing(border-box);
-            overflow: hidden;
-            position: relative;
-            height: 205px;
-            width: 100%;
-            overflow: hidden;
-            border: 1px solid silver;
-            ul {
-                position: absolute;
-                padding: 0;
-                display: block;
-                margin-top: 0px;
-                width: 100%;
-                top: 0px;
-                li {
-                    margin:0;
-                    border:none;
-                    white-space: nowrap;
-                    overflow: hidden;
-                    padding: 6px 0px 6px 10px !important;
-                    display: block;
-                    position: absolute;
-                    width: 100%;
-                    border-top: 1px solid #EDEDED;
-                    line-height: 1em !important;
-                    cursor: pointer;
-                    color: #555;
-                    &:hover {
-                        background-color: #08c;
-                    }
-                }
-            }
-        }
-        .scrollbar {
-            .border-radius(3px);
-            position: absolute;
-            right: 1px;
-            width: 11px;
-            height: 25px;
-            background-color: rgb(190, 190, 190);
-            z-index: 2;
-            &:hover {
-                background-color: rgb(175, 175, 175);
-            }
-        }
-        .scrollbar-background {
-            position: absolute;
-            top: 0px;
-            right: 0px;
-            width: 14px;
-            height: 100%;
-            z-index: 1;
-            background-color: rgb(236, 236, 236);
-        }
-    }
-    .move-buttons {
-        margin: 90px 0px;
-        float: left;
-        .move-button {
-            height: 30px;
-            padding: 0px 50px;
-            margin-bottom: 10px;
-            cursor: pointer;
-            svg {
-                fill: @icon-move-color;
-            }
-            &:hover {
-                svg {
-                    fill: darken(@icon-move-color, 15%);
-                }
-                &.arrow-left.no-svg .svg {
-                    background-position: 0% 100%;
-                }
-                &.arrow-right.no-svg .svg {
-                    background-position: 100% 100%;
-                }
-            }
-            &.no-svg .svg {
-                background-repeat: no-repeat;
-                background-position: center;
-                background: url('../icons/megalist-icons.png');
-                width: 32px;
-                height: 32px;
-            }
-            &.arrow-left.no-svg .svg {
-                background-position: 0% 0%;
-            }
-            &.arrow-right.no-svg .svg {
-                background-position: 100% 0%;
-            }
-        }
-    }
-}

+ 2 - 21
flaskbb/templates/management/forum_form.html

@@ -3,7 +3,7 @@
 
 
 {% extends theme("management/management_layout.html") %}
 {% extends theme("management/management_layout.html") %}
 {% block management_content %}
 {% block management_content %}
-{% from theme("macros.html") import render_field, render_submit_field, render_boolean_field, navlink with context %}
+{% from theme("macros.html") import render_field, render_submit_field, render_boolean_field, render_select_field, navlink with context %}
 
 
 <div class="col-md-3">
 <div class="col-md-3">
     <ul class="nav nav-pills nav-stacked">
     <ul class="nav nav-pills nav-stacked">
@@ -31,11 +31,7 @@
                         {{ render_field(form.moderators) }}
                         {{ render_field(form.moderators) }}
                         {{ render_boolean_field(form.show_moderators) }}
                         {{ render_boolean_field(form.show_moderators) }}
                         {{ render_boolean_field(form.locked) }}
                         {{ render_boolean_field(form.locked) }}
-
-			<div id="perms">
-			    <h4>{{ _("Group Access to the forum") }}</h4>
-			    {{ form.groups }}
-			</div>
+                        {{ render_select_field(form.groups) }}
 
 
                         <div class="row">
                         <div class="row">
                             {{ render_submit_field(form.submit, div_class="col-lg-offset-0 col-lg-9") }}
                             {{ render_submit_field(form.submit, div_class="col-lg-offset-0 col-lg-9") }}
@@ -47,18 +43,3 @@
     </div>
     </div>
 </div>
 </div>
 {% endblock %}
 {% endblock %}
-
-{% block scripts %}
-{{ super() }}
-<script type="application/javascript" >
-    $(document).ready(function() {
-        var options = {
-            PLACEHOLDER_TEXT: "Search Groups",
-            BUILD_FULL_POST: false
-        };
-
-        $('#groups').megalist(options);
-        $('#groups').attr('inited', true);
-    });
-</script>
-{% endblock %}

+ 1 - 9
flaskbb/templates/management/management_layout.html

@@ -1,8 +1,5 @@
 {% extends theme("layout.html") %}
 {% extends theme("layout.html") %}
-{% block css %}
-    {{ super() }}
-    <link rel="stylesheet" href="{{ url_for('static', filename='css/megalist-multiselect.css') }}" >
-{% endblock %}
+
 {% block content %}
 {% block content %}
 {%- from theme('macros.html') import navlink with context -%}
 {%- from theme('macros.html') import navlink with context -%}
 
 
@@ -24,8 +21,3 @@
 {% block management_content %}{% endblock %}
 {% block management_content %}{% endblock %}
 
 
 {% endblock %}
 {% endblock %}
-{% block javascript %}
-    {{ super() }}
-    <script src="{{ url_for('static', filename='js/modernizr.custom.js') }}"></script>
-    <script src="{{ url_for('static', filename='js/megalist-multiselect.js') }}"></script>
-{% endblock %}