" )
+ .css({
+ position: "absolute",
+ width: iframe.outerWidth(),
+ height: iframe.outerHeight()
+ })
+ .appendTo( iframe.parent() )
+ .offset( iframe.offset() )[0];
+ });
+ },
+
+ _unblockFrames: function() {
+ if ( this.iframeBlocks ) {
+ this.iframeBlocks.remove();
+ delete this.iframeBlocks;
+ }
+ },
+
+ _allowInteraction: function( event ) {
+ if ( $( event.target ).closest(".ui-dialog").length ) {
+ return true;
+ }
+
+ // TODO: Remove hack when datepicker implements
+ // the .ui-front logic (#8989)
+ return !!$( event.target ).closest(".ui-datepicker").length;
+ },
+
+ _createOverlay: function() {
+ if ( !this.options.modal ) {
+ return;
+ }
+
+ var that = this,
+ widgetFullName = this.widgetFullName;
+ if ( !$.ui.dialog.overlayInstances ) {
+ // Prevent use of anchors and inputs.
+ // We use a delay in case the overlay is created from an
+ // event that we're going to be cancelling. (#2804)
+ this._delay(function() {
+ // Handle .dialog().dialog("close") (#4065)
+ if ( $.ui.dialog.overlayInstances ) {
+ this.document.bind( "focusin.dialog", function( event ) {
+ if ( !that._allowInteraction( event ) ) {
+ event.preventDefault();
+ $(".ui-dialog:visible:last .ui-dialog-content")
+ .data( widgetFullName )._focusTabbable();
+ }
+ });
+ }
+ });
+ }
+
+ this.overlay = $("
")
+ .addClass("ui-widget-overlay ui-front")
+ .appendTo( this._appendTo() );
+ this._on( this.overlay, {
+ mousedown: "_keepFocus"
+ });
+ $.ui.dialog.overlayInstances++;
+ },
+
+ _destroyOverlay: function() {
+ if ( !this.options.modal ) {
+ return;
+ }
+
+ if ( this.overlay ) {
+ $.ui.dialog.overlayInstances--;
+
+ if ( !$.ui.dialog.overlayInstances ) {
+ this.document.unbind( "focusin.dialog" );
+ }
+ this.overlay.remove();
+ this.overlay = null;
+ }
+ }
+});
+
+$.ui.dialog.overlayInstances = 0;
+
+// DEPRECATED
+if ( $.uiBackCompat !== false ) {
+ // position option with array notation
+ // just override with old implementation
+ $.widget( "ui.dialog", $.ui.dialog, {
+ _position: function() {
+ var position = this.options.position,
+ myAt = [],
+ offset = [ 0, 0 ],
+ isVisible;
+
+ if ( position ) {
+ if ( typeof position === "string" || (typeof position === "object" && "0" in position ) ) {
+ myAt = position.split ? position.split(" ") : [ position[0], position[1] ];
+ if ( myAt.length === 1 ) {
+ myAt[1] = myAt[0];
+ }
+
+ $.each( [ "left", "top" ], function( i, offsetPosition ) {
+ if ( +myAt[ i ] === myAt[ i ] ) {
+ offset[ i ] = myAt[ i ];
+ myAt[ i ] = offsetPosition;
+ }
+ });
+
+ position = {
+ my: myAt[0] + (offset[0] < 0 ? offset[0] : "+" + offset[0]) + " " +
+ myAt[1] + (offset[1] < 0 ? offset[1] : "+" + offset[1]),
+ at: myAt.join(" ")
+ };
+ }
+
+ position = $.extend( {}, $.ui.dialog.prototype.options.position, position );
+ } else {
+ position = $.ui.dialog.prototype.options.position;
+ }
+
+ // need to show the dialog to get the actual offset in the position plugin
+ isVisible = this.uiDialog.is(":visible");
+ if ( !isVisible ) {
+ this.uiDialog.show();
+ }
+ this.uiDialog.position( position );
+ if ( !isVisible ) {
+ this.uiDialog.hide();
+ }
+ }
+ });
+}
+
+}( jQuery ) );
+(function( $, undefined ) {
+
+$.widget("ui.draggable", $.ui.mouse, {
+ version: "1.10.4",
+ widgetEventPrefix: "drag",
+ options: {
+ addClasses: true,
+ appendTo: "parent",
+ axis: false,
+ connectToSortable: false,
+ containment: false,
+ cursor: "auto",
+ cursorAt: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ iframeFix: false,
+ opacity: false,
+ refreshPositions: false,
+ revert: false,
+ revertDuration: 500,
+ scope: "default",
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ snap: false,
+ snapMode: "both",
+ snapTolerance: 20,
+ stack: false,
+ zIndex: false,
+
+ // callbacks
+ drag: null,
+ start: null,
+ stop: null
+ },
+ _create: function() {
+
+ if (this.options.helper === "original" && !(/^(?:r|a|f)/).test(this.element.css("position"))) {
+ this.element[0].style.position = "relative";
+ }
+ if (this.options.addClasses){
+ this.element.addClass("ui-draggable");
+ }
+ if (this.options.disabled){
+ this.element.addClass("ui-draggable-disabled");
+ }
+
+ this._mouseInit();
+
+ },
+
+ _destroy: function() {
+ this.element.removeClass( "ui-draggable ui-draggable-dragging ui-draggable-disabled" );
+ this._mouseDestroy();
+ },
+
+ _mouseCapture: function(event) {
+
+ var o = this.options;
+
+ // among others, prevent a drag on a resizable-handle
+ if (this.helper || o.disabled || $(event.target).closest(".ui-resizable-handle").length > 0) {
+ return false;
+ }
+
+ //Quit if we're not on a valid handle
+ this.handle = this._getHandle(event);
+ if (!this.handle) {
+ return false;
+ }
+
+ $(o.iframeFix === true ? "iframe" : o.iframeFix).each(function() {
+ $("
")
+ .css({
+ width: this.offsetWidth+"px", height: this.offsetHeight+"px",
+ position: "absolute", opacity: "0.001", zIndex: 1000
+ })
+ .css($(this).offset())
+ .appendTo("body");
+ });
+
+ return true;
+
+ },
+
+ _mouseStart: function(event) {
+
+ var o = this.options;
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ this.helper.addClass("ui-draggable-dragging");
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ //If ddmanager is used for droppables, set the global draggable
+ if($.ui.ddmanager) {
+ $.ui.ddmanager.current = this;
+ }
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Store the helper's css position
+ this.cssPosition = this.helper.css( "position" );
+ this.scrollParent = this.helper.scrollParent();
+ this.offsetParent = this.helper.offsetParent();
+ this.offsetParentCssPosition = this.offsetParent.css( "position" );
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.positionAbs = this.element.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ //Reset scroll cache
+ this.offset.scroll = false;
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ //Generate the original position
+ this.originalPosition = this.position = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Set a containment if given in the options
+ this._setContainment();
+
+ //Trigger event + callbacks
+ if(this._trigger("start", event) === false) {
+ this._clear();
+ return false;
+ }
+
+ //Recache the helper size
+ this._cacheHelperProportions();
+
+ //Prepare the droppable offsets
+ if ($.ui.ddmanager && !o.dropBehaviour) {
+ $.ui.ddmanager.prepareOffsets(this, event);
+ }
+
+
+ this._mouseDrag(event, true); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has started (see #5003)
+ if ( $.ui.ddmanager ) {
+ $.ui.ddmanager.dragStart(this, event);
+ }
+
+ return true;
+ },
+
+ _mouseDrag: function(event, noPropagation) {
+ // reset any necessary cached properties (see #5009)
+ if ( this.offsetParentCssPosition === "fixed" ) {
+ this.offset.parent = this._getParentOffset();
+ }
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Call plugins and callbacks and use the resulting position if something is returned
+ if (!noPropagation) {
+ var ui = this._uiHash();
+ if(this._trigger("drag", event, ui) === false) {
+ this._mouseUp({});
+ return false;
+ }
+ this.position = ui.position;
+ }
+
+ if(!this.options.axis || this.options.axis !== "y") {
+ this.helper[0].style.left = this.position.left+"px";
+ }
+ if(!this.options.axis || this.options.axis !== "x") {
+ this.helper[0].style.top = this.position.top+"px";
+ }
+ if($.ui.ddmanager) {
+ $.ui.ddmanager.drag(this, event);
+ }
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ //If we are using droppables, inform the manager about the drop
+ var that = this,
+ dropped = false;
+ if ($.ui.ddmanager && !this.options.dropBehaviour) {
+ dropped = $.ui.ddmanager.drop(this, event);
+ }
+
+ //if a drop comes from outside (a sortable)
+ if(this.dropped) {
+ dropped = this.dropped;
+ this.dropped = false;
+ }
+
+ //if the original element is no longer in the DOM don't bother to continue (see #8269)
+ if ( this.options.helper === "original" && !$.contains( this.element[ 0 ].ownerDocument, this.element[ 0 ] ) ) {
+ return false;
+ }
+
+ if((this.options.revert === "invalid" && !dropped) || (this.options.revert === "valid" && dropped) || this.options.revert === true || ($.isFunction(this.options.revert) && this.options.revert.call(this.element, dropped))) {
+ $(this.helper).animate(this.originalPosition, parseInt(this.options.revertDuration, 10), function() {
+ if(that._trigger("stop", event) !== false) {
+ that._clear();
+ }
+ });
+ } else {
+ if(this._trigger("stop", event) !== false) {
+ this._clear();
+ }
+ }
+
+ return false;
+ },
+
+ _mouseUp: function(event) {
+ //Remove frame helpers
+ $("div.ui-draggable-iframeFix").each(function() {
+ this.parentNode.removeChild(this);
+ });
+
+ //If the ddmanager is used for droppables, inform the manager that dragging has stopped (see #5003)
+ if( $.ui.ddmanager ) {
+ $.ui.ddmanager.dragStop(this, event);
+ }
+
+ return $.ui.mouse.prototype._mouseUp.call(this, event);
+ },
+
+ cancel: function() {
+
+ if(this.helper.is(".ui-draggable-dragging")) {
+ this._mouseUp({});
+ } else {
+ this._clear();
+ }
+
+ return this;
+
+ },
+
+ _getHandle: function(event) {
+ return this.options.handle ?
+ !!$( event.target ).closest( this.element.find( this.options.handle ) ).length :
+ true;
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options,
+ helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event])) : (o.helper === "clone" ? this.element.clone().removeAttr("id") : this.element);
+
+ if(!helper.parents("body").length) {
+ helper.appendTo((o.appendTo === "parent" ? this.element[0].parentNode : o.appendTo));
+ }
+
+ if(helper[0] !== this.element[0] && !(/(fixed|absolute)/).test(helper.css("position"))) {
+ helper.css("position", "absolute");
+ }
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj === "string") {
+ obj = obj.split(" ");
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ("left" in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ("right" in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ("top" in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ("bottom" in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+ //Get the offsetParent and cache its position
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ //This needs to be actually done for all browsers, since pageX/pageY includes this information
+ //Ugly IE fix
+ if((this.offsetParent[0] === document.body) ||
+ (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
+ po = { top: 0, left: 0 };
+ }
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition === "relative") {
+ var p = this.element.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.element.css("marginLeft"),10) || 0),
+ top: (parseInt(this.element.css("marginTop"),10) || 0),
+ right: (parseInt(this.element.css("marginRight"),10) || 0),
+ bottom: (parseInt(this.element.css("marginBottom"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var over, c, ce,
+ o = this.options;
+
+ if ( !o.containment ) {
+ this.containment = null;
+ return;
+ }
+
+ if ( o.containment === "window" ) {
+ this.containment = [
+ $( window ).scrollLeft() - this.offset.relative.left - this.offset.parent.left,
+ $( window ).scrollTop() - this.offset.relative.top - this.offset.parent.top,
+ $( window ).scrollLeft() + $( window ).width() - this.helperProportions.width - this.margins.left,
+ $( window ).scrollTop() + ( $( window ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
+ ];
+ return;
+ }
+
+ if ( o.containment === "document") {
+ this.containment = [
+ 0,
+ 0,
+ $( document ).width() - this.helperProportions.width - this.margins.left,
+ ( $( document ).height() || document.body.parentNode.scrollHeight ) - this.helperProportions.height - this.margins.top
+ ];
+ return;
+ }
+
+ if ( o.containment.constructor === Array ) {
+ this.containment = o.containment;
+ return;
+ }
+
+ if ( o.containment === "parent" ) {
+ o.containment = this.helper[ 0 ].parentNode;
+ }
+
+ c = $( o.containment );
+ ce = c[ 0 ];
+
+ if( !ce ) {
+ return;
+ }
+
+ over = c.css( "overflow" ) !== "hidden";
+
+ this.containment = [
+ ( parseInt( c.css( "borderLeftWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingLeft" ), 10 ) || 0 ),
+ ( parseInt( c.css( "borderTopWidth" ), 10 ) || 0 ) + ( parseInt( c.css( "paddingTop" ), 10 ) || 0 ) ,
+ ( over ? Math.max( ce.scrollWidth, ce.offsetWidth ) : ce.offsetWidth ) - ( parseInt( c.css( "borderRightWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingRight" ), 10 ) || 0 ) - this.helperProportions.width - this.margins.left - this.margins.right,
+ ( over ? Math.max( ce.scrollHeight, ce.offsetHeight ) : ce.offsetHeight ) - ( parseInt( c.css( "borderBottomWidth" ), 10 ) || 0 ) - ( parseInt( c.css( "paddingBottom" ), 10 ) || 0 ) - this.helperProportions.height - this.margins.top - this.margins.bottom
+ ];
+ this.relative_container = c;
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) {
+ pos = this.position;
+ }
+
+ var mod = d === "absolute" ? 1 : -1,
+ scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent;
+
+ //Cache the scroll
+ if (!this.offset.scroll) {
+ this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
+ }
+
+ return {
+ top: (
+ pos.top + // The absolute mouse position
+ this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top ) * mod )
+ ),
+ left: (
+ pos.left + // The absolute mouse position
+ this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left ) * mod )
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var containment, co, top, left,
+ o = this.options,
+ scroll = this.cssPosition === "absolute" && !( this.scrollParent[ 0 ] !== document && $.contains( this.scrollParent[ 0 ], this.offsetParent[ 0 ] ) ) ? this.offsetParent : this.scrollParent,
+ pageX = event.pageX,
+ pageY = event.pageY;
+
+ //Cache the scroll
+ if (!this.offset.scroll) {
+ this.offset.scroll = {top : scroll.scrollTop(), left : scroll.scrollLeft()};
+ }
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ // If we are not dragging yet, we won't check for options
+ if ( this.originalPosition ) {
+ if ( this.containment ) {
+ if ( this.relative_container ){
+ co = this.relative_container.offset();
+ containment = [
+ this.containment[ 0 ] + co.left,
+ this.containment[ 1 ] + co.top,
+ this.containment[ 2 ] + co.left,
+ this.containment[ 3 ] + co.top
+ ];
+ }
+ else {
+ containment = this.containment;
+ }
+
+ if(event.pageX - this.offset.click.left < containment[0]) {
+ pageX = containment[0] + this.offset.click.left;
+ }
+ if(event.pageY - this.offset.click.top < containment[1]) {
+ pageY = containment[1] + this.offset.click.top;
+ }
+ if(event.pageX - this.offset.click.left > containment[2]) {
+ pageX = containment[2] + this.offset.click.left;
+ }
+ if(event.pageY - this.offset.click.top > containment[3]) {
+ pageY = containment[3] + this.offset.click.top;
+ }
+ }
+
+ if(o.grid) {
+ //Check for grid elements set to 0 to prevent divide by 0 error causing invalid argument errors in IE (see ticket #6950)
+ top = o.grid[1] ? this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1] : this.originalPageY;
+ pageY = containment ? ((top - this.offset.click.top >= containment[1] || top - this.offset.click.top > containment[3]) ? top : ((top - this.offset.click.top >= containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ left = o.grid[0] ? this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0] : this.originalPageX;
+ pageX = containment ? ((left - this.offset.click.left >= containment[0] || left - this.offset.click.left > containment[2]) ? left : ((left - this.offset.click.left >= containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY - // The absolute mouse position
+ this.offset.click.top - // Click offset (relative to the element)
+ this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
+ ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : this.offset.scroll.top )
+ ),
+ left: (
+ pageX - // The absolute mouse position
+ this.offset.click.left - // Click offset (relative to the element)
+ this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
+ ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : this.offset.scroll.left )
+ )
+ };
+
+ },
+
+ _clear: function() {
+ this.helper.removeClass("ui-draggable-dragging");
+ if(this.helper[0] !== this.element[0] && !this.cancelHelperRemoval) {
+ this.helper.remove();
+ }
+ this.helper = null;
+ this.cancelHelperRemoval = false;
+ },
+
+ // From now on bulk stuff - mainly helpers
+
+ _trigger: function(type, event, ui) {
+ ui = ui || this._uiHash();
+ $.ui.plugin.call(this, type, [event, ui]);
+ //The absolute position has to be recalculated after plugins
+ if(type === "drag") {
+ this.positionAbs = this._convertPositionTo("absolute");
+ }
+ return $.Widget.prototype._trigger.call(this, type, event, ui);
+ },
+
+ plugins: {},
+
+ _uiHash: function() {
+ return {
+ helper: this.helper,
+ position: this.position,
+ originalPosition: this.originalPosition,
+ offset: this.positionAbs
+ };
+ }
+
+});
+
+$.ui.plugin.add("draggable", "connectToSortable", {
+ start: function(event, ui) {
+
+ var inst = $(this).data("ui-draggable"), o = inst.options,
+ uiSortable = $.extend({}, ui, { item: inst.element });
+ inst.sortables = [];
+ $(o.connectToSortable).each(function() {
+ var sortable = $.data(this, "ui-sortable");
+ if (sortable && !sortable.options.disabled) {
+ inst.sortables.push({
+ instance: sortable,
+ shouldRevert: sortable.options.revert
+ });
+ sortable.refreshPositions(); // Call the sortable's refreshPositions at drag start to refresh the containerCache since the sortable container cache is used in drag and needs to be up to date (this will ensure it's initialised as well as being kept in step with any changes that might have happened on the page).
+ sortable._trigger("activate", event, uiSortable);
+ }
+ });
+
+ },
+ stop: function(event, ui) {
+
+ //If we are still over the sortable, we fake the stop event of the sortable, but also remove helper
+ var inst = $(this).data("ui-draggable"),
+ uiSortable = $.extend({}, ui, { item: inst.element });
+
+ $.each(inst.sortables, function() {
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+
+ inst.cancelHelperRemoval = true; //Don't remove the helper in the draggable instance
+ this.instance.cancelHelperRemoval = false; //Remove it in the sortable instance (so sortable plugins like revert still work)
+
+ //The sortable revert is supported, and we have to set a temporary dropped variable on the draggable to support revert: "valid/invalid"
+ if(this.shouldRevert) {
+ this.instance.options.revert = this.shouldRevert;
+ }
+
+ //Trigger the stop of the sortable
+ this.instance._mouseStop(event);
+
+ this.instance.options.helper = this.instance.options._helper;
+
+ //If the helper has been the original item, restore properties in the sortable
+ if(inst.options.helper === "original") {
+ this.instance.currentItem.css({ top: "auto", left: "auto" });
+ }
+
+ } else {
+ this.instance.cancelHelperRemoval = false; //Remove the helper in the sortable instance
+ this.instance._trigger("deactivate", event, uiSortable);
+ }
+
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var inst = $(this).data("ui-draggable"), that = this;
+
+ $.each(inst.sortables, function() {
+
+ var innermostIntersecting = false,
+ thisSortable = this;
+
+ //Copy over some variables to allow calling the sortable's native _intersectsWith
+ this.instance.positionAbs = inst.positionAbs;
+ this.instance.helperProportions = inst.helperProportions;
+ this.instance.offset.click = inst.offset.click;
+
+ if(this.instance._intersectsWith(this.instance.containerCache)) {
+ innermostIntersecting = true;
+ $.each(inst.sortables, function () {
+ this.instance.positionAbs = inst.positionAbs;
+ this.instance.helperProportions = inst.helperProportions;
+ this.instance.offset.click = inst.offset.click;
+ if (this !== thisSortable &&
+ this.instance._intersectsWith(this.instance.containerCache) &&
+ $.contains(thisSortable.instance.element[0], this.instance.element[0])
+ ) {
+ innermostIntersecting = false;
+ }
+ return innermostIntersecting;
+ });
+ }
+
+
+ if(innermostIntersecting) {
+ //If it intersects, we use a little isOver variable and set it once, so our move-in stuff gets fired only once
+ if(!this.instance.isOver) {
+
+ this.instance.isOver = 1;
+ //Now we fake the start of dragging for the sortable instance,
+ //by cloning the list group item, appending it to the sortable and using it as inst.currentItem
+ //We can then fire the start event of the sortable with our passed browser event, and our own helper (so it doesn't create a new one)
+ this.instance.currentItem = $(that).clone().removeAttr("id").appendTo(this.instance.element).data("ui-sortable-item", true);
+ this.instance.options._helper = this.instance.options.helper; //Store helper option to later restore it
+ this.instance.options.helper = function() { return ui.helper[0]; };
+
+ event.target = this.instance.currentItem[0];
+ this.instance._mouseCapture(event, true);
+ this.instance._mouseStart(event, true, true);
+
+ //Because the browser event is way off the new appended portlet, we modify a couple of variables to reflect the changes
+ this.instance.offset.click.top = inst.offset.click.top;
+ this.instance.offset.click.left = inst.offset.click.left;
+ this.instance.offset.parent.left -= inst.offset.parent.left - this.instance.offset.parent.left;
+ this.instance.offset.parent.top -= inst.offset.parent.top - this.instance.offset.parent.top;
+
+ inst._trigger("toSortable", event);
+ inst.dropped = this.instance.element; //draggable revert needs that
+ //hack so receive/update callbacks work (mostly)
+ inst.currentItem = inst.element;
+ this.instance.fromOutside = inst;
+
+ }
+
+ //Provided we did all the previous steps, we can fire the drag event of the sortable on every draggable drag, when it intersects with the sortable
+ if(this.instance.currentItem) {
+ this.instance._mouseDrag(event);
+ }
+
+ } else {
+
+ //If it doesn't intersect with the sortable, and it intersected before,
+ //we fake the drag stop of the sortable, but make sure it doesn't remove the helper by using cancelHelperRemoval
+ if(this.instance.isOver) {
+
+ this.instance.isOver = 0;
+ this.instance.cancelHelperRemoval = true;
+
+ //Prevent reverting on this forced stop
+ this.instance.options.revert = false;
+
+ // The out event needs to be triggered independently
+ this.instance._trigger("out", event, this.instance._uiHash(this.instance));
+
+ this.instance._mouseStop(event, true);
+ this.instance.options.helper = this.instance.options._helper;
+
+ //Now we remove our currentItem, the list group clone again, and the placeholder, and animate the helper back to it's original size
+ this.instance.currentItem.remove();
+ if(this.instance.placeholder) {
+ this.instance.placeholder.remove();
+ }
+
+ inst._trigger("fromSortable", event);
+ inst.dropped = false; //draggable revert needs that
+ }
+
+ }
+
+ });
+
+ }
+});
+
+$.ui.plugin.add("draggable", "cursor", {
+ start: function() {
+ var t = $("body"), o = $(this).data("ui-draggable").options;
+ if (t.css("cursor")) {
+ o._cursor = t.css("cursor");
+ }
+ t.css("cursor", o.cursor);
+ },
+ stop: function() {
+ var o = $(this).data("ui-draggable").options;
+ if (o._cursor) {
+ $("body").css("cursor", o._cursor);
+ }
+ }
+});
+
+$.ui.plugin.add("draggable", "opacity", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data("ui-draggable").options;
+ if(t.css("opacity")) {
+ o._opacity = t.css("opacity");
+ }
+ t.css("opacity", o.opacity);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data("ui-draggable").options;
+ if(o._opacity) {
+ $(ui.helper).css("opacity", o._opacity);
+ }
+ }
+});
+
+$.ui.plugin.add("draggable", "scroll", {
+ start: function() {
+ var i = $(this).data("ui-draggable");
+ if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
+ i.overflowOffset = i.scrollParent.offset();
+ }
+ },
+ drag: function( event ) {
+
+ var i = $(this).data("ui-draggable"), o = i.options, scrolled = false;
+
+ if(i.scrollParent[0] !== document && i.scrollParent[0].tagName !== "HTML") {
+
+ if(!o.axis || o.axis !== "x") {
+ if((i.overflowOffset.top + i.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop + o.scrollSpeed;
+ } else if(event.pageY - i.overflowOffset.top < o.scrollSensitivity) {
+ i.scrollParent[0].scrollTop = scrolled = i.scrollParent[0].scrollTop - o.scrollSpeed;
+ }
+ }
+
+ if(!o.axis || o.axis !== "y") {
+ if((i.overflowOffset.left + i.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft + o.scrollSpeed;
+ } else if(event.pageX - i.overflowOffset.left < o.scrollSensitivity) {
+ i.scrollParent[0].scrollLeft = scrolled = i.scrollParent[0].scrollLeft - o.scrollSpeed;
+ }
+ }
+
+ } else {
+
+ if(!o.axis || o.axis !== "x") {
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+ }
+ }
+
+ if(!o.axis || o.axis !== "y") {
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+ }
+ }
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
+ $.ui.ddmanager.prepareOffsets(i, event);
+ }
+
+ }
+});
+
+$.ui.plugin.add("draggable", "snap", {
+ start: function() {
+
+ var i = $(this).data("ui-draggable"),
+ o = i.options;
+
+ i.snapElements = [];
+
+ $(o.snap.constructor !== String ? ( o.snap.items || ":data(ui-draggable)" ) : o.snap).each(function() {
+ var $t = $(this),
+ $o = $t.offset();
+ if(this !== i.element[0]) {
+ i.snapElements.push({
+ item: this,
+ width: $t.outerWidth(), height: $t.outerHeight(),
+ top: $o.top, left: $o.left
+ });
+ }
+ });
+
+ },
+ drag: function(event, ui) {
+
+ var ts, bs, ls, rs, l, r, t, b, i, first,
+ inst = $(this).data("ui-draggable"),
+ o = inst.options,
+ d = o.snapTolerance,
+ x1 = ui.offset.left, x2 = x1 + inst.helperProportions.width,
+ y1 = ui.offset.top, y2 = y1 + inst.helperProportions.height;
+
+ for (i = inst.snapElements.length - 1; i >= 0; i--){
+
+ l = inst.snapElements[i].left;
+ r = l + inst.snapElements[i].width;
+ t = inst.snapElements[i].top;
+ b = t + inst.snapElements[i].height;
+
+ if ( x2 < l - d || x1 > r + d || y2 < t - d || y1 > b + d || !$.contains( inst.snapElements[ i ].item.ownerDocument, inst.snapElements[ i ].item ) ) {
+ if(inst.snapElements[i].snapping) {
+ (inst.options.snap.release && inst.options.snap.release.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ }
+ inst.snapElements[i].snapping = false;
+ continue;
+ }
+
+ if(o.snapMode !== "inner") {
+ ts = Math.abs(t - y2) <= d;
+ bs = Math.abs(b - y1) <= d;
+ ls = Math.abs(l - x2) <= d;
+ rs = Math.abs(r - x1) <= d;
+ if(ts) {
+ ui.position.top = inst._convertPositionTo("relative", { top: t - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ }
+ if(bs) {
+ ui.position.top = inst._convertPositionTo("relative", { top: b, left: 0 }).top - inst.margins.top;
+ }
+ if(ls) {
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l - inst.helperProportions.width }).left - inst.margins.left;
+ }
+ if(rs) {
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r }).left - inst.margins.left;
+ }
+ }
+
+ first = (ts || bs || ls || rs);
+
+ if(o.snapMode !== "outer") {
+ ts = Math.abs(t - y1) <= d;
+ bs = Math.abs(b - y2) <= d;
+ ls = Math.abs(l - x1) <= d;
+ rs = Math.abs(r - x2) <= d;
+ if(ts) {
+ ui.position.top = inst._convertPositionTo("relative", { top: t, left: 0 }).top - inst.margins.top;
+ }
+ if(bs) {
+ ui.position.top = inst._convertPositionTo("relative", { top: b - inst.helperProportions.height, left: 0 }).top - inst.margins.top;
+ }
+ if(ls) {
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: l }).left - inst.margins.left;
+ }
+ if(rs) {
+ ui.position.left = inst._convertPositionTo("relative", { top: 0, left: r - inst.helperProportions.width }).left - inst.margins.left;
+ }
+ }
+
+ if(!inst.snapElements[i].snapping && (ts || bs || ls || rs || first)) {
+ (inst.options.snap.snap && inst.options.snap.snap.call(inst.element, event, $.extend(inst._uiHash(), { snapItem: inst.snapElements[i].item })));
+ }
+ inst.snapElements[i].snapping = (ts || bs || ls || rs || first);
+
+ }
+
+ }
+});
+
+$.ui.plugin.add("draggable", "stack", {
+ start: function() {
+ var min,
+ o = this.data("ui-draggable").options,
+ group = $.makeArray($(o.stack)).sort(function(a,b) {
+ return (parseInt($(a).css("zIndex"),10) || 0) - (parseInt($(b).css("zIndex"),10) || 0);
+ });
+
+ if (!group.length) { return; }
+
+ min = parseInt($(group[0]).css("zIndex"), 10) || 0;
+ $(group).each(function(i) {
+ $(this).css("zIndex", min + i);
+ });
+ this.css("zIndex", (min + group.length));
+ }
+});
+
+$.ui.plugin.add("draggable", "zIndex", {
+ start: function(event, ui) {
+ var t = $(ui.helper), o = $(this).data("ui-draggable").options;
+ if(t.css("zIndex")) {
+ o._zIndex = t.css("zIndex");
+ }
+ t.css("zIndex", o.zIndex);
+ },
+ stop: function(event, ui) {
+ var o = $(this).data("ui-draggable").options;
+ if(o._zIndex) {
+ $(ui.helper).css("zIndex", o._zIndex);
+ }
+ }
+});
+
+})(jQuery);
+(function( $, undefined ) {
+
+function isOverAxis( x, reference, size ) {
+ return ( x > reference ) && ( x < ( reference + size ) );
+}
+
+$.widget("ui.droppable", {
+ version: "1.10.4",
+ widgetEventPrefix: "drop",
+ options: {
+ accept: "*",
+ activeClass: false,
+ addClasses: true,
+ greedy: false,
+ hoverClass: false,
+ scope: "default",
+ tolerance: "intersect",
+
+ // callbacks
+ activate: null,
+ deactivate: null,
+ drop: null,
+ out: null,
+ over: null
+ },
+ _create: function() {
+
+ var proportions,
+ o = this.options,
+ accept = o.accept;
+
+ this.isover = false;
+ this.isout = true;
+
+ this.accept = $.isFunction(accept) ? accept : function(d) {
+ return d.is(accept);
+ };
+
+ this.proportions = function( /* valueToWrite */ ) {
+ if ( arguments.length ) {
+ // Store the droppable's proportions
+ proportions = arguments[ 0 ];
+ } else {
+ // Retrieve or derive the droppable's proportions
+ return proportions ?
+ proportions :
+ proportions = {
+ width: this.element[ 0 ].offsetWidth,
+ height: this.element[ 0 ].offsetHeight
+ };
+ }
+ };
+
+ // Add the reference and positions to the manager
+ $.ui.ddmanager.droppables[o.scope] = $.ui.ddmanager.droppables[o.scope] || [];
+ $.ui.ddmanager.droppables[o.scope].push(this);
+
+ (o.addClasses && this.element.addClass("ui-droppable"));
+
+ },
+
+ _destroy: function() {
+ var i = 0,
+ drop = $.ui.ddmanager.droppables[this.options.scope];
+
+ for ( ; i < drop.length; i++ ) {
+ if ( drop[i] === this ) {
+ drop.splice(i, 1);
+ }
+ }
+
+ this.element.removeClass("ui-droppable ui-droppable-disabled");
+ },
+
+ _setOption: function(key, value) {
+
+ if(key === "accept") {
+ this.accept = $.isFunction(value) ? value : function(d) {
+ return d.is(value);
+ };
+ }
+ $.Widget.prototype._setOption.apply(this, arguments);
+ },
+
+ _activate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) {
+ this.element.addClass(this.options.activeClass);
+ }
+ if(draggable){
+ this._trigger("activate", event, this.ui(draggable));
+ }
+ },
+
+ _deactivate: function(event) {
+ var draggable = $.ui.ddmanager.current;
+ if(this.options.activeClass) {
+ this.element.removeClass(this.options.activeClass);
+ }
+ if(draggable){
+ this._trigger("deactivate", event, this.ui(draggable));
+ }
+ },
+
+ _over: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+
+ // Bail if draggable and droppable are same element
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
+ return;
+ }
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) {
+ this.element.addClass(this.options.hoverClass);
+ }
+ this._trigger("over", event, this.ui(draggable));
+ }
+
+ },
+
+ _out: function(event) {
+
+ var draggable = $.ui.ddmanager.current;
+
+ // Bail if draggable and droppable are same element
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
+ return;
+ }
+
+ if (this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.hoverClass) {
+ this.element.removeClass(this.options.hoverClass);
+ }
+ this._trigger("out", event, this.ui(draggable));
+ }
+
+ },
+
+ _drop: function(event,custom) {
+
+ var draggable = custom || $.ui.ddmanager.current,
+ childrenIntersection = false;
+
+ // Bail if draggable and droppable are same element
+ if (!draggable || (draggable.currentItem || draggable.element)[0] === this.element[0]) {
+ return false;
+ }
+
+ this.element.find(":data(ui-droppable)").not(".ui-draggable-dragging").each(function() {
+ var inst = $.data(this, "ui-droppable");
+ if(
+ inst.options.greedy &&
+ !inst.options.disabled &&
+ inst.options.scope === draggable.options.scope &&
+ inst.accept.call(inst.element[0], (draggable.currentItem || draggable.element)) &&
+ $.ui.intersect(draggable, $.extend(inst, { offset: inst.element.offset() }), inst.options.tolerance)
+ ) { childrenIntersection = true; return false; }
+ });
+ if(childrenIntersection) {
+ return false;
+ }
+
+ if(this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ if(this.options.activeClass) {
+ this.element.removeClass(this.options.activeClass);
+ }
+ if(this.options.hoverClass) {
+ this.element.removeClass(this.options.hoverClass);
+ }
+ this._trigger("drop", event, this.ui(draggable));
+ return this.element;
+ }
+
+ return false;
+
+ },
+
+ ui: function(c) {
+ return {
+ draggable: (c.currentItem || c.element),
+ helper: c.helper,
+ position: c.position,
+ offset: c.positionAbs
+ };
+ }
+
+});
+
+$.ui.intersect = function(draggable, droppable, toleranceMode) {
+
+ if (!droppable.offset) {
+ return false;
+ }
+
+ var draggableLeft, draggableTop,
+ x1 = (draggable.positionAbs || draggable.position.absolute).left,
+ y1 = (draggable.positionAbs || draggable.position.absolute).top,
+ x2 = x1 + draggable.helperProportions.width,
+ y2 = y1 + draggable.helperProportions.height,
+ l = droppable.offset.left,
+ t = droppable.offset.top,
+ r = l + droppable.proportions().width,
+ b = t + droppable.proportions().height;
+
+ switch (toleranceMode) {
+ case "fit":
+ return (l <= x1 && x2 <= r && t <= y1 && y2 <= b);
+ case "intersect":
+ return (l < x1 + (draggable.helperProportions.width / 2) && // Right Half
+ x2 - (draggable.helperProportions.width / 2) < r && // Left Half
+ t < y1 + (draggable.helperProportions.height / 2) && // Bottom Half
+ y2 - (draggable.helperProportions.height / 2) < b ); // Top Half
+ case "pointer":
+ draggableLeft = ((draggable.positionAbs || draggable.position.absolute).left + (draggable.clickOffset || draggable.offset.click).left);
+ draggableTop = ((draggable.positionAbs || draggable.position.absolute).top + (draggable.clickOffset || draggable.offset.click).top);
+ return isOverAxis( draggableTop, t, droppable.proportions().height ) && isOverAxis( draggableLeft, l, droppable.proportions().width );
+ case "touch":
+ return (
+ (y1 >= t && y1 <= b) || // Top edge touching
+ (y2 >= t && y2 <= b) || // Bottom edge touching
+ (y1 < t && y2 > b) // Surrounded vertically
+ ) && (
+ (x1 >= l && x1 <= r) || // Left edge touching
+ (x2 >= l && x2 <= r) || // Right edge touching
+ (x1 < l && x2 > r) // Surrounded horizontally
+ );
+ default:
+ return false;
+ }
+
+};
+
+/*
+ This manager tracks offsets of draggables and droppables
+*/
+$.ui.ddmanager = {
+ current: null,
+ droppables: { "default": [] },
+ prepareOffsets: function(t, event) {
+
+ var i, j,
+ m = $.ui.ddmanager.droppables[t.options.scope] || [],
+ type = event ? event.type : null, // workaround for #2317
+ list = (t.currentItem || t.element).find(":data(ui-droppable)").addBack();
+
+ droppablesLoop: for (i = 0; i < m.length; i++) {
+
+ //No disabled and non-accepted
+ if(m[i].options.disabled || (t && !m[i].accept.call(m[i].element[0],(t.currentItem || t.element)))) {
+ continue;
+ }
+
+ // Filter out elements in the current dragged item
+ for (j=0; j < list.length; j++) {
+ if(list[j] === m[i].element[0]) {
+ m[i].proportions().height = 0;
+ continue droppablesLoop;
+ }
+ }
+
+ m[i].visible = m[i].element.css("display") !== "none";
+ if(!m[i].visible) {
+ continue;
+ }
+
+ //Activate the droppable if used directly from draggables
+ if(type === "mousedown") {
+ m[i]._activate.call(m[i], event);
+ }
+
+ m[ i ].offset = m[ i ].element.offset();
+ m[ i ].proportions({ width: m[ i ].element[ 0 ].offsetWidth, height: m[ i ].element[ 0 ].offsetHeight });
+
+ }
+
+ },
+ drop: function(draggable, event) {
+
+ var dropped = false;
+ // Create a copy of the droppables in case the list changes during the drop (#9116)
+ $.each(($.ui.ddmanager.droppables[draggable.options.scope] || []).slice(), function() {
+
+ if(!this.options) {
+ return;
+ }
+ if (!this.options.disabled && this.visible && $.ui.intersect(draggable, this, this.options.tolerance)) {
+ dropped = this._drop.call(this, event) || dropped;
+ }
+
+ if (!this.options.disabled && this.visible && this.accept.call(this.element[0],(draggable.currentItem || draggable.element))) {
+ this.isout = true;
+ this.isover = false;
+ this._deactivate.call(this, event);
+ }
+
+ });
+ return dropped;
+
+ },
+ dragStart: function( draggable, event ) {
+ //Listen for scrolling so that if the dragging causes scrolling the position of the droppables can be recalculated (see #5003)
+ draggable.element.parentsUntil( "body" ).bind( "scroll.droppable", function() {
+ if( !draggable.options.refreshPositions ) {
+ $.ui.ddmanager.prepareOffsets( draggable, event );
+ }
+ });
+ },
+ drag: function(draggable, event) {
+
+ //If you have a highly dynamic page, you might try this option. It renders positions every time you move the mouse.
+ if(draggable.options.refreshPositions) {
+ $.ui.ddmanager.prepareOffsets(draggable, event);
+ }
+
+ //Run through all droppables and check their positions based on specific tolerance options
+ $.each($.ui.ddmanager.droppables[draggable.options.scope] || [], function() {
+
+ if(this.options.disabled || this.greedyChild || !this.visible) {
+ return;
+ }
+
+ var parentInstance, scope, parent,
+ intersects = $.ui.intersect(draggable, this, this.options.tolerance),
+ c = !intersects && this.isover ? "isout" : (intersects && !this.isover ? "isover" : null);
+ if(!c) {
+ return;
+ }
+
+ if (this.options.greedy) {
+ // find droppable parents with same scope
+ scope = this.options.scope;
+ parent = this.element.parents(":data(ui-droppable)").filter(function () {
+ return $.data(this, "ui-droppable").options.scope === scope;
+ });
+
+ if (parent.length) {
+ parentInstance = $.data(parent[0], "ui-droppable");
+ parentInstance.greedyChild = (c === "isover");
+ }
+ }
+
+ // we just moved into a greedy child
+ if (parentInstance && c === "isover") {
+ parentInstance.isover = false;
+ parentInstance.isout = true;
+ parentInstance._out.call(parentInstance, event);
+ }
+
+ this[c] = true;
+ this[c === "isout" ? "isover" : "isout"] = false;
+ this[c === "isover" ? "_over" : "_out"].call(this, event);
+
+ // we just moved out of a greedy child
+ if (parentInstance && c === "isout") {
+ parentInstance.isout = false;
+ parentInstance.isover = true;
+ parentInstance._over.call(parentInstance, event);
+ }
+ });
+
+ },
+ dragStop: function( draggable, event ) {
+ draggable.element.parentsUntil( "body" ).unbind( "scroll.droppable" );
+ //Call prepareOffsets one final time since IE does not fire return scroll events when overflow was caused by drag (see #5003)
+ if( !draggable.options.refreshPositions ) {
+ $.ui.ddmanager.prepareOffsets( draggable, event );
+ }
+ }
+};
+
+})(jQuery);
+(function($, undefined) {
+
+var dataSpace = "ui-effects-";
+
+$.effects = {
+ effect: {}
+};
+
+/*!
+ * jQuery Color Animations v2.1.2
+ * https://github.com/jquery/jquery-color
+ *
+ * Copyright 2013 jQuery Foundation and other contributors
+ * Released under the MIT license.
+ * http://jquery.org/license
+ *
+ * Date: Wed Jan 16 08:47:09 2013 -0600
+ */
+(function( jQuery, undefined ) {
+
+ var stepHooks = "backgroundColor borderBottomColor borderLeftColor borderRightColor borderTopColor color columnRuleColor outlineColor textDecorationColor textEmphasisColor",
+
+ // plusequals test for += 100 -= 100
+ rplusequals = /^([\-+])=\s*(\d+\.?\d*)/,
+ // a set of RE's that can match strings and generate color tuples.
+ stringParsers = [{
+ re: /rgba?\(\s*(\d{1,3})\s*,\s*(\d{1,3})\s*,\s*(\d{1,3})\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
+ parse: function( execResult ) {
+ return [
+ execResult[ 1 ],
+ execResult[ 2 ],
+ execResult[ 3 ],
+ execResult[ 4 ]
+ ];
+ }
+ }, {
+ re: /rgba?\(\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
+ parse: function( execResult ) {
+ return [
+ execResult[ 1 ] * 2.55,
+ execResult[ 2 ] * 2.55,
+ execResult[ 3 ] * 2.55,
+ execResult[ 4 ]
+ ];
+ }
+ }, {
+ // this regex ignores A-F because it's compared against an already lowercased string
+ re: /#([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})/,
+ parse: function( execResult ) {
+ return [
+ parseInt( execResult[ 1 ], 16 ),
+ parseInt( execResult[ 2 ], 16 ),
+ parseInt( execResult[ 3 ], 16 )
+ ];
+ }
+ }, {
+ // this regex ignores A-F because it's compared against an already lowercased string
+ re: /#([a-f0-9])([a-f0-9])([a-f0-9])/,
+ parse: function( execResult ) {
+ return [
+ parseInt( execResult[ 1 ] + execResult[ 1 ], 16 ),
+ parseInt( execResult[ 2 ] + execResult[ 2 ], 16 ),
+ parseInt( execResult[ 3 ] + execResult[ 3 ], 16 )
+ ];
+ }
+ }, {
+ re: /hsla?\(\s*(\d+(?:\.\d+)?)\s*,\s*(\d+(?:\.\d+)?)\%\s*,\s*(\d+(?:\.\d+)?)\%\s*(?:,\s*(\d?(?:\.\d+)?)\s*)?\)/,
+ space: "hsla",
+ parse: function( execResult ) {
+ return [
+ execResult[ 1 ],
+ execResult[ 2 ] / 100,
+ execResult[ 3 ] / 100,
+ execResult[ 4 ]
+ ];
+ }
+ }],
+
+ // jQuery.Color( )
+ color = jQuery.Color = function( color, green, blue, alpha ) {
+ return new jQuery.Color.fn.parse( color, green, blue, alpha );
+ },
+ spaces = {
+ rgba: {
+ props: {
+ red: {
+ idx: 0,
+ type: "byte"
+ },
+ green: {
+ idx: 1,
+ type: "byte"
+ },
+ blue: {
+ idx: 2,
+ type: "byte"
+ }
+ }
+ },
+
+ hsla: {
+ props: {
+ hue: {
+ idx: 0,
+ type: "degrees"
+ },
+ saturation: {
+ idx: 1,
+ type: "percent"
+ },
+ lightness: {
+ idx: 2,
+ type: "percent"
+ }
+ }
+ }
+ },
+ propTypes = {
+ "byte": {
+ floor: true,
+ max: 255
+ },
+ "percent": {
+ max: 1
+ },
+ "degrees": {
+ mod: 360,
+ floor: true
+ }
+ },
+ support = color.support = {},
+
+ // element for support tests
+ supportElem = jQuery( "
" )[ 0 ],
+
+ // colors = jQuery.Color.names
+ colors,
+
+ // local aliases of functions called often
+ each = jQuery.each;
+
+// determine rgba support immediately
+supportElem.style.cssText = "background-color:rgba(1,1,1,.5)";
+support.rgba = supportElem.style.backgroundColor.indexOf( "rgba" ) > -1;
+
+// define cache name and alpha properties
+// for rgba and hsla spaces
+each( spaces, function( spaceName, space ) {
+ space.cache = "_" + spaceName;
+ space.props.alpha = {
+ idx: 3,
+ type: "percent",
+ def: 1
+ };
+});
+
+function clamp( value, prop, allowEmpty ) {
+ var type = propTypes[ prop.type ] || {};
+
+ if ( value == null ) {
+ return (allowEmpty || !prop.def) ? null : prop.def;
+ }
+
+ // ~~ is an short way of doing floor for positive numbers
+ value = type.floor ? ~~value : parseFloat( value );
+
+ // IE will pass in empty strings as value for alpha,
+ // which will hit this case
+ if ( isNaN( value ) ) {
+ return prop.def;
+ }
+
+ if ( type.mod ) {
+ // we add mod before modding to make sure that negatives values
+ // get converted properly: -10 -> 350
+ return (value + type.mod) % type.mod;
+ }
+
+ // for now all property types without mod have min and max
+ return 0 > value ? 0 : type.max < value ? type.max : value;
+}
+
+function stringParse( string ) {
+ var inst = color(),
+ rgba = inst._rgba = [];
+
+ string = string.toLowerCase();
+
+ each( stringParsers, function( i, parser ) {
+ var parsed,
+ match = parser.re.exec( string ),
+ values = match && parser.parse( match ),
+ spaceName = parser.space || "rgba";
+
+ if ( values ) {
+ parsed = inst[ spaceName ]( values );
+
+ // if this was an rgba parse the assignment might happen twice
+ // oh well....
+ inst[ spaces[ spaceName ].cache ] = parsed[ spaces[ spaceName ].cache ];
+ rgba = inst._rgba = parsed._rgba;
+
+ // exit each( stringParsers ) here because we matched
+ return false;
+ }
+ });
+
+ // Found a stringParser that handled it
+ if ( rgba.length ) {
+
+ // if this came from a parsed string, force "transparent" when alpha is 0
+ // chrome, (and maybe others) return "transparent" as rgba(0,0,0,0)
+ if ( rgba.join() === "0,0,0,0" ) {
+ jQuery.extend( rgba, colors.transparent );
+ }
+ return inst;
+ }
+
+ // named colors
+ return colors[ string ];
+}
+
+color.fn = jQuery.extend( color.prototype, {
+ parse: function( red, green, blue, alpha ) {
+ if ( red === undefined ) {
+ this._rgba = [ null, null, null, null ];
+ return this;
+ }
+ if ( red.jquery || red.nodeType ) {
+ red = jQuery( red ).css( green );
+ green = undefined;
+ }
+
+ var inst = this,
+ type = jQuery.type( red ),
+ rgba = this._rgba = [];
+
+ // more than 1 argument specified - assume ( red, green, blue, alpha )
+ if ( green !== undefined ) {
+ red = [ red, green, blue, alpha ];
+ type = "array";
+ }
+
+ if ( type === "string" ) {
+ return this.parse( stringParse( red ) || colors._default );
+ }
+
+ if ( type === "array" ) {
+ each( spaces.rgba.props, function( key, prop ) {
+ rgba[ prop.idx ] = clamp( red[ prop.idx ], prop );
+ });
+ return this;
+ }
+
+ if ( type === "object" ) {
+ if ( red instanceof color ) {
+ each( spaces, function( spaceName, space ) {
+ if ( red[ space.cache ] ) {
+ inst[ space.cache ] = red[ space.cache ].slice();
+ }
+ });
+ } else {
+ each( spaces, function( spaceName, space ) {
+ var cache = space.cache;
+ each( space.props, function( key, prop ) {
+
+ // if the cache doesn't exist, and we know how to convert
+ if ( !inst[ cache ] && space.to ) {
+
+ // if the value was null, we don't need to copy it
+ // if the key was alpha, we don't need to copy it either
+ if ( key === "alpha" || red[ key ] == null ) {
+ return;
+ }
+ inst[ cache ] = space.to( inst._rgba );
+ }
+
+ // this is the only case where we allow nulls for ALL properties.
+ // call clamp with alwaysAllowEmpty
+ inst[ cache ][ prop.idx ] = clamp( red[ key ], prop, true );
+ });
+
+ // everything defined but alpha?
+ if ( inst[ cache ] && jQuery.inArray( null, inst[ cache ].slice( 0, 3 ) ) < 0 ) {
+ // use the default of 1
+ inst[ cache ][ 3 ] = 1;
+ if ( space.from ) {
+ inst._rgba = space.from( inst[ cache ] );
+ }
+ }
+ });
+ }
+ return this;
+ }
+ },
+ is: function( compare ) {
+ var is = color( compare ),
+ same = true,
+ inst = this;
+
+ each( spaces, function( _, space ) {
+ var localCache,
+ isCache = is[ space.cache ];
+ if (isCache) {
+ localCache = inst[ space.cache ] || space.to && space.to( inst._rgba ) || [];
+ each( space.props, function( _, prop ) {
+ if ( isCache[ prop.idx ] != null ) {
+ same = ( isCache[ prop.idx ] === localCache[ prop.idx ] );
+ return same;
+ }
+ });
+ }
+ return same;
+ });
+ return same;
+ },
+ _space: function() {
+ var used = [],
+ inst = this;
+ each( spaces, function( spaceName, space ) {
+ if ( inst[ space.cache ] ) {
+ used.push( spaceName );
+ }
+ });
+ return used.pop();
+ },
+ transition: function( other, distance ) {
+ var end = color( other ),
+ spaceName = end._space(),
+ space = spaces[ spaceName ],
+ startColor = this.alpha() === 0 ? color( "transparent" ) : this,
+ start = startColor[ space.cache ] || space.to( startColor._rgba ),
+ result = start.slice();
+
+ end = end[ space.cache ];
+ each( space.props, function( key, prop ) {
+ var index = prop.idx,
+ startValue = start[ index ],
+ endValue = end[ index ],
+ type = propTypes[ prop.type ] || {};
+
+ // if null, don't override start value
+ if ( endValue === null ) {
+ return;
+ }
+ // if null - use end
+ if ( startValue === null ) {
+ result[ index ] = endValue;
+ } else {
+ if ( type.mod ) {
+ if ( endValue - startValue > type.mod / 2 ) {
+ startValue += type.mod;
+ } else if ( startValue - endValue > type.mod / 2 ) {
+ startValue -= type.mod;
+ }
+ }
+ result[ index ] = clamp( ( endValue - startValue ) * distance + startValue, prop );
+ }
+ });
+ return this[ spaceName ]( result );
+ },
+ blend: function( opaque ) {
+ // if we are already opaque - return ourself
+ if ( this._rgba[ 3 ] === 1 ) {
+ return this;
+ }
+
+ var rgb = this._rgba.slice(),
+ a = rgb.pop(),
+ blend = color( opaque )._rgba;
+
+ return color( jQuery.map( rgb, function( v, i ) {
+ return ( 1 - a ) * blend[ i ] + a * v;
+ }));
+ },
+ toRgbaString: function() {
+ var prefix = "rgba(",
+ rgba = jQuery.map( this._rgba, function( v, i ) {
+ return v == null ? ( i > 2 ? 1 : 0 ) : v;
+ });
+
+ if ( rgba[ 3 ] === 1 ) {
+ rgba.pop();
+ prefix = "rgb(";
+ }
+
+ return prefix + rgba.join() + ")";
+ },
+ toHslaString: function() {
+ var prefix = "hsla(",
+ hsla = jQuery.map( this.hsla(), function( v, i ) {
+ if ( v == null ) {
+ v = i > 2 ? 1 : 0;
+ }
+
+ // catch 1 and 2
+ if ( i && i < 3 ) {
+ v = Math.round( v * 100 ) + "%";
+ }
+ return v;
+ });
+
+ if ( hsla[ 3 ] === 1 ) {
+ hsla.pop();
+ prefix = "hsl(";
+ }
+ return prefix + hsla.join() + ")";
+ },
+ toHexString: function( includeAlpha ) {
+ var rgba = this._rgba.slice(),
+ alpha = rgba.pop();
+
+ if ( includeAlpha ) {
+ rgba.push( ~~( alpha * 255 ) );
+ }
+
+ return "#" + jQuery.map( rgba, function( v ) {
+
+ // default to 0 when nulls exist
+ v = ( v || 0 ).toString( 16 );
+ return v.length === 1 ? "0" + v : v;
+ }).join("");
+ },
+ toString: function() {
+ return this._rgba[ 3 ] === 0 ? "transparent" : this.toRgbaString();
+ }
+});
+color.fn.parse.prototype = color.fn;
+
+// hsla conversions adapted from:
+// https://code.google.com/p/maashaack/source/browse/packages/graphics/trunk/src/graphics/colors/HUE2RGB.as?r=5021
+
+function hue2rgb( p, q, h ) {
+ h = ( h + 1 ) % 1;
+ if ( h * 6 < 1 ) {
+ return p + (q - p) * h * 6;
+ }
+ if ( h * 2 < 1) {
+ return q;
+ }
+ if ( h * 3 < 2 ) {
+ return p + (q - p) * ((2/3) - h) * 6;
+ }
+ return p;
+}
+
+spaces.hsla.to = function ( rgba ) {
+ if ( rgba[ 0 ] == null || rgba[ 1 ] == null || rgba[ 2 ] == null ) {
+ return [ null, null, null, rgba[ 3 ] ];
+ }
+ var r = rgba[ 0 ] / 255,
+ g = rgba[ 1 ] / 255,
+ b = rgba[ 2 ] / 255,
+ a = rgba[ 3 ],
+ max = Math.max( r, g, b ),
+ min = Math.min( r, g, b ),
+ diff = max - min,
+ add = max + min,
+ l = add * 0.5,
+ h, s;
+
+ if ( min === max ) {
+ h = 0;
+ } else if ( r === max ) {
+ h = ( 60 * ( g - b ) / diff ) + 360;
+ } else if ( g === max ) {
+ h = ( 60 * ( b - r ) / diff ) + 120;
+ } else {
+ h = ( 60 * ( r - g ) / diff ) + 240;
+ }
+
+ // chroma (diff) == 0 means greyscale which, by definition, saturation = 0%
+ // otherwise, saturation is based on the ratio of chroma (diff) to lightness (add)
+ if ( diff === 0 ) {
+ s = 0;
+ } else if ( l <= 0.5 ) {
+ s = diff / add;
+ } else {
+ s = diff / ( 2 - add );
+ }
+ return [ Math.round(h) % 360, s, l, a == null ? 1 : a ];
+};
+
+spaces.hsla.from = function ( hsla ) {
+ if ( hsla[ 0 ] == null || hsla[ 1 ] == null || hsla[ 2 ] == null ) {
+ return [ null, null, null, hsla[ 3 ] ];
+ }
+ var h = hsla[ 0 ] / 360,
+ s = hsla[ 1 ],
+ l = hsla[ 2 ],
+ a = hsla[ 3 ],
+ q = l <= 0.5 ? l * ( 1 + s ) : l + s - l * s,
+ p = 2 * l - q;
+
+ return [
+ Math.round( hue2rgb( p, q, h + ( 1 / 3 ) ) * 255 ),
+ Math.round( hue2rgb( p, q, h ) * 255 ),
+ Math.round( hue2rgb( p, q, h - ( 1 / 3 ) ) * 255 ),
+ a
+ ];
+};
+
+
+each( spaces, function( spaceName, space ) {
+ var props = space.props,
+ cache = space.cache,
+ to = space.to,
+ from = space.from;
+
+ // makes rgba() and hsla()
+ color.fn[ spaceName ] = function( value ) {
+
+ // generate a cache for this space if it doesn't exist
+ if ( to && !this[ cache ] ) {
+ this[ cache ] = to( this._rgba );
+ }
+ if ( value === undefined ) {
+ return this[ cache ].slice();
+ }
+
+ var ret,
+ type = jQuery.type( value ),
+ arr = ( type === "array" || type === "object" ) ? value : arguments,
+ local = this[ cache ].slice();
+
+ each( props, function( key, prop ) {
+ var val = arr[ type === "object" ? key : prop.idx ];
+ if ( val == null ) {
+ val = local[ prop.idx ];
+ }
+ local[ prop.idx ] = clamp( val, prop );
+ });
+
+ if ( from ) {
+ ret = color( from( local ) );
+ ret[ cache ] = local;
+ return ret;
+ } else {
+ return color( local );
+ }
+ };
+
+ // makes red() green() blue() alpha() hue() saturation() lightness()
+ each( props, function( key, prop ) {
+ // alpha is included in more than one space
+ if ( color.fn[ key ] ) {
+ return;
+ }
+ color.fn[ key ] = function( value ) {
+ var vtype = jQuery.type( value ),
+ fn = ( key === "alpha" ? ( this._hsla ? "hsla" : "rgba" ) : spaceName ),
+ local = this[ fn ](),
+ cur = local[ prop.idx ],
+ match;
+
+ if ( vtype === "undefined" ) {
+ return cur;
+ }
+
+ if ( vtype === "function" ) {
+ value = value.call( this, cur );
+ vtype = jQuery.type( value );
+ }
+ if ( value == null && prop.empty ) {
+ return this;
+ }
+ if ( vtype === "string" ) {
+ match = rplusequals.exec( value );
+ if ( match ) {
+ value = cur + parseFloat( match[ 2 ] ) * ( match[ 1 ] === "+" ? 1 : -1 );
+ }
+ }
+ local[ prop.idx ] = value;
+ return this[ fn ]( local );
+ };
+ });
+});
+
+// add cssHook and .fx.step function for each named hook.
+// accept a space separated string of properties
+color.hook = function( hook ) {
+ var hooks = hook.split( " " );
+ each( hooks, function( i, hook ) {
+ jQuery.cssHooks[ hook ] = {
+ set: function( elem, value ) {
+ var parsed, curElem,
+ backgroundColor = "";
+
+ if ( value !== "transparent" && ( jQuery.type( value ) !== "string" || ( parsed = stringParse( value ) ) ) ) {
+ value = color( parsed || value );
+ if ( !support.rgba && value._rgba[ 3 ] !== 1 ) {
+ curElem = hook === "backgroundColor" ? elem.parentNode : elem;
+ while (
+ (backgroundColor === "" || backgroundColor === "transparent") &&
+ curElem && curElem.style
+ ) {
+ try {
+ backgroundColor = jQuery.css( curElem, "backgroundColor" );
+ curElem = curElem.parentNode;
+ } catch ( e ) {
+ }
+ }
+
+ value = value.blend( backgroundColor && backgroundColor !== "transparent" ?
+ backgroundColor :
+ "_default" );
+ }
+
+ value = value.toRgbaString();
+ }
+ try {
+ elem.style[ hook ] = value;
+ } catch( e ) {
+ // wrapped to prevent IE from throwing errors on "invalid" values like 'auto' or 'inherit'
+ }
+ }
+ };
+ jQuery.fx.step[ hook ] = function( fx ) {
+ if ( !fx.colorInit ) {
+ fx.start = color( fx.elem, hook );
+ fx.end = color( fx.end );
+ fx.colorInit = true;
+ }
+ jQuery.cssHooks[ hook ].set( fx.elem, fx.start.transition( fx.end, fx.pos ) );
+ };
+ });
+
+};
+
+color.hook( stepHooks );
+
+jQuery.cssHooks.borderColor = {
+ expand: function( value ) {
+ var expanded = {};
+
+ each( [ "Top", "Right", "Bottom", "Left" ], function( i, part ) {
+ expanded[ "border" + part + "Color" ] = value;
+ });
+ return expanded;
+ }
+};
+
+// Basic color names only.
+// Usage of any of the other color names requires adding yourself or including
+// jquery.color.svg-names.js.
+colors = jQuery.Color.names = {
+ // 4.1. Basic color keywords
+ aqua: "#00ffff",
+ black: "#000000",
+ blue: "#0000ff",
+ fuchsia: "#ff00ff",
+ gray: "#808080",
+ green: "#008000",
+ lime: "#00ff00",
+ maroon: "#800000",
+ navy: "#000080",
+ olive: "#808000",
+ purple: "#800080",
+ red: "#ff0000",
+ silver: "#c0c0c0",
+ teal: "#008080",
+ white: "#ffffff",
+ yellow: "#ffff00",
+
+ // 4.2.3. "transparent" color keyword
+ transparent: [ null, null, null, 0 ],
+
+ _default: "#ffffff"
+};
+
+})( jQuery );
+
+
+/******************************************************************************/
+/****************************** CLASS ANIMATIONS ******************************/
+/******************************************************************************/
+(function() {
+
+var classAnimationActions = [ "add", "remove", "toggle" ],
+ shorthandStyles = {
+ border: 1,
+ borderBottom: 1,
+ borderColor: 1,
+ borderLeft: 1,
+ borderRight: 1,
+ borderTop: 1,
+ borderWidth: 1,
+ margin: 1,
+ padding: 1
+ };
+
+$.each([ "borderLeftStyle", "borderRightStyle", "borderBottomStyle", "borderTopStyle" ], function( _, prop ) {
+ $.fx.step[ prop ] = function( fx ) {
+ if ( fx.end !== "none" && !fx.setAttr || fx.pos === 1 && !fx.setAttr ) {
+ jQuery.style( fx.elem, prop, fx.end );
+ fx.setAttr = true;
+ }
+ };
+});
+
+function getElementStyles( elem ) {
+ var key, len,
+ style = elem.ownerDocument.defaultView ?
+ elem.ownerDocument.defaultView.getComputedStyle( elem, null ) :
+ elem.currentStyle,
+ styles = {};
+
+ if ( style && style.length && style[ 0 ] && style[ style[ 0 ] ] ) {
+ len = style.length;
+ while ( len-- ) {
+ key = style[ len ];
+ if ( typeof style[ key ] === "string" ) {
+ styles[ $.camelCase( key ) ] = style[ key ];
+ }
+ }
+ // support: Opera, IE <9
+ } else {
+ for ( key in style ) {
+ if ( typeof style[ key ] === "string" ) {
+ styles[ key ] = style[ key ];
+ }
+ }
+ }
+
+ return styles;
+}
+
+
+function styleDifference( oldStyle, newStyle ) {
+ var diff = {},
+ name, value;
+
+ for ( name in newStyle ) {
+ value = newStyle[ name ];
+ if ( oldStyle[ name ] !== value ) {
+ if ( !shorthandStyles[ name ] ) {
+ if ( $.fx.step[ name ] || !isNaN( parseFloat( value ) ) ) {
+ diff[ name ] = value;
+ }
+ }
+ }
+ }
+
+ return diff;
+}
+
+// support: jQuery <1.8
+if ( !$.fn.addBack ) {
+ $.fn.addBack = function( selector ) {
+ return this.add( selector == null ?
+ this.prevObject : this.prevObject.filter( selector )
+ );
+ };
+}
+
+$.effects.animateClass = function( value, duration, easing, callback ) {
+ var o = $.speed( duration, easing, callback );
+
+ return this.queue( function() {
+ var animated = $( this ),
+ baseClass = animated.attr( "class" ) || "",
+ applyClassChange,
+ allAnimations = o.children ? animated.find( "*" ).addBack() : animated;
+
+ // map the animated objects to store the original styles.
+ allAnimations = allAnimations.map(function() {
+ var el = $( this );
+ return {
+ el: el,
+ start: getElementStyles( this )
+ };
+ });
+
+ // apply class change
+ applyClassChange = function() {
+ $.each( classAnimationActions, function(i, action) {
+ if ( value[ action ] ) {
+ animated[ action + "Class" ]( value[ action ] );
+ }
+ });
+ };
+ applyClassChange();
+
+ // map all animated objects again - calculate new styles and diff
+ allAnimations = allAnimations.map(function() {
+ this.end = getElementStyles( this.el[ 0 ] );
+ this.diff = styleDifference( this.start, this.end );
+ return this;
+ });
+
+ // apply original class
+ animated.attr( "class", baseClass );
+
+ // map all animated objects again - this time collecting a promise
+ allAnimations = allAnimations.map(function() {
+ var styleInfo = this,
+ dfd = $.Deferred(),
+ opts = $.extend({}, o, {
+ queue: false,
+ complete: function() {
+ dfd.resolve( styleInfo );
+ }
+ });
+
+ this.el.animate( this.diff, opts );
+ return dfd.promise();
+ });
+
+ // once all animations have completed:
+ $.when.apply( $, allAnimations.get() ).done(function() {
+
+ // set the final class
+ applyClassChange();
+
+ // for each animated element,
+ // clear all css properties that were animated
+ $.each( arguments, function() {
+ var el = this.el;
+ $.each( this.diff, function(key) {
+ el.css( key, "" );
+ });
+ });
+
+ // this is guarnteed to be there if you use jQuery.speed()
+ // it also handles dequeuing the next anim...
+ o.complete.call( animated[ 0 ] );
+ });
+ });
+};
+
+$.fn.extend({
+ addClass: (function( orig ) {
+ return function( classNames, speed, easing, callback ) {
+ return speed ?
+ $.effects.animateClass.call( this,
+ { add: classNames }, speed, easing, callback ) :
+ orig.apply( this, arguments );
+ };
+ })( $.fn.addClass ),
+
+ removeClass: (function( orig ) {
+ return function( classNames, speed, easing, callback ) {
+ return arguments.length > 1 ?
+ $.effects.animateClass.call( this,
+ { remove: classNames }, speed, easing, callback ) :
+ orig.apply( this, arguments );
+ };
+ })( $.fn.removeClass ),
+
+ toggleClass: (function( orig ) {
+ return function( classNames, force, speed, easing, callback ) {
+ if ( typeof force === "boolean" || force === undefined ) {
+ if ( !speed ) {
+ // without speed parameter
+ return orig.apply( this, arguments );
+ } else {
+ return $.effects.animateClass.call( this,
+ (force ? { add: classNames } : { remove: classNames }),
+ speed, easing, callback );
+ }
+ } else {
+ // without force parameter
+ return $.effects.animateClass.call( this,
+ { toggle: classNames }, force, speed, easing );
+ }
+ };
+ })( $.fn.toggleClass ),
+
+ switchClass: function( remove, add, speed, easing, callback) {
+ return $.effects.animateClass.call( this, {
+ add: add,
+ remove: remove
+ }, speed, easing, callback );
+ }
+});
+
+})();
+
+/******************************************************************************/
+/*********************************** EFFECTS **********************************/
+/******************************************************************************/
+
+(function() {
+
+$.extend( $.effects, {
+ version: "1.10.4",
+
+ // Saves a set of properties in a data storage
+ save: function( element, set ) {
+ for( var i=0; i < set.length; i++ ) {
+ if ( set[ i ] !== null ) {
+ element.data( dataSpace + set[ i ], element[ 0 ].style[ set[ i ] ] );
+ }
+ }
+ },
+
+ // Restores a set of previously saved properties from a data storage
+ restore: function( element, set ) {
+ var val, i;
+ for( i=0; i < set.length; i++ ) {
+ if ( set[ i ] !== null ) {
+ val = element.data( dataSpace + set[ i ] );
+ // support: jQuery 1.6.2
+ // http://bugs.jquery.com/ticket/9917
+ // jQuery 1.6.2 incorrectly returns undefined for any falsy value.
+ // We can't differentiate between "" and 0 here, so we just assume
+ // empty string since it's likely to be a more common value...
+ if ( val === undefined ) {
+ val = "";
+ }
+ element.css( set[ i ], val );
+ }
+ }
+ },
+
+ setMode: function( el, mode ) {
+ if (mode === "toggle") {
+ mode = el.is( ":hidden" ) ? "show" : "hide";
+ }
+ return mode;
+ },
+
+ // Translates a [top,left] array into a baseline value
+ // this should be a little more flexible in the future to handle a string & hash
+ getBaseline: function( origin, original ) {
+ var y, x;
+ switch ( origin[ 0 ] ) {
+ case "top": y = 0; break;
+ case "middle": y = 0.5; break;
+ case "bottom": y = 1; break;
+ default: y = origin[ 0 ] / original.height;
+ }
+ switch ( origin[ 1 ] ) {
+ case "left": x = 0; break;
+ case "center": x = 0.5; break;
+ case "right": x = 1; break;
+ default: x = origin[ 1 ] / original.width;
+ }
+ return {
+ x: x,
+ y: y
+ };
+ },
+
+ // Wraps the element around a wrapper that copies position properties
+ createWrapper: function( element ) {
+
+ // if the element is already wrapped, return it
+ if ( element.parent().is( ".ui-effects-wrapper" )) {
+ return element.parent();
+ }
+
+ // wrap the element
+ var props = {
+ width: element.outerWidth(true),
+ height: element.outerHeight(true),
+ "float": element.css( "float" )
+ },
+ wrapper = $( "
" )
+ .addClass( "ui-effects-wrapper" )
+ .css({
+ fontSize: "100%",
+ background: "transparent",
+ border: "none",
+ margin: 0,
+ padding: 0
+ }),
+ // Store the size in case width/height are defined in % - Fixes #5245
+ size = {
+ width: element.width(),
+ height: element.height()
+ },
+ active = document.activeElement;
+
+ // support: Firefox
+ // Firefox incorrectly exposes anonymous content
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=561664
+ try {
+ active.id;
+ } catch( e ) {
+ active = document.body;
+ }
+
+ element.wrap( wrapper );
+
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+
+ wrapper = element.parent(); //Hotfix for jQuery 1.4 since some change in wrap() seems to actually lose the reference to the wrapped element
+
+ // transfer positioning properties to the wrapper
+ if ( element.css( "position" ) === "static" ) {
+ wrapper.css({ position: "relative" });
+ element.css({ position: "relative" });
+ } else {
+ $.extend( props, {
+ position: element.css( "position" ),
+ zIndex: element.css( "z-index" )
+ });
+ $.each([ "top", "left", "bottom", "right" ], function(i, pos) {
+ props[ pos ] = element.css( pos );
+ if ( isNaN( parseInt( props[ pos ], 10 ) ) ) {
+ props[ pos ] = "auto";
+ }
+ });
+ element.css({
+ position: "relative",
+ top: 0,
+ left: 0,
+ right: "auto",
+ bottom: "auto"
+ });
+ }
+ element.css(size);
+
+ return wrapper.css( props ).show();
+ },
+
+ removeWrapper: function( element ) {
+ var active = document.activeElement;
+
+ if ( element.parent().is( ".ui-effects-wrapper" ) ) {
+ element.parent().replaceWith( element );
+
+ // Fixes #7595 - Elements lose focus when wrapped.
+ if ( element[ 0 ] === active || $.contains( element[ 0 ], active ) ) {
+ $( active ).focus();
+ }
+ }
+
+
+ return element;
+ },
+
+ setTransition: function( element, list, factor, value ) {
+ value = value || {};
+ $.each( list, function( i, x ) {
+ var unit = element.cssUnit( x );
+ if ( unit[ 0 ] > 0 ) {
+ value[ x ] = unit[ 0 ] * factor + unit[ 1 ];
+ }
+ });
+ return value;
+ }
+});
+
+// return an effect options object for the given parameters:
+function _normalizeArguments( effect, options, speed, callback ) {
+
+ // allow passing all options as the first parameter
+ if ( $.isPlainObject( effect ) ) {
+ options = effect;
+ effect = effect.effect;
+ }
+
+ // convert to an object
+ effect = { effect: effect };
+
+ // catch (effect, null, ...)
+ if ( options == null ) {
+ options = {};
+ }
+
+ // catch (effect, callback)
+ if ( $.isFunction( options ) ) {
+ callback = options;
+ speed = null;
+ options = {};
+ }
+
+ // catch (effect, speed, ?)
+ if ( typeof options === "number" || $.fx.speeds[ options ] ) {
+ callback = speed;
+ speed = options;
+ options = {};
+ }
+
+ // catch (effect, options, callback)
+ if ( $.isFunction( speed ) ) {
+ callback = speed;
+ speed = null;
+ }
+
+ // add options to effect
+ if ( options ) {
+ $.extend( effect, options );
+ }
+
+ speed = speed || options.duration;
+ effect.duration = $.fx.off ? 0 :
+ typeof speed === "number" ? speed :
+ speed in $.fx.speeds ? $.fx.speeds[ speed ] :
+ $.fx.speeds._default;
+
+ effect.complete = callback || options.complete;
+
+ return effect;
+}
+
+function standardAnimationOption( option ) {
+ // Valid standard speeds (nothing, number, named speed)
+ if ( !option || typeof option === "number" || $.fx.speeds[ option ] ) {
+ return true;
+ }
+
+ // Invalid strings - treat as "normal" speed
+ if ( typeof option === "string" && !$.effects.effect[ option ] ) {
+ return true;
+ }
+
+ // Complete callback
+ if ( $.isFunction( option ) ) {
+ return true;
+ }
+
+ // Options hash (but not naming an effect)
+ if ( typeof option === "object" && !option.effect ) {
+ return true;
+ }
+
+ // Didn't match any standard API
+ return false;
+}
+
+$.fn.extend({
+ effect: function( /* effect, options, speed, callback */ ) {
+ var args = _normalizeArguments.apply( this, arguments ),
+ mode = args.mode,
+ queue = args.queue,
+ effectMethod = $.effects.effect[ args.effect ];
+
+ if ( $.fx.off || !effectMethod ) {
+ // delegate to the original method (e.g., .show()) if possible
+ if ( mode ) {
+ return this[ mode ]( args.duration, args.complete );
+ } else {
+ return this.each( function() {
+ if ( args.complete ) {
+ args.complete.call( this );
+ }
+ });
+ }
+ }
+
+ function run( next ) {
+ var elem = $( this ),
+ complete = args.complete,
+ mode = args.mode;
+
+ function done() {
+ if ( $.isFunction( complete ) ) {
+ complete.call( elem[0] );
+ }
+ if ( $.isFunction( next ) ) {
+ next();
+ }
+ }
+
+ // If the element already has the correct final state, delegate to
+ // the core methods so the internal tracking of "olddisplay" works.
+ if ( elem.is( ":hidden" ) ? mode === "hide" : mode === "show" ) {
+ elem[ mode ]();
+ done();
+ } else {
+ effectMethod.call( elem[0], args, done );
+ }
+ }
+
+ return queue === false ? this.each( run ) : this.queue( queue || "fx", run );
+ },
+
+ show: (function( orig ) {
+ return function( option ) {
+ if ( standardAnimationOption( option ) ) {
+ return orig.apply( this, arguments );
+ } else {
+ var args = _normalizeArguments.apply( this, arguments );
+ args.mode = "show";
+ return this.effect.call( this, args );
+ }
+ };
+ })( $.fn.show ),
+
+ hide: (function( orig ) {
+ return function( option ) {
+ if ( standardAnimationOption( option ) ) {
+ return orig.apply( this, arguments );
+ } else {
+ var args = _normalizeArguments.apply( this, arguments );
+ args.mode = "hide";
+ return this.effect.call( this, args );
+ }
+ };
+ })( $.fn.hide ),
+
+ toggle: (function( orig ) {
+ return function( option ) {
+ if ( standardAnimationOption( option ) || typeof option === "boolean" ) {
+ return orig.apply( this, arguments );
+ } else {
+ var args = _normalizeArguments.apply( this, arguments );
+ args.mode = "toggle";
+ return this.effect.call( this, args );
+ }
+ };
+ })( $.fn.toggle ),
+
+ // helper functions
+ cssUnit: function(key) {
+ var style = this.css( key ),
+ val = [];
+
+ $.each( [ "em", "px", "%", "pt" ], function( i, unit ) {
+ if ( style.indexOf( unit ) > 0 ) {
+ val = [ parseFloat( style ), unit ];
+ }
+ });
+ return val;
+ }
+});
+
+})();
+
+/******************************************************************************/
+/*********************************** EASING ***********************************/
+/******************************************************************************/
+
+(function() {
+
+// based on easing equations from Robert Penner (http://www.robertpenner.com/easing)
+
+var baseEasings = {};
+
+$.each( [ "Quad", "Cubic", "Quart", "Quint", "Expo" ], function( i, name ) {
+ baseEasings[ name ] = function( p ) {
+ return Math.pow( p, i + 2 );
+ };
+});
+
+$.extend( baseEasings, {
+ Sine: function ( p ) {
+ return 1 - Math.cos( p * Math.PI / 2 );
+ },
+ Circ: function ( p ) {
+ return 1 - Math.sqrt( 1 - p * p );
+ },
+ Elastic: function( p ) {
+ return p === 0 || p === 1 ? p :
+ -Math.pow( 2, 8 * (p - 1) ) * Math.sin( ( (p - 1) * 80 - 7.5 ) * Math.PI / 15 );
+ },
+ Back: function( p ) {
+ return p * p * ( 3 * p - 2 );
+ },
+ Bounce: function ( p ) {
+ var pow2,
+ bounce = 4;
+
+ while ( p < ( ( pow2 = Math.pow( 2, --bounce ) ) - 1 ) / 11 ) {}
+ return 1 / Math.pow( 4, 3 - bounce ) - 7.5625 * Math.pow( ( pow2 * 3 - 2 ) / 22 - p, 2 );
+ }
+});
+
+$.each( baseEasings, function( name, easeIn ) {
+ $.easing[ "easeIn" + name ] = easeIn;
+ $.easing[ "easeOut" + name ] = function( p ) {
+ return 1 - easeIn( 1 - p );
+ };
+ $.easing[ "easeInOut" + name ] = function( p ) {
+ return p < 0.5 ?
+ easeIn( p * 2 ) / 2 :
+ 1 - easeIn( p * -2 + 2 ) / 2;
+ };
+});
+
+})();
+
+})(jQuery);
+(function( $, undefined ) {
+
+var rvertical = /up|down|vertical/,
+ rpositivemotion = /up|left|vertical|horizontal/;
+
+$.effects.effect.blind = function( o, done ) {
+ // Create element
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
+ mode = $.effects.setMode( el, o.mode || "hide" ),
+ direction = o.direction || "up",
+ vertical = rvertical.test( direction ),
+ ref = vertical ? "height" : "width",
+ ref2 = vertical ? "top" : "left",
+ motion = rpositivemotion.test( direction ),
+ animation = {},
+ show = mode === "show",
+ wrapper, distance, margin;
+
+ // if already wrapped, the wrapper's properties are my property. #6245
+ if ( el.parent().is( ".ui-effects-wrapper" ) ) {
+ $.effects.save( el.parent(), props );
+ } else {
+ $.effects.save( el, props );
+ }
+ el.show();
+ wrapper = $.effects.createWrapper( el ).css({
+ overflow: "hidden"
+ });
+
+ distance = wrapper[ ref ]();
+ margin = parseFloat( wrapper.css( ref2 ) ) || 0;
+
+ animation[ ref ] = show ? distance : 0;
+ if ( !motion ) {
+ el
+ .css( vertical ? "bottom" : "right", 0 )
+ .css( vertical ? "top" : "left", "auto" )
+ .css({ position: "absolute" });
+
+ animation[ ref2 ] = show ? margin : distance + margin;
+ }
+
+ // start at 0 if we are showing
+ if ( show ) {
+ wrapper.css( ref, 0 );
+ if ( ! motion ) {
+ wrapper.css( ref2, margin + distance );
+ }
+ }
+
+ // Animate
+ wrapper.animate( animation, {
+ duration: o.duration,
+ easing: o.easing,
+ queue: false,
+ complete: function() {
+ if ( mode === "hide" ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ }
+ });
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.bounce = function( o, done ) {
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
+
+ // defaults:
+ mode = $.effects.setMode( el, o.mode || "effect" ),
+ hide = mode === "hide",
+ show = mode === "show",
+ direction = o.direction || "up",
+ distance = o.distance,
+ times = o.times || 5,
+
+ // number of internal animations
+ anims = times * 2 + ( show || hide ? 1 : 0 ),
+ speed = o.duration / anims,
+ easing = o.easing,
+
+ // utility:
+ ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
+ motion = ( direction === "up" || direction === "left" ),
+ i,
+ upAnim,
+ downAnim,
+
+ // we will need to re-assemble the queue to stack our animations in place
+ queue = el.queue(),
+ queuelen = queue.length;
+
+ // Avoid touching opacity to prevent clearType and PNG issues in IE
+ if ( show || hide ) {
+ props.push( "opacity" );
+ }
+
+ $.effects.save( el, props );
+ el.show();
+ $.effects.createWrapper( el ); // Create Wrapper
+
+ // default distance for the BIGGEST bounce is the outer Distance / 3
+ if ( !distance ) {
+ distance = el[ ref === "top" ? "outerHeight" : "outerWidth" ]() / 3;
+ }
+
+ if ( show ) {
+ downAnim = { opacity: 1 };
+ downAnim[ ref ] = 0;
+
+ // if we are showing, force opacity 0 and set the initial position
+ // then do the "first" animation
+ el.css( "opacity", 0 )
+ .css( ref, motion ? -distance * 2 : distance * 2 )
+ .animate( downAnim, speed, easing );
+ }
+
+ // start at the smallest distance if we are hiding
+ if ( hide ) {
+ distance = distance / Math.pow( 2, times - 1 );
+ }
+
+ downAnim = {};
+ downAnim[ ref ] = 0;
+ // Bounces up/down/left/right then back to 0 -- times * 2 animations happen here
+ for ( i = 0; i < times; i++ ) {
+ upAnim = {};
+ upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
+
+ el.animate( upAnim, speed, easing )
+ .animate( downAnim, speed, easing );
+
+ distance = hide ? distance * 2 : distance / 2;
+ }
+
+ // Last Bounce when Hiding
+ if ( hide ) {
+ upAnim = { opacity: 0 };
+ upAnim[ ref ] = ( motion ? "-=" : "+=" ) + distance;
+
+ el.animate( upAnim, speed, easing );
+ }
+
+ el.queue(function() {
+ if ( hide ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ });
+
+ // inject all the animations we just queued to be first in line (after "inprogress")
+ if ( queuelen > 1) {
+ queue.splice.apply( queue,
+ [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
+ }
+ el.dequeue();
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.clip = function( o, done ) {
+ // Create element
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
+ mode = $.effects.setMode( el, o.mode || "hide" ),
+ show = mode === "show",
+ direction = o.direction || "vertical",
+ vert = direction === "vertical",
+ size = vert ? "height" : "width",
+ position = vert ? "top" : "left",
+ animation = {},
+ wrapper, animate, distance;
+
+ // Save & Show
+ $.effects.save( el, props );
+ el.show();
+
+ // Create Wrapper
+ wrapper = $.effects.createWrapper( el ).css({
+ overflow: "hidden"
+ });
+ animate = ( el[0].tagName === "IMG" ) ? wrapper : el;
+ distance = animate[ size ]();
+
+ // Shift
+ if ( show ) {
+ animate.css( size, 0 );
+ animate.css( position, distance / 2 );
+ }
+
+ // Create Animation Object:
+ animation[ size ] = show ? distance : 0;
+ animation[ position ] = show ? 0 : distance / 2;
+
+ // Animate
+ animate.animate( animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: function() {
+ if ( !show ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ }
+ });
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.drop = function( o, done ) {
+
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "opacity", "height", "width" ],
+ mode = $.effects.setMode( el, o.mode || "hide" ),
+ show = mode === "show",
+ direction = o.direction || "left",
+ ref = ( direction === "up" || direction === "down" ) ? "top" : "left",
+ motion = ( direction === "up" || direction === "left" ) ? "pos" : "neg",
+ animation = {
+ opacity: show ? 1 : 0
+ },
+ distance;
+
+ // Adjust
+ $.effects.save( el, props );
+ el.show();
+ $.effects.createWrapper( el );
+
+ distance = o.distance || el[ ref === "top" ? "outerHeight": "outerWidth" ]( true ) / 2;
+
+ if ( show ) {
+ el
+ .css( "opacity", 0 )
+ .css( ref, motion === "pos" ? -distance : distance );
+ }
+
+ // Animation
+ animation[ ref ] = ( show ?
+ ( motion === "pos" ? "+=" : "-=" ) :
+ ( motion === "pos" ? "-=" : "+=" ) ) +
+ distance;
+
+ // Animate
+ el.animate( animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: function() {
+ if ( mode === "hide" ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ }
+ });
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.explode = function( o, done ) {
+
+ var rows = o.pieces ? Math.round( Math.sqrt( o.pieces ) ) : 3,
+ cells = rows,
+ el = $( this ),
+ mode = $.effects.setMode( el, o.mode || "hide" ),
+ show = mode === "show",
+
+ // show and then visibility:hidden the element before calculating offset
+ offset = el.show().css( "visibility", "hidden" ).offset(),
+
+ // width and height of a piece
+ width = Math.ceil( el.outerWidth() / cells ),
+ height = Math.ceil( el.outerHeight() / rows ),
+ pieces = [],
+
+ // loop
+ i, j, left, top, mx, my;
+
+ // children animate complete:
+ function childComplete() {
+ pieces.push( this );
+ if ( pieces.length === rows * cells ) {
+ animComplete();
+ }
+ }
+
+ // clone the element for each row and cell.
+ for( i = 0; i < rows ; i++ ) { // ===>
+ top = offset.top + i * height;
+ my = i - ( rows - 1 ) / 2 ;
+
+ for( j = 0; j < cells ; j++ ) { // |||
+ left = offset.left + j * width;
+ mx = j - ( cells - 1 ) / 2 ;
+
+ // Create a clone of the now hidden main element that will be absolute positioned
+ // within a wrapper div off the -left and -top equal to size of our pieces
+ el
+ .clone()
+ .appendTo( "body" )
+ .wrap( "
" )
+ .css({
+ position: "absolute",
+ visibility: "visible",
+ left: -j * width,
+ top: -i * height
+ })
+
+ // select the wrapper - make it overflow: hidden and absolute positioned based on
+ // where the original was located +left and +top equal to the size of pieces
+ .parent()
+ .addClass( "ui-effects-explode" )
+ .css({
+ position: "absolute",
+ overflow: "hidden",
+ width: width,
+ height: height,
+ left: left + ( show ? mx * width : 0 ),
+ top: top + ( show ? my * height : 0 ),
+ opacity: show ? 0 : 1
+ }).animate({
+ left: left + ( show ? 0 : mx * width ),
+ top: top + ( show ? 0 : my * height ),
+ opacity: show ? 1 : 0
+ }, o.duration || 500, o.easing, childComplete );
+ }
+ }
+
+ function animComplete() {
+ el.css({
+ visibility: "visible"
+ });
+ $( pieces ).remove();
+ if ( !show ) {
+ el.hide();
+ }
+ done();
+ }
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.fade = function( o, done ) {
+ var el = $( this ),
+ mode = $.effects.setMode( el, o.mode || "toggle" );
+
+ el.animate({
+ opacity: mode
+ }, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: done
+ });
+};
+
+})( jQuery );
+(function( $, undefined ) {
+
+$.effects.effect.fold = function( o, done ) {
+
+ // Create element
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
+ mode = $.effects.setMode( el, o.mode || "hide" ),
+ show = mode === "show",
+ hide = mode === "hide",
+ size = o.size || 15,
+ percent = /([0-9]+)%/.exec( size ),
+ horizFirst = !!o.horizFirst,
+ widthFirst = show !== horizFirst,
+ ref = widthFirst ? [ "width", "height" ] : [ "height", "width" ],
+ duration = o.duration / 2,
+ wrapper, distance,
+ animation1 = {},
+ animation2 = {};
+
+ $.effects.save( el, props );
+ el.show();
+
+ // Create Wrapper
+ wrapper = $.effects.createWrapper( el ).css({
+ overflow: "hidden"
+ });
+ distance = widthFirst ?
+ [ wrapper.width(), wrapper.height() ] :
+ [ wrapper.height(), wrapper.width() ];
+
+ if ( percent ) {
+ size = parseInt( percent[ 1 ], 10 ) / 100 * distance[ hide ? 0 : 1 ];
+ }
+ if ( show ) {
+ wrapper.css( horizFirst ? {
+ height: 0,
+ width: size
+ } : {
+ height: size,
+ width: 0
+ });
+ }
+
+ // Animation
+ animation1[ ref[ 0 ] ] = show ? distance[ 0 ] : size;
+ animation2[ ref[ 1 ] ] = show ? distance[ 1 ] : 0;
+
+ // Animate
+ wrapper
+ .animate( animation1, duration, o.easing )
+ .animate( animation2, duration, o.easing, function() {
+ if ( hide ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ });
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.highlight = function( o, done ) {
+ var elem = $( this ),
+ props = [ "backgroundImage", "backgroundColor", "opacity" ],
+ mode = $.effects.setMode( elem, o.mode || "show" ),
+ animation = {
+ backgroundColor: elem.css( "backgroundColor" )
+ };
+
+ if (mode === "hide") {
+ animation.opacity = 0;
+ }
+
+ $.effects.save( elem, props );
+
+ elem
+ .show()
+ .css({
+ backgroundImage: "none",
+ backgroundColor: o.color || "#ffff99"
+ })
+ .animate( animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: function() {
+ if ( mode === "hide" ) {
+ elem.hide();
+ }
+ $.effects.restore( elem, props );
+ done();
+ }
+ });
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.pulsate = function( o, done ) {
+ var elem = $( this ),
+ mode = $.effects.setMode( elem, o.mode || "show" ),
+ show = mode === "show",
+ hide = mode === "hide",
+ showhide = ( show || mode === "hide" ),
+
+ // showing or hiding leaves of the "last" animation
+ anims = ( ( o.times || 5 ) * 2 ) + ( showhide ? 1 : 0 ),
+ duration = o.duration / anims,
+ animateTo = 0,
+ queue = elem.queue(),
+ queuelen = queue.length,
+ i;
+
+ if ( show || !elem.is(":visible")) {
+ elem.css( "opacity", 0 ).show();
+ animateTo = 1;
+ }
+
+ // anims - 1 opacity "toggles"
+ for ( i = 1; i < anims; i++ ) {
+ elem.animate({
+ opacity: animateTo
+ }, duration, o.easing );
+ animateTo = 1 - animateTo;
+ }
+
+ elem.animate({
+ opacity: animateTo
+ }, duration, o.easing);
+
+ elem.queue(function() {
+ if ( hide ) {
+ elem.hide();
+ }
+ done();
+ });
+
+ // We just queued up "anims" animations, we need to put them next in the queue
+ if ( queuelen > 1 ) {
+ queue.splice.apply( queue,
+ [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
+ }
+ elem.dequeue();
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.puff = function( o, done ) {
+ var elem = $( this ),
+ mode = $.effects.setMode( elem, o.mode || "hide" ),
+ hide = mode === "hide",
+ percent = parseInt( o.percent, 10 ) || 150,
+ factor = percent / 100,
+ original = {
+ height: elem.height(),
+ width: elem.width(),
+ outerHeight: elem.outerHeight(),
+ outerWidth: elem.outerWidth()
+ };
+
+ $.extend( o, {
+ effect: "scale",
+ queue: false,
+ fade: true,
+ mode: mode,
+ complete: done,
+ percent: hide ? percent : 100,
+ from: hide ?
+ original :
+ {
+ height: original.height * factor,
+ width: original.width * factor,
+ outerHeight: original.outerHeight * factor,
+ outerWidth: original.outerWidth * factor
+ }
+ });
+
+ elem.effect( o );
+};
+
+$.effects.effect.scale = function( o, done ) {
+
+ // Create element
+ var el = $( this ),
+ options = $.extend( true, {}, o ),
+ mode = $.effects.setMode( el, o.mode || "effect" ),
+ percent = parseInt( o.percent, 10 ) ||
+ ( parseInt( o.percent, 10 ) === 0 ? 0 : ( mode === "hide" ? 0 : 100 ) ),
+ direction = o.direction || "both",
+ origin = o.origin,
+ original = {
+ height: el.height(),
+ width: el.width(),
+ outerHeight: el.outerHeight(),
+ outerWidth: el.outerWidth()
+ },
+ factor = {
+ y: direction !== "horizontal" ? (percent / 100) : 1,
+ x: direction !== "vertical" ? (percent / 100) : 1
+ };
+
+ // We are going to pass this effect to the size effect:
+ options.effect = "size";
+ options.queue = false;
+ options.complete = done;
+
+ // Set default origin and restore for show/hide
+ if ( mode !== "effect" ) {
+ options.origin = origin || ["middle","center"];
+ options.restore = true;
+ }
+
+ options.from = o.from || ( mode === "show" ? {
+ height: 0,
+ width: 0,
+ outerHeight: 0,
+ outerWidth: 0
+ } : original );
+ options.to = {
+ height: original.height * factor.y,
+ width: original.width * factor.x,
+ outerHeight: original.outerHeight * factor.y,
+ outerWidth: original.outerWidth * factor.x
+ };
+
+ // Fade option to support puff
+ if ( options.fade ) {
+ if ( mode === "show" ) {
+ options.from.opacity = 0;
+ options.to.opacity = 1;
+ }
+ if ( mode === "hide" ) {
+ options.from.opacity = 1;
+ options.to.opacity = 0;
+ }
+ }
+
+ // Animate
+ el.effect( options );
+
+};
+
+$.effects.effect.size = function( o, done ) {
+
+ // Create element
+ var original, baseline, factor,
+ el = $( this ),
+ props0 = [ "position", "top", "bottom", "left", "right", "width", "height", "overflow", "opacity" ],
+
+ // Always restore
+ props1 = [ "position", "top", "bottom", "left", "right", "overflow", "opacity" ],
+
+ // Copy for children
+ props2 = [ "width", "height", "overflow" ],
+ cProps = [ "fontSize" ],
+ vProps = [ "borderTopWidth", "borderBottomWidth", "paddingTop", "paddingBottom" ],
+ hProps = [ "borderLeftWidth", "borderRightWidth", "paddingLeft", "paddingRight" ],
+
+ // Set options
+ mode = $.effects.setMode( el, o.mode || "effect" ),
+ restore = o.restore || mode !== "effect",
+ scale = o.scale || "both",
+ origin = o.origin || [ "middle", "center" ],
+ position = el.css( "position" ),
+ props = restore ? props0 : props1,
+ zero = {
+ height: 0,
+ width: 0,
+ outerHeight: 0,
+ outerWidth: 0
+ };
+
+ if ( mode === "show" ) {
+ el.show();
+ }
+ original = {
+ height: el.height(),
+ width: el.width(),
+ outerHeight: el.outerHeight(),
+ outerWidth: el.outerWidth()
+ };
+
+ if ( o.mode === "toggle" && mode === "show" ) {
+ el.from = o.to || zero;
+ el.to = o.from || original;
+ } else {
+ el.from = o.from || ( mode === "show" ? zero : original );
+ el.to = o.to || ( mode === "hide" ? zero : original );
+ }
+
+ // Set scaling factor
+ factor = {
+ from: {
+ y: el.from.height / original.height,
+ x: el.from.width / original.width
+ },
+ to: {
+ y: el.to.height / original.height,
+ x: el.to.width / original.width
+ }
+ };
+
+ // Scale the css box
+ if ( scale === "box" || scale === "both" ) {
+
+ // Vertical props scaling
+ if ( factor.from.y !== factor.to.y ) {
+ props = props.concat( vProps );
+ el.from = $.effects.setTransition( el, vProps, factor.from.y, el.from );
+ el.to = $.effects.setTransition( el, vProps, factor.to.y, el.to );
+ }
+
+ // Horizontal props scaling
+ if ( factor.from.x !== factor.to.x ) {
+ props = props.concat( hProps );
+ el.from = $.effects.setTransition( el, hProps, factor.from.x, el.from );
+ el.to = $.effects.setTransition( el, hProps, factor.to.x, el.to );
+ }
+ }
+
+ // Scale the content
+ if ( scale === "content" || scale === "both" ) {
+
+ // Vertical props scaling
+ if ( factor.from.y !== factor.to.y ) {
+ props = props.concat( cProps ).concat( props2 );
+ el.from = $.effects.setTransition( el, cProps, factor.from.y, el.from );
+ el.to = $.effects.setTransition( el, cProps, factor.to.y, el.to );
+ }
+ }
+
+ $.effects.save( el, props );
+ el.show();
+ $.effects.createWrapper( el );
+ el.css( "overflow", "hidden" ).css( el.from );
+
+ // Adjust
+ if (origin) { // Calculate baseline shifts
+ baseline = $.effects.getBaseline( origin, original );
+ el.from.top = ( original.outerHeight - el.outerHeight() ) * baseline.y;
+ el.from.left = ( original.outerWidth - el.outerWidth() ) * baseline.x;
+ el.to.top = ( original.outerHeight - el.to.outerHeight ) * baseline.y;
+ el.to.left = ( original.outerWidth - el.to.outerWidth ) * baseline.x;
+ }
+ el.css( el.from ); // set top & left
+
+ // Animate
+ if ( scale === "content" || scale === "both" ) { // Scale the children
+
+ // Add margins/font-size
+ vProps = vProps.concat([ "marginTop", "marginBottom" ]).concat(cProps);
+ hProps = hProps.concat([ "marginLeft", "marginRight" ]);
+ props2 = props0.concat(vProps).concat(hProps);
+
+ el.find( "*[width]" ).each( function(){
+ var child = $( this ),
+ c_original = {
+ height: child.height(),
+ width: child.width(),
+ outerHeight: child.outerHeight(),
+ outerWidth: child.outerWidth()
+ };
+ if (restore) {
+ $.effects.save(child, props2);
+ }
+
+ child.from = {
+ height: c_original.height * factor.from.y,
+ width: c_original.width * factor.from.x,
+ outerHeight: c_original.outerHeight * factor.from.y,
+ outerWidth: c_original.outerWidth * factor.from.x
+ };
+ child.to = {
+ height: c_original.height * factor.to.y,
+ width: c_original.width * factor.to.x,
+ outerHeight: c_original.height * factor.to.y,
+ outerWidth: c_original.width * factor.to.x
+ };
+
+ // Vertical props scaling
+ if ( factor.from.y !== factor.to.y ) {
+ child.from = $.effects.setTransition( child, vProps, factor.from.y, child.from );
+ child.to = $.effects.setTransition( child, vProps, factor.to.y, child.to );
+ }
+
+ // Horizontal props scaling
+ if ( factor.from.x !== factor.to.x ) {
+ child.from = $.effects.setTransition( child, hProps, factor.from.x, child.from );
+ child.to = $.effects.setTransition( child, hProps, factor.to.x, child.to );
+ }
+
+ // Animate children
+ child.css( child.from );
+ child.animate( child.to, o.duration, o.easing, function() {
+
+ // Restore children
+ if ( restore ) {
+ $.effects.restore( child, props2 );
+ }
+ });
+ });
+ }
+
+ // Animate
+ el.animate( el.to, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: function() {
+ if ( el.to.opacity === 0 ) {
+ el.css( "opacity", el.from.opacity );
+ }
+ if( mode === "hide" ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ if ( !restore ) {
+
+ // we need to calculate our new positioning based on the scaling
+ if ( position === "static" ) {
+ el.css({
+ position: "relative",
+ top: el.to.top,
+ left: el.to.left
+ });
+ } else {
+ $.each([ "top", "left" ], function( idx, pos ) {
+ el.css( pos, function( _, str ) {
+ var val = parseInt( str, 10 ),
+ toRef = idx ? el.to.left : el.to.top;
+
+ // if original was "auto", recalculate the new value from wrapper
+ if ( str === "auto" ) {
+ return toRef + "px";
+ }
+
+ return val + toRef + "px";
+ });
+ });
+ }
+ }
+
+ $.effects.removeWrapper( el );
+ done();
+ }
+ });
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.shake = function( o, done ) {
+
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "height", "width" ],
+ mode = $.effects.setMode( el, o.mode || "effect" ),
+ direction = o.direction || "left",
+ distance = o.distance || 20,
+ times = o.times || 3,
+ anims = times * 2 + 1,
+ speed = Math.round(o.duration/anims),
+ ref = (direction === "up" || direction === "down") ? "top" : "left",
+ positiveMotion = (direction === "up" || direction === "left"),
+ animation = {},
+ animation1 = {},
+ animation2 = {},
+ i,
+
+ // we will need to re-assemble the queue to stack our animations in place
+ queue = el.queue(),
+ queuelen = queue.length;
+
+ $.effects.save( el, props );
+ el.show();
+ $.effects.createWrapper( el );
+
+ // Animation
+ animation[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance;
+ animation1[ ref ] = ( positiveMotion ? "+=" : "-=" ) + distance * 2;
+ animation2[ ref ] = ( positiveMotion ? "-=" : "+=" ) + distance * 2;
+
+ // Animate
+ el.animate( animation, speed, o.easing );
+
+ // Shakes
+ for ( i = 1; i < times; i++ ) {
+ el.animate( animation1, speed, o.easing ).animate( animation2, speed, o.easing );
+ }
+ el
+ .animate( animation1, speed, o.easing )
+ .animate( animation, speed / 2, o.easing )
+ .queue(function() {
+ if ( mode === "hide" ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ });
+
+ // inject all the animations we just queued to be first in line (after "inprogress")
+ if ( queuelen > 1) {
+ queue.splice.apply( queue,
+ [ 1, 0 ].concat( queue.splice( queuelen, anims + 1 ) ) );
+ }
+ el.dequeue();
+
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.slide = function( o, done ) {
+
+ // Create element
+ var el = $( this ),
+ props = [ "position", "top", "bottom", "left", "right", "width", "height" ],
+ mode = $.effects.setMode( el, o.mode || "show" ),
+ show = mode === "show",
+ direction = o.direction || "left",
+ ref = (direction === "up" || direction === "down") ? "top" : "left",
+ positiveMotion = (direction === "up" || direction === "left"),
+ distance,
+ animation = {};
+
+ // Adjust
+ $.effects.save( el, props );
+ el.show();
+ distance = o.distance || el[ ref === "top" ? "outerHeight" : "outerWidth" ]( true );
+
+ $.effects.createWrapper( el ).css({
+ overflow: "hidden"
+ });
+
+ if ( show ) {
+ el.css( ref, positiveMotion ? (isNaN(distance) ? "-" + distance : -distance) : distance );
+ }
+
+ // Animation
+ animation[ ref ] = ( show ?
+ ( positiveMotion ? "+=" : "-=") :
+ ( positiveMotion ? "-=" : "+=")) +
+ distance;
+
+ // Animate
+ el.animate( animation, {
+ queue: false,
+ duration: o.duration,
+ easing: o.easing,
+ complete: function() {
+ if ( mode === "hide" ) {
+ el.hide();
+ }
+ $.effects.restore( el, props );
+ $.effects.removeWrapper( el );
+ done();
+ }
+ });
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.effects.effect.transfer = function( o, done ) {
+ var elem = $( this ),
+ target = $( o.to ),
+ targetFixed = target.css( "position" ) === "fixed",
+ body = $("body"),
+ fixTop = targetFixed ? body.scrollTop() : 0,
+ fixLeft = targetFixed ? body.scrollLeft() : 0,
+ endPosition = target.offset(),
+ animation = {
+ top: endPosition.top - fixTop ,
+ left: endPosition.left - fixLeft ,
+ height: target.innerHeight(),
+ width: target.innerWidth()
+ },
+ startPosition = elem.offset(),
+ transfer = $( "
" )
+ .appendTo( document.body )
+ .addClass( o.className )
+ .css({
+ top: startPosition.top - fixTop ,
+ left: startPosition.left - fixLeft ,
+ height: elem.innerHeight(),
+ width: elem.innerWidth(),
+ position: targetFixed ? "fixed" : "absolute"
+ })
+ .animate( animation, o.duration, o.easing, function() {
+ transfer.remove();
+ done();
+ });
+};
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.widget( "ui.menu", {
+ version: "1.10.4",
+ defaultElement: "
",
+ delay: 300,
+ options: {
+ icons: {
+ submenu: "ui-icon-carat-1-e"
+ },
+ menus: "ul",
+ position: {
+ my: "left top",
+ at: "right top"
+ },
+ role: "menu",
+
+ // callbacks
+ blur: null,
+ focus: null,
+ select: null
+ },
+
+ _create: function() {
+ this.activeMenu = this.element;
+ // flag used to prevent firing of the click handler
+ // as the event bubbles up through nested menus
+ this.mouseHandled = false;
+ this.element
+ .uniqueId()
+ .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
+ .toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length )
+ .attr({
+ role: this.options.role,
+ tabIndex: 0
+ })
+ // need to catch all clicks on disabled menu
+ // not possible through _on
+ .bind( "click" + this.eventNamespace, $.proxy(function( event ) {
+ if ( this.options.disabled ) {
+ event.preventDefault();
+ }
+ }, this ));
+
+ if ( this.options.disabled ) {
+ this.element
+ .addClass( "ui-state-disabled" )
+ .attr( "aria-disabled", "true" );
+ }
+
+ this._on({
+ // Prevent focus from sticking to links inside menu after clicking
+ // them (focus should always stay on UL during navigation).
+ "mousedown .ui-menu-item > a": function( event ) {
+ event.preventDefault();
+ },
+ "click .ui-state-disabled > a": function( event ) {
+ event.preventDefault();
+ },
+ "click .ui-menu-item:has(a)": function( event ) {
+ var target = $( event.target ).closest( ".ui-menu-item" );
+ if ( !this.mouseHandled && target.not( ".ui-state-disabled" ).length ) {
+ this.select( event );
+
+ // Only set the mouseHandled flag if the event will bubble, see #9469.
+ if ( !event.isPropagationStopped() ) {
+ this.mouseHandled = true;
+ }
+
+ // Open submenu on click
+ if ( target.has( ".ui-menu" ).length ) {
+ this.expand( event );
+ } else if ( !this.element.is( ":focus" ) && $( this.document[ 0 ].activeElement ).closest( ".ui-menu" ).length ) {
+
+ // Redirect focus to the menu
+ this.element.trigger( "focus", [ true ] );
+
+ // If the active item is on the top level, let it stay active.
+ // Otherwise, blur the active item since it is no longer visible.
+ if ( this.active && this.active.parents( ".ui-menu" ).length === 1 ) {
+ clearTimeout( this.timer );
+ }
+ }
+ }
+ },
+ "mouseenter .ui-menu-item": function( event ) {
+ var target = $( event.currentTarget );
+ // Remove ui-state-active class from siblings of the newly focused menu item
+ // to avoid a jump caused by adjacent elements both having a class with a border
+ target.siblings().children( ".ui-state-active" ).removeClass( "ui-state-active" );
+ this.focus( event, target );
+ },
+ mouseleave: "collapseAll",
+ "mouseleave .ui-menu": "collapseAll",
+ focus: function( event, keepActiveItem ) {
+ // If there's already an active item, keep it active
+ // If not, activate the first item
+ var item = this.active || this.element.children( ".ui-menu-item" ).eq( 0 );
+
+ if ( !keepActiveItem ) {
+ this.focus( event, item );
+ }
+ },
+ blur: function( event ) {
+ this._delay(function() {
+ if ( !$.contains( this.element[0], this.document[0].activeElement ) ) {
+ this.collapseAll( event );
+ }
+ });
+ },
+ keydown: "_keydown"
+ });
+
+ this.refresh();
+
+ // Clicks outside of a menu collapse any open menus
+ this._on( this.document, {
+ click: function( event ) {
+ if ( !$( event.target ).closest( ".ui-menu" ).length ) {
+ this.collapseAll( event );
+ }
+
+ // Reset the mouseHandled flag
+ this.mouseHandled = false;
+ }
+ });
+ },
+
+ _destroy: function() {
+ // Destroy (sub)menus
+ this.element
+ .removeAttr( "aria-activedescendant" )
+ .find( ".ui-menu" ).addBack()
+ .removeClass( "ui-menu ui-widget ui-widget-content ui-corner-all ui-menu-icons" )
+ .removeAttr( "role" )
+ .removeAttr( "tabIndex" )
+ .removeAttr( "aria-labelledby" )
+ .removeAttr( "aria-expanded" )
+ .removeAttr( "aria-hidden" )
+ .removeAttr( "aria-disabled" )
+ .removeUniqueId()
+ .show();
+
+ // Destroy menu items
+ this.element.find( ".ui-menu-item" )
+ .removeClass( "ui-menu-item" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-disabled" )
+ .children( "a" )
+ .removeUniqueId()
+ .removeClass( "ui-corner-all ui-state-hover" )
+ .removeAttr( "tabIndex" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-haspopup" )
+ .children().each( function() {
+ var elem = $( this );
+ if ( elem.data( "ui-menu-submenu-carat" ) ) {
+ elem.remove();
+ }
+ });
+
+ // Destroy menu dividers
+ this.element.find( ".ui-menu-divider" ).removeClass( "ui-menu-divider ui-widget-content" );
+ },
+
+ _keydown: function( event ) {
+ var match, prev, character, skip, regex,
+ preventDefault = true;
+
+ function escape( value ) {
+ return value.replace( /[\-\[\]{}()*+?.,\\\^$|#\s]/g, "\\$&" );
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.PAGE_UP:
+ this.previousPage( event );
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ this.nextPage( event );
+ break;
+ case $.ui.keyCode.HOME:
+ this._move( "first", "first", event );
+ break;
+ case $.ui.keyCode.END:
+ this._move( "last", "last", event );
+ break;
+ case $.ui.keyCode.UP:
+ this.previous( event );
+ break;
+ case $.ui.keyCode.DOWN:
+ this.next( event );
+ break;
+ case $.ui.keyCode.LEFT:
+ this.collapse( event );
+ break;
+ case $.ui.keyCode.RIGHT:
+ if ( this.active && !this.active.is( ".ui-state-disabled" ) ) {
+ this.expand( event );
+ }
+ break;
+ case $.ui.keyCode.ENTER:
+ case $.ui.keyCode.SPACE:
+ this._activate( event );
+ break;
+ case $.ui.keyCode.ESCAPE:
+ this.collapse( event );
+ break;
+ default:
+ preventDefault = false;
+ prev = this.previousFilter || "";
+ character = String.fromCharCode( event.keyCode );
+ skip = false;
+
+ clearTimeout( this.filterTimer );
+
+ if ( character === prev ) {
+ skip = true;
+ } else {
+ character = prev + character;
+ }
+
+ regex = new RegExp( "^" + escape( character ), "i" );
+ match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
+ return regex.test( $( this ).children( "a" ).text() );
+ });
+ match = skip && match.index( this.active.next() ) !== -1 ?
+ this.active.nextAll( ".ui-menu-item" ) :
+ match;
+
+ // If no matches on the current filter, reset to the last character pressed
+ // to move down the menu to the first item that starts with that character
+ if ( !match.length ) {
+ character = String.fromCharCode( event.keyCode );
+ regex = new RegExp( "^" + escape( character ), "i" );
+ match = this.activeMenu.children( ".ui-menu-item" ).filter(function() {
+ return regex.test( $( this ).children( "a" ).text() );
+ });
+ }
+
+ if ( match.length ) {
+ this.focus( event, match );
+ if ( match.length > 1 ) {
+ this.previousFilter = character;
+ this.filterTimer = this._delay(function() {
+ delete this.previousFilter;
+ }, 1000 );
+ } else {
+ delete this.previousFilter;
+ }
+ } else {
+ delete this.previousFilter;
+ }
+ }
+
+ if ( preventDefault ) {
+ event.preventDefault();
+ }
+ },
+
+ _activate: function( event ) {
+ if ( !this.active.is( ".ui-state-disabled" ) ) {
+ if ( this.active.children( "a[aria-haspopup='true']" ).length ) {
+ this.expand( event );
+ } else {
+ this.select( event );
+ }
+ }
+ },
+
+ refresh: function() {
+ var menus,
+ icon = this.options.icons.submenu,
+ submenus = this.element.find( this.options.menus );
+
+ this.element.toggleClass( "ui-menu-icons", !!this.element.find( ".ui-icon" ).length );
+
+ // Initialize nested menus
+ submenus.filter( ":not(.ui-menu)" )
+ .addClass( "ui-menu ui-widget ui-widget-content ui-corner-all" )
+ .hide()
+ .attr({
+ role: this.options.role,
+ "aria-hidden": "true",
+ "aria-expanded": "false"
+ })
+ .each(function() {
+ var menu = $( this ),
+ item = menu.prev( "a" ),
+ submenuCarat = $( "" )
+ .addClass( "ui-menu-icon ui-icon " + icon )
+ .data( "ui-menu-submenu-carat", true );
+
+ item
+ .attr( "aria-haspopup", "true" )
+ .prepend( submenuCarat );
+ menu.attr( "aria-labelledby", item.attr( "id" ) );
+ });
+
+ menus = submenus.add( this.element );
+
+ // Don't refresh list items that are already adapted
+ menus.children( ":not(.ui-menu-item):has(a)" )
+ .addClass( "ui-menu-item" )
+ .attr( "role", "presentation" )
+ .children( "a" )
+ .uniqueId()
+ .addClass( "ui-corner-all" )
+ .attr({
+ tabIndex: -1,
+ role: this._itemRole()
+ });
+
+ // Initialize unlinked menu-items containing spaces and/or dashes only as dividers
+ menus.children( ":not(.ui-menu-item)" ).each(function() {
+ var item = $( this );
+ // hyphen, em dash, en dash
+ if ( !/[^\-\u2014\u2013\s]/.test( item.text() ) ) {
+ item.addClass( "ui-widget-content ui-menu-divider" );
+ }
+ });
+
+ // Add aria-disabled attribute to any disabled menu item
+ menus.children( ".ui-state-disabled" ).attr( "aria-disabled", "true" );
+
+ // If the active item has been removed, blur the menu
+ if ( this.active && !$.contains( this.element[ 0 ], this.active[ 0 ] ) ) {
+ this.blur();
+ }
+ },
+
+ _itemRole: function() {
+ return {
+ menu: "menuitem",
+ listbox: "option"
+ }[ this.options.role ];
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "icons" ) {
+ this.element.find( ".ui-menu-icon" )
+ .removeClass( this.options.icons.submenu )
+ .addClass( value.submenu );
+ }
+ this._super( key, value );
+ },
+
+ focus: function( event, item ) {
+ var nested, focused;
+ this.blur( event, event && event.type === "focus" );
+
+ this._scrollIntoView( item );
+
+ this.active = item.first();
+ focused = this.active.children( "a" ).addClass( "ui-state-focus" );
+ // Only update aria-activedescendant if there's a role
+ // otherwise we assume focus is managed elsewhere
+ if ( this.options.role ) {
+ this.element.attr( "aria-activedescendant", focused.attr( "id" ) );
+ }
+
+ // Highlight active parent menu item, if any
+ this.active
+ .parent()
+ .closest( ".ui-menu-item" )
+ .children( "a:first" )
+ .addClass( "ui-state-active" );
+
+ if ( event && event.type === "keydown" ) {
+ this._close();
+ } else {
+ this.timer = this._delay(function() {
+ this._close();
+ }, this.delay );
+ }
+
+ nested = item.children( ".ui-menu" );
+ if ( nested.length && event && ( /^mouse/.test( event.type ) ) ) {
+ this._startOpening(nested);
+ }
+ this.activeMenu = item.parent();
+
+ this._trigger( "focus", event, { item: item } );
+ },
+
+ _scrollIntoView: function( item ) {
+ var borderTop, paddingTop, offset, scroll, elementHeight, itemHeight;
+ if ( this._hasScroll() ) {
+ borderTop = parseFloat( $.css( this.activeMenu[0], "borderTopWidth" ) ) || 0;
+ paddingTop = parseFloat( $.css( this.activeMenu[0], "paddingTop" ) ) || 0;
+ offset = item.offset().top - this.activeMenu.offset().top - borderTop - paddingTop;
+ scroll = this.activeMenu.scrollTop();
+ elementHeight = this.activeMenu.height();
+ itemHeight = item.height();
+
+ if ( offset < 0 ) {
+ this.activeMenu.scrollTop( scroll + offset );
+ } else if ( offset + itemHeight > elementHeight ) {
+ this.activeMenu.scrollTop( scroll + offset - elementHeight + itemHeight );
+ }
+ }
+ },
+
+ blur: function( event, fromFocus ) {
+ if ( !fromFocus ) {
+ clearTimeout( this.timer );
+ }
+
+ if ( !this.active ) {
+ return;
+ }
+
+ this.active.children( "a" ).removeClass( "ui-state-focus" );
+ this.active = null;
+
+ this._trigger( "blur", event, { item: this.active } );
+ },
+
+ _startOpening: function( submenu ) {
+ clearTimeout( this.timer );
+
+ // Don't open if already open fixes a Firefox bug that caused a .5 pixel
+ // shift in the submenu position when mousing over the carat icon
+ if ( submenu.attr( "aria-hidden" ) !== "true" ) {
+ return;
+ }
+
+ this.timer = this._delay(function() {
+ this._close();
+ this._open( submenu );
+ }, this.delay );
+ },
+
+ _open: function( submenu ) {
+ var position = $.extend({
+ of: this.active
+ }, this.options.position );
+
+ clearTimeout( this.timer );
+ this.element.find( ".ui-menu" ).not( submenu.parents( ".ui-menu" ) )
+ .hide()
+ .attr( "aria-hidden", "true" );
+
+ submenu
+ .show()
+ .removeAttr( "aria-hidden" )
+ .attr( "aria-expanded", "true" )
+ .position( position );
+ },
+
+ collapseAll: function( event, all ) {
+ clearTimeout( this.timer );
+ this.timer = this._delay(function() {
+ // If we were passed an event, look for the submenu that contains the event
+ var currentMenu = all ? this.element :
+ $( event && event.target ).closest( this.element.find( ".ui-menu" ) );
+
+ // If we found no valid submenu ancestor, use the main menu to close all sub menus anyway
+ if ( !currentMenu.length ) {
+ currentMenu = this.element;
+ }
+
+ this._close( currentMenu );
+
+ this.blur( event );
+ this.activeMenu = currentMenu;
+ }, this.delay );
+ },
+
+ // With no arguments, closes the currently active menu - if nothing is active
+ // it closes all menus. If passed an argument, it will search for menus BELOW
+ _close: function( startMenu ) {
+ if ( !startMenu ) {
+ startMenu = this.active ? this.active.parent() : this.element;
+ }
+
+ startMenu
+ .find( ".ui-menu" )
+ .hide()
+ .attr( "aria-hidden", "true" )
+ .attr( "aria-expanded", "false" )
+ .end()
+ .find( "a.ui-state-active" )
+ .removeClass( "ui-state-active" );
+ },
+
+ collapse: function( event ) {
+ var newItem = this.active &&
+ this.active.parent().closest( ".ui-menu-item", this.element );
+ if ( newItem && newItem.length ) {
+ this._close();
+ this.focus( event, newItem );
+ }
+ },
+
+ expand: function( event ) {
+ var newItem = this.active &&
+ this.active
+ .children( ".ui-menu " )
+ .children( ".ui-menu-item" )
+ .first();
+
+ if ( newItem && newItem.length ) {
+ this._open( newItem.parent() );
+
+ // Delay so Firefox will not hide activedescendant change in expanding submenu from AT
+ this._delay(function() {
+ this.focus( event, newItem );
+ });
+ }
+ },
+
+ next: function( event ) {
+ this._move( "next", "first", event );
+ },
+
+ previous: function( event ) {
+ this._move( "prev", "last", event );
+ },
+
+ isFirstItem: function() {
+ return this.active && !this.active.prevAll( ".ui-menu-item" ).length;
+ },
+
+ isLastItem: function() {
+ return this.active && !this.active.nextAll( ".ui-menu-item" ).length;
+ },
+
+ _move: function( direction, filter, event ) {
+ var next;
+ if ( this.active ) {
+ if ( direction === "first" || direction === "last" ) {
+ next = this.active
+ [ direction === "first" ? "prevAll" : "nextAll" ]( ".ui-menu-item" )
+ .eq( -1 );
+ } else {
+ next = this.active
+ [ direction + "All" ]( ".ui-menu-item" )
+ .eq( 0 );
+ }
+ }
+ if ( !next || !next.length || !this.active ) {
+ next = this.activeMenu.children( ".ui-menu-item" )[ filter ]();
+ }
+
+ this.focus( event, next );
+ },
+
+ nextPage: function( event ) {
+ var item, base, height;
+
+ if ( !this.active ) {
+ this.next( event );
+ return;
+ }
+ if ( this.isLastItem() ) {
+ return;
+ }
+ if ( this._hasScroll() ) {
+ base = this.active.offset().top;
+ height = this.element.height();
+ this.active.nextAll( ".ui-menu-item" ).each(function() {
+ item = $( this );
+ return item.offset().top - base - height < 0;
+ });
+
+ this.focus( event, item );
+ } else {
+ this.focus( event, this.activeMenu.children( ".ui-menu-item" )
+ [ !this.active ? "first" : "last" ]() );
+ }
+ },
+
+ previousPage: function( event ) {
+ var item, base, height;
+ if ( !this.active ) {
+ this.next( event );
+ return;
+ }
+ if ( this.isFirstItem() ) {
+ return;
+ }
+ if ( this._hasScroll() ) {
+ base = this.active.offset().top;
+ height = this.element.height();
+ this.active.prevAll( ".ui-menu-item" ).each(function() {
+ item = $( this );
+ return item.offset().top - base + height > 0;
+ });
+
+ this.focus( event, item );
+ } else {
+ this.focus( event, this.activeMenu.children( ".ui-menu-item" ).first() );
+ }
+ },
+
+ _hasScroll: function() {
+ return this.element.outerHeight() < this.element.prop( "scrollHeight" );
+ },
+
+ select: function( event ) {
+ // TODO: It should never be possible to not have an active item at this
+ // point, but the tests don't trigger mouseenter before click.
+ this.active = this.active || $( event.target ).closest( ".ui-menu-item" );
+ var ui = { item: this.active };
+ if ( !this.active.has( ".ui-menu" ).length ) {
+ this.collapseAll( event, true );
+ }
+ this._trigger( "select", event, ui );
+ }
+});
+
+}( jQuery ));
+(function( $, undefined ) {
+
+$.widget( "ui.progressbar", {
+ version: "1.10.4",
+ options: {
+ max: 100,
+ value: 0,
+
+ change: null,
+ complete: null
+ },
+
+ min: 0,
+
+ _create: function() {
+ // Constrain initial value
+ this.oldValue = this.options.value = this._constrainedValue();
+
+ this.element
+ .addClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .attr({
+ // Only set static values, aria-valuenow and aria-valuemax are
+ // set inside _refreshValue()
+ role: "progressbar",
+ "aria-valuemin": this.min
+ });
+
+ this.valueDiv = $( "" )
+ .appendTo( this.element );
+
+ this._refreshValue();
+ },
+
+ _destroy: function() {
+ this.element
+ .removeClass( "ui-progressbar ui-widget ui-widget-content ui-corner-all" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-valuemin" )
+ .removeAttr( "aria-valuemax" )
+ .removeAttr( "aria-valuenow" );
+
+ this.valueDiv.remove();
+ },
+
+ value: function( newValue ) {
+ if ( newValue === undefined ) {
+ return this.options.value;
+ }
+
+ this.options.value = this._constrainedValue( newValue );
+ this._refreshValue();
+ },
+
+ _constrainedValue: function( newValue ) {
+ if ( newValue === undefined ) {
+ newValue = this.options.value;
+ }
+
+ this.indeterminate = newValue === false;
+
+ // sanitize value
+ if ( typeof newValue !== "number" ) {
+ newValue = 0;
+ }
+
+ return this.indeterminate ? false :
+ Math.min( this.options.max, Math.max( this.min, newValue ) );
+ },
+
+ _setOptions: function( options ) {
+ // Ensure "value" option is set after other values (like max)
+ var value = options.value;
+ delete options.value;
+
+ this._super( options );
+
+ this.options.value = this._constrainedValue( value );
+ this._refreshValue();
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "max" ) {
+ // Don't allow a max less than min
+ value = Math.max( this.min, value );
+ }
+
+ this._super( key, value );
+ },
+
+ _percentage: function() {
+ return this.indeterminate ? 100 : 100 * ( this.options.value - this.min ) / ( this.options.max - this.min );
+ },
+
+ _refreshValue: function() {
+ var value = this.options.value,
+ percentage = this._percentage();
+
+ this.valueDiv
+ .toggle( this.indeterminate || value > this.min )
+ .toggleClass( "ui-corner-right", value === this.options.max )
+ .width( percentage.toFixed(0) + "%" );
+
+ this.element.toggleClass( "ui-progressbar-indeterminate", this.indeterminate );
+
+ if ( this.indeterminate ) {
+ this.element.removeAttr( "aria-valuenow" );
+ if ( !this.overlayDiv ) {
+ this.overlayDiv = $( "" ).appendTo( this.valueDiv );
+ }
+ } else {
+ this.element.attr({
+ "aria-valuemax": this.options.max,
+ "aria-valuenow": value
+ });
+ if ( this.overlayDiv ) {
+ this.overlayDiv.remove();
+ this.overlayDiv = null;
+ }
+ }
+
+ if ( this.oldValue !== value ) {
+ this.oldValue = value;
+ this._trigger( "change" );
+ }
+ if ( value === this.options.max ) {
+ this._trigger( "complete" );
+ }
+ }
+});
+
+})( jQuery );
+(function( $, undefined ) {
+
+function num(v) {
+ return parseInt(v, 10) || 0;
+}
+
+function isNumber(value) {
+ return !isNaN(parseInt(value, 10));
+}
+
+$.widget("ui.resizable", $.ui.mouse, {
+ version: "1.10.4",
+ widgetEventPrefix: "resize",
+ options: {
+ alsoResize: false,
+ animate: false,
+ animateDuration: "slow",
+ animateEasing: "swing",
+ aspectRatio: false,
+ autoHide: false,
+ containment: false,
+ ghost: false,
+ grid: false,
+ handles: "e,s,se",
+ helper: false,
+ maxHeight: null,
+ maxWidth: null,
+ minHeight: 10,
+ minWidth: 10,
+ // See #7960
+ zIndex: 90,
+
+ // callbacks
+ resize: null,
+ start: null,
+ stop: null
+ },
+ _create: function() {
+
+ var n, i, handle, axis, hname,
+ that = this,
+ o = this.options;
+ this.element.addClass("ui-resizable");
+
+ $.extend(this, {
+ _aspectRatio: !!(o.aspectRatio),
+ aspectRatio: o.aspectRatio,
+ originalElement: this.element,
+ _proportionallyResizeElements: [],
+ _helper: o.helper || o.ghost || o.animate ? o.helper || "ui-resizable-helper" : null
+ });
+
+ //Wrap the element if it cannot hold child nodes
+ if(this.element[0].nodeName.match(/canvas|textarea|input|select|button|img/i)) {
+
+ //Create a wrapper element and set the wrapper to the new current internal element
+ this.element.wrap(
+ $("").css({
+ position: this.element.css("position"),
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight(),
+ top: this.element.css("top"),
+ left: this.element.css("left")
+ })
+ );
+
+ //Overwrite the original this.element
+ this.element = this.element.parent().data(
+ "ui-resizable", this.element.data("ui-resizable")
+ );
+
+ this.elementIsWrapper = true;
+
+ //Move margins to the wrapper
+ this.element.css({ marginLeft: this.originalElement.css("marginLeft"), marginTop: this.originalElement.css("marginTop"), marginRight: this.originalElement.css("marginRight"), marginBottom: this.originalElement.css("marginBottom") });
+ this.originalElement.css({ marginLeft: 0, marginTop: 0, marginRight: 0, marginBottom: 0});
+
+ //Prevent Safari textarea resize
+ this.originalResizeStyle = this.originalElement.css("resize");
+ this.originalElement.css("resize", "none");
+
+ //Push the actual element to our proportionallyResize internal array
+ this._proportionallyResizeElements.push(this.originalElement.css({ position: "static", zoom: 1, display: "block" }));
+
+ // avoid IE jump (hard set the margin)
+ this.originalElement.css({ margin: this.originalElement.css("margin") });
+
+ // fix handlers offset
+ this._proportionallyResize();
+
+ }
+
+ this.handles = o.handles || (!$(".ui-resizable-handle", this.element).length ? "e,s,se" : { n: ".ui-resizable-n", e: ".ui-resizable-e", s: ".ui-resizable-s", w: ".ui-resizable-w", se: ".ui-resizable-se", sw: ".ui-resizable-sw", ne: ".ui-resizable-ne", nw: ".ui-resizable-nw" });
+ if(this.handles.constructor === String) {
+
+ if ( this.handles === "all") {
+ this.handles = "n,e,s,w,se,sw,ne,nw";
+ }
+
+ n = this.handles.split(",");
+ this.handles = {};
+
+ for(i = 0; i < n.length; i++) {
+
+ handle = $.trim(n[i]);
+ hname = "ui-resizable-"+handle;
+ axis = $("");
+
+ // Apply zIndex to all handles - see #7960
+ axis.css({ zIndex: o.zIndex });
+
+ //TODO : What's going on here?
+ if ("se" === handle) {
+ axis.addClass("ui-icon ui-icon-gripsmall-diagonal-se");
+ }
+
+ //Insert into internal handles object and append to element
+ this.handles[handle] = ".ui-resizable-"+handle;
+ this.element.append(axis);
+ }
+
+ }
+
+ this._renderAxis = function(target) {
+
+ var i, axis, padPos, padWrapper;
+
+ target = target || this.element;
+
+ for(i in this.handles) {
+
+ if(this.handles[i].constructor === String) {
+ this.handles[i] = $(this.handles[i], this.element).show();
+ }
+
+ //Apply pad to wrapper element, needed to fix axis position (textarea, inputs, scrolls)
+ if (this.elementIsWrapper && this.originalElement[0].nodeName.match(/textarea|input|select|button/i)) {
+
+ axis = $(this.handles[i], this.element);
+
+ //Checking the correct pad and border
+ padWrapper = /sw|ne|nw|se|n|s/.test(i) ? axis.outerHeight() : axis.outerWidth();
+
+ //The padding type i have to apply...
+ padPos = [ "padding",
+ /ne|nw|n/.test(i) ? "Top" :
+ /se|sw|s/.test(i) ? "Bottom" :
+ /^e$/.test(i) ? "Right" : "Left" ].join("");
+
+ target.css(padPos, padWrapper);
+
+ this._proportionallyResize();
+
+ }
+
+ //TODO: What's that good for? There's not anything to be executed left
+ if(!$(this.handles[i]).length) {
+ continue;
+ }
+ }
+ };
+
+ //TODO: make renderAxis a prototype function
+ this._renderAxis(this.element);
+
+ this._handles = $(".ui-resizable-handle", this.element)
+ .disableSelection();
+
+ //Matching axis name
+ this._handles.mouseover(function() {
+ if (!that.resizing) {
+ if (this.className) {
+ axis = this.className.match(/ui-resizable-(se|sw|ne|nw|n|e|s|w)/i);
+ }
+ //Axis, default = se
+ that.axis = axis && axis[1] ? axis[1] : "se";
+ }
+ });
+
+ //If we want to auto hide the elements
+ if (o.autoHide) {
+ this._handles.hide();
+ $(this.element)
+ .addClass("ui-resizable-autohide")
+ .mouseenter(function() {
+ if (o.disabled) {
+ return;
+ }
+ $(this).removeClass("ui-resizable-autohide");
+ that._handles.show();
+ })
+ .mouseleave(function(){
+ if (o.disabled) {
+ return;
+ }
+ if (!that.resizing) {
+ $(this).addClass("ui-resizable-autohide");
+ that._handles.hide();
+ }
+ });
+ }
+
+ //Initialize the mouse interaction
+ this._mouseInit();
+
+ },
+
+ _destroy: function() {
+
+ this._mouseDestroy();
+
+ var wrapper,
+ _destroy = function(exp) {
+ $(exp).removeClass("ui-resizable ui-resizable-disabled ui-resizable-resizing")
+ .removeData("resizable").removeData("ui-resizable").unbind(".resizable").find(".ui-resizable-handle").remove();
+ };
+
+ //TODO: Unwrap at same DOM position
+ if (this.elementIsWrapper) {
+ _destroy(this.element);
+ wrapper = this.element;
+ this.originalElement.css({
+ position: wrapper.css("position"),
+ width: wrapper.outerWidth(),
+ height: wrapper.outerHeight(),
+ top: wrapper.css("top"),
+ left: wrapper.css("left")
+ }).insertAfter( wrapper );
+ wrapper.remove();
+ }
+
+ this.originalElement.css("resize", this.originalResizeStyle);
+ _destroy(this.originalElement);
+
+ return this;
+ },
+
+ _mouseCapture: function(event) {
+ var i, handle,
+ capture = false;
+
+ for (i in this.handles) {
+ handle = $(this.handles[i])[0];
+ if (handle === event.target || $.contains(handle, event.target)) {
+ capture = true;
+ }
+ }
+
+ return !this.options.disabled && capture;
+ },
+
+ _mouseStart: function(event) {
+
+ var curleft, curtop, cursor,
+ o = this.options,
+ iniPos = this.element.position(),
+ el = this.element;
+
+ this.resizing = true;
+
+ // bugfix for http://dev.jquery.com/ticket/1749
+ if ( (/absolute/).test( el.css("position") ) ) {
+ el.css({ position: "absolute", top: el.css("top"), left: el.css("left") });
+ } else if (el.is(".ui-draggable")) {
+ el.css({ position: "absolute", top: iniPos.top, left: iniPos.left });
+ }
+
+ this._renderProxy();
+
+ curleft = num(this.helper.css("left"));
+ curtop = num(this.helper.css("top"));
+
+ if (o.containment) {
+ curleft += $(o.containment).scrollLeft() || 0;
+ curtop += $(o.containment).scrollTop() || 0;
+ }
+
+ //Store needed variables
+ this.offset = this.helper.offset();
+ this.position = { left: curleft, top: curtop };
+ this.size = this._helper ? { width: this.helper.width(), height: this.helper.height() } : { width: el.width(), height: el.height() };
+ this.originalSize = this._helper ? { width: el.outerWidth(), height: el.outerHeight() } : { width: el.width(), height: el.height() };
+ this.originalPosition = { left: curleft, top: curtop };
+ this.sizeDiff = { width: el.outerWidth() - el.width(), height: el.outerHeight() - el.height() };
+ this.originalMousePosition = { left: event.pageX, top: event.pageY };
+
+ //Aspect Ratio
+ this.aspectRatio = (typeof o.aspectRatio === "number") ? o.aspectRatio : ((this.originalSize.width / this.originalSize.height) || 1);
+
+ cursor = $(".ui-resizable-" + this.axis).css("cursor");
+ $("body").css("cursor", cursor === "auto" ? this.axis + "-resize" : cursor);
+
+ el.addClass("ui-resizable-resizing");
+ this._propagate("start", event);
+ return true;
+ },
+
+ _mouseDrag: function(event) {
+
+ //Increase performance, avoid regex
+ var data,
+ el = this.helper, props = {},
+ smp = this.originalMousePosition,
+ a = this.axis,
+ prevTop = this.position.top,
+ prevLeft = this.position.left,
+ prevWidth = this.size.width,
+ prevHeight = this.size.height,
+ dx = (event.pageX-smp.left)||0,
+ dy = (event.pageY-smp.top)||0,
+ trigger = this._change[a];
+
+ if (!trigger) {
+ return false;
+ }
+
+ // Calculate the attrs that will be change
+ data = trigger.apply(this, [event, dx, dy]);
+
+ // Put this in the mouseDrag handler since the user can start pressing shift while resizing
+ this._updateVirtualBoundaries(event.shiftKey);
+ if (this._aspectRatio || event.shiftKey) {
+ data = this._updateRatio(data, event);
+ }
+
+ data = this._respectSize(data, event);
+
+ this._updateCache(data);
+
+ // plugins callbacks need to be called first
+ this._propagate("resize", event);
+
+ if (this.position.top !== prevTop) {
+ props.top = this.position.top + "px";
+ }
+ if (this.position.left !== prevLeft) {
+ props.left = this.position.left + "px";
+ }
+ if (this.size.width !== prevWidth) {
+ props.width = this.size.width + "px";
+ }
+ if (this.size.height !== prevHeight) {
+ props.height = this.size.height + "px";
+ }
+ el.css(props);
+
+ if (!this._helper && this._proportionallyResizeElements.length) {
+ this._proportionallyResize();
+ }
+
+ // Call the user callback if the element was resized
+ if ( ! $.isEmptyObject(props) ) {
+ this._trigger("resize", event, this.ui());
+ }
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+
+ this.resizing = false;
+ var pr, ista, soffseth, soffsetw, s, left, top,
+ o = this.options, that = this;
+
+ if(this._helper) {
+
+ pr = this._proportionallyResizeElements;
+ ista = pr.length && (/textarea/i).test(pr[0].nodeName);
+ soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height;
+ soffsetw = ista ? 0 : that.sizeDiff.width;
+
+ s = { width: (that.helper.width() - soffsetw), height: (that.helper.height() - soffseth) };
+ left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null;
+ top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
+
+ if (!o.animate) {
+ this.element.css($.extend(s, { top: top, left: left }));
+ }
+
+ that.helper.height(that.size.height);
+ that.helper.width(that.size.width);
+
+ if (this._helper && !o.animate) {
+ this._proportionallyResize();
+ }
+ }
+
+ $("body").css("cursor", "auto");
+
+ this.element.removeClass("ui-resizable-resizing");
+
+ this._propagate("stop", event);
+
+ if (this._helper) {
+ this.helper.remove();
+ }
+
+ return false;
+
+ },
+
+ _updateVirtualBoundaries: function(forceAspectRatio) {
+ var pMinWidth, pMaxWidth, pMinHeight, pMaxHeight, b,
+ o = this.options;
+
+ b = {
+ minWidth: isNumber(o.minWidth) ? o.minWidth : 0,
+ maxWidth: isNumber(o.maxWidth) ? o.maxWidth : Infinity,
+ minHeight: isNumber(o.minHeight) ? o.minHeight : 0,
+ maxHeight: isNumber(o.maxHeight) ? o.maxHeight : Infinity
+ };
+
+ if(this._aspectRatio || forceAspectRatio) {
+ // We want to create an enclosing box whose aspect ration is the requested one
+ // First, compute the "projected" size for each dimension based on the aspect ratio and other dimension
+ pMinWidth = b.minHeight * this.aspectRatio;
+ pMinHeight = b.minWidth / this.aspectRatio;
+ pMaxWidth = b.maxHeight * this.aspectRatio;
+ pMaxHeight = b.maxWidth / this.aspectRatio;
+
+ if(pMinWidth > b.minWidth) {
+ b.minWidth = pMinWidth;
+ }
+ if(pMinHeight > b.minHeight) {
+ b.minHeight = pMinHeight;
+ }
+ if(pMaxWidth < b.maxWidth) {
+ b.maxWidth = pMaxWidth;
+ }
+ if(pMaxHeight < b.maxHeight) {
+ b.maxHeight = pMaxHeight;
+ }
+ }
+ this._vBoundaries = b;
+ },
+
+ _updateCache: function(data) {
+ this.offset = this.helper.offset();
+ if (isNumber(data.left)) {
+ this.position.left = data.left;
+ }
+ if (isNumber(data.top)) {
+ this.position.top = data.top;
+ }
+ if (isNumber(data.height)) {
+ this.size.height = data.height;
+ }
+ if (isNumber(data.width)) {
+ this.size.width = data.width;
+ }
+ },
+
+ _updateRatio: function( data ) {
+
+ var cpos = this.position,
+ csize = this.size,
+ a = this.axis;
+
+ if (isNumber(data.height)) {
+ data.width = (data.height * this.aspectRatio);
+ } else if (isNumber(data.width)) {
+ data.height = (data.width / this.aspectRatio);
+ }
+
+ if (a === "sw") {
+ data.left = cpos.left + (csize.width - data.width);
+ data.top = null;
+ }
+ if (a === "nw") {
+ data.top = cpos.top + (csize.height - data.height);
+ data.left = cpos.left + (csize.width - data.width);
+ }
+
+ return data;
+ },
+
+ _respectSize: function( data ) {
+
+ var o = this._vBoundaries,
+ a = this.axis,
+ ismaxw = isNumber(data.width) && o.maxWidth && (o.maxWidth < data.width), ismaxh = isNumber(data.height) && o.maxHeight && (o.maxHeight < data.height),
+ isminw = isNumber(data.width) && o.minWidth && (o.minWidth > data.width), isminh = isNumber(data.height) && o.minHeight && (o.minHeight > data.height),
+ dw = this.originalPosition.left + this.originalSize.width,
+ dh = this.position.top + this.size.height,
+ cw = /sw|nw|w/.test(a), ch = /nw|ne|n/.test(a);
+ if (isminw) {
+ data.width = o.minWidth;
+ }
+ if (isminh) {
+ data.height = o.minHeight;
+ }
+ if (ismaxw) {
+ data.width = o.maxWidth;
+ }
+ if (ismaxh) {
+ data.height = o.maxHeight;
+ }
+
+ if (isminw && cw) {
+ data.left = dw - o.minWidth;
+ }
+ if (ismaxw && cw) {
+ data.left = dw - o.maxWidth;
+ }
+ if (isminh && ch) {
+ data.top = dh - o.minHeight;
+ }
+ if (ismaxh && ch) {
+ data.top = dh - o.maxHeight;
+ }
+
+ // fixing jump error on top/left - bug #2330
+ if (!data.width && !data.height && !data.left && data.top) {
+ data.top = null;
+ } else if (!data.width && !data.height && !data.top && data.left) {
+ data.left = null;
+ }
+
+ return data;
+ },
+
+ _proportionallyResize: function() {
+
+ if (!this._proportionallyResizeElements.length) {
+ return;
+ }
+
+ var i, j, borders, paddings, prel,
+ element = this.helper || this.element;
+
+ for ( i=0; i < this._proportionallyResizeElements.length; i++) {
+
+ prel = this._proportionallyResizeElements[i];
+
+ if (!this.borderDif) {
+ this.borderDif = [];
+ borders = [prel.css("borderTopWidth"), prel.css("borderRightWidth"), prel.css("borderBottomWidth"), prel.css("borderLeftWidth")];
+ paddings = [prel.css("paddingTop"), prel.css("paddingRight"), prel.css("paddingBottom"), prel.css("paddingLeft")];
+
+ for ( j = 0; j < borders.length; j++ ) {
+ this.borderDif[ j ] = ( parseInt( borders[ j ], 10 ) || 0 ) + ( parseInt( paddings[ j ], 10 ) || 0 );
+ }
+ }
+
+ prel.css({
+ height: (element.height() - this.borderDif[0] - this.borderDif[2]) || 0,
+ width: (element.width() - this.borderDif[1] - this.borderDif[3]) || 0
+ });
+
+ }
+
+ },
+
+ _renderProxy: function() {
+
+ var el = this.element, o = this.options;
+ this.elementOffset = el.offset();
+
+ if(this._helper) {
+
+ this.helper = this.helper || $("");
+
+ this.helper.addClass(this._helper).css({
+ width: this.element.outerWidth() - 1,
+ height: this.element.outerHeight() - 1,
+ position: "absolute",
+ left: this.elementOffset.left +"px",
+ top: this.elementOffset.top +"px",
+ zIndex: ++o.zIndex //TODO: Don't modify option
+ });
+
+ this.helper
+ .appendTo("body")
+ .disableSelection();
+
+ } else {
+ this.helper = this.element;
+ }
+
+ },
+
+ _change: {
+ e: function(event, dx) {
+ return { width: this.originalSize.width + dx };
+ },
+ w: function(event, dx) {
+ var cs = this.originalSize, sp = this.originalPosition;
+ return { left: sp.left + dx, width: cs.width - dx };
+ },
+ n: function(event, dx, dy) {
+ var cs = this.originalSize, sp = this.originalPosition;
+ return { top: sp.top + dy, height: cs.height - dy };
+ },
+ s: function(event, dx, dy) {
+ return { height: this.originalSize.height + dy };
+ },
+ se: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ sw: function(event, dx, dy) {
+ return $.extend(this._change.s.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ },
+ ne: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.e.apply(this, [event, dx, dy]));
+ },
+ nw: function(event, dx, dy) {
+ return $.extend(this._change.n.apply(this, arguments), this._change.w.apply(this, [event, dx, dy]));
+ }
+ },
+
+ _propagate: function(n, event) {
+ $.ui.plugin.call(this, n, [event, this.ui()]);
+ (n !== "resize" && this._trigger(n, event, this.ui()));
+ },
+
+ plugins: {},
+
+ ui: function() {
+ return {
+ originalElement: this.originalElement,
+ element: this.element,
+ helper: this.helper,
+ position: this.position,
+ size: this.size,
+ originalSize: this.originalSize,
+ originalPosition: this.originalPosition
+ };
+ }
+
+});
+
+/*
+ * Resizable Extensions
+ */
+
+$.ui.plugin.add("resizable", "animate", {
+
+ stop: function( event ) {
+ var that = $(this).data("ui-resizable"),
+ o = that.options,
+ pr = that._proportionallyResizeElements,
+ ista = pr.length && (/textarea/i).test(pr[0].nodeName),
+ soffseth = ista && $.ui.hasScroll(pr[0], "left") /* TODO - jump height */ ? 0 : that.sizeDiff.height,
+ soffsetw = ista ? 0 : that.sizeDiff.width,
+ style = { width: (that.size.width - soffsetw), height: (that.size.height - soffseth) },
+ left = (parseInt(that.element.css("left"), 10) + (that.position.left - that.originalPosition.left)) || null,
+ top = (parseInt(that.element.css("top"), 10) + (that.position.top - that.originalPosition.top)) || null;
+
+ that.element.animate(
+ $.extend(style, top && left ? { top: top, left: left } : {}), {
+ duration: o.animateDuration,
+ easing: o.animateEasing,
+ step: function() {
+
+ var data = {
+ width: parseInt(that.element.css("width"), 10),
+ height: parseInt(that.element.css("height"), 10),
+ top: parseInt(that.element.css("top"), 10),
+ left: parseInt(that.element.css("left"), 10)
+ };
+
+ if (pr && pr.length) {
+ $(pr[0]).css({ width: data.width, height: data.height });
+ }
+
+ // propagating resize, and updating values for each animation step
+ that._updateCache(data);
+ that._propagate("resize", event);
+
+ }
+ }
+ );
+ }
+
+});
+
+$.ui.plugin.add("resizable", "containment", {
+
+ start: function() {
+ var element, p, co, ch, cw, width, height,
+ that = $(this).data("ui-resizable"),
+ o = that.options,
+ el = that.element,
+ oc = o.containment,
+ ce = (oc instanceof $) ? oc.get(0) : (/parent/.test(oc)) ? el.parent().get(0) : oc;
+
+ if (!ce) {
+ return;
+ }
+
+ that.containerElement = $(ce);
+
+ if (/document/.test(oc) || oc === document) {
+ that.containerOffset = { left: 0, top: 0 };
+ that.containerPosition = { left: 0, top: 0 };
+
+ that.parentData = {
+ element: $(document), left: 0, top: 0,
+ width: $(document).width(), height: $(document).height() || document.body.parentNode.scrollHeight
+ };
+ }
+
+ // i'm a node, so compute top, left, right, bottom
+ else {
+ element = $(ce);
+ p = [];
+ $([ "Top", "Right", "Left", "Bottom" ]).each(function(i, name) { p[i] = num(element.css("padding" + name)); });
+
+ that.containerOffset = element.offset();
+ that.containerPosition = element.position();
+ that.containerSize = { height: (element.innerHeight() - p[3]), width: (element.innerWidth() - p[1]) };
+
+ co = that.containerOffset;
+ ch = that.containerSize.height;
+ cw = that.containerSize.width;
+ width = ($.ui.hasScroll(ce, "left") ? ce.scrollWidth : cw );
+ height = ($.ui.hasScroll(ce) ? ce.scrollHeight : ch);
+
+ that.parentData = {
+ element: ce, left: co.left, top: co.top, width: width, height: height
+ };
+ }
+ },
+
+ resize: function( event ) {
+ var woset, hoset, isParent, isOffsetRelative,
+ that = $(this).data("ui-resizable"),
+ o = that.options,
+ co = that.containerOffset, cp = that.position,
+ pRatio = that._aspectRatio || event.shiftKey,
+ cop = { top:0, left:0 }, ce = that.containerElement;
+
+ if (ce[0] !== document && (/static/).test(ce.css("position"))) {
+ cop = co;
+ }
+
+ if (cp.left < (that._helper ? co.left : 0)) {
+ that.size.width = that.size.width + (that._helper ? (that.position.left - co.left) : (that.position.left - cop.left));
+ if (pRatio) {
+ that.size.height = that.size.width / that.aspectRatio;
+ }
+ that.position.left = o.helper ? co.left : 0;
+ }
+
+ if (cp.top < (that._helper ? co.top : 0)) {
+ that.size.height = that.size.height + (that._helper ? (that.position.top - co.top) : that.position.top);
+ if (pRatio) {
+ that.size.width = that.size.height * that.aspectRatio;
+ }
+ that.position.top = that._helper ? co.top : 0;
+ }
+
+ that.offset.left = that.parentData.left+that.position.left;
+ that.offset.top = that.parentData.top+that.position.top;
+
+ woset = Math.abs( (that._helper ? that.offset.left - cop.left : (that.offset.left - cop.left)) + that.sizeDiff.width );
+ hoset = Math.abs( (that._helper ? that.offset.top - cop.top : (that.offset.top - co.top)) + that.sizeDiff.height );
+
+ isParent = that.containerElement.get(0) === that.element.parent().get(0);
+ isOffsetRelative = /relative|absolute/.test(that.containerElement.css("position"));
+
+ if ( isParent && isOffsetRelative ) {
+ woset -= Math.abs( that.parentData.left );
+ }
+
+ if (woset + that.size.width >= that.parentData.width) {
+ that.size.width = that.parentData.width - woset;
+ if (pRatio) {
+ that.size.height = that.size.width / that.aspectRatio;
+ }
+ }
+
+ if (hoset + that.size.height >= that.parentData.height) {
+ that.size.height = that.parentData.height - hoset;
+ if (pRatio) {
+ that.size.width = that.size.height * that.aspectRatio;
+ }
+ }
+ },
+
+ stop: function(){
+ var that = $(this).data("ui-resizable"),
+ o = that.options,
+ co = that.containerOffset,
+ cop = that.containerPosition,
+ ce = that.containerElement,
+ helper = $(that.helper),
+ ho = helper.offset(),
+ w = helper.outerWidth() - that.sizeDiff.width,
+ h = helper.outerHeight() - that.sizeDiff.height;
+
+ if (that._helper && !o.animate && (/relative/).test(ce.css("position"))) {
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+ }
+
+ if (that._helper && !o.animate && (/static/).test(ce.css("position"))) {
+ $(this).css({ left: ho.left - cop.left - co.left, width: w, height: h });
+ }
+
+ }
+});
+
+$.ui.plugin.add("resizable", "alsoResize", {
+
+ start: function () {
+ var that = $(this).data("ui-resizable"),
+ o = that.options,
+ _store = function (exp) {
+ $(exp).each(function() {
+ var el = $(this);
+ el.data("ui-resizable-alsoresize", {
+ width: parseInt(el.width(), 10), height: parseInt(el.height(), 10),
+ left: parseInt(el.css("left"), 10), top: parseInt(el.css("top"), 10)
+ });
+ });
+ };
+
+ if (typeof(o.alsoResize) === "object" && !o.alsoResize.parentNode) {
+ if (o.alsoResize.length) { o.alsoResize = o.alsoResize[0]; _store(o.alsoResize); }
+ else { $.each(o.alsoResize, function (exp) { _store(exp); }); }
+ }else{
+ _store(o.alsoResize);
+ }
+ },
+
+ resize: function (event, ui) {
+ var that = $(this).data("ui-resizable"),
+ o = that.options,
+ os = that.originalSize,
+ op = that.originalPosition,
+ delta = {
+ height: (that.size.height - os.height) || 0, width: (that.size.width - os.width) || 0,
+ top: (that.position.top - op.top) || 0, left: (that.position.left - op.left) || 0
+ },
+
+ _alsoResize = function (exp, c) {
+ $(exp).each(function() {
+ var el = $(this), start = $(this).data("ui-resizable-alsoresize"), style = {},
+ css = c && c.length ? c : el.parents(ui.originalElement[0]).length ? ["width", "height"] : ["width", "height", "top", "left"];
+
+ $.each(css, function (i, prop) {
+ var sum = (start[prop]||0) + (delta[prop]||0);
+ if (sum && sum >= 0) {
+ style[prop] = sum || null;
+ }
+ });
+
+ el.css(style);
+ });
+ };
+
+ if (typeof(o.alsoResize) === "object" && !o.alsoResize.nodeType) {
+ $.each(o.alsoResize, function (exp, c) { _alsoResize(exp, c); });
+ }else{
+ _alsoResize(o.alsoResize);
+ }
+ },
+
+ stop: function () {
+ $(this).removeData("resizable-alsoresize");
+ }
+});
+
+$.ui.plugin.add("resizable", "ghost", {
+
+ start: function() {
+
+ var that = $(this).data("ui-resizable"), o = that.options, cs = that.size;
+
+ that.ghost = that.originalElement.clone();
+ that.ghost
+ .css({ opacity: 0.25, display: "block", position: "relative", height: cs.height, width: cs.width, margin: 0, left: 0, top: 0 })
+ .addClass("ui-resizable-ghost")
+ .addClass(typeof o.ghost === "string" ? o.ghost : "");
+
+ that.ghost.appendTo(that.helper);
+
+ },
+
+ resize: function(){
+ var that = $(this).data("ui-resizable");
+ if (that.ghost) {
+ that.ghost.css({ position: "relative", height: that.size.height, width: that.size.width });
+ }
+ },
+
+ stop: function() {
+ var that = $(this).data("ui-resizable");
+ if (that.ghost && that.helper) {
+ that.helper.get(0).removeChild(that.ghost.get(0));
+ }
+ }
+
+});
+
+$.ui.plugin.add("resizable", "grid", {
+
+ resize: function() {
+ var that = $(this).data("ui-resizable"),
+ o = that.options,
+ cs = that.size,
+ os = that.originalSize,
+ op = that.originalPosition,
+ a = that.axis,
+ grid = typeof o.grid === "number" ? [o.grid, o.grid] : o.grid,
+ gridX = (grid[0]||1),
+ gridY = (grid[1]||1),
+ ox = Math.round((cs.width - os.width) / gridX) * gridX,
+ oy = Math.round((cs.height - os.height) / gridY) * gridY,
+ newWidth = os.width + ox,
+ newHeight = os.height + oy,
+ isMaxWidth = o.maxWidth && (o.maxWidth < newWidth),
+ isMaxHeight = o.maxHeight && (o.maxHeight < newHeight),
+ isMinWidth = o.minWidth && (o.minWidth > newWidth),
+ isMinHeight = o.minHeight && (o.minHeight > newHeight);
+
+ o.grid = grid;
+
+ if (isMinWidth) {
+ newWidth = newWidth + gridX;
+ }
+ if (isMinHeight) {
+ newHeight = newHeight + gridY;
+ }
+ if (isMaxWidth) {
+ newWidth = newWidth - gridX;
+ }
+ if (isMaxHeight) {
+ newHeight = newHeight - gridY;
+ }
+
+ if (/^(se|s|e)$/.test(a)) {
+ that.size.width = newWidth;
+ that.size.height = newHeight;
+ } else if (/^(ne)$/.test(a)) {
+ that.size.width = newWidth;
+ that.size.height = newHeight;
+ that.position.top = op.top - oy;
+ } else if (/^(sw)$/.test(a)) {
+ that.size.width = newWidth;
+ that.size.height = newHeight;
+ that.position.left = op.left - ox;
+ } else {
+ if ( newHeight - gridY > 0 ) {
+ that.size.height = newHeight;
+ that.position.top = op.top - oy;
+ } else {
+ that.size.height = gridY;
+ that.position.top = op.top + os.height - gridY;
+ }
+ if ( newWidth - gridX > 0 ) {
+ that.size.width = newWidth;
+ that.position.left = op.left - ox;
+ } else {
+ that.size.width = gridX;
+ that.position.left = op.left + os.width - gridX;
+ }
+ }
+ }
+
+});
+
+})(jQuery);
+(function( $, undefined ) {
+
+$.widget("ui.selectable", $.ui.mouse, {
+ version: "1.10.4",
+ options: {
+ appendTo: "body",
+ autoRefresh: true,
+ distance: 0,
+ filter: "*",
+ tolerance: "touch",
+
+ // callbacks
+ selected: null,
+ selecting: null,
+ start: null,
+ stop: null,
+ unselected: null,
+ unselecting: null
+ },
+ _create: function() {
+ var selectees,
+ that = this;
+
+ this.element.addClass("ui-selectable");
+
+ this.dragged = false;
+
+ // cache selectee children based on filter
+ this.refresh = function() {
+ selectees = $(that.options.filter, that.element[0]);
+ selectees.addClass("ui-selectee");
+ selectees.each(function() {
+ var $this = $(this),
+ pos = $this.offset();
+ $.data(this, "selectable-item", {
+ element: this,
+ $element: $this,
+ left: pos.left,
+ top: pos.top,
+ right: pos.left + $this.outerWidth(),
+ bottom: pos.top + $this.outerHeight(),
+ startselected: false,
+ selected: $this.hasClass("ui-selected"),
+ selecting: $this.hasClass("ui-selecting"),
+ unselecting: $this.hasClass("ui-unselecting")
+ });
+ });
+ };
+ this.refresh();
+
+ this.selectees = selectees.addClass("ui-selectee");
+
+ this._mouseInit();
+
+ this.helper = $("");
+ },
+
+ _destroy: function() {
+ this.selectees
+ .removeClass("ui-selectee")
+ .removeData("selectable-item");
+ this.element
+ .removeClass("ui-selectable ui-selectable-disabled");
+ this._mouseDestroy();
+ },
+
+ _mouseStart: function(event) {
+ var that = this,
+ options = this.options;
+
+ this.opos = [event.pageX, event.pageY];
+
+ if (this.options.disabled) {
+ return;
+ }
+
+ this.selectees = $(options.filter, this.element[0]);
+
+ this._trigger("start", event);
+
+ $(options.appendTo).append(this.helper);
+ // position helper (lasso)
+ this.helper.css({
+ "left": event.pageX,
+ "top": event.pageY,
+ "width": 0,
+ "height": 0
+ });
+
+ if (options.autoRefresh) {
+ this.refresh();
+ }
+
+ this.selectees.filter(".ui-selected").each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.startselected = true;
+ if (!event.metaKey && !event.ctrlKey) {
+ selectee.$element.removeClass("ui-selected");
+ selectee.selected = false;
+ selectee.$element.addClass("ui-unselecting");
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ that._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ });
+
+ $(event.target).parents().addBack().each(function() {
+ var doSelect,
+ selectee = $.data(this, "selectable-item");
+ if (selectee) {
+ doSelect = (!event.metaKey && !event.ctrlKey) || !selectee.$element.hasClass("ui-selected");
+ selectee.$element
+ .removeClass(doSelect ? "ui-unselecting" : "ui-selected")
+ .addClass(doSelect ? "ui-selecting" : "ui-unselecting");
+ selectee.unselecting = !doSelect;
+ selectee.selecting = doSelect;
+ selectee.selected = doSelect;
+ // selectable (UN)SELECTING callback
+ if (doSelect) {
+ that._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ } else {
+ that._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ return false;
+ }
+ });
+
+ },
+
+ _mouseDrag: function(event) {
+
+ this.dragged = true;
+
+ if (this.options.disabled) {
+ return;
+ }
+
+ var tmp,
+ that = this,
+ options = this.options,
+ x1 = this.opos[0],
+ y1 = this.opos[1],
+ x2 = event.pageX,
+ y2 = event.pageY;
+
+ if (x1 > x2) { tmp = x2; x2 = x1; x1 = tmp; }
+ if (y1 > y2) { tmp = y2; y2 = y1; y1 = tmp; }
+ this.helper.css({left: x1, top: y1, width: x2-x1, height: y2-y1});
+
+ this.selectees.each(function() {
+ var selectee = $.data(this, "selectable-item"),
+ hit = false;
+
+ //prevent helper from being selected if appendTo: selectable
+ if (!selectee || selectee.element === that.element[0]) {
+ return;
+ }
+
+ if (options.tolerance === "touch") {
+ hit = ( !(selectee.left > x2 || selectee.right < x1 || selectee.top > y2 || selectee.bottom < y1) );
+ } else if (options.tolerance === "fit") {
+ hit = (selectee.left > x1 && selectee.right < x2 && selectee.top > y1 && selectee.bottom < y2);
+ }
+
+ if (hit) {
+ // SELECT
+ if (selectee.selected) {
+ selectee.$element.removeClass("ui-selected");
+ selectee.selected = false;
+ }
+ if (selectee.unselecting) {
+ selectee.$element.removeClass("ui-unselecting");
+ selectee.unselecting = false;
+ }
+ if (!selectee.selecting) {
+ selectee.$element.addClass("ui-selecting");
+ selectee.selecting = true;
+ // selectable SELECTING callback
+ that._trigger("selecting", event, {
+ selecting: selectee.element
+ });
+ }
+ } else {
+ // UNSELECT
+ if (selectee.selecting) {
+ if ((event.metaKey || event.ctrlKey) && selectee.startselected) {
+ selectee.$element.removeClass("ui-selecting");
+ selectee.selecting = false;
+ selectee.$element.addClass("ui-selected");
+ selectee.selected = true;
+ } else {
+ selectee.$element.removeClass("ui-selecting");
+ selectee.selecting = false;
+ if (selectee.startselected) {
+ selectee.$element.addClass("ui-unselecting");
+ selectee.unselecting = true;
+ }
+ // selectable UNSELECTING callback
+ that._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ if (selectee.selected) {
+ if (!event.metaKey && !event.ctrlKey && !selectee.startselected) {
+ selectee.$element.removeClass("ui-selected");
+ selectee.selected = false;
+
+ selectee.$element.addClass("ui-unselecting");
+ selectee.unselecting = true;
+ // selectable UNSELECTING callback
+ that._trigger("unselecting", event, {
+ unselecting: selectee.element
+ });
+ }
+ }
+ }
+ });
+
+ return false;
+ },
+
+ _mouseStop: function(event) {
+ var that = this;
+
+ this.dragged = false;
+
+ $(".ui-unselecting", this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass("ui-unselecting");
+ selectee.unselecting = false;
+ selectee.startselected = false;
+ that._trigger("unselected", event, {
+ unselected: selectee.element
+ });
+ });
+ $(".ui-selecting", this.element[0]).each(function() {
+ var selectee = $.data(this, "selectable-item");
+ selectee.$element.removeClass("ui-selecting").addClass("ui-selected");
+ selectee.selecting = false;
+ selectee.selected = true;
+ selectee.startselected = true;
+ that._trigger("selected", event, {
+ selected: selectee.element
+ });
+ });
+ this._trigger("stop", event);
+
+ this.helper.remove();
+
+ return false;
+ }
+
+});
+
+})(jQuery);
+(function( $, undefined ) {
+
+// number of pages in a slider
+// (how many times can you page up/down to go through the whole range)
+var numPages = 5;
+
+$.widget( "ui.slider", $.ui.mouse, {
+ version: "1.10.4",
+ widgetEventPrefix: "slide",
+
+ options: {
+ animate: false,
+ distance: 0,
+ max: 100,
+ min: 0,
+ orientation: "horizontal",
+ range: false,
+ step: 1,
+ value: 0,
+ values: null,
+
+ // callbacks
+ change: null,
+ slide: null,
+ start: null,
+ stop: null
+ },
+
+ _create: function() {
+ this._keySliding = false;
+ this._mouseSliding = false;
+ this._animateOff = true;
+ this._handleIndex = null;
+ this._detectOrientation();
+ this._mouseInit();
+
+ this.element
+ .addClass( "ui-slider" +
+ " ui-slider-" + this.orientation +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all");
+
+ this._refresh();
+ this._setOption( "disabled", this.options.disabled );
+
+ this._animateOff = false;
+ },
+
+ _refresh: function() {
+ this._createRange();
+ this._createHandles();
+ this._setupEvents();
+ this._refreshValue();
+ },
+
+ _createHandles: function() {
+ var i, handleCount,
+ options = this.options,
+ existingHandles = this.element.find( ".ui-slider-handle" ).addClass( "ui-state-default ui-corner-all" ),
+ handle = "",
+ handles = [];
+
+ handleCount = ( options.values && options.values.length ) || 1;
+
+ if ( existingHandles.length > handleCount ) {
+ existingHandles.slice( handleCount ).remove();
+ existingHandles = existingHandles.slice( 0, handleCount );
+ }
+
+ for ( i = existingHandles.length; i < handleCount; i++ ) {
+ handles.push( handle );
+ }
+
+ this.handles = existingHandles.add( $( handles.join( "" ) ).appendTo( this.element ) );
+
+ this.handle = this.handles.eq( 0 );
+
+ this.handles.each(function( i ) {
+ $( this ).data( "ui-slider-handle-index", i );
+ });
+ },
+
+ _createRange: function() {
+ var options = this.options,
+ classes = "";
+
+ if ( options.range ) {
+ if ( options.range === true ) {
+ if ( !options.values ) {
+ options.values = [ this._valueMin(), this._valueMin() ];
+ } else if ( options.values.length && options.values.length !== 2 ) {
+ options.values = [ options.values[0], options.values[0] ];
+ } else if ( $.isArray( options.values ) ) {
+ options.values = options.values.slice(0);
+ }
+ }
+
+ if ( !this.range || !this.range.length ) {
+ this.range = $( "" )
+ .appendTo( this.element );
+
+ classes = "ui-slider-range" +
+ // note: this isn't the most fittingly semantic framework class for this element,
+ // but worked best visually with a variety of themes
+ " ui-widget-header ui-corner-all";
+ } else {
+ this.range.removeClass( "ui-slider-range-min ui-slider-range-max" )
+ // Handle range switching from true to min/max
+ .css({
+ "left": "",
+ "bottom": ""
+ });
+ }
+
+ this.range.addClass( classes +
+ ( ( options.range === "min" || options.range === "max" ) ? " ui-slider-range-" + options.range : "" ) );
+ } else {
+ if ( this.range ) {
+ this.range.remove();
+ }
+ this.range = null;
+ }
+ },
+
+ _setupEvents: function() {
+ var elements = this.handles.add( this.range ).filter( "a" );
+ this._off( elements );
+ this._on( elements, this._handleEvents );
+ this._hoverable( elements );
+ this._focusable( elements );
+ },
+
+ _destroy: function() {
+ this.handles.remove();
+ if ( this.range ) {
+ this.range.remove();
+ }
+
+ this.element
+ .removeClass( "ui-slider" +
+ " ui-slider-horizontal" +
+ " ui-slider-vertical" +
+ " ui-widget" +
+ " ui-widget-content" +
+ " ui-corner-all" );
+
+ this._mouseDestroy();
+ },
+
+ _mouseCapture: function( event ) {
+ var position, normValue, distance, closestHandle, index, allowed, offset, mouseOverHandle,
+ that = this,
+ o = this.options;
+
+ if ( o.disabled ) {
+ return false;
+ }
+
+ this.elementSize = {
+ width: this.element.outerWidth(),
+ height: this.element.outerHeight()
+ };
+ this.elementOffset = this.element.offset();
+
+ position = { x: event.pageX, y: event.pageY };
+ normValue = this._normValueFromMouse( position );
+ distance = this._valueMax() - this._valueMin() + 1;
+ this.handles.each(function( i ) {
+ var thisDistance = Math.abs( normValue - that.values(i) );
+ if (( distance > thisDistance ) ||
+ ( distance === thisDistance &&
+ (i === that._lastChangedValue || that.values(i) === o.min ))) {
+ distance = thisDistance;
+ closestHandle = $( this );
+ index = i;
+ }
+ });
+
+ allowed = this._start( event, index );
+ if ( allowed === false ) {
+ return false;
+ }
+ this._mouseSliding = true;
+
+ this._handleIndex = index;
+
+ closestHandle
+ .addClass( "ui-state-active" )
+ .focus();
+
+ offset = closestHandle.offset();
+ mouseOverHandle = !$( event.target ).parents().addBack().is( ".ui-slider-handle" );
+ this._clickOffset = mouseOverHandle ? { left: 0, top: 0 } : {
+ left: event.pageX - offset.left - ( closestHandle.width() / 2 ),
+ top: event.pageY - offset.top -
+ ( closestHandle.height() / 2 ) -
+ ( parseInt( closestHandle.css("borderTopWidth"), 10 ) || 0 ) -
+ ( parseInt( closestHandle.css("borderBottomWidth"), 10 ) || 0) +
+ ( parseInt( closestHandle.css("marginTop"), 10 ) || 0)
+ };
+
+ if ( !this.handles.hasClass( "ui-state-hover" ) ) {
+ this._slide( event, index, normValue );
+ }
+ this._animateOff = true;
+ return true;
+ },
+
+ _mouseStart: function() {
+ return true;
+ },
+
+ _mouseDrag: function( event ) {
+ var position = { x: event.pageX, y: event.pageY },
+ normValue = this._normValueFromMouse( position );
+
+ this._slide( event, this._handleIndex, normValue );
+
+ return false;
+ },
+
+ _mouseStop: function( event ) {
+ this.handles.removeClass( "ui-state-active" );
+ this._mouseSliding = false;
+
+ this._stop( event, this._handleIndex );
+ this._change( event, this._handleIndex );
+
+ this._handleIndex = null;
+ this._clickOffset = null;
+ this._animateOff = false;
+
+ return false;
+ },
+
+ _detectOrientation: function() {
+ this.orientation = ( this.options.orientation === "vertical" ) ? "vertical" : "horizontal";
+ },
+
+ _normValueFromMouse: function( position ) {
+ var pixelTotal,
+ pixelMouse,
+ percentMouse,
+ valueTotal,
+ valueMouse;
+
+ if ( this.orientation === "horizontal" ) {
+ pixelTotal = this.elementSize.width;
+ pixelMouse = position.x - this.elementOffset.left - ( this._clickOffset ? this._clickOffset.left : 0 );
+ } else {
+ pixelTotal = this.elementSize.height;
+ pixelMouse = position.y - this.elementOffset.top - ( this._clickOffset ? this._clickOffset.top : 0 );
+ }
+
+ percentMouse = ( pixelMouse / pixelTotal );
+ if ( percentMouse > 1 ) {
+ percentMouse = 1;
+ }
+ if ( percentMouse < 0 ) {
+ percentMouse = 0;
+ }
+ if ( this.orientation === "vertical" ) {
+ percentMouse = 1 - percentMouse;
+ }
+
+ valueTotal = this._valueMax() - this._valueMin();
+ valueMouse = this._valueMin() + percentMouse * valueTotal;
+
+ return this._trimAlignValue( valueMouse );
+ },
+
+ _start: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+ return this._trigger( "start", event, uiHash );
+ },
+
+ _slide: function( event, index, newVal ) {
+ var otherVal,
+ newValues,
+ allowed;
+
+ if ( this.options.values && this.options.values.length ) {
+ otherVal = this.values( index ? 0 : 1 );
+
+ if ( ( this.options.values.length === 2 && this.options.range === true ) &&
+ ( ( index === 0 && newVal > otherVal) || ( index === 1 && newVal < otherVal ) )
+ ) {
+ newVal = otherVal;
+ }
+
+ if ( newVal !== this.values( index ) ) {
+ newValues = this.values();
+ newValues[ index ] = newVal;
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal,
+ values: newValues
+ } );
+ otherVal = this.values( index ? 0 : 1 );
+ if ( allowed !== false ) {
+ this.values( index, newVal );
+ }
+ }
+ } else {
+ if ( newVal !== this.value() ) {
+ // A slide can be canceled by returning false from the slide callback
+ allowed = this._trigger( "slide", event, {
+ handle: this.handles[ index ],
+ value: newVal
+ } );
+ if ( allowed !== false ) {
+ this.value( newVal );
+ }
+ }
+ }
+ },
+
+ _stop: function( event, index ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ this._trigger( "stop", event, uiHash );
+ },
+
+ _change: function( event, index ) {
+ if ( !this._keySliding && !this._mouseSliding ) {
+ var uiHash = {
+ handle: this.handles[ index ],
+ value: this.value()
+ };
+ if ( this.options.values && this.options.values.length ) {
+ uiHash.value = this.values( index );
+ uiHash.values = this.values();
+ }
+
+ //store the last changed value index for reference when handles overlap
+ this._lastChangedValue = index;
+
+ this._trigger( "change", event, uiHash );
+ }
+ },
+
+ value: function( newValue ) {
+ if ( arguments.length ) {
+ this.options.value = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, 0 );
+ return;
+ }
+
+ return this._value();
+ },
+
+ values: function( index, newValue ) {
+ var vals,
+ newValues,
+ i;
+
+ if ( arguments.length > 1 ) {
+ this.options.values[ index ] = this._trimAlignValue( newValue );
+ this._refreshValue();
+ this._change( null, index );
+ return;
+ }
+
+ if ( arguments.length ) {
+ if ( $.isArray( arguments[ 0 ] ) ) {
+ vals = this.options.values;
+ newValues = arguments[ 0 ];
+ for ( i = 0; i < vals.length; i += 1 ) {
+ vals[ i ] = this._trimAlignValue( newValues[ i ] );
+ this._change( null, i );
+ }
+ this._refreshValue();
+ } else {
+ if ( this.options.values && this.options.values.length ) {
+ return this._values( index );
+ } else {
+ return this.value();
+ }
+ }
+ } else {
+ return this._values();
+ }
+ },
+
+ _setOption: function( key, value ) {
+ var i,
+ valsLength = 0;
+
+ if ( key === "range" && this.options.range === true ) {
+ if ( value === "min" ) {
+ this.options.value = this._values( 0 );
+ this.options.values = null;
+ } else if ( value === "max" ) {
+ this.options.value = this._values( this.options.values.length-1 );
+ this.options.values = null;
+ }
+ }
+
+ if ( $.isArray( this.options.values ) ) {
+ valsLength = this.options.values.length;
+ }
+
+ $.Widget.prototype._setOption.apply( this, arguments );
+
+ switch ( key ) {
+ case "orientation":
+ this._detectOrientation();
+ this.element
+ .removeClass( "ui-slider-horizontal ui-slider-vertical" )
+ .addClass( "ui-slider-" + this.orientation );
+ this._refreshValue();
+ break;
+ case "value":
+ this._animateOff = true;
+ this._refreshValue();
+ this._change( null, 0 );
+ this._animateOff = false;
+ break;
+ case "values":
+ this._animateOff = true;
+ this._refreshValue();
+ for ( i = 0; i < valsLength; i += 1 ) {
+ this._change( null, i );
+ }
+ this._animateOff = false;
+ break;
+ case "min":
+ case "max":
+ this._animateOff = true;
+ this._refreshValue();
+ this._animateOff = false;
+ break;
+ case "range":
+ this._animateOff = true;
+ this._refresh();
+ this._animateOff = false;
+ break;
+ }
+ },
+
+ //internal value getter
+ // _value() returns value trimmed by min and max, aligned by step
+ _value: function() {
+ var val = this.options.value;
+ val = this._trimAlignValue( val );
+
+ return val;
+ },
+
+ //internal values getter
+ // _values() returns array of values trimmed by min and max, aligned by step
+ // _values( index ) returns single value trimmed by min and max, aligned by step
+ _values: function( index ) {
+ var val,
+ vals,
+ i;
+
+ if ( arguments.length ) {
+ val = this.options.values[ index ];
+ val = this._trimAlignValue( val );
+
+ return val;
+ } else if ( this.options.values && this.options.values.length ) {
+ // .slice() creates a copy of the array
+ // this copy gets trimmed by min and max and then returned
+ vals = this.options.values.slice();
+ for ( i = 0; i < vals.length; i+= 1) {
+ vals[ i ] = this._trimAlignValue( vals[ i ] );
+ }
+
+ return vals;
+ } else {
+ return [];
+ }
+ },
+
+ // returns the step-aligned value that val is closest to, between (inclusive) min and max
+ _trimAlignValue: function( val ) {
+ if ( val <= this._valueMin() ) {
+ return this._valueMin();
+ }
+ if ( val >= this._valueMax() ) {
+ return this._valueMax();
+ }
+ var step = ( this.options.step > 0 ) ? this.options.step : 1,
+ valModStep = (val - this._valueMin()) % step,
+ alignValue = val - valModStep;
+
+ if ( Math.abs(valModStep) * 2 >= step ) {
+ alignValue += ( valModStep > 0 ) ? step : ( -step );
+ }
+
+ // Since JavaScript has problems with large floats, round
+ // the final value to 5 digits after the decimal point (see #4124)
+ return parseFloat( alignValue.toFixed(5) );
+ },
+
+ _valueMin: function() {
+ return this.options.min;
+ },
+
+ _valueMax: function() {
+ return this.options.max;
+ },
+
+ _refreshValue: function() {
+ var lastValPercent, valPercent, value, valueMin, valueMax,
+ oRange = this.options.range,
+ o = this.options,
+ that = this,
+ animate = ( !this._animateOff ) ? o.animate : false,
+ _set = {};
+
+ if ( this.options.values && this.options.values.length ) {
+ this.handles.each(function( i ) {
+ valPercent = ( that.values(i) - that._valueMin() ) / ( that._valueMax() - that._valueMin() ) * 100;
+ _set[ that.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ $( this ).stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+ if ( that.options.range === true ) {
+ if ( that.orientation === "horizontal" ) {
+ if ( i === 0 ) {
+ that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { left: valPercent + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ that.range[ animate ? "animate" : "css" ]( { width: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ } else {
+ if ( i === 0 ) {
+ that.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { bottom: ( valPercent ) + "%" }, o.animate );
+ }
+ if ( i === 1 ) {
+ that.range[ animate ? "animate" : "css" ]( { height: ( valPercent - lastValPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ }
+ lastValPercent = valPercent;
+ });
+ } else {
+ value = this.value();
+ valueMin = this._valueMin();
+ valueMax = this._valueMax();
+ valPercent = ( valueMax !== valueMin ) ?
+ ( value - valueMin ) / ( valueMax - valueMin ) * 100 :
+ 0;
+ _set[ this.orientation === "horizontal" ? "left" : "bottom" ] = valPercent + "%";
+ this.handle.stop( 1, 1 )[ animate ? "animate" : "css" ]( _set, o.animate );
+
+ if ( oRange === "min" && this.orientation === "horizontal" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { width: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "horizontal" ) {
+ this.range[ animate ? "animate" : "css" ]( { width: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ if ( oRange === "min" && this.orientation === "vertical" ) {
+ this.range.stop( 1, 1 )[ animate ? "animate" : "css" ]( { height: valPercent + "%" }, o.animate );
+ }
+ if ( oRange === "max" && this.orientation === "vertical" ) {
+ this.range[ animate ? "animate" : "css" ]( { height: ( 100 - valPercent ) + "%" }, { queue: false, duration: o.animate } );
+ }
+ }
+ },
+
+ _handleEvents: {
+ keydown: function( event ) {
+ var allowed, curVal, newVal, step,
+ index = $( event.target ).data( "ui-slider-handle-index" );
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ case $.ui.keyCode.END:
+ case $.ui.keyCode.PAGE_UP:
+ case $.ui.keyCode.PAGE_DOWN:
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ event.preventDefault();
+ if ( !this._keySliding ) {
+ this._keySliding = true;
+ $( event.target ).addClass( "ui-state-active" );
+ allowed = this._start( event, index );
+ if ( allowed === false ) {
+ return;
+ }
+ }
+ break;
+ }
+
+ step = this.options.step;
+ if ( this.options.values && this.options.values.length ) {
+ curVal = newVal = this.values( index );
+ } else {
+ curVal = newVal = this.value();
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.HOME:
+ newVal = this._valueMin();
+ break;
+ case $.ui.keyCode.END:
+ newVal = this._valueMax();
+ break;
+ case $.ui.keyCode.PAGE_UP:
+ newVal = this._trimAlignValue( curVal + ( (this._valueMax() - this._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.PAGE_DOWN:
+ newVal = this._trimAlignValue( curVal - ( (this._valueMax() - this._valueMin()) / numPages ) );
+ break;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.RIGHT:
+ if ( curVal === this._valueMax() ) {
+ return;
+ }
+ newVal = this._trimAlignValue( curVal + step );
+ break;
+ case $.ui.keyCode.DOWN:
+ case $.ui.keyCode.LEFT:
+ if ( curVal === this._valueMin() ) {
+ return;
+ }
+ newVal = this._trimAlignValue( curVal - step );
+ break;
+ }
+
+ this._slide( event, index, newVal );
+ },
+ click: function( event ) {
+ event.preventDefault();
+ },
+ keyup: function( event ) {
+ var index = $( event.target ).data( "ui-slider-handle-index" );
+
+ if ( this._keySliding ) {
+ this._keySliding = false;
+ this._stop( event, index );
+ this._change( event, index );
+ $( event.target ).removeClass( "ui-state-active" );
+ }
+ }
+ }
+
+});
+
+}(jQuery));
+(function( $, undefined ) {
+
+function isOverAxis( x, reference, size ) {
+ return ( x > reference ) && ( x < ( reference + size ) );
+}
+
+function isFloating(item) {
+ return (/left|right/).test(item.css("float")) || (/inline|table-cell/).test(item.css("display"));
+}
+
+$.widget("ui.sortable", $.ui.mouse, {
+ version: "1.10.4",
+ widgetEventPrefix: "sort",
+ ready: false,
+ options: {
+ appendTo: "parent",
+ axis: false,
+ connectWith: false,
+ containment: false,
+ cursor: "auto",
+ cursorAt: false,
+ dropOnEmpty: true,
+ forcePlaceholderSize: false,
+ forceHelperSize: false,
+ grid: false,
+ handle: false,
+ helper: "original",
+ items: "> *",
+ opacity: false,
+ placeholder: false,
+ revert: false,
+ scroll: true,
+ scrollSensitivity: 20,
+ scrollSpeed: 20,
+ scope: "default",
+ tolerance: "intersect",
+ zIndex: 1000,
+
+ // callbacks
+ activate: null,
+ beforeStop: null,
+ change: null,
+ deactivate: null,
+ out: null,
+ over: null,
+ receive: null,
+ remove: null,
+ sort: null,
+ start: null,
+ stop: null,
+ update: null
+ },
+ _create: function() {
+
+ var o = this.options;
+ this.containerCache = {};
+ this.element.addClass("ui-sortable");
+
+ //Get the items
+ this.refresh();
+
+ //Let's determine if the items are being displayed horizontally
+ this.floating = this.items.length ? o.axis === "x" || isFloating(this.items[0].item) : false;
+
+ //Let's determine the parent's offset
+ this.offset = this.element.offset();
+
+ //Initialize mouse events for interaction
+ this._mouseInit();
+
+ //We're ready to go
+ this.ready = true;
+
+ },
+
+ _destroy: function() {
+ this.element
+ .removeClass("ui-sortable ui-sortable-disabled");
+ this._mouseDestroy();
+
+ for ( var i = this.items.length - 1; i >= 0; i-- ) {
+ this.items[i].item.removeData(this.widgetName + "-item");
+ }
+
+ return this;
+ },
+
+ _setOption: function(key, value){
+ if ( key === "disabled" ) {
+ this.options[ key ] = value;
+
+ this.widget().toggleClass( "ui-sortable-disabled", !!value );
+ } else {
+ // Don't call widget base _setOption for disable as it adds ui-state-disabled class
+ $.Widget.prototype._setOption.apply(this, arguments);
+ }
+ },
+
+ _mouseCapture: function(event, overrideHandle) {
+ var currentItem = null,
+ validHandle = false,
+ that = this;
+
+ if (this.reverting) {
+ return false;
+ }
+
+ if(this.options.disabled || this.options.type === "static") {
+ return false;
+ }
+
+ //We have to refresh the items data once first
+ this._refreshItems(event);
+
+ //Find out if the clicked node (or one of its parents) is a actual item in this.items
+ $(event.target).parents().each(function() {
+ if($.data(this, that.widgetName + "-item") === that) {
+ currentItem = $(this);
+ return false;
+ }
+ });
+ if($.data(event.target, that.widgetName + "-item") === that) {
+ currentItem = $(event.target);
+ }
+
+ if(!currentItem) {
+ return false;
+ }
+ if(this.options.handle && !overrideHandle) {
+ $(this.options.handle, currentItem).find("*").addBack().each(function() {
+ if(this === event.target) {
+ validHandle = true;
+ }
+ });
+ if(!validHandle) {
+ return false;
+ }
+ }
+
+ this.currentItem = currentItem;
+ this._removeCurrentsFromItems();
+ return true;
+
+ },
+
+ _mouseStart: function(event, overrideHandle, noActivation) {
+
+ var i, body,
+ o = this.options;
+
+ this.currentContainer = this;
+
+ //We only need to call refreshPositions, because the refreshItems call has been moved to mouseCapture
+ this.refreshPositions();
+
+ //Create and append the visible helper
+ this.helper = this._createHelper(event);
+
+ //Cache the helper size
+ this._cacheHelperProportions();
+
+ /*
+ * - Position generation -
+ * This block generates everything position related - it's the core of draggables.
+ */
+
+ //Cache the margins of the original element
+ this._cacheMargins();
+
+ //Get the next scrolling parent
+ this.scrollParent = this.helper.scrollParent();
+
+ //The element's absolute position on the page minus margins
+ this.offset = this.currentItem.offset();
+ this.offset = {
+ top: this.offset.top - this.margins.top,
+ left: this.offset.left - this.margins.left
+ };
+
+ $.extend(this.offset, {
+ click: { //Where the click happened, relative to the element
+ left: event.pageX - this.offset.left,
+ top: event.pageY - this.offset.top
+ },
+ parent: this._getParentOffset(),
+ relative: this._getRelativeOffset() //This is a relative to absolute position minus the actual position calculation - only used for relative positioned helper
+ });
+
+ // Only after we got the offset, we can change the helper's position to absolute
+ // TODO: Still need to figure out a way to make relative sorting possible
+ this.helper.css("position", "absolute");
+ this.cssPosition = this.helper.css("position");
+
+ //Generate the original position
+ this.originalPosition = this._generatePosition(event);
+ this.originalPageX = event.pageX;
+ this.originalPageY = event.pageY;
+
+ //Adjust the mouse offset relative to the helper if "cursorAt" is supplied
+ (o.cursorAt && this._adjustOffsetFromHelper(o.cursorAt));
+
+ //Cache the former DOM position
+ this.domPosition = { prev: this.currentItem.prev()[0], parent: this.currentItem.parent()[0] };
+
+ //If the helper is not the original, hide the original so it's not playing any role during the drag, won't cause anything bad this way
+ if(this.helper[0] !== this.currentItem[0]) {
+ this.currentItem.hide();
+ }
+
+ //Create the placeholder
+ this._createPlaceholder();
+
+ //Set a containment if given in the options
+ if(o.containment) {
+ this._setContainment();
+ }
+
+ if( o.cursor && o.cursor !== "auto" ) { // cursor option
+ body = this.document.find( "body" );
+
+ // support: IE
+ this.storedCursor = body.css( "cursor" );
+ body.css( "cursor", o.cursor );
+
+ this.storedStylesheet = $( "" ).appendTo( body );
+ }
+
+ if(o.opacity) { // opacity option
+ if (this.helper.css("opacity")) {
+ this._storedOpacity = this.helper.css("opacity");
+ }
+ this.helper.css("opacity", o.opacity);
+ }
+
+ if(o.zIndex) { // zIndex option
+ if (this.helper.css("zIndex")) {
+ this._storedZIndex = this.helper.css("zIndex");
+ }
+ this.helper.css("zIndex", o.zIndex);
+ }
+
+ //Prepare scrolling
+ if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
+ this.overflowOffset = this.scrollParent.offset();
+ }
+
+ //Call callbacks
+ this._trigger("start", event, this._uiHash());
+
+ //Recache the helper size
+ if(!this._preserveHelperProportions) {
+ this._cacheHelperProportions();
+ }
+
+
+ //Post "activate" events to possible containers
+ if( !noActivation ) {
+ for ( i = this.containers.length - 1; i >= 0; i-- ) {
+ this.containers[ i ]._trigger( "activate", event, this._uiHash( this ) );
+ }
+ }
+
+ //Prepare possible droppables
+ if($.ui.ddmanager) {
+ $.ui.ddmanager.current = this;
+ }
+
+ if ($.ui.ddmanager && !o.dropBehaviour) {
+ $.ui.ddmanager.prepareOffsets(this, event);
+ }
+
+ this.dragging = true;
+
+ this.helper.addClass("ui-sortable-helper");
+ this._mouseDrag(event); //Execute the drag once - this causes the helper not to be visible before getting its correct position
+ return true;
+
+ },
+
+ _mouseDrag: function(event) {
+ var i, item, itemElement, intersection,
+ o = this.options,
+ scrolled = false;
+
+ //Compute the helpers position
+ this.position = this._generatePosition(event);
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ if (!this.lastPositionAbs) {
+ this.lastPositionAbs = this.positionAbs;
+ }
+
+ //Do scrolling
+ if(this.options.scroll) {
+ if(this.scrollParent[0] !== document && this.scrollParent[0].tagName !== "HTML") {
+
+ if((this.overflowOffset.top + this.scrollParent[0].offsetHeight) - event.pageY < o.scrollSensitivity) {
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop + o.scrollSpeed;
+ } else if(event.pageY - this.overflowOffset.top < o.scrollSensitivity) {
+ this.scrollParent[0].scrollTop = scrolled = this.scrollParent[0].scrollTop - o.scrollSpeed;
+ }
+
+ if((this.overflowOffset.left + this.scrollParent[0].offsetWidth) - event.pageX < o.scrollSensitivity) {
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft + o.scrollSpeed;
+ } else if(event.pageX - this.overflowOffset.left < o.scrollSensitivity) {
+ this.scrollParent[0].scrollLeft = scrolled = this.scrollParent[0].scrollLeft - o.scrollSpeed;
+ }
+
+ } else {
+
+ if(event.pageY - $(document).scrollTop() < o.scrollSensitivity) {
+ scrolled = $(document).scrollTop($(document).scrollTop() - o.scrollSpeed);
+ } else if($(window).height() - (event.pageY - $(document).scrollTop()) < o.scrollSensitivity) {
+ scrolled = $(document).scrollTop($(document).scrollTop() + o.scrollSpeed);
+ }
+
+ if(event.pageX - $(document).scrollLeft() < o.scrollSensitivity) {
+ scrolled = $(document).scrollLeft($(document).scrollLeft() - o.scrollSpeed);
+ } else if($(window).width() - (event.pageX - $(document).scrollLeft()) < o.scrollSensitivity) {
+ scrolled = $(document).scrollLeft($(document).scrollLeft() + o.scrollSpeed);
+ }
+
+ }
+
+ if(scrolled !== false && $.ui.ddmanager && !o.dropBehaviour) {
+ $.ui.ddmanager.prepareOffsets(this, event);
+ }
+ }
+
+ //Regenerate the absolute position used for position checks
+ this.positionAbs = this._convertPositionTo("absolute");
+
+ //Set the helper position
+ if(!this.options.axis || this.options.axis !== "y") {
+ this.helper[0].style.left = this.position.left+"px";
+ }
+ if(!this.options.axis || this.options.axis !== "x") {
+ this.helper[0].style.top = this.position.top+"px";
+ }
+
+ //Rearrange
+ for (i = this.items.length - 1; i >= 0; i--) {
+
+ //Cache variables and intersection, continue if no intersection
+ item = this.items[i];
+ itemElement = item.item[0];
+ intersection = this._intersectsWithPointer(item);
+ if (!intersection) {
+ continue;
+ }
+
+ // Only put the placeholder inside the current Container, skip all
+ // items from other containers. This works because when moving
+ // an item from one container to another the
+ // currentContainer is switched before the placeholder is moved.
+ //
+ // Without this, moving items in "sub-sortables" can cause
+ // the placeholder to jitter beetween the outer and inner container.
+ if (item.instance !== this.currentContainer) {
+ continue;
+ }
+
+ // cannot intersect with itself
+ // no useless actions that have been done before
+ // no action if the item moved is the parent of the item checked
+ if (itemElement !== this.currentItem[0] &&
+ this.placeholder[intersection === 1 ? "next" : "prev"]()[0] !== itemElement &&
+ !$.contains(this.placeholder[0], itemElement) &&
+ (this.options.type === "semi-dynamic" ? !$.contains(this.element[0], itemElement) : true)
+ ) {
+
+ this.direction = intersection === 1 ? "down" : "up";
+
+ if (this.options.tolerance === "pointer" || this._intersectsWithSides(item)) {
+ this._rearrange(event, item);
+ } else {
+ break;
+ }
+
+ this._trigger("change", event, this._uiHash());
+ break;
+ }
+ }
+
+ //Post events to containers
+ this._contactContainers(event);
+
+ //Interconnect with droppables
+ if($.ui.ddmanager) {
+ $.ui.ddmanager.drag(this, event);
+ }
+
+ //Call callbacks
+ this._trigger("sort", event, this._uiHash());
+
+ this.lastPositionAbs = this.positionAbs;
+ return false;
+
+ },
+
+ _mouseStop: function(event, noPropagation) {
+
+ if(!event) {
+ return;
+ }
+
+ //If we are using droppables, inform the manager about the drop
+ if ($.ui.ddmanager && !this.options.dropBehaviour) {
+ $.ui.ddmanager.drop(this, event);
+ }
+
+ if(this.options.revert) {
+ var that = this,
+ cur = this.placeholder.offset(),
+ axis = this.options.axis,
+ animation = {};
+
+ if ( !axis || axis === "x" ) {
+ animation.left = cur.left - this.offset.parent.left - this.margins.left + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollLeft);
+ }
+ if ( !axis || axis === "y" ) {
+ animation.top = cur.top - this.offset.parent.top - this.margins.top + (this.offsetParent[0] === document.body ? 0 : this.offsetParent[0].scrollTop);
+ }
+ this.reverting = true;
+ $(this.helper).animate( animation, parseInt(this.options.revert, 10) || 500, function() {
+ that._clear(event);
+ });
+ } else {
+ this._clear(event, noPropagation);
+ }
+
+ return false;
+
+ },
+
+ cancel: function() {
+
+ if(this.dragging) {
+
+ this._mouseUp({ target: null });
+
+ if(this.options.helper === "original") {
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ } else {
+ this.currentItem.show();
+ }
+
+ //Post deactivating events to containers
+ for (var i = this.containers.length - 1; i >= 0; i--){
+ this.containers[i]._trigger("deactivate", null, this._uiHash(this));
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", null, this._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ if (this.placeholder) {
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ if(this.placeholder[0].parentNode) {
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+ }
+ if(this.options.helper !== "original" && this.helper && this.helper[0].parentNode) {
+ this.helper.remove();
+ }
+
+ $.extend(this, {
+ helper: null,
+ dragging: false,
+ reverting: false,
+ _noFinalSort: null
+ });
+
+ if(this.domPosition.prev) {
+ $(this.domPosition.prev).after(this.currentItem);
+ } else {
+ $(this.domPosition.parent).prepend(this.currentItem);
+ }
+ }
+
+ return this;
+
+ },
+
+ serialize: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected),
+ str = [];
+ o = o || {};
+
+ $(items).each(function() {
+ var res = ($(o.item || this).attr(o.attribute || "id") || "").match(o.expression || (/(.+)[\-=_](.+)/));
+ if (res) {
+ str.push((o.key || res[1]+"[]")+"="+(o.key && o.expression ? res[1] : res[2]));
+ }
+ });
+
+ if(!str.length && o.key) {
+ str.push(o.key + "=");
+ }
+
+ return str.join("&");
+
+ },
+
+ toArray: function(o) {
+
+ var items = this._getItemsAsjQuery(o && o.connected),
+ ret = [];
+
+ o = o || {};
+
+ items.each(function() { ret.push($(o.item || this).attr(o.attribute || "id") || ""); });
+ return ret;
+
+ },
+
+ /* Be careful with the following core functions */
+ _intersectsWith: function(item) {
+
+ var x1 = this.positionAbs.left,
+ x2 = x1 + this.helperProportions.width,
+ y1 = this.positionAbs.top,
+ y2 = y1 + this.helperProportions.height,
+ l = item.left,
+ r = l + item.width,
+ t = item.top,
+ b = t + item.height,
+ dyClick = this.offset.click.top,
+ dxClick = this.offset.click.left,
+ isOverElementHeight = ( this.options.axis === "x" ) || ( ( y1 + dyClick ) > t && ( y1 + dyClick ) < b ),
+ isOverElementWidth = ( this.options.axis === "y" ) || ( ( x1 + dxClick ) > l && ( x1 + dxClick ) < r ),
+ isOverElement = isOverElementHeight && isOverElementWidth;
+
+ if ( this.options.tolerance === "pointer" ||
+ this.options.forcePointerForContainers ||
+ (this.options.tolerance !== "pointer" && this.helperProportions[this.floating ? "width" : "height"] > item[this.floating ? "width" : "height"])
+ ) {
+ return isOverElement;
+ } else {
+
+ return (l < x1 + (this.helperProportions.width / 2) && // Right Half
+ x2 - (this.helperProportions.width / 2) < r && // Left Half
+ t < y1 + (this.helperProportions.height / 2) && // Bottom Half
+ y2 - (this.helperProportions.height / 2) < b ); // Top Half
+
+ }
+ },
+
+ _intersectsWithPointer: function(item) {
+
+ var isOverElementHeight = (this.options.axis === "x") || isOverAxis(this.positionAbs.top + this.offset.click.top, item.top, item.height),
+ isOverElementWidth = (this.options.axis === "y") || isOverAxis(this.positionAbs.left + this.offset.click.left, item.left, item.width),
+ isOverElement = isOverElementHeight && isOverElementWidth,
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (!isOverElement) {
+ return false;
+ }
+
+ return this.floating ?
+ ( ((horizontalDirection && horizontalDirection === "right") || verticalDirection === "down") ? 2 : 1 )
+ : ( verticalDirection && (verticalDirection === "down" ? 2 : 1) );
+
+ },
+
+ _intersectsWithSides: function(item) {
+
+ var isOverBottomHalf = isOverAxis(this.positionAbs.top + this.offset.click.top, item.top + (item.height/2), item.height),
+ isOverRightHalf = isOverAxis(this.positionAbs.left + this.offset.click.left, item.left + (item.width/2), item.width),
+ verticalDirection = this._getDragVerticalDirection(),
+ horizontalDirection = this._getDragHorizontalDirection();
+
+ if (this.floating && horizontalDirection) {
+ return ((horizontalDirection === "right" && isOverRightHalf) || (horizontalDirection === "left" && !isOverRightHalf));
+ } else {
+ return verticalDirection && ((verticalDirection === "down" && isOverBottomHalf) || (verticalDirection === "up" && !isOverBottomHalf));
+ }
+
+ },
+
+ _getDragVerticalDirection: function() {
+ var delta = this.positionAbs.top - this.lastPositionAbs.top;
+ return delta !== 0 && (delta > 0 ? "down" : "up");
+ },
+
+ _getDragHorizontalDirection: function() {
+ var delta = this.positionAbs.left - this.lastPositionAbs.left;
+ return delta !== 0 && (delta > 0 ? "right" : "left");
+ },
+
+ refresh: function(event) {
+ this._refreshItems(event);
+ this.refreshPositions();
+ return this;
+ },
+
+ _connectWith: function() {
+ var options = this.options;
+ return options.connectWith.constructor === String ? [options.connectWith] : options.connectWith;
+ },
+
+ _getItemsAsjQuery: function(connected) {
+
+ var i, j, cur, inst,
+ items = [],
+ queries = [],
+ connectWith = this._connectWith();
+
+ if(connectWith && connected) {
+ for (i = connectWith.length - 1; i >= 0; i--){
+ cur = $(connectWith[i]);
+ for ( j = cur.length - 1; j >= 0; j--){
+ inst = $.data(cur[j], this.widgetFullName);
+ if(inst && inst !== this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element) : $(inst.options.items, inst.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), inst]);
+ }
+ }
+ }
+ }
+
+ queries.push([$.isFunction(this.options.items) ? this.options.items.call(this.element, null, { options: this.options, item: this.currentItem }) : $(this.options.items, this.element).not(".ui-sortable-helper").not(".ui-sortable-placeholder"), this]);
+
+ function addItems() {
+ items.push( this );
+ }
+ for (i = queries.length - 1; i >= 0; i--){
+ queries[i][0].each( addItems );
+ }
+
+ return $(items);
+
+ },
+
+ _removeCurrentsFromItems: function() {
+
+ var list = this.currentItem.find(":data(" + this.widgetName + "-item)");
+
+ this.items = $.grep(this.items, function (item) {
+ for (var j=0; j < list.length; j++) {
+ if(list[j] === item.item[0]) {
+ return false;
+ }
+ }
+ return true;
+ });
+
+ },
+
+ _refreshItems: function(event) {
+
+ this.items = [];
+ this.containers = [this];
+
+ var i, j, cur, inst, targetData, _queries, item, queriesLength,
+ items = this.items,
+ queries = [[$.isFunction(this.options.items) ? this.options.items.call(this.element[0], event, { item: this.currentItem }) : $(this.options.items, this.element), this]],
+ connectWith = this._connectWith();
+
+ if(connectWith && this.ready) { //Shouldn't be run the first time through due to massive slow-down
+ for (i = connectWith.length - 1; i >= 0; i--){
+ cur = $(connectWith[i]);
+ for (j = cur.length - 1; j >= 0; j--){
+ inst = $.data(cur[j], this.widgetFullName);
+ if(inst && inst !== this && !inst.options.disabled) {
+ queries.push([$.isFunction(inst.options.items) ? inst.options.items.call(inst.element[0], event, { item: this.currentItem }) : $(inst.options.items, inst.element), inst]);
+ this.containers.push(inst);
+ }
+ }
+ }
+ }
+
+ for (i = queries.length - 1; i >= 0; i--) {
+ targetData = queries[i][1];
+ _queries = queries[i][0];
+
+ for (j=0, queriesLength = _queries.length; j < queriesLength; j++) {
+ item = $(_queries[j]);
+
+ item.data(this.widgetName + "-item", targetData); // Data for target checking (mouse manager)
+
+ items.push({
+ item: item,
+ instance: targetData,
+ width: 0, height: 0,
+ left: 0, top: 0
+ });
+ }
+ }
+
+ },
+
+ refreshPositions: function(fast) {
+
+ //This has to be redone because due to the item being moved out/into the offsetParent, the offsetParent's position will change
+ if(this.offsetParent && this.helper) {
+ this.offset.parent = this._getParentOffset();
+ }
+
+ var i, item, t, p;
+
+ for (i = this.items.length - 1; i >= 0; i--){
+ item = this.items[i];
+
+ //We ignore calculating positions of all connected containers when we're not over them
+ if(item.instance !== this.currentContainer && this.currentContainer && item.item[0] !== this.currentItem[0]) {
+ continue;
+ }
+
+ t = this.options.toleranceElement ? $(this.options.toleranceElement, item.item) : item.item;
+
+ if (!fast) {
+ item.width = t.outerWidth();
+ item.height = t.outerHeight();
+ }
+
+ p = t.offset();
+ item.left = p.left;
+ item.top = p.top;
+ }
+
+ if(this.options.custom && this.options.custom.refreshContainers) {
+ this.options.custom.refreshContainers.call(this);
+ } else {
+ for (i = this.containers.length - 1; i >= 0; i--){
+ p = this.containers[i].element.offset();
+ this.containers[i].containerCache.left = p.left;
+ this.containers[i].containerCache.top = p.top;
+ this.containers[i].containerCache.width = this.containers[i].element.outerWidth();
+ this.containers[i].containerCache.height = this.containers[i].element.outerHeight();
+ }
+ }
+
+ return this;
+ },
+
+ _createPlaceholder: function(that) {
+ that = that || this;
+ var className,
+ o = that.options;
+
+ if(!o.placeholder || o.placeholder.constructor === String) {
+ className = o.placeholder;
+ o.placeholder = {
+ element: function() {
+
+ var nodeName = that.currentItem[0].nodeName.toLowerCase(),
+ element = $( "<" + nodeName + ">", that.document[0] )
+ .addClass(className || that.currentItem[0].className+" ui-sortable-placeholder")
+ .removeClass("ui-sortable-helper");
+
+ if ( nodeName === "tr" ) {
+ that.currentItem.children().each(function() {
+ $( " | ", that.document[0] )
+ .attr( "colspan", $( this ).attr( "colspan" ) || 1 )
+ .appendTo( element );
+ });
+ } else if ( nodeName === "img" ) {
+ element.attr( "src", that.currentItem.attr( "src" ) );
+ }
+
+ if ( !className ) {
+ element.css( "visibility", "hidden" );
+ }
+
+ return element;
+ },
+ update: function(container, p) {
+
+ // 1. If a className is set as 'placeholder option, we don't force sizes - the class is responsible for that
+ // 2. The option 'forcePlaceholderSize can be enabled to force it even if a class name is specified
+ if(className && !o.forcePlaceholderSize) {
+ return;
+ }
+
+ //If the element doesn't have a actual height by itself (without styles coming from a stylesheet), it receives the inline height from the dragged item
+ if(!p.height()) { p.height(that.currentItem.innerHeight() - parseInt(that.currentItem.css("paddingTop")||0, 10) - parseInt(that.currentItem.css("paddingBottom")||0, 10)); }
+ if(!p.width()) { p.width(that.currentItem.innerWidth() - parseInt(that.currentItem.css("paddingLeft")||0, 10) - parseInt(that.currentItem.css("paddingRight")||0, 10)); }
+ }
+ };
+ }
+
+ //Create the placeholder
+ that.placeholder = $(o.placeholder.element.call(that.element, that.currentItem));
+
+ //Append it after the actual current item
+ that.currentItem.after(that.placeholder);
+
+ //Update the size of the placeholder (TODO: Logic to fuzzy, see line 316/317)
+ o.placeholder.update(that, that.placeholder);
+
+ },
+
+ _contactContainers: function(event) {
+ var i, j, dist, itemWithLeastDistance, posProperty, sizeProperty, base, cur, nearBottom, floating,
+ innermostContainer = null,
+ innermostIndex = null;
+
+ // get innermost container that intersects with item
+ for (i = this.containers.length - 1; i >= 0; i--) {
+
+ // never consider a container that's located within the item itself
+ if($.contains(this.currentItem[0], this.containers[i].element[0])) {
+ continue;
+ }
+
+ if(this._intersectsWith(this.containers[i].containerCache)) {
+
+ // if we've already found a container and it's more "inner" than this, then continue
+ if(innermostContainer && $.contains(this.containers[i].element[0], innermostContainer.element[0])) {
+ continue;
+ }
+
+ innermostContainer = this.containers[i];
+ innermostIndex = i;
+
+ } else {
+ // container doesn't intersect. trigger "out" event if necessary
+ if(this.containers[i].containerCache.over) {
+ this.containers[i]._trigger("out", event, this._uiHash(this));
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ }
+
+ // if no intersecting containers found, return
+ if(!innermostContainer) {
+ return;
+ }
+
+ // move the item into the container if it's not there already
+ if(this.containers.length === 1) {
+ if (!this.containers[innermostIndex].containerCache.over) {
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ }
+ } else {
+
+ //When entering a new container, we will find the item with the least distance and append our item near it
+ dist = 10000;
+ itemWithLeastDistance = null;
+ floating = innermostContainer.floating || isFloating(this.currentItem);
+ posProperty = floating ? "left" : "top";
+ sizeProperty = floating ? "width" : "height";
+ base = this.positionAbs[posProperty] + this.offset.click[posProperty];
+ for (j = this.items.length - 1; j >= 0; j--) {
+ if(!$.contains(this.containers[innermostIndex].element[0], this.items[j].item[0])) {
+ continue;
+ }
+ if(this.items[j].item[0] === this.currentItem[0]) {
+ continue;
+ }
+ if (floating && !isOverAxis(this.positionAbs.top + this.offset.click.top, this.items[j].top, this.items[j].height)) {
+ continue;
+ }
+ cur = this.items[j].item.offset()[posProperty];
+ nearBottom = false;
+ if(Math.abs(cur - base) > Math.abs(cur + this.items[j][sizeProperty] - base)){
+ nearBottom = true;
+ cur += this.items[j][sizeProperty];
+ }
+
+ if(Math.abs(cur - base) < dist) {
+ dist = Math.abs(cur - base); itemWithLeastDistance = this.items[j];
+ this.direction = nearBottom ? "up": "down";
+ }
+ }
+
+ //Check if dropOnEmpty is enabled
+ if(!itemWithLeastDistance && !this.options.dropOnEmpty) {
+ return;
+ }
+
+ if(this.currentContainer === this.containers[innermostIndex]) {
+ return;
+ }
+
+ itemWithLeastDistance ? this._rearrange(event, itemWithLeastDistance, null, true) : this._rearrange(event, null, this.containers[innermostIndex].element, true);
+ this._trigger("change", event, this._uiHash());
+ this.containers[innermostIndex]._trigger("change", event, this._uiHash(this));
+ this.currentContainer = this.containers[innermostIndex];
+
+ //Update the placeholder
+ this.options.placeholder.update(this.currentContainer, this.placeholder);
+
+ this.containers[innermostIndex]._trigger("over", event, this._uiHash(this));
+ this.containers[innermostIndex].containerCache.over = 1;
+ }
+
+
+ },
+
+ _createHelper: function(event) {
+
+ var o = this.options,
+ helper = $.isFunction(o.helper) ? $(o.helper.apply(this.element[0], [event, this.currentItem])) : (o.helper === "clone" ? this.currentItem.clone() : this.currentItem);
+
+ //Add the helper to the DOM if that didn't happen already
+ if(!helper.parents("body").length) {
+ $(o.appendTo !== "parent" ? o.appendTo : this.currentItem[0].parentNode)[0].appendChild(helper[0]);
+ }
+
+ if(helper[0] === this.currentItem[0]) {
+ this._storedCSS = { width: this.currentItem[0].style.width, height: this.currentItem[0].style.height, position: this.currentItem.css("position"), top: this.currentItem.css("top"), left: this.currentItem.css("left") };
+ }
+
+ if(!helper[0].style.width || o.forceHelperSize) {
+ helper.width(this.currentItem.width());
+ }
+ if(!helper[0].style.height || o.forceHelperSize) {
+ helper.height(this.currentItem.height());
+ }
+
+ return helper;
+
+ },
+
+ _adjustOffsetFromHelper: function(obj) {
+ if (typeof obj === "string") {
+ obj = obj.split(" ");
+ }
+ if ($.isArray(obj)) {
+ obj = {left: +obj[0], top: +obj[1] || 0};
+ }
+ if ("left" in obj) {
+ this.offset.click.left = obj.left + this.margins.left;
+ }
+ if ("right" in obj) {
+ this.offset.click.left = this.helperProportions.width - obj.right + this.margins.left;
+ }
+ if ("top" in obj) {
+ this.offset.click.top = obj.top + this.margins.top;
+ }
+ if ("bottom" in obj) {
+ this.offset.click.top = this.helperProportions.height - obj.bottom + this.margins.top;
+ }
+ },
+
+ _getParentOffset: function() {
+
+
+ //Get the offsetParent and cache its position
+ this.offsetParent = this.helper.offsetParent();
+ var po = this.offsetParent.offset();
+
+ // This is a special case where we need to modify a offset calculated on start, since the following happened:
+ // 1. The position of the helper is absolute, so it's position is calculated based on the next positioned parent
+ // 2. The actual offset parent is a child of the scroll parent, and the scroll parent isn't the document, which means that
+ // the scroll is included in the initial calculation of the offset of the parent, and never recalculated upon drag
+ if(this.cssPosition === "absolute" && this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) {
+ po.left += this.scrollParent.scrollLeft();
+ po.top += this.scrollParent.scrollTop();
+ }
+
+ // This needs to be actually done for all browsers, since pageX/pageY includes this information
+ // with an ugly IE fix
+ if( this.offsetParent[0] === document.body || (this.offsetParent[0].tagName && this.offsetParent[0].tagName.toLowerCase() === "html" && $.ui.ie)) {
+ po = { top: 0, left: 0 };
+ }
+
+ return {
+ top: po.top + (parseInt(this.offsetParent.css("borderTopWidth"),10) || 0),
+ left: po.left + (parseInt(this.offsetParent.css("borderLeftWidth"),10) || 0)
+ };
+
+ },
+
+ _getRelativeOffset: function() {
+
+ if(this.cssPosition === "relative") {
+ var p = this.currentItem.position();
+ return {
+ top: p.top - (parseInt(this.helper.css("top"),10) || 0) + this.scrollParent.scrollTop(),
+ left: p.left - (parseInt(this.helper.css("left"),10) || 0) + this.scrollParent.scrollLeft()
+ };
+ } else {
+ return { top: 0, left: 0 };
+ }
+
+ },
+
+ _cacheMargins: function() {
+ this.margins = {
+ left: (parseInt(this.currentItem.css("marginLeft"),10) || 0),
+ top: (parseInt(this.currentItem.css("marginTop"),10) || 0)
+ };
+ },
+
+ _cacheHelperProportions: function() {
+ this.helperProportions = {
+ width: this.helper.outerWidth(),
+ height: this.helper.outerHeight()
+ };
+ },
+
+ _setContainment: function() {
+
+ var ce, co, over,
+ o = this.options;
+ if(o.containment === "parent") {
+ o.containment = this.helper[0].parentNode;
+ }
+ if(o.containment === "document" || o.containment === "window") {
+ this.containment = [
+ 0 - this.offset.relative.left - this.offset.parent.left,
+ 0 - this.offset.relative.top - this.offset.parent.top,
+ $(o.containment === "document" ? document : window).width() - this.helperProportions.width - this.margins.left,
+ ($(o.containment === "document" ? document : window).height() || document.body.parentNode.scrollHeight) - this.helperProportions.height - this.margins.top
+ ];
+ }
+
+ if(!(/^(document|window|parent)$/).test(o.containment)) {
+ ce = $(o.containment)[0];
+ co = $(o.containment).offset();
+ over = ($(ce).css("overflow") !== "hidden");
+
+ this.containment = [
+ co.left + (parseInt($(ce).css("borderLeftWidth"),10) || 0) + (parseInt($(ce).css("paddingLeft"),10) || 0) - this.margins.left,
+ co.top + (parseInt($(ce).css("borderTopWidth"),10) || 0) + (parseInt($(ce).css("paddingTop"),10) || 0) - this.margins.top,
+ co.left+(over ? Math.max(ce.scrollWidth,ce.offsetWidth) : ce.offsetWidth) - (parseInt($(ce).css("borderLeftWidth"),10) || 0) - (parseInt($(ce).css("paddingRight"),10) || 0) - this.helperProportions.width - this.margins.left,
+ co.top+(over ? Math.max(ce.scrollHeight,ce.offsetHeight) : ce.offsetHeight) - (parseInt($(ce).css("borderTopWidth"),10) || 0) - (parseInt($(ce).css("paddingBottom"),10) || 0) - this.helperProportions.height - this.margins.top
+ ];
+ }
+
+ },
+
+ _convertPositionTo: function(d, pos) {
+
+ if(!pos) {
+ pos = this.position;
+ }
+ var mod = d === "absolute" ? 1 : -1,
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent,
+ scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ return {
+ top: (
+ pos.top + // The absolute mouse position
+ this.offset.relative.top * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top * mod - // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ) * mod)
+ ),
+ left: (
+ pos.left + // The absolute mouse position
+ this.offset.relative.left * mod + // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left * mod - // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ) * mod)
+ )
+ };
+
+ },
+
+ _generatePosition: function(event) {
+
+ var top, left,
+ o = this.options,
+ pageX = event.pageX,
+ pageY = event.pageY,
+ scroll = this.cssPosition === "absolute" && !(this.scrollParent[0] !== document && $.contains(this.scrollParent[0], this.offsetParent[0])) ? this.offsetParent : this.scrollParent, scrollIsRootNode = (/(html|body)/i).test(scroll[0].tagName);
+
+ // This is another very weird special case that only happens for relative elements:
+ // 1. If the css position is relative
+ // 2. and the scroll parent is the document or similar to the offset parent
+ // we have to refresh the relative offset during the scroll so there are no jumps
+ if(this.cssPosition === "relative" && !(this.scrollParent[0] !== document && this.scrollParent[0] !== this.offsetParent[0])) {
+ this.offset.relative = this._getRelativeOffset();
+ }
+
+ /*
+ * - Position constraining -
+ * Constrain the position to a mix of grid, containment.
+ */
+
+ if(this.originalPosition) { //If we are not dragging yet, we won't check for options
+
+ if(this.containment) {
+ if(event.pageX - this.offset.click.left < this.containment[0]) {
+ pageX = this.containment[0] + this.offset.click.left;
+ }
+ if(event.pageY - this.offset.click.top < this.containment[1]) {
+ pageY = this.containment[1] + this.offset.click.top;
+ }
+ if(event.pageX - this.offset.click.left > this.containment[2]) {
+ pageX = this.containment[2] + this.offset.click.left;
+ }
+ if(event.pageY - this.offset.click.top > this.containment[3]) {
+ pageY = this.containment[3] + this.offset.click.top;
+ }
+ }
+
+ if(o.grid) {
+ top = this.originalPageY + Math.round((pageY - this.originalPageY) / o.grid[1]) * o.grid[1];
+ pageY = this.containment ? ( (top - this.offset.click.top >= this.containment[1] && top - this.offset.click.top <= this.containment[3]) ? top : ((top - this.offset.click.top >= this.containment[1]) ? top - o.grid[1] : top + o.grid[1])) : top;
+
+ left = this.originalPageX + Math.round((pageX - this.originalPageX) / o.grid[0]) * o.grid[0];
+ pageX = this.containment ? ( (left - this.offset.click.left >= this.containment[0] && left - this.offset.click.left <= this.containment[2]) ? left : ((left - this.offset.click.left >= this.containment[0]) ? left - o.grid[0] : left + o.grid[0])) : left;
+ }
+
+ }
+
+ return {
+ top: (
+ pageY - // The absolute mouse position
+ this.offset.click.top - // Click offset (relative to the element)
+ this.offset.relative.top - // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.top + // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollTop() : ( scrollIsRootNode ? 0 : scroll.scrollTop() ) ))
+ ),
+ left: (
+ pageX - // The absolute mouse position
+ this.offset.click.left - // Click offset (relative to the element)
+ this.offset.relative.left - // Only for relative positioned nodes: Relative offset from element to offset parent
+ this.offset.parent.left + // The offsetParent's offset without borders (offset + border)
+ ( ( this.cssPosition === "fixed" ? -this.scrollParent.scrollLeft() : scrollIsRootNode ? 0 : scroll.scrollLeft() ))
+ )
+ };
+
+ },
+
+ _rearrange: function(event, i, a, hardRefresh) {
+
+ a ? a[0].appendChild(this.placeholder[0]) : i.item[0].parentNode.insertBefore(this.placeholder[0], (this.direction === "down" ? i.item[0] : i.item[0].nextSibling));
+
+ //Various things done here to improve the performance:
+ // 1. we create a setTimeout, that calls refreshPositions
+ // 2. on the instance, we have a counter variable, that get's higher after every append
+ // 3. on the local scope, we copy the counter variable, and check in the timeout, if it's still the same
+ // 4. this lets only the last addition to the timeout stack through
+ this.counter = this.counter ? ++this.counter : 1;
+ var counter = this.counter;
+
+ this._delay(function() {
+ if(counter === this.counter) {
+ this.refreshPositions(!hardRefresh); //Precompute after each DOM insertion, NOT on mousemove
+ }
+ });
+
+ },
+
+ _clear: function(event, noPropagation) {
+
+ this.reverting = false;
+ // We delay all events that have to be triggered to after the point where the placeholder has been removed and
+ // everything else normalized again
+ var i,
+ delayedTriggers = [];
+
+ // We first have to update the dom position of the actual currentItem
+ // Note: don't do it if the current item is already removed (by a user), or it gets reappended (see #4088)
+ if(!this._noFinalSort && this.currentItem.parent().length) {
+ this.placeholder.before(this.currentItem);
+ }
+ this._noFinalSort = null;
+
+ if(this.helper[0] === this.currentItem[0]) {
+ for(i in this._storedCSS) {
+ if(this._storedCSS[i] === "auto" || this._storedCSS[i] === "static") {
+ this._storedCSS[i] = "";
+ }
+ }
+ this.currentItem.css(this._storedCSS).removeClass("ui-sortable-helper");
+ } else {
+ this.currentItem.show();
+ }
+
+ if(this.fromOutside && !noPropagation) {
+ delayedTriggers.push(function(event) { this._trigger("receive", event, this._uiHash(this.fromOutside)); });
+ }
+ if((this.fromOutside || this.domPosition.prev !== this.currentItem.prev().not(".ui-sortable-helper")[0] || this.domPosition.parent !== this.currentItem.parent()[0]) && !noPropagation) {
+ delayedTriggers.push(function(event) { this._trigger("update", event, this._uiHash()); }); //Trigger update callback if the DOM position has changed
+ }
+
+ // Check if the items Container has Changed and trigger appropriate
+ // events.
+ if (this !== this.currentContainer) {
+ if(!noPropagation) {
+ delayedTriggers.push(function(event) { this._trigger("remove", event, this._uiHash()); });
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("receive", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
+ delayedTriggers.push((function(c) { return function(event) { c._trigger("update", event, this._uiHash(this)); }; }).call(this, this.currentContainer));
+ }
+ }
+
+
+ //Post events to containers
+ function delayEvent( type, instance, container ) {
+ return function( event ) {
+ container._trigger( type, event, instance._uiHash( instance ) );
+ };
+ }
+ for (i = this.containers.length - 1; i >= 0; i--){
+ if (!noPropagation) {
+ delayedTriggers.push( delayEvent( "deactivate", this, this.containers[ i ] ) );
+ }
+ if(this.containers[i].containerCache.over) {
+ delayedTriggers.push( delayEvent( "out", this, this.containers[ i ] ) );
+ this.containers[i].containerCache.over = 0;
+ }
+ }
+
+ //Do what was originally in plugins
+ if ( this.storedCursor ) {
+ this.document.find( "body" ).css( "cursor", this.storedCursor );
+ this.storedStylesheet.remove();
+ }
+ if(this._storedOpacity) {
+ this.helper.css("opacity", this._storedOpacity);
+ }
+ if(this._storedZIndex) {
+ this.helper.css("zIndex", this._storedZIndex === "auto" ? "" : this._storedZIndex);
+ }
+
+ this.dragging = false;
+ if(this.cancelHelperRemoval) {
+ if(!noPropagation) {
+ this._trigger("beforeStop", event, this._uiHash());
+ for (i=0; i < delayedTriggers.length; i++) {
+ delayedTriggers[i].call(this, event);
+ } //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+
+ this.fromOutside = false;
+ return false;
+ }
+
+ if(!noPropagation) {
+ this._trigger("beforeStop", event, this._uiHash());
+ }
+
+ //$(this.placeholder[0]).remove(); would have been the jQuery way - unfortunately, it unbinds ALL events from the original node!
+ this.placeholder[0].parentNode.removeChild(this.placeholder[0]);
+
+ if(this.helper[0] !== this.currentItem[0]) {
+ this.helper.remove();
+ }
+ this.helper = null;
+
+ if(!noPropagation) {
+ for (i=0; i < delayedTriggers.length; i++) {
+ delayedTriggers[i].call(this, event);
+ } //Trigger all delayed events
+ this._trigger("stop", event, this._uiHash());
+ }
+
+ this.fromOutside = false;
+ return true;
+
+ },
+
+ _trigger: function() {
+ if ($.Widget.prototype._trigger.apply(this, arguments) === false) {
+ this.cancel();
+ }
+ },
+
+ _uiHash: function(_inst) {
+ var inst = _inst || this;
+ return {
+ helper: inst.helper,
+ placeholder: inst.placeholder || $([]),
+ position: inst.position,
+ originalPosition: inst.originalPosition,
+ offset: inst.positionAbs,
+ item: inst.currentItem,
+ sender: _inst ? _inst.element : null
+ };
+ }
+
+});
+
+})(jQuery);
+(function( $ ) {
+
+function modifier( fn ) {
+ return function() {
+ var previous = this.element.val();
+ fn.apply( this, arguments );
+ this._refresh();
+ if ( previous !== this.element.val() ) {
+ this._trigger( "change" );
+ }
+ };
+}
+
+$.widget( "ui.spinner", {
+ version: "1.10.4",
+ defaultElement: "",
+ widgetEventPrefix: "spin",
+ options: {
+ culture: null,
+ icons: {
+ down: "ui-icon-triangle-1-s",
+ up: "ui-icon-triangle-1-n"
+ },
+ incremental: true,
+ max: null,
+ min: null,
+ numberFormat: null,
+ page: 10,
+ step: 1,
+
+ change: null,
+ spin: null,
+ start: null,
+ stop: null
+ },
+
+ _create: function() {
+ // handle string values that need to be parsed
+ this._setOption( "max", this.options.max );
+ this._setOption( "min", this.options.min );
+ this._setOption( "step", this.options.step );
+
+ // Only format if there is a value, prevents the field from being marked
+ // as invalid in Firefox, see #9573.
+ if ( this.value() !== "" ) {
+ // Format the value, but don't constrain.
+ this._value( this.element.val(), true );
+ }
+
+ this._draw();
+ this._on( this._events );
+ this._refresh();
+
+ // turning off autocomplete prevents the browser from remembering the
+ // value when navigating through history, so we re-enable autocomplete
+ // if the page is unloaded before the widget is destroyed. #7790
+ this._on( this.window, {
+ beforeunload: function() {
+ this.element.removeAttr( "autocomplete" );
+ }
+ });
+ },
+
+ _getCreateOptions: function() {
+ var options = {},
+ element = this.element;
+
+ $.each( [ "min", "max", "step" ], function( i, option ) {
+ var value = element.attr( option );
+ if ( value !== undefined && value.length ) {
+ options[ option ] = value;
+ }
+ });
+
+ return options;
+ },
+
+ _events: {
+ keydown: function( event ) {
+ if ( this._start( event ) && this._keydown( event ) ) {
+ event.preventDefault();
+ }
+ },
+ keyup: "_stop",
+ focus: function() {
+ this.previous = this.element.val();
+ },
+ blur: function( event ) {
+ if ( this.cancelBlur ) {
+ delete this.cancelBlur;
+ return;
+ }
+
+ this._stop();
+ this._refresh();
+ if ( this.previous !== this.element.val() ) {
+ this._trigger( "change", event );
+ }
+ },
+ mousewheel: function( event, delta ) {
+ if ( !delta ) {
+ return;
+ }
+ if ( !this.spinning && !this._start( event ) ) {
+ return false;
+ }
+
+ this._spin( (delta > 0 ? 1 : -1) * this.options.step, event );
+ clearTimeout( this.mousewheelTimer );
+ this.mousewheelTimer = this._delay(function() {
+ if ( this.spinning ) {
+ this._stop( event );
+ }
+ }, 100 );
+ event.preventDefault();
+ },
+ "mousedown .ui-spinner-button": function( event ) {
+ var previous;
+
+ // We never want the buttons to have focus; whenever the user is
+ // interacting with the spinner, the focus should be on the input.
+ // If the input is focused then this.previous is properly set from
+ // when the input first received focus. If the input is not focused
+ // then we need to set this.previous based on the value before spinning.
+ previous = this.element[0] === this.document[0].activeElement ?
+ this.previous : this.element.val();
+ function checkFocus() {
+ var isActive = this.element[0] === this.document[0].activeElement;
+ if ( !isActive ) {
+ this.element.focus();
+ this.previous = previous;
+ // support: IE
+ // IE sets focus asynchronously, so we need to check if focus
+ // moved off of the input because the user clicked on the button.
+ this._delay(function() {
+ this.previous = previous;
+ });
+ }
+ }
+
+ // ensure focus is on (or stays on) the text field
+ event.preventDefault();
+ checkFocus.call( this );
+
+ // support: IE
+ // IE doesn't prevent moving focus even with event.preventDefault()
+ // so we set a flag to know when we should ignore the blur event
+ // and check (again) if focus moved off of the input.
+ this.cancelBlur = true;
+ this._delay(function() {
+ delete this.cancelBlur;
+ checkFocus.call( this );
+ });
+
+ if ( this._start( event ) === false ) {
+ return;
+ }
+
+ this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
+ },
+ "mouseup .ui-spinner-button": "_stop",
+ "mouseenter .ui-spinner-button": function( event ) {
+ // button will add ui-state-active if mouse was down while mouseleave and kept down
+ if ( !$( event.currentTarget ).hasClass( "ui-state-active" ) ) {
+ return;
+ }
+
+ if ( this._start( event ) === false ) {
+ return false;
+ }
+ this._repeat( null, $( event.currentTarget ).hasClass( "ui-spinner-up" ) ? 1 : -1, event );
+ },
+ // TODO: do we really want to consider this a stop?
+ // shouldn't we just stop the repeater and wait until mouseup before
+ // we trigger the stop event?
+ "mouseleave .ui-spinner-button": "_stop"
+ },
+
+ _draw: function() {
+ var uiSpinner = this.uiSpinner = this.element
+ .addClass( "ui-spinner-input" )
+ .attr( "autocomplete", "off" )
+ .wrap( this._uiSpinnerHtml() )
+ .parent()
+ // add buttons
+ .append( this._buttonHtml() );
+
+ this.element.attr( "role", "spinbutton" );
+
+ // button bindings
+ this.buttons = uiSpinner.find( ".ui-spinner-button" )
+ .attr( "tabIndex", -1 )
+ .button()
+ .removeClass( "ui-corner-all" );
+
+ // IE 6 doesn't understand height: 50% for the buttons
+ // unless the wrapper has an explicit height
+ if ( this.buttons.height() > Math.ceil( uiSpinner.height() * 0.5 ) &&
+ uiSpinner.height() > 0 ) {
+ uiSpinner.height( uiSpinner.height() );
+ }
+
+ // disable spinner if element was already disabled
+ if ( this.options.disabled ) {
+ this.disable();
+ }
+ },
+
+ _keydown: function( event ) {
+ var options = this.options,
+ keyCode = $.ui.keyCode;
+
+ switch ( event.keyCode ) {
+ case keyCode.UP:
+ this._repeat( null, 1, event );
+ return true;
+ case keyCode.DOWN:
+ this._repeat( null, -1, event );
+ return true;
+ case keyCode.PAGE_UP:
+ this._repeat( null, options.page, event );
+ return true;
+ case keyCode.PAGE_DOWN:
+ this._repeat( null, -options.page, event );
+ return true;
+ }
+
+ return false;
+ },
+
+ _uiSpinnerHtml: function() {
+ return "";
+ },
+
+ _buttonHtml: function() {
+ return "" +
+ "" +
+ "▲" +
+ "" +
+ "" +
+ "▼" +
+ "";
+ },
+
+ _start: function( event ) {
+ if ( !this.spinning && this._trigger( "start", event ) === false ) {
+ return false;
+ }
+
+ if ( !this.counter ) {
+ this.counter = 1;
+ }
+ this.spinning = true;
+ return true;
+ },
+
+ _repeat: function( i, steps, event ) {
+ i = i || 500;
+
+ clearTimeout( this.timer );
+ this.timer = this._delay(function() {
+ this._repeat( 40, steps, event );
+ }, i );
+
+ this._spin( steps * this.options.step, event );
+ },
+
+ _spin: function( step, event ) {
+ var value = this.value() || 0;
+
+ if ( !this.counter ) {
+ this.counter = 1;
+ }
+
+ value = this._adjustValue( value + step * this._increment( this.counter ) );
+
+ if ( !this.spinning || this._trigger( "spin", event, { value: value } ) !== false) {
+ this._value( value );
+ this.counter++;
+ }
+ },
+
+ _increment: function( i ) {
+ var incremental = this.options.incremental;
+
+ if ( incremental ) {
+ return $.isFunction( incremental ) ?
+ incremental( i ) :
+ Math.floor( i*i*i/50000 - i*i/500 + 17*i/200 + 1 );
+ }
+
+ return 1;
+ },
+
+ _precision: function() {
+ var precision = this._precisionOf( this.options.step );
+ if ( this.options.min !== null ) {
+ precision = Math.max( precision, this._precisionOf( this.options.min ) );
+ }
+ return precision;
+ },
+
+ _precisionOf: function( num ) {
+ var str = num.toString(),
+ decimal = str.indexOf( "." );
+ return decimal === -1 ? 0 : str.length - decimal - 1;
+ },
+
+ _adjustValue: function( value ) {
+ var base, aboveMin,
+ options = this.options;
+
+ // make sure we're at a valid step
+ // - find out where we are relative to the base (min or 0)
+ base = options.min !== null ? options.min : 0;
+ aboveMin = value - base;
+ // - round to the nearest step
+ aboveMin = Math.round(aboveMin / options.step) * options.step;
+ // - rounding is based on 0, so adjust back to our base
+ value = base + aboveMin;
+
+ // fix precision from bad JS floating point math
+ value = parseFloat( value.toFixed( this._precision() ) );
+
+ // clamp the value
+ if ( options.max !== null && value > options.max) {
+ return options.max;
+ }
+ if ( options.min !== null && value < options.min ) {
+ return options.min;
+ }
+
+ return value;
+ },
+
+ _stop: function( event ) {
+ if ( !this.spinning ) {
+ return;
+ }
+
+ clearTimeout( this.timer );
+ clearTimeout( this.mousewheelTimer );
+ this.counter = 0;
+ this.spinning = false;
+ this._trigger( "stop", event );
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "culture" || key === "numberFormat" ) {
+ var prevValue = this._parse( this.element.val() );
+ this.options[ key ] = value;
+ this.element.val( this._format( prevValue ) );
+ return;
+ }
+
+ if ( key === "max" || key === "min" || key === "step" ) {
+ if ( typeof value === "string" ) {
+ value = this._parse( value );
+ }
+ }
+ if ( key === "icons" ) {
+ this.buttons.first().find( ".ui-icon" )
+ .removeClass( this.options.icons.up )
+ .addClass( value.up );
+ this.buttons.last().find( ".ui-icon" )
+ .removeClass( this.options.icons.down )
+ .addClass( value.down );
+ }
+
+ this._super( key, value );
+
+ if ( key === "disabled" ) {
+ if ( value ) {
+ this.element.prop( "disabled", true );
+ this.buttons.button( "disable" );
+ } else {
+ this.element.prop( "disabled", false );
+ this.buttons.button( "enable" );
+ }
+ }
+ },
+
+ _setOptions: modifier(function( options ) {
+ this._super( options );
+ this._value( this.element.val() );
+ }),
+
+ _parse: function( val ) {
+ if ( typeof val === "string" && val !== "" ) {
+ val = window.Globalize && this.options.numberFormat ?
+ Globalize.parseFloat( val, 10, this.options.culture ) : +val;
+ }
+ return val === "" || isNaN( val ) ? null : val;
+ },
+
+ _format: function( value ) {
+ if ( value === "" ) {
+ return "";
+ }
+ return window.Globalize && this.options.numberFormat ?
+ Globalize.format( value, this.options.numberFormat, this.options.culture ) :
+ value;
+ },
+
+ _refresh: function() {
+ this.element.attr({
+ "aria-valuemin": this.options.min,
+ "aria-valuemax": this.options.max,
+ // TODO: what should we do with values that can't be parsed?
+ "aria-valuenow": this._parse( this.element.val() )
+ });
+ },
+
+ // update the value without triggering change
+ _value: function( value, allowAny ) {
+ var parsed;
+ if ( value !== "" ) {
+ parsed = this._parse( value );
+ if ( parsed !== null ) {
+ if ( !allowAny ) {
+ parsed = this._adjustValue( parsed );
+ }
+ value = this._format( parsed );
+ }
+ }
+ this.element.val( value );
+ this._refresh();
+ },
+
+ _destroy: function() {
+ this.element
+ .removeClass( "ui-spinner-input" )
+ .prop( "disabled", false )
+ .removeAttr( "autocomplete" )
+ .removeAttr( "role" )
+ .removeAttr( "aria-valuemin" )
+ .removeAttr( "aria-valuemax" )
+ .removeAttr( "aria-valuenow" );
+ this.uiSpinner.replaceWith( this.element );
+ },
+
+ stepUp: modifier(function( steps ) {
+ this._stepUp( steps );
+ }),
+ _stepUp: function( steps ) {
+ if ( this._start() ) {
+ this._spin( (steps || 1) * this.options.step );
+ this._stop();
+ }
+ },
+
+ stepDown: modifier(function( steps ) {
+ this._stepDown( steps );
+ }),
+ _stepDown: function( steps ) {
+ if ( this._start() ) {
+ this._spin( (steps || 1) * -this.options.step );
+ this._stop();
+ }
+ },
+
+ pageUp: modifier(function( pages ) {
+ this._stepUp( (pages || 1) * this.options.page );
+ }),
+
+ pageDown: modifier(function( pages ) {
+ this._stepDown( (pages || 1) * this.options.page );
+ }),
+
+ value: function( newVal ) {
+ if ( !arguments.length ) {
+ return this._parse( this.element.val() );
+ }
+ modifier( this._value ).call( this, newVal );
+ },
+
+ widget: function() {
+ return this.uiSpinner;
+ }
+});
+
+}( jQuery ) );
+(function( $, undefined ) {
+
+var tabId = 0,
+ rhash = /#.*$/;
+
+function getNextTabId() {
+ return ++tabId;
+}
+
+function isLocal( anchor ) {
+ // support: IE7
+ // IE7 doesn't normalize the href property when set via script (#9317)
+ anchor = anchor.cloneNode( false );
+
+ return anchor.hash.length > 1 &&
+ decodeURIComponent( anchor.href.replace( rhash, "" ) ) ===
+ decodeURIComponent( location.href.replace( rhash, "" ) );
+}
+
+$.widget( "ui.tabs", {
+ version: "1.10.4",
+ delay: 300,
+ options: {
+ active: null,
+ collapsible: false,
+ event: "click",
+ heightStyle: "content",
+ hide: null,
+ show: null,
+
+ // callbacks
+ activate: null,
+ beforeActivate: null,
+ beforeLoad: null,
+ load: null
+ },
+
+ _create: function() {
+ var that = this,
+ options = this.options;
+
+ this.running = false;
+
+ this.element
+ .addClass( "ui-tabs ui-widget ui-widget-content ui-corner-all" )
+ .toggleClass( "ui-tabs-collapsible", options.collapsible )
+ // Prevent users from focusing disabled tabs via click
+ .delegate( ".ui-tabs-nav > li", "mousedown" + this.eventNamespace, function( event ) {
+ if ( $( this ).is( ".ui-state-disabled" ) ) {
+ event.preventDefault();
+ }
+ })
+ // support: IE <9
+ // Preventing the default action in mousedown doesn't prevent IE
+ // from focusing the element, so if the anchor gets focused, blur.
+ // We don't have to worry about focusing the previously focused
+ // element since clicking on a non-focusable element should focus
+ // the body anyway.
+ .delegate( ".ui-tabs-anchor", "focus" + this.eventNamespace, function() {
+ if ( $( this ).closest( "li" ).is( ".ui-state-disabled" ) ) {
+ this.blur();
+ }
+ });
+
+ this._processTabs();
+ options.active = this._initialActive();
+
+ // Take disabling tabs via class attribute from HTML
+ // into account and update option properly.
+ if ( $.isArray( options.disabled ) ) {
+ options.disabled = $.unique( options.disabled.concat(
+ $.map( this.tabs.filter( ".ui-state-disabled" ), function( li ) {
+ return that.tabs.index( li );
+ })
+ ) ).sort();
+ }
+
+ // check for length avoids error when initializing empty list
+ if ( this.options.active !== false && this.anchors.length ) {
+ this.active = this._findActive( options.active );
+ } else {
+ this.active = $();
+ }
+
+ this._refresh();
+
+ if ( this.active.length ) {
+ this.load( options.active );
+ }
+ },
+
+ _initialActive: function() {
+ var active = this.options.active,
+ collapsible = this.options.collapsible,
+ locationHash = location.hash.substring( 1 );
+
+ if ( active === null ) {
+ // check the fragment identifier in the URL
+ if ( locationHash ) {
+ this.tabs.each(function( i, tab ) {
+ if ( $( tab ).attr( "aria-controls" ) === locationHash ) {
+ active = i;
+ return false;
+ }
+ });
+ }
+
+ // check for a tab marked active via a class
+ if ( active === null ) {
+ active = this.tabs.index( this.tabs.filter( ".ui-tabs-active" ) );
+ }
+
+ // no active tab, set to false
+ if ( active === null || active === -1 ) {
+ active = this.tabs.length ? 0 : false;
+ }
+ }
+
+ // handle numbers: negative, out of range
+ if ( active !== false ) {
+ active = this.tabs.index( this.tabs.eq( active ) );
+ if ( active === -1 ) {
+ active = collapsible ? false : 0;
+ }
+ }
+
+ // don't allow collapsible: false and active: false
+ if ( !collapsible && active === false && this.anchors.length ) {
+ active = 0;
+ }
+
+ return active;
+ },
+
+ _getCreateEventData: function() {
+ return {
+ tab: this.active,
+ panel: !this.active.length ? $() : this._getPanelForTab( this.active )
+ };
+ },
+
+ _tabKeydown: function( event ) {
+ var focusedTab = $( this.document[0].activeElement ).closest( "li" ),
+ selectedIndex = this.tabs.index( focusedTab ),
+ goingForward = true;
+
+ if ( this._handlePageNav( event ) ) {
+ return;
+ }
+
+ switch ( event.keyCode ) {
+ case $.ui.keyCode.RIGHT:
+ case $.ui.keyCode.DOWN:
+ selectedIndex++;
+ break;
+ case $.ui.keyCode.UP:
+ case $.ui.keyCode.LEFT:
+ goingForward = false;
+ selectedIndex--;
+ break;
+ case $.ui.keyCode.END:
+ selectedIndex = this.anchors.length - 1;
+ break;
+ case $.ui.keyCode.HOME:
+ selectedIndex = 0;
+ break;
+ case $.ui.keyCode.SPACE:
+ // Activate only, no collapsing
+ event.preventDefault();
+ clearTimeout( this.activating );
+ this._activate( selectedIndex );
+ return;
+ case $.ui.keyCode.ENTER:
+ // Toggle (cancel delayed activation, allow collapsing)
+ event.preventDefault();
+ clearTimeout( this.activating );
+ // Determine if we should collapse or activate
+ this._activate( selectedIndex === this.options.active ? false : selectedIndex );
+ return;
+ default:
+ return;
+ }
+
+ // Focus the appropriate tab, based on which key was pressed
+ event.preventDefault();
+ clearTimeout( this.activating );
+ selectedIndex = this._focusNextTab( selectedIndex, goingForward );
+
+ // Navigating with control key will prevent automatic activation
+ if ( !event.ctrlKey ) {
+ // Update aria-selected immediately so that AT think the tab is already selected.
+ // Otherwise AT may confuse the user by stating that they need to activate the tab,
+ // but the tab will already be activated by the time the announcement finishes.
+ focusedTab.attr( "aria-selected", "false" );
+ this.tabs.eq( selectedIndex ).attr( "aria-selected", "true" );
+
+ this.activating = this._delay(function() {
+ this.option( "active", selectedIndex );
+ }, this.delay );
+ }
+ },
+
+ _panelKeydown: function( event ) {
+ if ( this._handlePageNav( event ) ) {
+ return;
+ }
+
+ // Ctrl+up moves focus to the current tab
+ if ( event.ctrlKey && event.keyCode === $.ui.keyCode.UP ) {
+ event.preventDefault();
+ this.active.focus();
+ }
+ },
+
+ // Alt+page up/down moves focus to the previous/next tab (and activates)
+ _handlePageNav: function( event ) {
+ if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_UP ) {
+ this._activate( this._focusNextTab( this.options.active - 1, false ) );
+ return true;
+ }
+ if ( event.altKey && event.keyCode === $.ui.keyCode.PAGE_DOWN ) {
+ this._activate( this._focusNextTab( this.options.active + 1, true ) );
+ return true;
+ }
+ },
+
+ _findNextTab: function( index, goingForward ) {
+ var lastTabIndex = this.tabs.length - 1;
+
+ function constrain() {
+ if ( index > lastTabIndex ) {
+ index = 0;
+ }
+ if ( index < 0 ) {
+ index = lastTabIndex;
+ }
+ return index;
+ }
+
+ while ( $.inArray( constrain(), this.options.disabled ) !== -1 ) {
+ index = goingForward ? index + 1 : index - 1;
+ }
+
+ return index;
+ },
+
+ _focusNextTab: function( index, goingForward ) {
+ index = this._findNextTab( index, goingForward );
+ this.tabs.eq( index ).focus();
+ return index;
+ },
+
+ _setOption: function( key, value ) {
+ if ( key === "active" ) {
+ // _activate() will handle invalid values and update this.options
+ this._activate( value );
+ return;
+ }
+
+ if ( key === "disabled" ) {
+ // don't use the widget factory's disabled handling
+ this._setupDisabled( value );
+ return;
+ }
+
+ this._super( key, value);
+
+ if ( key === "collapsible" ) {
+ this.element.toggleClass( "ui-tabs-collapsible", value );
+ // Setting collapsible: false while collapsed; open first panel
+ if ( !value && this.options.active === false ) {
+ this._activate( 0 );
+ }
+ }
+
+ if ( key === "event" ) {
+ this._setupEvents( value );
+ }
+
+ if ( key === "heightStyle" ) {
+ this._setupHeightStyle( value );
+ }
+ },
+
+ _tabId: function( tab ) {
+ return tab.attr( "aria-controls" ) || "ui-tabs-" + getNextTabId();
+ },
+
+ _sanitizeSelector: function( hash ) {
+ return hash ? hash.replace( /[!"$%&'()*+,.\/:;<=>?@\[\]\^`{|}~]/g, "\\$&" ) : "";
+ },
+
+ refresh: function() {
+ var options = this.options,
+ lis = this.tablist.children( ":has(a[href])" );
+
+ // get disabled tabs from class attribute from HTML
+ // this will get converted to a boolean if needed in _refresh()
+ options.disabled = $.map( lis.filter( ".ui-state-disabled" ), function( tab ) {
+ return lis.index( tab );
+ });
+
+ this._processTabs();
+
+ // was collapsed or no tabs
+ if ( options.active === false || !this.anchors.length ) {
+ options.active = false;
+ this.active = $();
+ // was active, but active tab is gone
+ } else if ( this.active.length && !$.contains( this.tablist[ 0 ], this.active[ 0 ] ) ) {
+ // all remaining tabs are disabled
+ if ( this.tabs.length === options.disabled.length ) {
+ options.active = false;
+ this.active = $();
+ // activate previous tab
+ } else {
+ this._activate( this._findNextTab( Math.max( 0, options.active - 1 ), false ) );
+ }
+ // was active, active tab still exists
+ } else {
+ // make sure active index is correct
+ options.active = this.tabs.index( this.active );
+ }
+
+ this._refresh();
+ },
+
+ _refresh: function() {
+ this._setupDisabled( this.options.disabled );
+ this._setupEvents( this.options.event );
+ this._setupHeightStyle( this.options.heightStyle );
+
+ this.tabs.not( this.active ).attr({
+ "aria-selected": "false",
+ tabIndex: -1
+ });
+ this.panels.not( this._getPanelForTab( this.active ) )
+ .hide()
+ .attr({
+ "aria-expanded": "false",
+ "aria-hidden": "true"
+ });
+
+ // Make sure one tab is in the tab order
+ if ( !this.active.length ) {
+ this.tabs.eq( 0 ).attr( "tabIndex", 0 );
+ } else {
+ this.active
+ .addClass( "ui-tabs-active ui-state-active" )
+ .attr({
+ "aria-selected": "true",
+ tabIndex: 0
+ });
+ this._getPanelForTab( this.active )
+ .show()
+ .attr({
+ "aria-expanded": "true",
+ "aria-hidden": "false"
+ });
+ }
+ },
+
+ _processTabs: function() {
+ var that = this;
+
+ this.tablist = this._getList()
+ .addClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
+ .attr( "role", "tablist" );
+
+ this.tabs = this.tablist.find( "> li:has(a[href])" )
+ .addClass( "ui-state-default ui-corner-top" )
+ .attr({
+ role: "tab",
+ tabIndex: -1
+ });
+
+ this.anchors = this.tabs.map(function() {
+ return $( "a", this )[ 0 ];
+ })
+ .addClass( "ui-tabs-anchor" )
+ .attr({
+ role: "presentation",
+ tabIndex: -1
+ });
+
+ this.panels = $();
+
+ this.anchors.each(function( i, anchor ) {
+ var selector, panel, panelId,
+ anchorId = $( anchor ).uniqueId().attr( "id" ),
+ tab = $( anchor ).closest( "li" ),
+ originalAriaControls = tab.attr( "aria-controls" );
+
+ // inline tab
+ if ( isLocal( anchor ) ) {
+ selector = anchor.hash;
+ panel = that.element.find( that._sanitizeSelector( selector ) );
+ // remote tab
+ } else {
+ panelId = that._tabId( tab );
+ selector = "#" + panelId;
+ panel = that.element.find( selector );
+ if ( !panel.length ) {
+ panel = that._createPanel( panelId );
+ panel.insertAfter( that.panels[ i - 1 ] || that.tablist );
+ }
+ panel.attr( "aria-live", "polite" );
+ }
+
+ if ( panel.length) {
+ that.panels = that.panels.add( panel );
+ }
+ if ( originalAriaControls ) {
+ tab.data( "ui-tabs-aria-controls", originalAriaControls );
+ }
+ tab.attr({
+ "aria-controls": selector.substring( 1 ),
+ "aria-labelledby": anchorId
+ });
+ panel.attr( "aria-labelledby", anchorId );
+ });
+
+ this.panels
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
+ .attr( "role", "tabpanel" );
+ },
+
+ // allow overriding how to find the list for rare usage scenarios (#7715)
+ _getList: function() {
+ return this.tablist || this.element.find( "ol,ul" ).eq( 0 );
+ },
+
+ _createPanel: function( id ) {
+ return $( "" )
+ .attr( "id", id )
+ .addClass( "ui-tabs-panel ui-widget-content ui-corner-bottom" )
+ .data( "ui-tabs-destroy", true );
+ },
+
+ _setupDisabled: function( disabled ) {
+ if ( $.isArray( disabled ) ) {
+ if ( !disabled.length ) {
+ disabled = false;
+ } else if ( disabled.length === this.anchors.length ) {
+ disabled = true;
+ }
+ }
+
+ // disable tabs
+ for ( var i = 0, li; ( li = this.tabs[ i ] ); i++ ) {
+ if ( disabled === true || $.inArray( i, disabled ) !== -1 ) {
+ $( li )
+ .addClass( "ui-state-disabled" )
+ .attr( "aria-disabled", "true" );
+ } else {
+ $( li )
+ .removeClass( "ui-state-disabled" )
+ .removeAttr( "aria-disabled" );
+ }
+ }
+
+ this.options.disabled = disabled;
+ },
+
+ _setupEvents: function( event ) {
+ var events = {
+ click: function( event ) {
+ event.preventDefault();
+ }
+ };
+ if ( event ) {
+ $.each( event.split(" "), function( index, eventName ) {
+ events[ eventName ] = "_eventHandler";
+ });
+ }
+
+ this._off( this.anchors.add( this.tabs ).add( this.panels ) );
+ this._on( this.anchors, events );
+ this._on( this.tabs, { keydown: "_tabKeydown" } );
+ this._on( this.panels, { keydown: "_panelKeydown" } );
+
+ this._focusable( this.tabs );
+ this._hoverable( this.tabs );
+ },
+
+ _setupHeightStyle: function( heightStyle ) {
+ var maxHeight,
+ parent = this.element.parent();
+
+ if ( heightStyle === "fill" ) {
+ maxHeight = parent.height();
+ maxHeight -= this.element.outerHeight() - this.element.height();
+
+ this.element.siblings( ":visible" ).each(function() {
+ var elem = $( this ),
+ position = elem.css( "position" );
+
+ if ( position === "absolute" || position === "fixed" ) {
+ return;
+ }
+ maxHeight -= elem.outerHeight( true );
+ });
+
+ this.element.children().not( this.panels ).each(function() {
+ maxHeight -= $( this ).outerHeight( true );
+ });
+
+ this.panels.each(function() {
+ $( this ).height( Math.max( 0, maxHeight -
+ $( this ).innerHeight() + $( this ).height() ) );
+ })
+ .css( "overflow", "auto" );
+ } else if ( heightStyle === "auto" ) {
+ maxHeight = 0;
+ this.panels.each(function() {
+ maxHeight = Math.max( maxHeight, $( this ).height( "" ).height() );
+ }).height( maxHeight );
+ }
+ },
+
+ _eventHandler: function( event ) {
+ var options = this.options,
+ active = this.active,
+ anchor = $( event.currentTarget ),
+ tab = anchor.closest( "li" ),
+ clickedIsActive = tab[ 0 ] === active[ 0 ],
+ collapsing = clickedIsActive && options.collapsible,
+ toShow = collapsing ? $() : this._getPanelForTab( tab ),
+ toHide = !active.length ? $() : this._getPanelForTab( active ),
+ eventData = {
+ oldTab: active,
+ oldPanel: toHide,
+ newTab: collapsing ? $() : tab,
+ newPanel: toShow
+ };
+
+ event.preventDefault();
+
+ if ( tab.hasClass( "ui-state-disabled" ) ||
+ // tab is already loading
+ tab.hasClass( "ui-tabs-loading" ) ||
+ // can't switch durning an animation
+ this.running ||
+ // click on active header, but not collapsible
+ ( clickedIsActive && !options.collapsible ) ||
+ // allow canceling activation
+ ( this._trigger( "beforeActivate", event, eventData ) === false ) ) {
+ return;
+ }
+
+ options.active = collapsing ? false : this.tabs.index( tab );
+
+ this.active = clickedIsActive ? $() : tab;
+ if ( this.xhr ) {
+ this.xhr.abort();
+ }
+
+ if ( !toHide.length && !toShow.length ) {
+ $.error( "jQuery UI Tabs: Mismatching fragment identifier." );
+ }
+
+ if ( toShow.length ) {
+ this.load( this.tabs.index( tab ), event );
+ }
+ this._toggle( event, eventData );
+ },
+
+ // handles show/hide for selecting tabs
+ _toggle: function( event, eventData ) {
+ var that = this,
+ toShow = eventData.newPanel,
+ toHide = eventData.oldPanel;
+
+ this.running = true;
+
+ function complete() {
+ that.running = false;
+ that._trigger( "activate", event, eventData );
+ }
+
+ function show() {
+ eventData.newTab.closest( "li" ).addClass( "ui-tabs-active ui-state-active" );
+
+ if ( toShow.length && that.options.show ) {
+ that._show( toShow, that.options.show, complete );
+ } else {
+ toShow.show();
+ complete();
+ }
+ }
+
+ // start out by hiding, then showing, then completing
+ if ( toHide.length && this.options.hide ) {
+ this._hide( toHide, this.options.hide, function() {
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
+ show();
+ });
+ } else {
+ eventData.oldTab.closest( "li" ).removeClass( "ui-tabs-active ui-state-active" );
+ toHide.hide();
+ show();
+ }
+
+ toHide.attr({
+ "aria-expanded": "false",
+ "aria-hidden": "true"
+ });
+ eventData.oldTab.attr( "aria-selected", "false" );
+ // If we're switching tabs, remove the old tab from the tab order.
+ // If we're opening from collapsed state, remove the previous tab from the tab order.
+ // If we're collapsing, then keep the collapsing tab in the tab order.
+ if ( toShow.length && toHide.length ) {
+ eventData.oldTab.attr( "tabIndex", -1 );
+ } else if ( toShow.length ) {
+ this.tabs.filter(function() {
+ return $( this ).attr( "tabIndex" ) === 0;
+ })
+ .attr( "tabIndex", -1 );
+ }
+
+ toShow.attr({
+ "aria-expanded": "true",
+ "aria-hidden": "false"
+ });
+ eventData.newTab.attr({
+ "aria-selected": "true",
+ tabIndex: 0
+ });
+ },
+
+ _activate: function( index ) {
+ var anchor,
+ active = this._findActive( index );
+
+ // trying to activate the already active panel
+ if ( active[ 0 ] === this.active[ 0 ] ) {
+ return;
+ }
+
+ // trying to collapse, simulate a click on the current active header
+ if ( !active.length ) {
+ active = this.active;
+ }
+
+ anchor = active.find( ".ui-tabs-anchor" )[ 0 ];
+ this._eventHandler({
+ target: anchor,
+ currentTarget: anchor,
+ preventDefault: $.noop
+ });
+ },
+
+ _findActive: function( index ) {
+ return index === false ? $() : this.tabs.eq( index );
+ },
+
+ _getIndex: function( index ) {
+ // meta-function to give users option to provide a href string instead of a numerical index.
+ if ( typeof index === "string" ) {
+ index = this.anchors.index( this.anchors.filter( "[href$='" + index + "']" ) );
+ }
+
+ return index;
+ },
+
+ _destroy: function() {
+ if ( this.xhr ) {
+ this.xhr.abort();
+ }
+
+ this.element.removeClass( "ui-tabs ui-widget ui-widget-content ui-corner-all ui-tabs-collapsible" );
+
+ this.tablist
+ .removeClass( "ui-tabs-nav ui-helper-reset ui-helper-clearfix ui-widget-header ui-corner-all" )
+ .removeAttr( "role" );
+
+ this.anchors
+ .removeClass( "ui-tabs-anchor" )
+ .removeAttr( "role" )
+ .removeAttr( "tabIndex" )
+ .removeUniqueId();
+
+ this.tabs.add( this.panels ).each(function() {
+ if ( $.data( this, "ui-tabs-destroy" ) ) {
+ $( this ).remove();
+ } else {
+ $( this )
+ .removeClass( "ui-state-default ui-state-active ui-state-disabled " +
+ "ui-corner-top ui-corner-bottom ui-widget-content ui-tabs-active ui-tabs-panel" )
+ .removeAttr( "tabIndex" )
+ .removeAttr( "aria-live" )
+ .removeAttr( "aria-busy" )
+ .removeAttr( "aria-selected" )
+ .removeAttr( "aria-labelledby" )
+ .removeAttr( "aria-hidden" )
+ .removeAttr( "aria-expanded" )
+ .removeAttr( "role" );
+ }
+ });
+
+ this.tabs.each(function() {
+ var li = $( this ),
+ prev = li.data( "ui-tabs-aria-controls" );
+ if ( prev ) {
+ li
+ .attr( "aria-controls", prev )
+ .removeData( "ui-tabs-aria-controls" );
+ } else {
+ li.removeAttr( "aria-controls" );
+ }
+ });
+
+ this.panels.show();
+
+ if ( this.options.heightStyle !== "content" ) {
+ this.panels.css( "height", "" );
+ }
+ },
+
+ enable: function( index ) {
+ var disabled = this.options.disabled;
+ if ( disabled === false ) {
+ return;
+ }
+
+ if ( index === undefined ) {
+ disabled = false;
+ } else {
+ index = this._getIndex( index );
+ if ( $.isArray( disabled ) ) {
+ disabled = $.map( disabled, function( num ) {
+ return num !== index ? num : null;
+ });
+ } else {
+ disabled = $.map( this.tabs, function( li, num ) {
+ return num !== index ? num : null;
+ });
+ }
+ }
+ this._setupDisabled( disabled );
+ },
+
+ disable: function( index ) {
+ var disabled = this.options.disabled;
+ if ( disabled === true ) {
+ return;
+ }
+
+ if ( index === undefined ) {
+ disabled = true;
+ } else {
+ index = this._getIndex( index );
+ if ( $.inArray( index, disabled ) !== -1 ) {
+ return;
+ }
+ if ( $.isArray( disabled ) ) {
+ disabled = $.merge( [ index ], disabled ).sort();
+ } else {
+ disabled = [ index ];
+ }
+ }
+ this._setupDisabled( disabled );
+ },
+
+ load: function( index, event ) {
+ index = this._getIndex( index );
+ var that = this,
+ tab = this.tabs.eq( index ),
+ anchor = tab.find( ".ui-tabs-anchor" ),
+ panel = this._getPanelForTab( tab ),
+ eventData = {
+ tab: tab,
+ panel: panel
+ };
+
+ // not remote
+ if ( isLocal( anchor[ 0 ] ) ) {
+ return;
+ }
+
+ this.xhr = $.ajax( this._ajaxSettings( anchor, event, eventData ) );
+
+ // support: jQuery <1.8
+ // jQuery <1.8 returns false if the request is canceled in beforeSend,
+ // but as of 1.8, $.ajax() always returns a jqXHR object.
+ if ( this.xhr && this.xhr.statusText !== "canceled" ) {
+ tab.addClass( "ui-tabs-loading" );
+ panel.attr( "aria-busy", "true" );
+
+ this.xhr
+ .success(function( response ) {
+ // support: jQuery <1.8
+ // http://bugs.jquery.com/ticket/11778
+ setTimeout(function() {
+ panel.html( response );
+ that._trigger( "load", event, eventData );
+ }, 1 );
+ })
+ .complete(function( jqXHR, status ) {
+ // support: jQuery <1.8
+ // http://bugs.jquery.com/ticket/11778
+ setTimeout(function() {
+ if ( status === "abort" ) {
+ that.panels.stop( false, true );
+ }
+
+ tab.removeClass( "ui-tabs-loading" );
+ panel.removeAttr( "aria-busy" );
+
+ if ( jqXHR === that.xhr ) {
+ delete that.xhr;
+ }
+ }, 1 );
+ });
+ }
+ },
+
+ _ajaxSettings: function( anchor, event, eventData ) {
+ var that = this;
+ return {
+ url: anchor.attr( "href" ),
+ beforeSend: function( jqXHR, settings ) {
+ return that._trigger( "beforeLoad", event,
+ $.extend( { jqXHR : jqXHR, ajaxSettings: settings }, eventData ) );
+ }
+ };
+ },
+
+ _getPanelForTab: function( tab ) {
+ var id = $( tab ).attr( "aria-controls" );
+ return this.element.find( this._sanitizeSelector( "#" + id ) );
+ }
+});
+
+})( jQuery );
+(function( $ ) {
+
+var increments = 0;
+
+function addDescribedBy( elem, id ) {
+ var describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ );
+ describedby.push( id );
+ elem
+ .data( "ui-tooltip-id", id )
+ .attr( "aria-describedby", $.trim( describedby.join( " " ) ) );
+}
+
+function removeDescribedBy( elem ) {
+ var id = elem.data( "ui-tooltip-id" ),
+ describedby = (elem.attr( "aria-describedby" ) || "").split( /\s+/ ),
+ index = $.inArray( id, describedby );
+ if ( index !== -1 ) {
+ describedby.splice( index, 1 );
+ }
+
+ elem.removeData( "ui-tooltip-id" );
+ describedby = $.trim( describedby.join( " " ) );
+ if ( describedby ) {
+ elem.attr( "aria-describedby", describedby );
+ } else {
+ elem.removeAttr( "aria-describedby" );
+ }
+}
+
+$.widget( "ui.tooltip", {
+ version: "1.10.4",
+ options: {
+ content: function() {
+ // support: IE<9, Opera in jQuery <1.7
+ // .text() can't accept undefined, so coerce to a string
+ var title = $( this ).attr( "title" ) || "";
+ // Escape title, since we're going from an attribute to raw HTML
+ return $( "
" ).text( title ).html();
+ },
+ hide: true,
+ // Disabled elements have inconsistent behavior across browsers (#8661)
+ items: "[title]:not([disabled])",
+ position: {
+ my: "left top+15",
+ at: "left bottom",
+ collision: "flipfit flip"
+ },
+ show: true,
+ tooltipClass: null,
+ track: false,
+
+ // callbacks
+ close: null,
+ open: null
+ },
+
+ _create: function() {
+ this._on({
+ mouseover: "open",
+ focusin: "open"
+ });
+
+ // IDs of generated tooltips, needed for destroy
+ this.tooltips = {};
+ // IDs of parent tooltips where we removed the title attribute
+ this.parents = {};
+
+ if ( this.options.disabled ) {
+ this._disable();
+ }
+ },
+
+ _setOption: function( key, value ) {
+ var that = this;
+
+ if ( key === "disabled" ) {
+ this[ value ? "_disable" : "_enable" ]();
+ this.options[ key ] = value;
+ // disable element style changes
+ return;
+ }
+
+ this._super( key, value );
+
+ if ( key === "content" ) {
+ $.each( this.tooltips, function( id, element ) {
+ that._updateContent( element );
+ });
+ }
+ },
+
+ _disable: function() {
+ var that = this;
+
+ // close open tooltips
+ $.each( this.tooltips, function( id, element ) {
+ var event = $.Event( "blur" );
+ event.target = event.currentTarget = element[0];
+ that.close( event, true );
+ });
+
+ // remove title attributes to prevent native tooltips
+ this.element.find( this.options.items ).addBack().each(function() {
+ var element = $( this );
+ if ( element.is( "[title]" ) ) {
+ element
+ .data( "ui-tooltip-title", element.attr( "title" ) )
+ .attr( "title", "" );
+ }
+ });
+ },
+
+ _enable: function() {
+ // restore title attributes
+ this.element.find( this.options.items ).addBack().each(function() {
+ var element = $( this );
+ if ( element.data( "ui-tooltip-title" ) ) {
+ element.attr( "title", element.data( "ui-tooltip-title" ) );
+ }
+ });
+ },
+
+ open: function( event ) {
+ var that = this,
+ target = $( event ? event.target : this.element )
+ // we need closest here due to mouseover bubbling,
+ // but always pointing at the same event target
+ .closest( this.options.items );
+
+ // No element to show a tooltip for or the tooltip is already open
+ if ( !target.length || target.data( "ui-tooltip-id" ) ) {
+ return;
+ }
+
+ if ( target.attr( "title" ) ) {
+ target.data( "ui-tooltip-title", target.attr( "title" ) );
+ }
+
+ target.data( "ui-tooltip-open", true );
+
+ // kill parent tooltips, custom or native, for hover
+ if ( event && event.type === "mouseover" ) {
+ target.parents().each(function() {
+ var parent = $( this ),
+ blurEvent;
+ if ( parent.data( "ui-tooltip-open" ) ) {
+ blurEvent = $.Event( "blur" );
+ blurEvent.target = blurEvent.currentTarget = this;
+ that.close( blurEvent, true );
+ }
+ if ( parent.attr( "title" ) ) {
+ parent.uniqueId();
+ that.parents[ this.id ] = {
+ element: this,
+ title: parent.attr( "title" )
+ };
+ parent.attr( "title", "" );
+ }
+ });
+ }
+
+ this._updateContent( target, event );
+ },
+
+ _updateContent: function( target, event ) {
+ var content,
+ contentOption = this.options.content,
+ that = this,
+ eventType = event ? event.type : null;
+
+ if ( typeof contentOption === "string" ) {
+ return this._open( event, target, contentOption );
+ }
+
+ content = contentOption.call( target[0], function( response ) {
+ // ignore async response if tooltip was closed already
+ if ( !target.data( "ui-tooltip-open" ) ) {
+ return;
+ }
+ // IE may instantly serve a cached response for ajax requests
+ // delay this call to _open so the other call to _open runs first
+ that._delay(function() {
+ // jQuery creates a special event for focusin when it doesn't
+ // exist natively. To improve performance, the native event
+ // object is reused and the type is changed. Therefore, we can't
+ // rely on the type being correct after the event finished
+ // bubbling, so we set it back to the previous value. (#8740)
+ if ( event ) {
+ event.type = eventType;
+ }
+ this._open( event, target, response );
+ });
+ });
+ if ( content ) {
+ this._open( event, target, content );
+ }
+ },
+
+ _open: function( event, target, content ) {
+ var tooltip, events, delayedShow,
+ positionOption = $.extend( {}, this.options.position );
+
+ if ( !content ) {
+ return;
+ }
+
+ // Content can be updated multiple times. If the tooltip already
+ // exists, then just update the content and bail.
+ tooltip = this._find( target );
+ if ( tooltip.length ) {
+ tooltip.find( ".ui-tooltip-content" ).html( content );
+ return;
+ }
+
+ // if we have a title, clear it to prevent the native tooltip
+ // we have to check first to avoid defining a title if none exists
+ // (we don't want to cause an element to start matching [title])
+ //
+ // We use removeAttr only for key events, to allow IE to export the correct
+ // accessible attributes. For mouse events, set to empty string to avoid
+ // native tooltip showing up (happens only when removing inside mouseover).
+ if ( target.is( "[title]" ) ) {
+ if ( event && event.type === "mouseover" ) {
+ target.attr( "title", "" );
+ } else {
+ target.removeAttr( "title" );
+ }
+ }
+
+ tooltip = this._tooltip( target );
+ addDescribedBy( target, tooltip.attr( "id" ) );
+ tooltip.find( ".ui-tooltip-content" ).html( content );
+
+ function position( event ) {
+ positionOption.of = event;
+ if ( tooltip.is( ":hidden" ) ) {
+ return;
+ }
+ tooltip.position( positionOption );
+ }
+ if ( this.options.track && event && /^mouse/.test( event.type ) ) {
+ this._on( this.document, {
+ mousemove: position
+ });
+ // trigger once to override element-relative positioning
+ position( event );
+ } else {
+ tooltip.position( $.extend({
+ of: target
+ }, this.options.position ) );
+ }
+
+ tooltip.hide();
+
+ this._show( tooltip, this.options.show );
+ // Handle tracking tooltips that are shown with a delay (#8644). As soon
+ // as the tooltip is visible, position the tooltip using the most recent
+ // event.
+ if ( this.options.show && this.options.show.delay ) {
+ delayedShow = this.delayedShow = setInterval(function() {
+ if ( tooltip.is( ":visible" ) ) {
+ position( positionOption.of );
+ clearInterval( delayedShow );
+ }
+ }, $.fx.interval );
+ }
+
+ this._trigger( "open", event, { tooltip: tooltip } );
+
+ events = {
+ keyup: function( event ) {
+ if ( event.keyCode === $.ui.keyCode.ESCAPE ) {
+ var fakeEvent = $.Event(event);
+ fakeEvent.currentTarget = target[0];
+ this.close( fakeEvent, true );
+ }
+ },
+ remove: function() {
+ this._removeTooltip( tooltip );
+ }
+ };
+ if ( !event || event.type === "mouseover" ) {
+ events.mouseleave = "close";
+ }
+ if ( !event || event.type === "focusin" ) {
+ events.focusout = "close";
+ }
+ this._on( true, target, events );
+ },
+
+ close: function( event ) {
+ var that = this,
+ target = $( event ? event.currentTarget : this.element ),
+ tooltip = this._find( target );
+
+ // disabling closes the tooltip, so we need to track when we're closing
+ // to avoid an infinite loop in case the tooltip becomes disabled on close
+ if ( this.closing ) {
+ return;
+ }
+
+ // Clear the interval for delayed tracking tooltips
+ clearInterval( this.delayedShow );
+
+ // only set title if we had one before (see comment in _open())
+ if ( target.data( "ui-tooltip-title" ) ) {
+ target.attr( "title", target.data( "ui-tooltip-title" ) );
+ }
+
+ removeDescribedBy( target );
+
+ tooltip.stop( true );
+ this._hide( tooltip, this.options.hide, function() {
+ that._removeTooltip( $( this ) );
+ });
+
+ target.removeData( "ui-tooltip-open" );
+ this._off( target, "mouseleave focusout keyup" );
+ // Remove 'remove' binding only on delegated targets
+ if ( target[0] !== this.element[0] ) {
+ this._off( target, "remove" );
+ }
+ this._off( this.document, "mousemove" );
+
+ if ( event && event.type === "mouseleave" ) {
+ $.each( this.parents, function( id, parent ) {
+ $( parent.element ).attr( "title", parent.title );
+ delete that.parents[ id ];
+ });
+ }
+
+ this.closing = true;
+ this._trigger( "close", event, { tooltip: tooltip } );
+ this.closing = false;
+ },
+
+ _tooltip: function( element ) {
+ var id = "ui-tooltip-" + increments++,
+ tooltip = $( "" )
+ .attr({
+ id: id,
+ role: "tooltip"
+ })
+ .addClass( "ui-tooltip ui-widget ui-corner-all ui-widget-content " +
+ ( this.options.tooltipClass || "" ) );
+ $( "
" )
+ .addClass( "ui-tooltip-content" )
+ .appendTo( tooltip );
+ tooltip.appendTo( this.document[0].body );
+ this.tooltips[ id ] = element;
+ return tooltip;
+ },
+
+ _find: function( target ) {
+ var id = target.data( "ui-tooltip-id" );
+ return id ? $( "#" + id ) : $();
+ },
+
+ _removeTooltip: function( tooltip ) {
+ tooltip.remove();
+ delete this.tooltips[ tooltip.attr( "id" ) ];
+ },
+
+ _destroy: function() {
+ var that = this;
+
+ // close open tooltips
+ $.each( this.tooltips, function( id, element ) {
+ // Delegate to close method to handle common cleanup
+ var event = $.Event( "blur" );
+ event.target = event.currentTarget = element[0];
+ that.close( event, true );
+
+ // Remove immediately; destroying an open tooltip doesn't use the
+ // hide animation
+ $( "#" + id ).remove();
+
+ // Restore the title
+ if ( element.data( "ui-tooltip-title" ) ) {
+ element.attr( "title", element.data( "ui-tooltip-title" ) );
+ element.removeData( "ui-tooltip-title" );
+ }
+ });
+ }
+});
+
+}( jQuery ) );
diff --git a/tutor/app/assets/javascripts/problems.js.coffee b/tutor/app/assets/javascripts/problems.js.coffee
index a02e65fd..d27fb224 100644
--- a/tutor/app/assets/javascripts/problems.js.coffee
+++ b/tutor/app/assets/javascripts/problems.js.coffee
@@ -38,6 +38,12 @@ activate = ->
time = setInterval(timer, 1000)
timer
+# [Design - Story Design]
+# Start counter if the two labels with ids "mins" and "secs" exists
+# Parameters: none
+# Returns: none
+# Author: Mussab ElDash
jQuery ->
- activate()
+ if element_exists("mins") and element_exists("secs")
+ activate()
return
diff --git a/tutor/app/assets/javascripts/replies.js b/tutor/app/assets/javascripts/replies.js
new file mode 100644
index 00000000..b719d590
--- /dev/null
+++ b/tutor/app/assets/javascripts/replies.js
@@ -0,0 +1,127 @@
+// # Place all the behaviors and hooks related to the matching controller here.
+// # All this logic will automatically be available in application.js.
+// # You can use CoffeeScript in this file: http://coffeescript.org/
+
+// # [Post a Reply - Story 1.14]
+// # It is used to post a reply to the Database
+// # Parameters:
+// # id: id of the current Post
+// # Returns: JSON Request
+// # Author: Ahmed Mohamed Magdi
+function post_reply(_id) {
+ input = $("#input_field").val();
+ $.ajax({
+ error: function() {
+ alert("Failed to add constraints, Check again");
+ },
+ type: "POST",
+ url: "/replies",
+ data: {
+ content: input,
+ id: _id
+ },
+ datatype: "JSON",
+ success: function(data) {
+ window.location = window.location
+ }
+ });
+}
+
+// # [Edit a Reply - Story 1.19]
+// # For show the form for editing a reply
+// # Parameters:
+// # reply_id: for getting the id of the reply
+// # data: the content of the reply
+// # Returns: none
+// # Author: Ahmed Mohamed Magdi
+function edit_form_reply(reply_id,data) {
+ $("#reply_content_"+reply_id).html("
");
+}
+
+// # [Edit a Reply - Story 1.19]
+// # For updating reply in the database
+// # Parameters:
+// # reply_id: for getting the id of the reply
+// # Returns:
+// # JSON Request
+// # Author: Ahmed Mohamed Magdi
+function update_reply(reply_id) {
+ input_data = $("#textarea_"+reply_id).val();
+ $.ajax({
+ type: "PATCH",
+ url: "/replies/"+reply_id,
+ data:{
+ id: reply_id,
+ content: input_data
+ },
+ success: function(data) {
+ window.location = window.location
+ },
+ datatype: "JSON",
+ error: function() {
+ alert("Failed to add constraints, Check again");
+ }
+ });
+}
+
+// # [Edit a Reply - Story 1.19]
+// # For getting the content of a reply
+// # Parameters:
+// # reply_id: for getting the id of the reply
+// # post_id: for getting the id of the post
+// # Returns:
+// # JSON Request
+// # Author: Ahmed Mohamed Magdi
+function edit_reply(reply_id,post_id) {
+ $.ajax({
+ type: "GET",
+ url: "/replies/"+reply_id,
+ data:{
+ id: reply_id
+ },
+ success: function(data) {
+ // alert(data.content);
+ edit_form_reply(reply_id,data.content);
+ },
+ datatype: "JSON",
+ error: function() {
+ alert("Failed to add constraints, Check again");
+ }
+ });
+}
+
+// # [Delete a Reply - Story 1.16]
+// # For deleting a reply from the database
+// # Parameters:
+// # reply_id: for getting the id of the reply
+// # post_id: for getting the id of the post
+// # Returns:
+// # JSON Request
+// # Author: Ahmed Mohamed Magdi
+function delete_reply(reply_id,post_id) {
+ $.ajax({
+ type: "DELETE",
+ url: "/replies/"+reply_id,
+ data:{
+ id: reply_id
+ },
+ success: function(data) {
+ if (data) {
+ $("#reply_"+reply_id).fadeOut(500, function() {
+ $(this).remove();
+ });
+ }
+ else {
+ alert("Server Issue 500");
+ }
+ },
+ datatype: "JSON",
+ error: function() {
+ alert("Unable to connect to Server");
+ }
+ });
+}
\ No newline at end of file
diff --git a/tutor/app/assets/javascripts/solutions.js.coffee b/tutor/app/assets/javascripts/solutions.js.coffee
index b4551182..addad684 100644
--- a/tutor/app/assets/javascripts/solutions.js.coffee
+++ b/tutor/app/assets/javascripts/solutions.js.coffee
@@ -16,25 +16,139 @@ index_number = 0
return
test = $('#solution_input').val()
start_spin()
+ toggle_code_area()
$.ajax
type: "POST"
url: '/debuggers/' + problem_id
data: {code : input , case : test}
datatype: 'json'
success: (data) ->
+ clear_console()
+ variables = data
stop_spin()
if !data["success"]
compilation_error data["data"]["errors"]
return
variables = data["data"]
- toggleDebug()
+ toggleDebug(1)
debug_console()
jump_state 0
error: ->
+ clear_console()
stop_spin()
+ toggle_code_area()
return
return
+# [Compiler: Test - Story 3.15]
+# Sends the code to the server to be compiled
+# Parameters:
+# problem_id: The id of the problem being solved
+# Returns:
+# none
+# Author: Ahmed Akram
+@compile = (problem_id) ->
+ input = $('#solution_code').val()
+ if input.length is 0
+ alert "You didn't write any code"
+ return
+ start_spin()
+ toggle_code_area()
+ $.ajax
+ type: "POST"
+ url: '/solutions/compile_solution'
+ data: {code : input, problem_id : problem_id}
+ datatype: 'json'
+ success: (unique) ->
+ clear_console()
+ stop_spin()
+ toggle_code_area()
+ if !unique["success"]
+ compilation_error unique["errors"]
+ return
+ $('.compilation_succeeded').html("Compilation Succeeded!")
+ error: ->
+ clear_console()
+ stop_spin()
+ toggle_code_area()
+ return
+ return
+
+# [Compiler: Test - Story 3.15]
+# Sends the code and the test case to the server to be tested
+# Parameters:
+# problem_id: The id of the problem being solved
+# Returns:
+# none
+# Author: Ahmed Akram
+@run_input = (problem_id) ->
+ code = $('#solution_code').val()
+ if code.length is 0
+ alert "You didn't write any code"
+ return
+ test = $('#solution_input').val()
+ start_spin()
+ toggle_code_area()
+ $.ajax
+ type: "POST"
+ url: '/solutions/execute'
+ data: {code : code, problem_id : problem_id, input : test}
+ datatype: 'json'
+ success: (data) ->
+ clear_console()
+ stop_spin()
+ toggle_code_area()
+ if data["compiler_error"]
+ compilation_error data["compiler_output"]
+ return
+ else if data["executer_feedback"]
+ input_test_output data["executer_output"]
+ return
+ else
+ runtime_error data["executer_output"]
+ return
+ error: ->
+ clear_console()
+ stop_spin()
+ toggle_code_area()
+ return
+ return
+
+# [Compiler: Test - Story 3.15]
+# Fills the console with the runtime errors
+# Parameters:
+# data: The hash containing the response
+# Returns:
+# none
+# Author: Ahmed Akram
+input_test_output = (data) ->
+ $('#output_section').html(data["message"])
+ return
+
+# [Compiler: Test - Story 3.15]
+# Disables the code and input area while debugging, compiling or running test case
+# Parameters:
+# none
+# Returns:
+# none
+# Author: Ahmed Akram
+toggle_code_area = ->
+ $('#solution_code').prop 'disabled', !$('#solution_code').prop 'disabled'
+ $('#solution_input').prop 'disabled', !$('#solution_input').prop 'disabled'
+
+# [Compiler: Test - Story 3.15]
+# Fills the console with the runtime errors
+# Parameters:
+# data: The hash containing the response
+# Returns:
+# none
+# Author: Ahmed Akram
+runtime_error = (data) ->
+ $('#runtime_error').toggle(true)
+ $('#error_section').html(if data["errors"] then data["errors"] else data)
+ $('#explanation_section').html(data["explanation"])
+ return
+
# [Debugger: Debug - Story 3.6]
# Fills the console with the compilation errors
# Parameters: none
@@ -50,7 +164,11 @@ compilation_error = (data) ->
# Parameters: none
# Returns: none
# Author: Mussab ElDash
-clear_console = ->
+clear_console = () ->
+ $('#runtime_error').toggle(false)
+ $('#validate_case').html("")
+ $('#output_section').html("")
+ $('.compilation_succeeded').html("")
$('.compilation_failed').html("")
$('.compilation_feedback').html("")
return
@@ -90,45 +208,29 @@ debug_console = ->
@toggle_spin = ->
$('#spinner').toggleClass "spinner"
-# [Execute Line By Line - Story 3.8]
+# [Compiler: Test - Story 3.15]
# Toggles debugging mode by changing the available buttons.
-# Parameters: none
-# Returns: none
-# Author: Rami Khalil (Temporary)
-@toggleDebug = ->
- $('#debugButton').prop 'hidden', !$('#debugButton').prop 'hidden'
- $('#compileButton').prop 'hidden', !$('#compileButton').prop 'hidden'
- $('#testButton').prop 'hidden', !$('#testButton').prop 'hidden'
+# Parameters:
+# none
+# Returns:
+# none
+# Author: Ahmed Akram + Rami Khalil
+@toggleDebug = (state) ->
+ $('#debugButton').toggle(!state)
+ $('#compileButton').toggle(!state)
+ $('#testButton').toggle(!state)
+ $('#nextButton').toggle(state)
+ $('#previousButton').toggle(state)
+ $('#stopButton').toggle(state)
editor = ace.edit("editor")
- editor.setReadOnly !editor.getReadOnly()
-
+ editor.setReadOnly(state)
normal_theme = "ace/theme/twilight"
debug_theme = normal_theme + "-debug"
-
if editor.getTheme() == normal_theme
editor.setTheme debug_theme
else
editor.setTheme normal_theme
- $('#nextButton').prop 'hidden', !$('#nextButton').prop 'hidden'
- $('#previousButton').prop 'hidden', !$('#previousButton').prop 'hidden'
- $('#stopButton').prop 'hidden', !$('#stopButton').prop 'hidden'
-
-# [Compile - Story 3.4]
-# Sends the code to the server to be compiled.
-# Parameters: none
-# Returns: none
-# Author: Rami Khalil (Temporary)
-@compile = () ->
- alert "Compiling"
-
-# [Test - Story 3.15]
-# Sends the code and the input to be processed on the server.
-# Parameters: none
-# Returns: none
-# Author: Rami Khalil (Temporary)
-@test = () ->
- alert "Testing"
# [Execute Line By Line - Story 3.8]
# Moves to the next state of execution.
@@ -175,6 +277,11 @@ debug_console = ->
# Author: Rami Khalil + Khaled Helmy
@jump_state = (state_number) ->
highlight_line variables[state_number]['line']
+ if state_number isnt 0
+ clear_console()
+ $("#output_section").html variables[state_number]["stream"]
+ if variables[state_number]["exception"]
+ runtime_error variables[state_number]["exception"]
update_memory_contents state_number
# [View Variables - Story 3.7]
@@ -204,11 +311,12 @@ debug_console = ->
# Returns: none
# Author: Rami Khalil (Temporary)
@stop = () ->
- toggleDebug()
+ toggleDebug(0)
index_number = 0;
+ toggle_code_area()
variables = null;
-# To be Used when changing to ajax in order not to refresh page
+
# [Compiler: Validate - Story 3.5]
# submits a solution in the form without refreshing
# using ajax showing an alert box for success and failure scenarios
@@ -221,29 +329,39 @@ debug_console = ->
code = $('#solution_code').val()
mins = parseInt($('#mins').text())
secs = parseInt($('#secs').text())
- time = mins*60 + secs
- start_spin()
+ time = (mins * 60) + secs
+ if code.length is 0
+ alert 'Blank submissions are not allowed'
+ return
+ toggle_code_area()
+ start_spin()
$.ajax
type: "POST"
url: '/solutions'
data: {problem_id: problem_id, code: code, time: time}
datatype: 'json'
success: (data) ->
+ clear_console()
stop_spin()
- success = $('#validate_success')
- errors = $('#validate_error')
- success.html("")
- for i in data["success"]
- success.append("#{i}
")
- errors.html("")
- for i in data["failure"]
- errors.append("#{i}
")
- if code.length isnt 0
- alert 'Solution has been submitted successfully'
- else
- alert 'Blank submissions are not allowed'
+ toggle_code_area()
+ if data['compiler_error']
+ compilation_error(data['compiler_output'])
+ return
+ out = $('#validate_case')
+ out.html("")
+ content = ""
+ for i in data
+ if data["success"]
+ content += "
#{i['test_case']}, \
+ #{i['response']}
"
+ else
+ content += "
#{i['test_case']}, \
+ #{i['response']}
"
+ out.html(content)
return
error: (data) ->
+ clear_console()
stop_spin()
+ toggle_code_area()
return
return
diff --git a/tutor/app/assets/javascripts/test_cases.js b/tutor/app/assets/javascripts/test_cases.js
new file mode 100644
index 00000000..35a9a0b4
--- /dev/null
+++ b/tutor/app/assets/javascripts/test_cases.js
@@ -0,0 +1,14 @@
+//[Edit test case-story 4.9]
+//Toggle to view previous test cases
+//Parameters:
+// id: Element that will be toggled.
+//Returns: none.
+//Author: Nadine Adel
+function toggle_visibility(id) {
+ var element = document.getElementById(id);
+ if(element.style.display == 'block')
+ element.style.display = 'none';
+ else
+ element.style.display = 'block';
+}
+
diff --git a/tutor/app/assets/javascripts/tips.js b/tutor/app/assets/javascripts/tips.js
new file mode 100644
index 00000000..689a2f4b
--- /dev/null
+++ b/tutor/app/assets/javascripts/tips.js
@@ -0,0 +1,13 @@
+//[View hints and tips-story 4.22]
+//Toggle to view previous tips
+//Parameters:
+// id: Element that will be toggled.
+//Returns: none.
+//Author: Nadine Adel
+function toggle_visibility(id) {
+ var element = document.getElementById(id);
+ if(element.style.display == 'block')
+ element.style.display = 'none';
+ else
+ element.style.display = 'block';
+}
\ No newline at end of file
diff --git a/tutor/app/assets/javascripts/tips.js.coffee b/tutor/app/assets/javascripts/tips.js.coffee
index 24f83d18..e69de29b 100644
--- a/tutor/app/assets/javascripts/tips.js.coffee
+++ b/tutor/app/assets/javascripts/tips.js.coffee
@@ -1,3 +0,0 @@
-# Place all the behaviors and hooks related to the matching controller here.
-# All this logic will automatically be available in application.js.
-# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/tutor/app/assets/javascripts/topics.js.coffee b/tutor/app/assets/javascripts/topics.js.coffee
index dfb461e2..6891ae5b 100644
--- a/tutor/app/assets/javascripts/topics.js.coffee
+++ b/tutor/app/assets/javascripts/topics.js.coffee
@@ -5,3 +5,64 @@
create = document.getElementById("create_track")
create.hidden = !create.hidden
return
+
+# [Edit Track Rating - Story 4.13]
+# Makes the list of track sortable.
+# Sends array of new order of tracks to the sort action
+# in topic's controller.
+# Parameters: none
+# Returns: none
+# Author: Lin Kassem
+$(document).ready ->
+ $("#list-of-tracks").sortable
+ placeholder: "ui-state-highlight"
+ axis: "y"
+ tolerance: "pointer"
+ forcePlaceholderSize: true
+ cursor: "move"
+ containment: "parent"
+
+ $("#edit_track_rating_dialog").dialog
+ autoOpen: false
+ modal: true
+ width: "auto"
+ height: "auto"
+ autoResize: false
+ buttons:
+ Done: ->
+ array = $("#list-of-tracks").sortable("toArray")
+ $.ajax
+ url: "/topics/sort"
+ type: "POST"
+ beforeSend: (xhr) ->
+ xhr.setRequestHeader "X-CSRF-Token",
+ $("meta[name=\"csrf-token\"]").attr("content")
+ return
+
+ data:
+ methodParam: array
+
+ success: (data) ->
+ alert "New track ratings saved!"
+ location.reload()
+ return
+
+ error: (args) ->
+ alert "Error on ajax post"
+ return
+
+ $(this).dialog "close"
+ return
+
+ Cancel: ->
+ $(this).dialog "close"
+ alert "Changes will not be saved !"
+
+ return
+
+ $("#opener").click(->
+ $("#edit_track_rating_dialog").dialog "open"
+ $("#edit_track_rating_dialog").css "overflow", "hidden"
+ return
+ ).disableSelection()
+ return
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/application.css b/tutor/app/assets/stylesheets/application.css
index 2288e797..d13f78b7 100644
--- a/tutor/app/assets/stylesheets/application.css
+++ b/tutor/app/assets/stylesheets/application.css
@@ -11,6 +11,7 @@
*= require_self
*= require_tree .
*/
+= require posts
.side-left {
height:700px;
@@ -29,7 +30,7 @@
width:78%;
margin-left:1%;
min-width:500px;
- overflow:scroll;
+ overflow:auto;
position:static;
}
@@ -52,5 +53,5 @@
}
.home {
- overflow:scroll;
+ overflow:auto;
}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/hints.css b/tutor/app/assets/stylesheets/hints.css
index e7fc76e4..5f6e8d1f 100644
--- a/tutor/app/assets/stylesheets/hints.css
+++ b/tutor/app/assets/stylesheets/hints.css
@@ -1,3 +1,10 @@
.hidden {
display: none;
+}
+
+a.Lst {
+ font-size: 30px;
+ line-height: 0px;
+ padding-left: 0px;
+ font-weight: normal;
}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/hints.css.scss b/tutor/app/assets/stylesheets/hints.css.scss
new file mode 100644
index 00000000..7c9e053a
--- /dev/null
+++ b/tutor/app/assets/stylesheets/hints.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the Hints controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/tutor/app/assets/stylesheets/jquery-ui.css b/tutor/app/assets/stylesheets/jquery-ui.css
new file mode 100644
index 00000000..d788bec6
--- /dev/null
+++ b/tutor/app/assets/stylesheets/jquery-ui.css
@@ -0,0 +1,1172 @@
+/*! jQuery UI - v1.10.4 - 2014-01-17
+* http://jqueryui.com
+* Includes: jquery.ui.core.css, jquery.ui.accordion.css, jquery.ui.autocomplete.css, jquery.ui.button.css, jquery.ui.datepicker.css, jquery.ui.dialog.css, jquery.ui.menu.css, jquery.ui.progressbar.css, jquery.ui.resizable.css, jquery.ui.selectable.css, jquery.ui.slider.css, jquery.ui.spinner.css, jquery.ui.tabs.css, jquery.ui.tooltip.css, jquery.ui.theme.css
+* To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Verdana%2CArial%2Csans-serif&fwDefault=normal&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=cccccc&bgTextureHeader=highlight_soft&bgImgOpacityHeader=75&borderColorHeader=aaaaaa&fcHeader=222222&iconColorHeader=222222&bgColorContent=ffffff&bgTextureContent=flat&bgImgOpacityContent=75&borderColorContent=aaaaaa&fcContent=222222&iconColorContent=222222&bgColorDefault=e6e6e6&bgTextureDefault=glass&bgImgOpacityDefault=75&borderColorDefault=d3d3d3&fcDefault=555555&iconColorDefault=888888&bgColorHover=dadada&bgTextureHover=glass&bgImgOpacityHover=75&borderColorHover=999999&fcHover=212121&iconColorHover=454545&bgColorActive=ffffff&bgTextureActive=glass&bgImgOpacityActive=65&borderColorActive=aaaaaa&fcActive=212121&iconColorActive=454545&bgColorHighlight=fbf9ee&bgTextureHighlight=glass&bgImgOpacityHighlight=55&borderColorHighlight=fcefa1&fcHighlight=363636&iconColorHighlight=2e83ff&bgColorError=fef1ec&bgTextureError=glass&bgImgOpacityError=95&borderColorError=cd0a0a&fcError=cd0a0a&iconColorError=cd0a0a&bgColorOverlay=aaaaaa&bgTextureOverlay=flat&bgImgOpacityOverlay=0&opacityOverlay=30&bgColorShadow=aaaaaa&bgTextureShadow=flat&bgImgOpacityShadow=0&opacityShadow=30&thicknessShadow=8px&offsetTopShadow=-8px&offsetLeftShadow=-8px&cornerRadiusShadow=8px
+* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
+
+/* Layout helpers
+----------------------------------*/
+.ui-helper-hidden {
+ display: none;
+}
+.ui-helper-hidden-accessible {
+ border: 0;
+ clip: rect(0 0 0 0);
+ height: 1px;
+ margin: -1px;
+ overflow: hidden;
+ padding: 0;
+ position: absolute;
+ width: 1px;
+}
+.ui-helper-reset {
+ margin: 0;
+ padding: 0;
+ border: 0;
+ outline: 0;
+ line-height: 1.3;
+ text-decoration: none;
+ font-size: 100%;
+ list-style: none;
+}
+.ui-helper-clearfix:before,
+.ui-helper-clearfix:after {
+ content: "";
+ display: table;
+ border-collapse: collapse;
+}
+.ui-helper-clearfix:after {
+ clear: both;
+}
+.ui-helper-clearfix {
+ min-height: 0; /* support: IE7 */
+}
+.ui-helper-zfix {
+ width: 100%;
+ height: 100%;
+ top: 0;
+ left: 0;
+ position: absolute;
+ opacity: 0;
+ filter:Alpha(Opacity=0);
+}
+
+.ui-front {
+ z-index: 100;
+}
+
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-disabled {
+ cursor: default !important;
+}
+
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ display: block;
+ text-indent: -99999px;
+ overflow: hidden;
+ background-repeat: no-repeat;
+}
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Overlays */
+.ui-widget-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+}
+.ui-accordion .ui-accordion-header {
+ display: block;
+ cursor: pointer;
+ position: relative;
+ margin-top: 2px;
+ padding: .5em .5em .5em .7em;
+ min-height: 0; /* support: IE7 */
+}
+.ui-accordion .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-noicons {
+ padding-left: .7em;
+}
+.ui-accordion .ui-accordion-icons .ui-accordion-icons {
+ padding-left: 2.2em;
+}
+.ui-accordion .ui-accordion-header .ui-accordion-header-icon {
+ position: absolute;
+ left: .5em;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-accordion .ui-accordion-content {
+ padding: 1em 2.2em;
+ border-top: 0;
+ overflow: auto;
+}
+.ui-autocomplete {
+ position: absolute;
+ top: 0;
+ left: 0;
+ cursor: default;
+}
+.ui-button {
+ display: inline-block;
+ position: relative;
+ padding: 0;
+ line-height: normal;
+ margin-right: .1em;
+ cursor: pointer;
+ vertical-align: middle;
+ text-align: center;
+ overflow: visible; /* removes extra width in IE */
+}
+.ui-button,
+.ui-button:link,
+.ui-button:visited,
+.ui-button:hover,
+.ui-button:active {
+ text-decoration: none;
+}
+/* to make room for the icon, a width needs to be set here */
+.ui-button-icon-only {
+ width: 2.2em;
+}
+/* button elements seem to need a little more width */
+button.ui-button-icon-only {
+ width: 2.4em;
+}
+.ui-button-icons-only {
+ width: 3.4em;
+}
+button.ui-button-icons-only {
+ width: 3.7em;
+}
+
+/* button text element */
+.ui-button .ui-button-text {
+ display: block;
+ line-height: normal;
+}
+.ui-button-text-only .ui-button-text {
+ padding: .4em 1em;
+}
+.ui-button-icon-only .ui-button-text,
+.ui-button-icons-only .ui-button-text {
+ padding: .4em;
+ text-indent: -9999999px;
+}
+.ui-button-text-icon-primary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 1em .4em 2.1em;
+}
+.ui-button-text-icon-secondary .ui-button-text,
+.ui-button-text-icons .ui-button-text {
+ padding: .4em 2.1em .4em 1em;
+}
+.ui-button-text-icons .ui-button-text {
+ padding-left: 2.1em;
+ padding-right: 2.1em;
+}
+/* no icon support for input elements, provide padding by default */
+input.ui-button {
+ padding: .4em 1em;
+}
+
+/* button icon element(s) */
+.ui-button-icon-only .ui-icon,
+.ui-button-text-icon-primary .ui-icon,
+.ui-button-text-icon-secondary .ui-icon,
+.ui-button-text-icons .ui-icon,
+.ui-button-icons-only .ui-icon {
+ position: absolute;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-button-icon-only .ui-icon {
+ left: 50%;
+ margin-left: -8px;
+}
+.ui-button-text-icon-primary .ui-button-icon-primary,
+.ui-button-text-icons .ui-button-icon-primary,
+.ui-button-icons-only .ui-button-icon-primary {
+ left: .5em;
+}
+.ui-button-text-icon-secondary .ui-button-icon-secondary,
+.ui-button-text-icons .ui-button-icon-secondary,
+.ui-button-icons-only .ui-button-icon-secondary {
+ right: .5em;
+}
+
+/* button sets */
+.ui-buttonset {
+ margin-right: 7px;
+}
+.ui-buttonset .ui-button {
+ margin-left: 0;
+ margin-right: -.3em;
+}
+
+/* workarounds */
+/* reset extra padding in Firefox, see h5bp.com/l */
+input.ui-button::-moz-focus-inner,
+button.ui-button::-moz-focus-inner {
+ border: 0;
+ padding: 0;
+}
+.ui-datepicker {
+ width: 17em;
+ padding: .2em .2em 0;
+ display: none;
+}
+.ui-datepicker .ui-datepicker-header {
+ position: relative;
+ padding: .2em 0;
+}
+.ui-datepicker .ui-datepicker-prev,
+.ui-datepicker .ui-datepicker-next {
+ position: absolute;
+ top: 2px;
+ width: 1.8em;
+ height: 1.8em;
+}
+.ui-datepicker .ui-datepicker-prev-hover,
+.ui-datepicker .ui-datepicker-next-hover {
+ top: 1px;
+}
+.ui-datepicker .ui-datepicker-prev {
+ left: 2px;
+}
+.ui-datepicker .ui-datepicker-next {
+ right: 2px;
+}
+.ui-datepicker .ui-datepicker-prev-hover {
+ left: 1px;
+}
+.ui-datepicker .ui-datepicker-next-hover {
+ right: 1px;
+}
+.ui-datepicker .ui-datepicker-prev span,
+.ui-datepicker .ui-datepicker-next span {
+ display: block;
+ position: absolute;
+ left: 50%;
+ margin-left: -8px;
+ top: 50%;
+ margin-top: -8px;
+}
+.ui-datepicker .ui-datepicker-title {
+ margin: 0 2.3em;
+ line-height: 1.8em;
+ text-align: center;
+}
+.ui-datepicker .ui-datepicker-title select {
+ font-size: 1em;
+ margin: 1px 0;
+}
+.ui-datepicker select.ui-datepicker-month,
+.ui-datepicker select.ui-datepicker-year {
+ width: 49%;
+}
+.ui-datepicker table {
+ width: 100%;
+ font-size: .9em;
+ border-collapse: collapse;
+ margin: 0 0 .4em;
+}
+.ui-datepicker th {
+ padding: .7em .3em;
+ text-align: center;
+ font-weight: bold;
+ border: 0;
+}
+.ui-datepicker td {
+ border: 0;
+ padding: 1px;
+}
+.ui-datepicker td span,
+.ui-datepicker td a {
+ display: block;
+ padding: .2em;
+ text-align: right;
+ text-decoration: none;
+}
+.ui-datepicker .ui-datepicker-buttonpane {
+ background-image: none;
+ margin: .7em 0 0 0;
+ padding: 0 .2em;
+ border-left: 0;
+ border-right: 0;
+ border-bottom: 0;
+}
+.ui-datepicker .ui-datepicker-buttonpane button {
+ float: right;
+ margin: .5em .2em .4em;
+ cursor: pointer;
+ padding: .2em .6em .3em .6em;
+ width: auto;
+ overflow: visible;
+}
+.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current {
+ float: left;
+}
+
+/* with multiple calendars */
+.ui-datepicker.ui-datepicker-multi {
+ width: auto;
+}
+.ui-datepicker-multi .ui-datepicker-group {
+ float: left;
+}
+.ui-datepicker-multi .ui-datepicker-group table {
+ width: 95%;
+ margin: 0 auto .4em;
+}
+.ui-datepicker-multi-2 .ui-datepicker-group {
+ width: 50%;
+}
+.ui-datepicker-multi-3 .ui-datepicker-group {
+ width: 33.3%;
+}
+.ui-datepicker-multi-4 .ui-datepicker-group {
+ width: 25%;
+}
+.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header {
+ border-left-width: 0;
+}
+.ui-datepicker-multi .ui-datepicker-buttonpane {
+ clear: left;
+}
+.ui-datepicker-row-break {
+ clear: both;
+ width: 100%;
+ font-size: 0;
+}
+
+/* RTL support */
+.ui-datepicker-rtl {
+ direction: rtl;
+}
+.ui-datepicker-rtl .ui-datepicker-prev {
+ right: 2px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next {
+ left: 2px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-prev:hover {
+ right: 1px;
+ left: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-next:hover {
+ left: 1px;
+ right: auto;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane {
+ clear: right;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button {
+ float: left;
+}
+.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,
+.ui-datepicker-rtl .ui-datepicker-group {
+ float: right;
+}
+.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,
+.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header {
+ border-right-width: 0;
+ border-left-width: 1px;
+}
+.ui-dialog {
+ overflow: hidden;
+ position: absolute;
+ top: 0;
+ left: 0;
+ padding: .2em;
+ outline: 0;
+}
+.ui-dialog .ui-dialog-titlebar {
+ padding: .4em 1em;
+ position: relative;
+}
+.ui-dialog .ui-dialog-title {
+ float: left;
+ margin: .1em 0;
+ white-space: nowrap;
+ width: 90%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.ui-dialog .ui-dialog-titlebar-close {
+ display:none;
+}
+.ui-dialog .ui-dialog-content {
+ position: relative;
+ border: 0;
+ padding: .5em 1em;
+ background: none;
+ overflow: auto;
+}
+.ui-dialog .ui-dialog-buttonpane {
+ text-align: left;
+ border-width: 1px 0 0 0;
+ background-image: none;
+ margin-top: .5em;
+ padding: .3em 1em .5em .4em;
+}
+.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset {
+ float: right;
+}
+.ui-dialog .ui-dialog-buttonpane button {
+ margin: .5em .4em .5em 0;
+ cursor: pointer;
+}
+.ui-dialog .ui-resizable-se {
+ width: 12px;
+ height: 12px;
+ right: -5px;
+ bottom: -5px;
+ background-position: 16px 16px;
+}
+.ui-draggable .ui-dialog-titlebar {
+ cursor: move;
+}
+.ui-menu {
+ list-style: none;
+ padding: 2px;
+ margin: 0;
+ display: block;
+ outline: none;
+}
+.ui-menu .ui-menu {
+ margin-top: -3px;
+ position: absolute;
+}
+.ui-menu .ui-menu-item {
+ margin: 0;
+ padding: 0;
+ width: 100%;
+ /* support: IE10, see #8844 */
+ list-style-image: url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);
+}
+.ui-menu .ui-menu-divider {
+ margin: 5px -2px 5px -2px;
+ height: 0;
+ font-size: 0;
+ line-height: 0;
+ border-width: 1px 0 0 0;
+}
+.ui-menu .ui-menu-item a {
+ text-decoration: none;
+ display: block;
+ padding: 2px .4em;
+ line-height: 1.5;
+ min-height: 0; /* support: IE7 */
+ font-weight: normal;
+}
+.ui-menu .ui-menu-item a.ui-state-focus,
+.ui-menu .ui-menu-item a.ui-state-active {
+ font-weight: normal;
+ margin: -1px;
+}
+
+.ui-menu .ui-state-disabled {
+ font-weight: normal;
+ margin: .4em 0 .2em;
+ line-height: 1.5;
+}
+.ui-menu .ui-state-disabled a {
+ cursor: default;
+}
+
+/* icon support */
+.ui-menu-icons {
+ position: relative;
+}
+.ui-menu-icons .ui-menu-item a {
+ position: relative;
+ padding-left: 2em;
+}
+
+/* left-aligned */
+.ui-menu .ui-icon {
+ position: absolute;
+ top: .2em;
+ left: .2em;
+}
+
+/* right-aligned */
+.ui-menu .ui-menu-icon {
+ position: static;
+ float: right;
+}
+.ui-progressbar {
+ height: 2em;
+ text-align: left;
+ overflow: hidden;
+}
+.ui-progressbar .ui-progressbar-value {
+ margin: -1px;
+ height: 100%;
+}
+.ui-progressbar .ui-progressbar-overlay {
+ background: url("images/animated-overlay.gif");
+ height: 100%;
+ filter: alpha(opacity=25);
+ opacity: 0.25;
+}
+.ui-progressbar-indeterminate .ui-progressbar-value {
+ background-image: none;
+}
+.ui-resizable {
+ position: relative;
+}
+.ui-resizable-handle {
+ position: absolute;
+ font-size: 0.1px;
+ display: block;
+}
+.ui-resizable-disabled .ui-resizable-handle,
+.ui-resizable-autohide .ui-resizable-handle {
+ display: none;
+}
+.ui-resizable-n {
+ cursor: n-resize;
+ height: 7px;
+ width: 100%;
+ top: -5px;
+ left: 0;
+}
+.ui-resizable-s {
+ cursor: s-resize;
+ height: 7px;
+ width: 100%;
+ bottom: -5px;
+ left: 0;
+}
+.ui-resizable-e {
+ cursor: e-resize;
+ width: 7px;
+ right: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-w {
+ cursor: w-resize;
+ width: 7px;
+ left: -5px;
+ top: 0;
+ height: 100%;
+}
+.ui-resizable-se {
+ cursor: se-resize;
+ width: 12px;
+ height: 12px;
+ right: 1px;
+ bottom: 1px;
+}
+.ui-resizable-sw {
+ cursor: sw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ bottom: -5px;
+}
+.ui-resizable-nw {
+ cursor: nw-resize;
+ width: 9px;
+ height: 9px;
+ left: -5px;
+ top: -5px;
+}
+.ui-resizable-ne {
+ cursor: ne-resize;
+ width: 9px;
+ height: 9px;
+ right: -5px;
+ top: -5px;
+}
+.ui-selectable-helper {
+ position: absolute;
+ z-index: 100;
+ border: 1px dotted black;
+}
+.ui-slider {
+ position: relative;
+ text-align: left;
+}
+.ui-slider .ui-slider-handle {
+ position: absolute;
+ z-index: 2;
+ width: 1.2em;
+ height: 1.2em;
+ cursor: default;
+}
+.ui-slider .ui-slider-range {
+ position: absolute;
+ z-index: 1;
+ font-size: .7em;
+ display: block;
+ border: 0;
+ background-position: 0 0;
+}
+
+/* For IE8 - See #6727 */
+.ui-slider.ui-state-disabled .ui-slider-handle,
+.ui-slider.ui-state-disabled .ui-slider-range {
+ filter: inherit;
+}
+
+.ui-slider-horizontal {
+ height: .8em;
+}
+.ui-slider-horizontal .ui-slider-handle {
+ top: -.3em;
+ margin-left: -.6em;
+}
+.ui-slider-horizontal .ui-slider-range {
+ top: 0;
+ height: 100%;
+}
+.ui-slider-horizontal .ui-slider-range-min {
+ left: 0;
+}
+.ui-slider-horizontal .ui-slider-range-max {
+ right: 0;
+}
+
+.ui-slider-vertical {
+ width: .8em;
+ height: 100px;
+}
+.ui-slider-vertical .ui-slider-handle {
+ left: -.3em;
+ margin-left: 0;
+ margin-bottom: -.6em;
+}
+.ui-slider-vertical .ui-slider-range {
+ left: 0;
+ width: 100%;
+}
+.ui-slider-vertical .ui-slider-range-min {
+ bottom: 0;
+}
+.ui-slider-vertical .ui-slider-range-max {
+ top: 0;
+}
+.ui-spinner {
+ position: relative;
+ display: inline-block;
+ overflow: hidden;
+ padding: 0;
+ vertical-align: middle;
+}
+.ui-spinner-input {
+ border: none;
+ background: none;
+ color: inherit;
+ padding: 0;
+ margin: .2em 0;
+ vertical-align: middle;
+ margin-left: .4em;
+ margin-right: 22px;
+}
+.ui-spinner-button {
+ width: 16px;
+ height: 50%;
+ font-size: .5em;
+ padding: 0;
+ margin: 0;
+ text-align: center;
+ position: absolute;
+ cursor: default;
+ display: block;
+ overflow: hidden;
+ right: 0;
+}
+/* more specificity required here to override default borders */
+.ui-spinner a.ui-spinner-button {
+ border-top: none;
+ border-bottom: none;
+ border-right: none;
+}
+/* vertically center icon */
+.ui-spinner .ui-icon {
+ position: absolute;
+ margin-top: -8px;
+ top: 50%;
+ left: 0;
+}
+.ui-spinner-up {
+ top: 0;
+}
+.ui-spinner-down {
+ bottom: 0;
+}
+
+/* TR overrides */
+.ui-spinner .ui-icon-triangle-1-s {
+ /* need to fix icons sprite */
+ background-position: -65px -16px;
+}
+.ui-tabs {
+ position: relative;/* position: relative prevents IE scroll bug (element with position: relative inside container with overflow: auto appear as "fixed") */
+ padding: .2em;
+}
+.ui-tabs .ui-tabs-nav {
+ margin: 0;
+ padding: .2em .2em 0;
+}
+.ui-tabs .ui-tabs-nav li {
+ list-style: none;
+ float: left;
+ position: relative;
+ top: 0;
+ margin: 1px .2em 0 0;
+ border-bottom-width: 0;
+ padding: 0;
+ white-space: nowrap;
+}
+.ui-tabs .ui-tabs-nav .ui-tabs-anchor {
+ float: left;
+ padding: .5em 1em;
+ text-decoration: none;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active {
+ margin-bottom: -1px;
+ padding-bottom: 1px;
+}
+.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,
+.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor {
+ cursor: text;
+}
+.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor {
+ cursor: pointer;
+}
+.ui-tabs .ui-tabs-panel {
+ display: block;
+ border-width: 0;
+ padding: 1em 1.4em;
+ background: none;
+}
+.ui-tooltip {
+ padding: 8px;
+ position: absolute;
+ z-index: 9999;
+ max-width: 300px;
+ -webkit-box-shadow: 0 0 5px #aaa;
+ box-shadow: 0 0 5px #aaa;
+}
+body .ui-tooltip {
+ border-width: 2px;
+}
+
+/* Component containers
+----------------------------------*/
+.ui-widget {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1.1em;
+}
+.ui-widget .ui-widget {
+ font-size: 1em;
+}
+.ui-widget input,
+.ui-widget select,
+.ui-widget textarea,
+.ui-widget button {
+ font-family: Verdana,Arial,sans-serif;
+ font-size: 1em;
+}
+.ui-widget-content {
+ border: 1px solid #aaaaaa;
+ background: #ffffff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;
+ color: #222222;
+}
+.ui-widget-content a {
+ color: #222222;
+}
+.ui-widget-header {
+ border: 1px solid #aaaaaa;
+ background: #cccccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;
+ color: #222222;
+ font-weight: bold;
+}
+.ui-widget-header a {
+ color: #222222;
+}
+
+/* Interaction states
+----------------------------------*/
+.ui-state-default,
+.ui-widget-content .ui-state-default,
+.ui-widget-header .ui-state-default {
+ border: 1px solid #d3d3d3;
+ background: #e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;
+ font-weight: normal;
+ color: #555555;
+}
+.ui-state-default a,
+.ui-state-default a:link,
+.ui-state-default a:visited {
+ color: #555555;
+ text-decoration: none;
+}
+.ui-state-hover,
+.ui-widget-content .ui-state-hover,
+.ui-widget-header .ui-state-hover,
+.ui-state-focus,
+.ui-widget-content .ui-state-focus,
+.ui-widget-header .ui-state-focus {
+ border: 1px solid #999999;
+ background: #dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
+}
+.ui-state-hover a,
+.ui-state-hover a:hover,
+.ui-state-hover a:link,
+.ui-state-hover a:visited,
+.ui-state-focus a,
+.ui-state-focus a:hover,
+.ui-state-focus a:link,
+.ui-state-focus a:visited {
+ color: #212121;
+ text-decoration: none;
+}
+.ui-state-active,
+.ui-widget-content .ui-state-active,
+.ui-widget-header .ui-state-active {
+ border: 1px solid #aaaaaa;
+ background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;
+ font-weight: normal;
+ color: #212121;
+}
+.ui-state-active a,
+.ui-state-active a:link,
+.ui-state-active a:visited {
+ color: #212121;
+ text-decoration: none;
+}
+
+/* Interaction Cues
+----------------------------------*/
+.ui-state-highlight,
+.ui-widget-content .ui-state-highlight,
+.ui-widget-header .ui-state-highlight {
+ border: 1px solid #fcefa1;
+ background: #fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;
+ color: #363636;
+}
+.ui-state-highlight a,
+.ui-widget-content .ui-state-highlight a,
+.ui-widget-header .ui-state-highlight a {
+ color: #363636;
+}
+.ui-state-error,
+.ui-widget-content .ui-state-error,
+.ui-widget-header .ui-state-error {
+ border: 1px solid #cd0a0a;
+ background: #fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;
+ color: #cd0a0a;
+}
+.ui-state-error a,
+.ui-widget-content .ui-state-error a,
+.ui-widget-header .ui-state-error a {
+ color: #cd0a0a;
+}
+.ui-state-error-text,
+.ui-widget-content .ui-state-error-text,
+.ui-widget-header .ui-state-error-text {
+ color: #cd0a0a;
+}
+.ui-priority-primary,
+.ui-widget-content .ui-priority-primary,
+.ui-widget-header .ui-priority-primary {
+ font-weight: bold;
+}
+.ui-priority-secondary,
+.ui-widget-content .ui-priority-secondary,
+.ui-widget-header .ui-priority-secondary {
+ opacity: .7;
+ filter:Alpha(Opacity=70);
+ font-weight: normal;
+}
+.ui-state-disabled,
+.ui-widget-content .ui-state-disabled,
+.ui-widget-header .ui-state-disabled {
+ opacity: .35;
+ filter:Alpha(Opacity=35);
+ background-image: none;
+}
+.ui-state-disabled .ui-icon {
+ filter:Alpha(Opacity=35); /* For IE8 - See #6059 */
+}
+
+/* Icons
+----------------------------------*/
+
+/* states and images */
+.ui-icon {
+ width: 16px;
+ height: 16px;
+}
+.ui-icon,
+.ui-widget-content .ui-icon {
+ background-image: url(images/ui-icons_222222_256x240.png);
+}
+.ui-widget-header .ui-icon {
+ background-image: url(images/ui-icons_222222_256x240.png);
+}
+.ui-state-default .ui-icon {
+ background-image: url(images/ui-icons_888888_256x240.png);
+}
+.ui-state-hover .ui-icon,
+.ui-state-focus .ui-icon {
+ background-image: url(images/ui-icons_454545_256x240.png);
+}
+.ui-state-active .ui-icon {
+ background-image: url(images/ui-icons_454545_256x240.png);
+}
+.ui-state-highlight .ui-icon {
+ background-image: url(images/ui-icons_2e83ff_256x240.png);
+}
+.ui-state-error .ui-icon,
+.ui-state-error-text .ui-icon {
+ background-image: url(images/ui-icons_cd0a0a_256x240.png);
+}
+
+/* positioning */
+.ui-icon-blank { background-position: 16px 16px; }
+.ui-icon-carat-1-n { background-position: 0 0; }
+.ui-icon-carat-1-ne { background-position: -16px 0; }
+.ui-icon-carat-1-e { background-position: -32px 0; }
+.ui-icon-carat-1-se { background-position: -48px 0; }
+.ui-icon-carat-1-s { background-position: -64px 0; }
+.ui-icon-carat-1-sw { background-position: -80px 0; }
+.ui-icon-carat-1-w { background-position: -96px 0; }
+.ui-icon-carat-1-nw { background-position: -112px 0; }
+.ui-icon-carat-2-n-s { background-position: -128px 0; }
+.ui-icon-carat-2-e-w { background-position: -144px 0; }
+.ui-icon-triangle-1-n { background-position: 0 -16px; }
+.ui-icon-triangle-1-ne { background-position: -16px -16px; }
+.ui-icon-triangle-1-e { background-position: -32px -16px; }
+.ui-icon-triangle-1-se { background-position: -48px -16px; }
+.ui-icon-triangle-1-s { background-position: -64px -16px; }
+.ui-icon-triangle-1-sw { background-position: -80px -16px; }
+.ui-icon-triangle-1-w { background-position: -96px -16px; }
+.ui-icon-triangle-1-nw { background-position: -112px -16px; }
+.ui-icon-triangle-2-n-s { background-position: -128px -16px; }
+.ui-icon-triangle-2-e-w { background-position: -144px -16px; }
+.ui-icon-arrow-1-n { background-position: 0 -32px; }
+.ui-icon-arrow-1-ne { background-position: -16px -32px; }
+.ui-icon-arrow-1-e { background-position: -32px -32px; }
+.ui-icon-arrow-1-se { background-position: -48px -32px; }
+.ui-icon-arrow-1-s { background-position: -64px -32px; }
+.ui-icon-arrow-1-sw { background-position: -80px -32px; }
+.ui-icon-arrow-1-w { background-position: -96px -32px; }
+.ui-icon-arrow-1-nw { background-position: -112px -32px; }
+.ui-icon-arrow-2-n-s { background-position: -128px -32px; }
+.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; }
+.ui-icon-arrow-2-e-w { background-position: -160px -32px; }
+.ui-icon-arrow-2-se-nw { background-position: -176px -32px; }
+.ui-icon-arrowstop-1-n { background-position: -192px -32px; }
+.ui-icon-arrowstop-1-e { background-position: -208px -32px; }
+.ui-icon-arrowstop-1-s { background-position: -224px -32px; }
+.ui-icon-arrowstop-1-w { background-position: -240px -32px; }
+.ui-icon-arrowthick-1-n { background-position: 0 -48px; }
+.ui-icon-arrowthick-1-ne { background-position: -16px -48px; }
+.ui-icon-arrowthick-1-e { background-position: -32px -48px; }
+.ui-icon-arrowthick-1-se { background-position: -48px -48px; }
+.ui-icon-arrowthick-1-s { background-position: -64px -48px; }
+.ui-icon-arrowthick-1-sw { background-position: -80px -48px; }
+.ui-icon-arrowthick-1-w { background-position: -96px -48px; }
+.ui-icon-arrowthick-1-nw { background-position: -112px -48px; }
+.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; }
+.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; }
+.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; }
+.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; }
+.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; }
+.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; }
+.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; }
+.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; }
+.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; }
+.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; }
+.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; }
+.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; }
+.ui-icon-arrowreturn-1-w { background-position: -64px -64px; }
+.ui-icon-arrowreturn-1-n { background-position: -80px -64px; }
+.ui-icon-arrowreturn-1-e { background-position: -96px -64px; }
+.ui-icon-arrowreturn-1-s { background-position: -112px -64px; }
+.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; }
+.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; }
+.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; }
+.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; }
+.ui-icon-arrow-4 { background-position: 0 -80px; }
+.ui-icon-arrow-4-diag { background-position: -16px -80px; }
+.ui-icon-extlink { background-position: -32px -80px; }
+.ui-icon-newwin { background-position: -48px -80px; }
+.ui-icon-refresh { background-position: -64px -80px; }
+.ui-icon-shuffle { background-position: -80px -80px; }
+.ui-icon-transfer-e-w { background-position: -96px -80px; }
+.ui-icon-transferthick-e-w { background-position: -112px -80px; }
+.ui-icon-folder-collapsed { background-position: 0 -96px; }
+.ui-icon-folder-open { background-position: -16px -96px; }
+.ui-icon-document { background-position: -32px -96px; }
+.ui-icon-document-b { background-position: -48px -96px; }
+.ui-icon-note { background-position: -64px -96px; }
+.ui-icon-mail-closed { background-position: -80px -96px; }
+.ui-icon-mail-open { background-position: -96px -96px; }
+.ui-icon-suitcase { background-position: -112px -96px; }
+.ui-icon-comment { background-position: -128px -96px; }
+.ui-icon-person { background-position: -144px -96px; }
+.ui-icon-print { background-position: -160px -96px; }
+.ui-icon-trash { background-position: -176px -96px; }
+.ui-icon-locked { background-position: -192px -96px; }
+.ui-icon-unlocked { background-position: -208px -96px; }
+.ui-icon-bookmark { background-position: -224px -96px; }
+.ui-icon-tag { background-position: -240px -96px; }
+.ui-icon-home { background-position: 0 -112px; }
+.ui-icon-flag { background-position: -16px -112px; }
+.ui-icon-calendar { background-position: -32px -112px; }
+.ui-icon-cart { background-position: -48px -112px; }
+.ui-icon-pencil { background-position: -64px -112px; }
+.ui-icon-clock { background-position: -80px -112px; }
+.ui-icon-disk { background-position: -96px -112px; }
+.ui-icon-calculator { background-position: -112px -112px; }
+.ui-icon-zoomin { background-position: -128px -112px; }
+.ui-icon-zoomout { background-position: -144px -112px; }
+.ui-icon-search { background-position: -160px -112px; }
+.ui-icon-wrench { background-position: -176px -112px; }
+.ui-icon-gear { background-position: -192px -112px; }
+.ui-icon-heart { background-position: -208px -112px; }
+.ui-icon-star { background-position: -224px -112px; }
+.ui-icon-link { background-position: -240px -112px; }
+.ui-icon-cancel { background-position: 0 -128px; }
+.ui-icon-plus { background-position: -16px -128px; }
+.ui-icon-plusthick { background-position: -32px -128px; }
+.ui-icon-minus { background-position: -48px -128px; }
+.ui-icon-minusthick { background-position: -64px -128px; }
+.ui-icon-close { background-position: -80px -128px; }
+.ui-icon-closethick { background-position: -96px -128px; }
+.ui-icon-key { background-position: -112px -128px; }
+.ui-icon-lightbulb { background-position: -128px -128px; }
+.ui-icon-scissors { background-position: -144px -128px; }
+.ui-icon-clipboard { background-position: -160px -128px; }
+.ui-icon-copy { background-position: -176px -128px; }
+.ui-icon-contact { background-position: -192px -128px; }
+.ui-icon-image { background-position: -208px -128px; }
+.ui-icon-video { background-position: -224px -128px; }
+.ui-icon-script { background-position: -240px -128px; }
+.ui-icon-alert { background-position: 0 -144px; }
+.ui-icon-info { background-position: -16px -144px; }
+.ui-icon-notice { background-position: -32px -144px; }
+.ui-icon-help { background-position: -48px -144px; }
+.ui-icon-check { background-position: -64px -144px; }
+.ui-icon-bullet { background-position: -80px -144px; }
+.ui-icon-radio-on { background-position: -96px -144px; }
+.ui-icon-radio-off { background-position: -112px -144px; }
+.ui-icon-pin-w { background-position: -128px -144px; }
+.ui-icon-pin-s { background-position: -144px -144px; }
+.ui-icon-play { background-position: 0 -160px; }
+.ui-icon-pause { background-position: -16px -160px; }
+.ui-icon-seek-next { background-position: -32px -160px; }
+.ui-icon-seek-prev { background-position: -48px -160px; }
+.ui-icon-seek-end { background-position: -64px -160px; }
+.ui-icon-seek-start { background-position: -80px -160px; }
+/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */
+.ui-icon-seek-first { background-position: -80px -160px; }
+.ui-icon-stop { background-position: -96px -160px; }
+.ui-icon-eject { background-position: -112px -160px; }
+.ui-icon-volume-off { background-position: -128px -160px; }
+.ui-icon-volume-on { background-position: -144px -160px; }
+.ui-icon-power { background-position: 0 -176px; }
+.ui-icon-signal-diag { background-position: -16px -176px; }
+.ui-icon-signal { background-position: -32px -176px; }
+.ui-icon-battery-0 { background-position: -48px -176px; }
+.ui-icon-battery-1 { background-position: -64px -176px; }
+.ui-icon-battery-2 { background-position: -80px -176px; }
+.ui-icon-battery-3 { background-position: -96px -176px; }
+.ui-icon-circle-plus { background-position: 0 -192px; }
+.ui-icon-circle-minus { background-position: -16px -192px; }
+.ui-icon-circle-close { background-position: -32px -192px; }
+.ui-icon-circle-triangle-e { background-position: -48px -192px; }
+.ui-icon-circle-triangle-s { background-position: -64px -192px; }
+.ui-icon-circle-triangle-w { background-position: -80px -192px; }
+.ui-icon-circle-triangle-n { background-position: -96px -192px; }
+.ui-icon-circle-arrow-e { background-position: -112px -192px; }
+.ui-icon-circle-arrow-s { background-position: -128px -192px; }
+.ui-icon-circle-arrow-w { background-position: -144px -192px; }
+.ui-icon-circle-arrow-n { background-position: -160px -192px; }
+.ui-icon-circle-zoomin { background-position: -176px -192px; }
+.ui-icon-circle-zoomout { background-position: -192px -192px; }
+.ui-icon-circle-check { background-position: -208px -192px; }
+.ui-icon-circlesmall-plus { background-position: 0 -208px; }
+.ui-icon-circlesmall-minus { background-position: -16px -208px; }
+.ui-icon-circlesmall-close { background-position: -32px -208px; }
+.ui-icon-squaresmall-plus { background-position: -48px -208px; }
+.ui-icon-squaresmall-minus { background-position: -64px -208px; }
+.ui-icon-squaresmall-close { background-position: -80px -208px; }
+.ui-icon-grip-dotted-vertical { background-position: 0 -224px; }
+.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; }
+.ui-icon-grip-solid-vertical { background-position: -32px -224px; }
+.ui-icon-grip-solid-horizontal { background-position: -48px -224px; }
+.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; }
+.ui-icon-grip-diagonal-se { background-position: -80px -224px; }
+
+
+/* Misc visuals
+----------------------------------*/
+
+/* Corner radius */
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-left,
+.ui-corner-tl {
+ border-top-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-top,
+.ui-corner-right,
+.ui-corner-tr {
+ border-top-right-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-left,
+.ui-corner-bl {
+ border-bottom-left-radius: 4px;
+}
+.ui-corner-all,
+.ui-corner-bottom,
+.ui-corner-right,
+.ui-corner-br {
+ border-bottom-right-radius: 4px;
+}
+
+/* Overlays */
+.ui-widget-overlay {
+ background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
+ opacity: .3;
+ filter: Alpha(Opacity=30);
+}
+.ui-widget-shadow {
+ margin: -8px 0 0 -8px;
+ padding: 8px;
+ background: #aaaaaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;
+ opacity: .3;
+ filter: Alpha(Opacity=30);
+ border-radius: 8px;
+}
diff --git a/tutor/app/assets/stylesheets/lecturers.css b/tutor/app/assets/stylesheets/lecturers.css
deleted file mode 100644
index 2f23dc87..00000000
--- a/tutor/app/assets/stylesheets/lecturers.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.tabs {
- width:100%;
- display:inline-block;
-}
-.tab-links:after {
- display:block;
- clear:both;
- content:'';
-}
-.tab-links li {
- margin:0px 5px;
- float:left;
- list-style:none;
-}
-.tab-links a {
- padding:9px 15px;
- display:inline-block;
- border-radius:3px 3px 0px 0px;
- background:#7FB5DA;
- font-size:16px;
- font-weight:600;
- color:#4c4c4c;
- transition:all linear 0.15s;
-}
-.tab-links a:hover {
- background:#a7cce5;
- text-decoration:none;
-}
-li.active a, li.active a:hover {
- background:#fff;
- color:#4c4c4c;
-}
-.tab-content {
- padding:15px;
- border-radius:3px;
- box-shadow:-1px 1px 1px rgba(0,0,0,0.15);
- background:#fff;
-}
-.tab {
- display:none;
-}
-.tab.active {
- display:block;
-}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/posts.css.scss b/tutor/app/assets/stylesheets/posts.css.scss
index 1a7e1539..bec73e3c 100644
--- a/tutor/app/assets/stylesheets/posts.css.scss
+++ b/tutor/app/assets/stylesheets/posts.css.scss
@@ -1,3 +1,8 @@
-// Place all the styles related to the posts controller here.
-// They will automatically be included in application.css.
-// You can use Sass (SCSS) here: http://sass-lang.com/
+.greyLine {
+ border-bottom: 1px solid #D0D0D0;
+}
+
+.reply {
+ padding-left:30px;
+ border-bottom: 1px solid #D0D0D0;
+}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/replies.css.scss b/tutor/app/assets/stylesheets/replies.css.scss
new file mode 100644
index 00000000..89c9ee11
--- /dev/null
+++ b/tutor/app/assets/stylesheets/replies.css.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the replies controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: http://sass-lang.com/
diff --git a/tutor/app/assets/stylesheets/solutions.css b/tutor/app/assets/stylesheets/solutions.css
index 1ad66d56..ab2e5dc5 100644
--- a/tutor/app/assets/stylesheets/solutions.css
+++ b/tutor/app/assets/stylesheets/solutions.css
@@ -73,7 +73,7 @@ textarea {
border-width: thin;
max-height: 150px;
min-height: 150px;
- background: #3d1451;
+ background: white;
overflow: scroll;
overflow-y: scroll;
overflow-x: scroll;
@@ -114,4 +114,8 @@ textarea {
border-style: solid;
border-width: 3px;
font-family: monospace;
+}
+
+.hidden {
+ visibility: false;
}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/students.css b/tutor/app/assets/stylesheets/students.css
deleted file mode 100644
index 2f23dc87..00000000
--- a/tutor/app/assets/stylesheets/students.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.tabs {
- width:100%;
- display:inline-block;
-}
-.tab-links:after {
- display:block;
- clear:both;
- content:'';
-}
-.tab-links li {
- margin:0px 5px;
- float:left;
- list-style:none;
-}
-.tab-links a {
- padding:9px 15px;
- display:inline-block;
- border-radius:3px 3px 0px 0px;
- background:#7FB5DA;
- font-size:16px;
- font-weight:600;
- color:#4c4c4c;
- transition:all linear 0.15s;
-}
-.tab-links a:hover {
- background:#a7cce5;
- text-decoration:none;
-}
-li.active a, li.active a:hover {
- background:#fff;
- color:#4c4c4c;
-}
-.tab-content {
- padding:15px;
- border-radius:3px;
- box-shadow:-1px 1px 1px rgba(0,0,0,0.15);
- background:#fff;
-}
-.tab {
- display:none;
-}
-.tab.active {
- display:block;
-}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/style.css b/tutor/app/assets/stylesheets/style.css
new file mode 100644
index 00000000..8578dc19
--- /dev/null
+++ b/tutor/app/assets/stylesheets/style.css
@@ -0,0 +1 @@
+.handle:hover{cursor: move;}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/tabs.css b/tutor/app/assets/stylesheets/tabs.css
new file mode 100644
index 00000000..3502493b
--- /dev/null
+++ b/tutor/app/assets/stylesheets/tabs.css
@@ -0,0 +1,101 @@
+.tabs {
+ width:80%;
+ display:inline-block;
+}
+
+.tab-links:after {
+ display:block;
+ clear:both;
+ content:'';
+}
+
+.tab-links li {
+ margin:0px 5px;
+ float:left;
+ list-style:none;
+}
+
+.tab-links a {
+ padding:9px 15px;
+ display:inline-block;
+ border-radius:3px 3px 0px 0px;
+ background:#7FB5DA;
+ font-size:16px;
+ font-weight:600;
+ color:#4c4c4c;
+ transition:all linear 0.15s;
+}
+
+.tab-links a:hover {
+ background:#a7cce5;
+ text-decoration:none;
+}
+
+li.active a, li.active a:hover {
+ background:#fff;
+ color:#4c4c4c;
+}
+
+.tab-content {
+ padding:15px;
+ border-radius:3px;
+ box-shadow:-1px 1px 1px rgba(0,0,0,0.15);
+ background:#fff;
+}
+
+.tab {
+ display:none;
+}
+
+.tab.active {
+ display:block;
+}
+
+.profile_picture {
+ width:200px;
+ height:200px;
+ /*background-color:#999;*/
+ float:left;"
+}
+
+.navigation-list li{
+ border-radius:3px 3px 0px 0px;
+ background:#7FB5DA;
+ font-size:16px;
+ font-weight:600;
+ color:#4c4c4c;
+
+}
+.navigation-list li a{
+ color : black;
+ font-weight:600;
+ text-align: center;
+ transition:all linear 0.15s;
+
+}
+
+.navigation-list {
+ margin-bottom: 0;
+ padding-left: 0;
+ list-style: none;
+}
+
+.navigation-list > li {
+ position: relative;
+ display: block;
+}
+
+.navigation-list > li > a {
+ position: relative;
+ display: block;
+ padding: 10px 15px;
+}
+
+.navigation-list > li > a:hover{
+ text-decoration: none;
+ background-color: #a7cce5;
+}
+
+.navigation-list > li.disabled > a {
+ color: #b4bcc2;
+}
diff --git a/tutor/app/assets/stylesheets/teaching_assistants.css b/tutor/app/assets/stylesheets/teaching_assistants.css
deleted file mode 100644
index 2f23dc87..00000000
--- a/tutor/app/assets/stylesheets/teaching_assistants.css
+++ /dev/null
@@ -1,44 +0,0 @@
-.tabs {
- width:100%;
- display:inline-block;
-}
-.tab-links:after {
- display:block;
- clear:both;
- content:'';
-}
-.tab-links li {
- margin:0px 5px;
- float:left;
- list-style:none;
-}
-.tab-links a {
- padding:9px 15px;
- display:inline-block;
- border-radius:3px 3px 0px 0px;
- background:#7FB5DA;
- font-size:16px;
- font-weight:600;
- color:#4c4c4c;
- transition:all linear 0.15s;
-}
-.tab-links a:hover {
- background:#a7cce5;
- text-decoration:none;
-}
-li.active a, li.active a:hover {
- background:#fff;
- color:#4c4c4c;
-}
-.tab-content {
- padding:15px;
- border-radius:3px;
- box-shadow:-1px 1px 1px rgba(0,0,0,0.15);
- background:#fff;
-}
-.tab {
- display:none;
-}
-.tab.active {
- display:block;
-}
\ No newline at end of file
diff --git a/tutor/app/assets/stylesheets/test_cases.css b/tutor/app/assets/stylesheets/test_cases.css
new file mode 100644
index 00000000..c8b8a4b9
--- /dev/null
+++ b/tutor/app/assets/stylesheets/test_cases.css
@@ -0,0 +1,15 @@
+a.Lst {
+ font-size: 30px;
+ line-height: 0px;
+ padding-left: 0px;
+ font-weight: normal;
+}
+
+.red {
+ color: #FF0000;
+}
+
+.bold {
+ font-weight: bold;
+ }
+
diff --git a/tutor/app/assets/stylesheets/tips.css b/tutor/app/assets/stylesheets/tips.css
new file mode 100644
index 00000000..ced3bc84
--- /dev/null
+++ b/tutor/app/assets/stylesheets/tips.css
@@ -0,0 +1,6 @@
+a.Lst {
+ font-size: 30px;
+ line-height: 0px;
+ padding-left: 0px;
+ font-weight: normal;
+}
diff --git a/tutor/app/controllers/application_controller.rb b/tutor/app/controllers/application_controller.rb
index ad7a561a..897c93b6 100644
--- a/tutor/app/controllers/application_controller.rb
+++ b/tutor/app/controllers/application_controller.rb
@@ -5,45 +5,87 @@ class ApplicationController < ActionController::Base
before_action :authenticate!
protect_from_forgery with: :exception
before_action :update_sanitized_params, if: :devise_controller?
+ before_action :check_resource, if: :devise_controller?
- # [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
- # Permits some fields to be passed through sign up forms to update the lecturer,
- # student, and teaching_assistant models
- # Parameters: None
- # Returns: None
- # Author: Khaled Helmy
+ rescue_from Exception, :with => :render_not_found
+
private
- def update_sanitized_params
- if "#{resource_name}" == "lecturer"
- devise_parameter_sanitizer.for(:sign_up) {
- |u| u.permit(:name, :email, :password, :password_confirmation, :gender, :dob, :degree,
- :university, :department, :profile_image, :profile_image_cache)
- }
- elsif "#{resource_name}" == "student"
- devise_parameter_sanitizer.for(:sign_up) {
- |u| u.permit(:name, :email, :password, :password_confirmation, :gender, :dob, :university,
- :faculty, :major, :semester, :advising, :probation, :profile_image, :profile_image_cache)
- }
- elsif "#{resource_name}" == "teaching_assistant"
- devise_parameter_sanitizer.for(:sign_up) {
- |u| u.permit(:name, :email, :password, :password_confirmation, :gender, :dob,
- :graduated_from, :graduated_year, :degree, :university, :department,
- :profile_image, :profile_image_cache)
- }
+
+ def render_not_found(exception)
+ render "/public/404.html"
+ end
+
+ def render_error(exception)
+ render :template => "/public/500.html", :status => 500
+ end
+
+ # [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
+ # Permits some fields to be passed through sign up forms to update the lecturer,
+ # student, and teaching_assistant models
+ # Parameters: None
+ # Returns: None
+ # Author: Khaled Helmy
+ def update_sanitized_params
+ if "#{resource_name}" == "lecturer"
+ devise_parameter_sanitizer.for(:sign_up) {
+ |lecturer| lecturer.permit(:name, :email,
+ :password, :password_confirmation, :gender,
+ :dob, :degree, :university, :department,
+ :profile_image, :profile_image_cache)
+ }
+ elsif "#{resource_name}" == "student"
+ devise_parameter_sanitizer.for(:sign_up) {
+ |student| student.permit(:name, :email,
+ :password, :password_confirmation, :gender,
+ :dob, :university, :faculty, :major, :semester,
+ :advising, :probation, :profile_image,
+ :profile_image_cache)
+ }
+ elsif "#{resource_name}" == "teaching_assistant"
+ devise_parameter_sanitizer.for(:sign_up) {
+ |teaching_assistant| teaching_assistant.permit(:name,
+ :email, :password, :password_confirmation, :gender,
+ :dob, :graduated_from, :graduated_year, :degree,
+ :university, :department, :profile_image,
+ :profile_image_cache)
+ }
+ end
+ end
+
+ # [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
+ # Checks which type of users is currently signed in and if there
+ # is any, it blocks other types of users from using the
+ # authentication system
+ # Parameters: None
+ # Returns: None
+ # Author: Khaled Helmy
+ def check_resource
+ if student_signed_in?
+ if "#{resource_name}" == "lecturer" or "#{resource_name}" == "teaching_assistant"
+ redirect_to :root
+ end
+ elsif lecturer_signed_in?
+ if "#{resource_name}" == "student" or "#{resource_name}" == "teaching_assistant"
+ redirect_to :root
+ end
+ elsif teaching_assistant_signed_in?
+ if "#{resource_name}" == "lecturer" or "#{resource_name}" == "student"
+ redirect_to :root
+ end
+ end
end
- end
-
- # [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
- # Checks if a user is signed-in in order to be used in authentication over different pages
- # where they are redirected to homepage if they aren't authenticated
- # Parameters: None
- # Returns: None
- # Author: Khaled Helmy
- def authenticate!
- unless signed_in? or params[:controller].include?"devise"
- flash[:notice] = "You're not logged in!"
- redirect_to :root
+
+ # [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
+ # Checks if a user is signed-in in order to be used in authentication over different pages
+ # where they are redirected to homepage if they aren't authenticated
+ # Parameters: None
+ # Returns: None
+ # Author: Khaled Helmy
+ def authenticate!
+ unless signed_in? or params[:controller].include?"devise"
+ flash[:notice] = "You're not logged in!"
+ redirect_to :root
+ end
end
- end
end
\ No newline at end of file
diff --git a/tutor/app/controllers/courses_controller.rb b/tutor/app/controllers/courses_controller.rb
index 22242e91..8881048e 100644
--- a/tutor/app/controllers/courses_controller.rb
+++ b/tutor/app/controllers/courses_controller.rb
@@ -110,6 +110,7 @@ def create
@new_course.year = course_params[:year]
@new_course.semester = course_params[:semester]
@new_course.description = course_params[:description]
+ @new_course.university = current_lecturer.university
if @new_course.save
current_lecturer.courses << @new_course
@discussion_board = DiscussionBoard.new
@@ -256,4 +257,4 @@ def to_boolean value
end
end
-end
\ No newline at end of file
+end
diff --git a/tutor/app/controllers/hints_controller.rb b/tutor/app/controllers/hints_controller.rb
index 26c3e540..85df8076 100644
--- a/tutor/app/controllers/hints_controller.rb
+++ b/tutor/app/controllers/hints_controller.rb
@@ -1,4 +1,16 @@
class HintsController < ApplicationController
+
+ # [View hints and tips-Story 4.22]
+ # It fetches from database all the previous hints.
+ # Parameters:
+ # @hints: All the previous hints that had been entered before.
+ # @hints_check: All the previous hints that had been entered before to check if it is a hint or a tip.
+ # Returns: none
+ # Author: Nadine Adel
+ def index
+ @hints = Hint.all
+ @hints_check = Hint.all
+ end
# [Edit helping hints - Story 4.13 ]
# This action creates the form and retrives the data of the selected problem
@@ -55,4 +67,5 @@ def update
def hint_params
params.require(:hint).permit(:message, :submission_counter, :id)
end
-end
\ No newline at end of file
+end
+
diff --git a/tutor/app/controllers/model_answers_controller.rb b/tutor/app/controllers/model_answers_controller.rb
index 7f94c0a4..e460146a 100644
--- a/tutor/app/controllers/model_answers_controller.rb
+++ b/tutor/app/controllers/model_answers_controller.rb
@@ -46,6 +46,27 @@ def create
end
end
+ # [Remove Answer - Story 4.17]
+ # This action takes the model answer id, and check if it is not the last model answer in the
+ # problem and remove it from the database and then redirects the user to the edit problem
+ # page of the problem that had the answer with a "Model Answer successfully Deleted" message.
+ # Parameters:
+ # params[:id]: The current model answer's id
+ # Returns:
+ # flash[:notice]: A message indicating the success of the deletion
+ # Author: Ahmed Atef
+ def destroy
+ @model_answer = ModelAnswer.find_by_id(params[:id])
+ @current = Problem.find_by_id(@model_answer.problem_id)
+ if @current.model_answers.count == 1
+ flash[:notice] = "Cannot delete problem's last model answer"
+ redirect_to :back and return
+ elsif @model_answer.destroy
+ flash[:notice] = "Answer successfully Deleted"
+ redirect_to(:controller => 'problems', :action => 'edit', :id => @current.id)
+ end
+ end
+
# [Edit answer story 4.7]
# Answer that has been created before is edited
# Parameters:
@@ -77,13 +98,13 @@ def update
@answer = ModelAnswer.find(params[:id])
if @answer.update_attributes(post_params)
flash[:notice] = "Your Answer is now updated"
- redirect_to :controller => 'problems', :action => 'edit',
- :id => session[:problem_id]
+ redirect_to :controller => 'problems', :action => 'edit', :id => session[:problem_id]
else
- render :action=>'edit', :problem_id => @answer.problem_id
+ render :action=>'edit', :problem_id => @answer.problem_id
+
end
end
-
+
# [Add answer story 4.6]
# It shows answer that was entered before.
# Parameters:
diff --git a/tutor/app/controllers/posts_controller.rb b/tutor/app/controllers/posts_controller.rb
index afb13e76..345a576b 100644
--- a/tutor/app/controllers/posts_controller.rb
+++ b/tutor/app/controllers/posts_controller.rb
@@ -53,7 +53,9 @@ def destroy
# Author: Ahmed Atef
def show
@post = Post.find(params[:id])
- @replies = @post.replies.order("created_at desc")
+ @post.views_count = @post.views_count + 1
+ @post.save
+ @replies = @post.replies.order("created_at asc")
end
# [Add Post - Story 1.13]
diff --git a/tutor/app/controllers/problems_controller.rb b/tutor/app/controllers/problems_controller.rb
index fe3b2c73..dc839854 100644
--- a/tutor/app/controllers/problems_controller.rb
+++ b/tutor/app/controllers/problems_controller.rb
@@ -1,4 +1,5 @@
class ProblemsController < ApplicationController
+
# [Solve a problem - Story 3.1]
# Displays the problem statment that the user chose
# Parameters:
diff --git a/tutor/app/controllers/replies_controller.rb b/tutor/app/controllers/replies_controller.rb
new file mode 100644
index 00000000..0844135d
--- /dev/null
+++ b/tutor/app/controllers/replies_controller.rb
@@ -0,0 +1,75 @@
+class RepliesController < ApplicationController
+
+ # [Edit a Reply - Story 1.19]
+ # Description: Shows the replies with id
+ # Parameters:
+ # replies: reply with spacific id
+ # Returns:
+ # returns a the record in JSON form
+ # Author: Ahmed Mohamed Magdi
+ def show
+ reply = Reply.find_by_id(params[:id])
+ render json: reply
+ end
+
+ # [Post a Reply - Story 1.14]
+ # Description: Creates new reply and save to the database
+ # Parameters:
+ # reply_content: The content of the reply
+ # id: Post id for getting the post
+ # Returns:
+ # returns a the record in JSON form
+ # Author: Ahmed Mohamed Magdi
+ def create
+ reply_content = params[:content]
+ post = Post.find_by_id(params[:id])
+ reply = Reply.new
+ reply.content = reply_content
+ if lecturer_signed_in?
+ current_lecturer.replies << reply
+ elsif teaching_assistant_signed_in?
+ current_teaching_assistant.replies << reply
+ else
+ current_student.replies << reply
+ end
+ post.replies << reply
+ render json: reply
+ end
+
+ # [Delete a Reply - Story 1.16]
+ # Description: Deleting reply from the database
+ # Parameters:
+ # id: reply id for getting the reply
+ # Returns:
+ # true: if it successed is destroying the reply
+ # false: if it fails is destroying the reply
+ # Author: Ahmed Mohamed Magdi
+ def destroy
+ reply = Reply.find(params[:id])
+ if reply.destroy
+ render json: true
+ else
+ render json: false
+ end
+ end
+
+ # [Edit a Reply - Story 1.19]
+ # Description: Updating a reply in the database
+ # Parameters:
+ # id: reply id for getting the reply
+ # Returns:
+ # true: if it successed is saving the reply
+ # false: if it fails is saving the reply
+ # Author: Ahmed Mohamed Magdi
+ def update
+ reply = Reply.find(params[:id])
+ data = params[:content]
+ reply.content = data
+ if reply.save
+ render json: true
+ else
+ render json: false
+ end
+ end
+
+end
\ No newline at end of file
diff --git a/tutor/app/controllers/solutions_controller.rb b/tutor/app/controllers/solutions_controller.rb
index 1cc642e6..02fc0416 100644
--- a/tutor/app/controllers/solutions_controller.rb
+++ b/tutor/app/controllers/solutions_controller.rb
@@ -8,22 +8,14 @@ class SolutionsController < ApplicationController
# Returns: none
# Author: MOHAMEDSAEED
def create
- if params[:commit] == 'Submit'
- compile_solution
- if flash[:compiler_fail] || flash[:alert]
- redirect_to :back and return
- end
- submit_no_ajax
- elsif params[:commit] == 'Compile'
- compile_solution
- redirect_to :back and return
- elsif params[:commit] == 'Run Test Case'
- compile_solution
- if flash[:compiler_fail] || flash[:alert]
- redirect_to :back and return
- end
- execute
- end
+ solution = Solution.new(solution_params)
+ solution.student_id = current_student.id
+ solution.length = solution.code.length
+ solution.status = 0
+ solution.save
+ test_cases = solution.problem.test_cases
+ result = Solution.validate(solution, test_cases)
+ render json: result
end
# [Compiler: Test - Story 3.15]
@@ -34,16 +26,15 @@ def create
# A flash message containing the appropriate reply
# Author: Ahmed Akram
def execute
- file_name = @solution.file_name
- if Executer.execute(file_name, input[:input], solution_params[:problem_id])
- output = Executer.get_output()
- flash[:msg] = output
- else
- output = Executer.get_runtime_error(file_name, 'CoolSoft')
- flash[:msg] = output[:error]
- flash[:exp] = output[:explanation]
+ if lecturer_signed_in? || teaching_assistant_signed_in?
+ render json: {}
end
- redirect_to :back
+ id = current_student.id
+ pid = params[:problem_id]
+ input = params[:code]
+ cases = if params[:input] then params[:input] else "" end
+ result = Executer.create_solution(id, pid, input, cases)
+ render json: result
end
# [Compiler: Compile - Story 3.4]
@@ -54,25 +45,18 @@ def execute
# Returns: none
# Author: Ahmed Moataz
def compile_solution
- @solution = Solution.new(solution_params)
- @solution.student_id = current_student.id
- @solution.length = @solution.code.length
- @solution.status = 0
- if @solution.save
- compiler_feedback = Compiler.compiler_feedback(@solution)
+ solution = Solution.new(solution_params)
+ solution.student_id = current_student.id
+ solution.length = solution.code.length
+ if solution.save
+ compiler_feedback = Compiler.compiler_feedback(solution)
if compiler_feedback[:success]
- @solution.status = 3
- flash[:compiler_success] = "Compilation Succeeded!"
- flash[:previous_code] = compiler_feedback[:previous_code]
+ solution.status = 3
else
- @solution.status = 2
- flash[:compiler_fail] = "Compilation Failed!"
- flash[:compiler_feedback] = compiler_feedback[:errors]
- flash[:previous_code] = compiler_feedback[:previous_code]
+ solution.status = 2
end
- @solution.save
- else
- flash[:alert] = "You did not write any code!"
+ solution.save
+ render json: compiler_feedback
end
end
@@ -98,7 +82,7 @@ def submit_no_ajax
# none
# Author: MOHAMEDSAEED
def solution_params
- params.require(:solution).permit(:code, :problem_id)
+ params.permit(:code, :problem_id, :time)
end
# [Compiler: Test - Story 3.15]
@@ -112,4 +96,4 @@ def input
params.require(:solution).permit(:input)
end
-end
+end
\ No newline at end of file
diff --git a/tutor/app/controllers/students_controller.rb b/tutor/app/controllers/students_controller.rb
index ed4766f4..4c1af112 100644
--- a/tutor/app/controllers/students_controller.rb
+++ b/tutor/app/controllers/students_controller.rb
@@ -1,4 +1,5 @@
class StudentsController < ApplicationController
+
# [Profile - Story 5.8]
# Displays the profile of the student chosen
# Parameters:
@@ -9,4 +10,5 @@ def show
@student = Student.find(params[:id])
@courses = @student.courses.order("created_at desc")
end
-end
\ No newline at end of file
+
+end
diff --git a/tutor/app/controllers/test_cases_controller.rb b/tutor/app/controllers/test_cases_controller.rb
index 2875de54..d60f5604 100644
--- a/tutor/app/controllers/test_cases_controller.rb
+++ b/tutor/app/controllers/test_cases_controller.rb
@@ -1,8 +1,10 @@
class TestCasesController < ApplicationController
+
# [Add test case-story 4.8]
# Shows all the test cases.
- # Parameters: None
- # Returns:List of all the test cases related to a certain problem.
+ # Parameters: none
+ # Returns:
+ # List of all the test cases related to a certain problem.
# Author: Lin
def index
@test_cases = TestCase.all
@@ -10,45 +12,102 @@ def index
# [Add test case-story 4.8]
# Display the form that is used to add a test case.
- # Parameters: None
- # Returns: None
+ # Parameters: none
+ # Returns: none
# Author: Lin
def new
- @test_case = TestCase.new()
+ @problem = Problem.find(params[:problem_id])
+ session[:problem_id] = params[:problem_id]
+ if(@test_case == nil)
+ @test_case = TestCase.new()
+ end
end
# [Add test case-story 4.8]
# Saves the new test case into the database.(What the form the 'new' method will submit to)
# Parameters: None
- # Returns: In case of success a flash notice will appear:"Post created successfully"
- # In case of failure a flash notice will appear:"Can't add test case!"
+ # Returns:
+ # In case of success a flash notice will appear:"Post created successfully"
+ # In case of failure a flash notice will appear:"Can't add test case!"
# Author: Lin
def create
+ @problem = Problem.find_by_id(session[:problem_id])
if lecturer_signed_in?
@test_case = TestCase.new(post_params)
@test_case.owner_id = current_lecturer.id
@test_case.owner_type = "lecturer"
+ @test_case.problem_id = session[:problem_id]
elsif teaching_assistant_signed_in?
@test_case = TestCase.new(post_params)
@test_case.owner_id = current_teaching_assistant.id
@test_case.owner_type = "teaching assistant"
+ @test_case.problem_id = session[:problem_id]
end
if @test_case.save
+ @problem.test_cases << @test_case
flash[:notice] = "Your test case is now added"
- redirect_to :controller => 'problems', :action => 'edit', :id => @test_case.problem_id
+ redirect_to :controller => 'problems', :action => 'edit', :id => session[:problem_id]
else
- flash[:notice] = "Your test case cannot added"
+ render :action=>'new', :problem_id => @test_case.problem_id
+ end
+ end
+
+ # [Edit test case-story 4.9]
+ # Edit test case
+ # Parameters:
+ # @test_case:Test case to be edited.
+ # Returns: none
+ # Author: Nadine Adel
+ def edit
+ @test_case =TestCase.find(params[:id])
+ session[:problem_id] = params[:problem_id]
+ end
+
+ # [Edit test case-story 4.9]
+ # Update test case in the database
+ # Parameters:
+ # @test_case:Test case to be updated.
+ # Returns:
+ # Flash message if the test case is updated or not
+ # Author: Nadine Adel
+ def update
+ @test_case = TestCase.find(params[:id])
+ if @test_case.update_attributes(post_params)
+ flash[:notice] = "Your Testcase is now updated"
redirect_to :controller => 'problems', :action => 'edit', :id => @test_case.problem_id
+ else
+ render :action=>'edit', :problem_id => @test_case.problem_id
+ end
+ end
+
+ # [Remove Test Case - Story 4.16]
+ # This action takes the test case id, and check if it is not the last test case in the problem
+ # and removes it from the database and then redirects the user to the edit problem page of
+ # the problem that had the test case with a "Test case successfully Deleted" message.
+ # Parameters:
+ # params[:id]: The current test case's id
+ # Returns:
+ # flash[:notice]: A message indicating the success of the deletion
+ # Author: Ahmed Atef
+ def destroy
+ @test_case = TestCase.find_by_id(params[:id])
+ @current = Problem.find_by_id(@test_case.problem_id)
+ if @current.test_cases.count == 1
+ flash[:notice] = "Cannot delete problem's last test case"
+ redirect_to :back and return
+ elsif @test_case.destroy
+ flash[:notice] = "Test case successfully Deleted"
+ redirect_to(:controller => 'problems', :action => 'edit', :id => @current.id)
end
end
# [Add test case-story 4.8]
- # private method. Controls the test case parameters that can be accessed.
- # Parameters: None
- # Returns: None
+ # Private method. Controls the test case parameters that can be accessed.
+ # Parameters: none
+ # Returns: none
# Author: Lin
private
def post_params
params.require(:test_case).permit(:input, :output, :problem_id)
end
-end
\ No newline at end of file
+end
diff --git a/tutor/app/controllers/tips_controller.rb b/tutor/app/controllers/tips_controller.rb
index c91d88d7..9d8285ef 100644
--- a/tutor/app/controllers/tips_controller.rb
+++ b/tutor/app/controllers/tips_controller.rb
@@ -1,5 +1,17 @@
class TipsController < ApplicationController
+ # [View hints and tips-Story 4.22]
+ # It fetches from database all the previous tips.
+ # Parameters:
+ # @tips: All the previous tips that had been entered before.
+ # @tips_check:All the previous tips that had been entered before to check if it is a tip or hint.
+ # Returns : none.
+ # Author: Nadine Adel
+ def index
+ @tips= Hint.all
+ @tips_check = Hint.all
+ end
+
# [Add tip - Story 4.10]
# Allows Lecturer/TA to create a tip to help the student_users while solving a problem
# Parameters: none
@@ -43,7 +55,18 @@ def create
end
end
+ # [Show tip - Story 4.23]
+ # Show the content of the tip that was created or edited with edit and delete buttons
+ # Parameters:
+ # model_answer_id: The model answer id
+ # id: tip id that should be viewed
+ # Returns:
+ # @tip: tip that will be viewed
+ # Author: Ahmed Osam
def show
+ @tip = Hint.find_by_id(params[:id])
+ redirect_to :controller => 'model_answers', :action => 'edit',
+ :model_answer_id => session[:model_answer_id]
end
def index
@@ -51,18 +74,75 @@ def index
@tips_check = Hint.all
end
+ # [Remove tip - Story 4.20]
+ # Finds the tip that wanted to be removed
+ # Parameters:
+ # id: id of the tip required to be removed
+ # Returns:
+ # @tip: tip which will be removed
+ # Author: Ahmed Osam
def destroy
+ @tip = Hint.find_by_id(params[:id])
+ @tip.destroy
+ redirect_to :controller => 'model_answers', :action => 'edit', :id => params[:answer_id]
end
+ # [Edit tip - Story 4.10]
+ # Finds the tip that wanted to be edited
+ # Parameters:
+ # id: id of the tip required to be edited
+ # Returns:
+ # @tip: tip which will be edited
+ # Author: Ahmed Osam
def edit
+ @tip = Hint.find_by_id(params[:id])
end
+ # [Edit tip - Story 4.11]
+ # update the tip with the new parameters
+ # Parameters:
+ # id: id of the tip required to be edited
+ # time: udated time in which tip will be shown after
+ # message: updated content of tip
+ # Returns:
+ # @tip: new updated tip
+ # Author: Ahmed Osam
def update
+ @tip = Hint.find(params[:id])
+ @tip.time = tip_params_edit[:time]
+ @tip.message = tip_params_edit[:message]
+ if @tip.save
+ render :action => 'show'
+ else
+ render :action => 'edit'
+ end
end
+ # [Add tip - Story 4.10]
+ # Take information from the form on create tip page
+ # Parameters:
+ # tip: name of the form
+ # message: is the content of the tip
+ # time: is a countdown timer that tip will appear after it ends
+ # Returns:
+ # none
+ # Author: Ahmed Osam
private
- def tip_params
- params.require(:tip).permit(:message, :time)
- end
+ def tip_params
+ params.require(:tip).permit(:message, :time)
+ end
+
+ # [Edit tip - Story 4.11]
+ # Take new information from a form on edit tip page
+ # Parameters:
+ # tip_edit: name of the form
+ # time: udated time in which tip will be shown after
+ # message: updated content of tip
+ # Returns:
+ # none
+ # Author: Ahmed Osam
+ def tip_params_edit
+ params.require(:tip_edit).permit(:message, :time)
+ end
end
\ No newline at end of file
diff --git a/tutor/app/controllers/topics_controller.rb b/tutor/app/controllers/topics_controller.rb
index 51504a49..ac95d9ef 100644
--- a/tutor/app/controllers/topics_controller.rb
+++ b/tutor/app/controllers/topics_controller.rb
@@ -6,7 +6,7 @@ class TopicsController < ApplicationController
# This Action should be put in the future in the
# Topic controller
# Parameters:
- # id: The id of the topic
+ # id: The id of the topic
# Returns: The view of the requested topic
# Author: Mussab ElDash
def show
@@ -35,11 +35,10 @@ def show
# [Specify Topics - Story 1.2]
# Description: This action takes the passed course id and assings
- # the respective course to an instance variable.
+ # the respective course to an instance variable.
# Parameters:
# params[:course_id]: The current course id
- # Returns:
- # none
+ # Returns: none
# Author: Ahmed Akram
def new
@course = Course.find(params[:course_id])
@@ -51,12 +50,12 @@ def new
# [Specify Topics - Story 1.2]
# Description: This action takes the passed course id and assings
- # the respective topics of that course to an instance
- # variable.
+ # the respective topics of that course to an instance
+ # variable.
# Parameters:
# params[:id]: The current course id
# Returns:
- # @topics: A list of all topics belonging to the course
+ # @topics: A list of all topics belonging to the course
# Author: Ahmed Akram
def index
@course = Course.find(params[:course_id])
@@ -65,15 +64,15 @@ def index
# [Specify Topics - Story 1.2]
# Description: This action takes the passed parameters from
- # the creation form, creates a new Topic record
- # and assigns it to the respective course. If the
- # creation fails the user is redirected to the form
- # with a "Failed" message.
+ # the creation form, creates a new Topic record
+ # and assigns it to the respective course. If the
+ # creation fails the user is redirected to the form
+ # with a "Failed" message.
# Parameters:
# topic_params[]: A list that has all fields entered by the user to in the
- # create_topic_form
+ # create_topic_form
# Returns:
- # flash[:notice]: A message indicating the success or failure of the creation
+ # flash[:notice]: A message indicating the success or failure of the creation
# Author: Ahmed Akram
def create
@new_topic = Topic.new
@@ -93,13 +92,38 @@ def create
end
end
+ # [Edit Track Rating - Story 4.3]
+ # Changes the difficulty of tracks that belong to a certain topic
+ # to match the order specified by TA/Lecturer through
+ # drag and drop sortable list.
+ # Parameters:
+ # params[:methodParam]: The array of the sorted trackes.
+ # Returns: none
+ # Author: Lin Kassem
+ def sort
+ @track = Track.find_by_id(params[:methodParam][0])
+ @topic = @track.topic
+ @tracks = @topic.tracks
+ new_Order_Array = params[:methodParam]
+
+ @tracks.each do |track|
+ track.difficulty = (params[:methodParam]).index(track.id.to_s) + 1
+ puts(track.save)
+ end
+ render :nothing => true
+ end
+
private
def topic_params
params.require(:topic).permit(:title,:description)
end
def course_params
- params.permit(:course_id)
+ params.permit(:course_id )
+ end
+
+ def track_params
+ params.permit(:difficulty)
end
end
diff --git a/tutor/app/controllers/tracks_controller.rb b/tutor/app/controllers/tracks_controller.rb
index 31dc004e..6d1be948 100644
--- a/tutor/app/controllers/tracks_controller.rb
+++ b/tutor/app/controllers/tracks_controller.rb
@@ -6,6 +6,10 @@ class TracksController < ApplicationController
# id: The id of the Track
# Returns: Page with list of the problems
# Author: Mussab ElDash
+ def index
+ @tracks= Track.all
+ end
+
def show
id = params[:id]
@track = Track.find_by_id(id)
@@ -142,4 +146,4 @@ def permit_create
return permit
end
end
-end
+end
\ No newline at end of file
diff --git a/tutor/app/helpers/hints_helper.rb b/tutor/app/helpers/hints_helper.rb
new file mode 100644
index 00000000..f40a7961
--- /dev/null
+++ b/tutor/app/helpers/hints_helper.rb
@@ -0,0 +1,2 @@
+module HintsHelper
+end
diff --git a/tutor/app/helpers/replies_helper.rb b/tutor/app/helpers/replies_helper.rb
new file mode 100644
index 00000000..ab5f4ccc
--- /dev/null
+++ b/tutor/app/helpers/replies_helper.rb
@@ -0,0 +1,2 @@
+module RepliesHelper
+end
diff --git a/tutor/app/models/admin.rb b/tutor/app/models/admin.rb
index 42355e95..fe0bd288 100644
--- a/tutor/app/models/admin.rb
+++ b/tutor/app/models/admin.rb
@@ -5,7 +5,7 @@ class Admin < ActiveRecord::Base
#Relations
#Scoops
-
+
#Methods
end
diff --git a/tutor/app/models/compiler.rb b/tutor/app/models/compiler.rb
index b027ca0f..c8e11bb1 100644
--- a/tutor/app/models/compiler.rb
+++ b/tutor/app/models/compiler.rb
@@ -33,10 +33,10 @@ def self.compiler_feedback(solution)
new_code = append_class(solution)
feedback = compile(solution, new_code)
if feedback == ""
- return { success: true, errors: nil, previous_code: (solution.code) }
+ return {success: true, errors: nil}
else
new_feedback = change_error_headers(solution, feedback)
- return { success: false, errors: new_feedback, previous_code: (solution.code) }
+ return {success: false, errors: new_feedback}
end
end
@@ -63,7 +63,7 @@ def self.append_class(solution)
# Author: Ahmed Moataz
def self.change_error_headers(solution, feedback)
header = solution.file_name
- return feedback.gsub('students_solutions/Java/' + header, 'CoolSoft')
+ return feedback.gsub('students_solutions/Java/' + header, 'CoolSoft').gsub(header, 'CoolSoft')
end
end
\ No newline at end of file
diff --git a/tutor/app/models/contest.rb b/tutor/app/models/contest.rb
new file mode 100644
index 00000000..7eb6855d
--- /dev/null
+++ b/tutor/app/models/contest.rb
@@ -0,0 +1,17 @@
+class Contest < ActiveRecord::Base
+
+ #Validations
+
+ #Relations
+ belongs_to :owner, polymorphic: true
+ belongs_to :course
+
+ has_many :progress, class_name: 'ContestProgress'
+ has_and_belongs_to_many :registered_students, class_name:"Student", join_table: "contests_students"
+ has_and_belongs_to_many :problems, join_table: "contests_problems"
+
+ #Scoops
+
+ #Methods
+
+end
diff --git a/tutor/app/models/contest_progress.rb b/tutor/app/models/contest_progress.rb
new file mode 100644
index 00000000..c16e135d
--- /dev/null
+++ b/tutor/app/models/contest_progress.rb
@@ -0,0 +1,14 @@
+class ContestProgress < ActiveRecord::Base
+
+ #Validations
+
+ #Relations
+ belongs_to :contest
+ belongs_to :student
+ belongs_to :problem
+
+ #Scoops
+
+ #Methods
+
+end
diff --git a/tutor/app/models/course.rb b/tutor/app/models/course.rb
index 19a510b4..82dd5726 100644
--- a/tutor/app/models/course.rb
+++ b/tutor/app/models/course.rb
@@ -19,9 +19,9 @@ class Course < ActiveRecord::Base
has_one :discussion_board, dependent: :destroy
has_many :topics, dependent: :destroy
has_many :acknowledgements, dependent: :destroy
-
has_many :course_students
has_many :students, through: :course_students
+ has_many :contests, dependent: :destroy
#Scoops
diff --git a/tutor/app/models/debugger.rb b/tutor/app/models/debugger.rb
index 23fa4028..a430ab1e 100644
--- a/tutor/app/models/debugger.rb
+++ b/tutor/app/models/debugger.rb
@@ -62,21 +62,22 @@ def input(input)
# Authors: Mussab ElDash + Rami Khalil
def start(class_name, input)
$all = []
+ source_path = "#{Rails.root.to_s}/#{Solution::JAVA_PATH}"
Dir.chdir(Solution::CLASS_PATH){
begin
- $input, $output, $error, $wait_thread = Open3.popen3("jdb", class_name, *input)
+ $input, $output, $error, $wait_thread = Open3.popen3("jdb",
+ "-sourcepath", source_path, class_name, *input)
buffer_until_ready
input "stop in #{class_name}.main"
buffer_until_ready
input "run"
- num = get_line
+ nums = get_line
locals = get_variables
- hash = {:line => num, :locals => locals}
- $all << hash
+ nums[:locals] = locals
+ $all << nums
debug
rescue => e
unless e.message === 'Exited'
- p e.message
return false
end
end
@@ -84,7 +85,6 @@ def start(class_name, input)
begin
Process.kill("TERM", $wait_thread.pid)
rescue => e
- p e.message
end
return $all
end
@@ -99,10 +99,14 @@ def debug
while counter < 100 && !$input.closed? do
begin
input "step"
- num = get_line
- locals = get_variables
- hash = {:line => num, :locals => locals}
- $all << hash
+ nums = get_line
+ locals = []
+ begin
+ locals = get_variables
+ rescue => e
+ end
+ nums[:locals] = locals
+ $all << nums
counter += 1
rescue => e
$input.close
@@ -118,24 +122,83 @@ def debug
# Author: Mussab ElDash
def get_line
out_stream = buffer_until_complete
- list_of_lines = out_stream.split(/\n+/)
- before_last_line = list_of_lines[-2]
- /, line=\d+/ =~ before_last_line
- before_last_regex_capture = $&
- /\d+/ =~ before_last_regex_capture
- before_last_regex_capture = $&
- last_line = list_of_lines[-2]
- /^\d+/=~ last_line
- last_regex_capture = $&
- if last_regex_capture
- return last_regex_capture.to_i
- elsif before_last_regex_capture
- return before_last_regex_capture.to_i
+ exceptions = has_exception out_stream
+ stream = get_stream out_stream
+ /,\sline=\d+/ =~ out_stream
+ line_first = $&
+ begin
+ input "list"
+ out_stream = buffer_until_complete
+ /\n\d+\s=>/ =~ out_stream
+ line_second = $&
+ rescue => e
+ end
+ if line_first
+ line_first = line_first[7..-1]
+ exceptions[:line] = line_first.to_i
+ if $all[-1]
+ exceptions[:stream] = "#{$all[-1][:stream]}#{stream}"
+ else
+ exceptions[:stream] = ""
+ end
+ return exceptions
+ elsif line_second
+ line_second = line_second[0..-4]
+ exceptions[:line] = line_second.to_i
+ return exceptions
else
raise 'Exited'
end
end
+ # [Debugger: Debug - Story 3.6]
+ # Checks if there is a runtime error thrown
+ # Parameters:
+ # line: The line to be checked if it has a runtime error
+ # Returns: A hash of the exception and its explanation if exists
+ # Author: Mussab ElDash
+ def get_stream(line)
+ /^>\s.+\n/ =~ line
+ stream = $&
+ if stream
+ stream = stream[2..-1]
+ p stream
+ return stream
+ end
+ return ""
+ end
+
+ # [Debugger: Debug - Story 3.6]
+ # Checks if there is a runtime error thrown
+ # Parameters:
+ # line: The line to be checked if it has a runtime error
+ # Returns: A hash of the exception and its explanation if exists
+ # Author: Mussab ElDash
+ def has_exception(line)
+ /Exception occurred: / =~ line
+ if $&
+ exception = get_exception
+ return {:status => false, :exception => Executer.get_runtime_explaination(exception)}
+ end
+ return {:status => true}
+ end
+
+ # [Debugger: Debug - Story 3.6]
+ # Checks if there is a runtime error thrown
+ # Parameters:
+ # line: The line to be checked if it has a runtime error
+ # Returns: A hash of the exception and its explanation if exists
+ # Author: Mussab ElDash
+ def get_exception
+ input "step"
+ out_stream = buffer_until_complete
+ ragex_first = /[[:space:]]+at\s[[:alnum:]]+\.main\([[:alnum:]]+\.java:\d+\)/m
+ regex_second = /[[:space:]]+The application exited\n*/
+ regex = /#{ragex_first}#{regex_second}/
+ out_stream = out_stream.sub(regex, "")
+ return out_stream
+ end
+
# [Debugger: Debug - Story 3.6]
# Create a java file and start debugging
# Parameters:
@@ -153,7 +216,7 @@ def self.debug(student_id, problem_id, code, input)
return {:success => false, data: compile_status}
end
debugger = Debugger.new
- class_name = solution.class_file_name
+ class_name = solution.file_name
debugging = debugger.start(class_name, input.split(" "))
java_file = solution.java_file_name true, true
class_file = solution.class_file_name true, true
diff --git a/tutor/app/models/executer.rb b/tutor/app/models/executer.rb
index 674aa112..ce21e6c9 100644
--- a/tutor/app/models/executer.rb
+++ b/tutor/app/models/executer.rb
@@ -3,26 +3,46 @@ class Executer
# [Compiler: Test - Story 3.15]
# Runs the solution submitted on the submitted test case
# Parameters:
- # file_name: The submitted file name
+ # sol: The created solution
# input: Test case entered by the user
# Returns:
# @execute_res: The execution result
# Author: Ahmed Akram
- def self.execute(file_name, input, problem_id)
+ def self.execute(sol, input)
+ @solution = sol
class_path = Solution::CLASS_PATH
- validity = check_input_validity(input, problem_id)
+ file_name = @solution.file_name
+ validity = check_input_validity(input, @solution.problem.id)
if validity[:status]
@execute_res = %x[#{'java -cp ' + class_path + ' ' + file_name + ' ' + input + ' 2>&1'}]
- puts @execute_res
if @execute_res.include?("Exception")
- return false
+ return {executer_feedback: false, executer_output: get_runtime_error()}
else
- return true
+ return {executer_feedback: true, executer_output: get_output()}
end
else
- @execute_res = validity[:msg]
- return false
+ return {executer_feedback: false, executer_output: validity[:msg]}
+ end
+ end
+
+ # [Compiler: Test - Story 3.15]
+ # Creates a new solution with the passed parameters
+ # Parameters:
+ # student_id: The solver id
+ # problem_id: The problem id
+ # code: The submitted code
+ # input: Test case entered by the user
+ # Returns:
+ # none
+ # Author: Ahmed Akram
+ def self.create_solution(student_id, problem_id, code, input)
+ solution = Solution.create({code: code, student_id: student_id,
+ problem_id: problem_id})
+ compile_status = Compiler.compiler_feedback(solution)
+ unless compile_status[:success]
+ return {compiler_error: true, compiler_output: compile_status}
end
+ return execute(solution, input)
end
# [Compiler: Test - Story 3.15]
@@ -68,18 +88,42 @@ def self.remove_class_name(file_name, error, sub_name)
# is a custom message to explain the error
# Author: Ahmed Akram
def self.get_runtime_error(file_name, sub_name)
- @execute_res = remove_class_name(file_name, @execute_res, sub_name)
+ execute_res = remove_class_name(file_name, execute_res, sub_name)
+ return get_runtime_explaination(execute_res)
+ end
- if @execute_res.include?("/ by zero")
- message = "Division by Zero results in infinity, "\
- "which computers can not understand. Be careful !"
- return msg = {error: @execute_res, explanation: message}
+ # [Debugger: Debug - Story 3.6]
+ # Returns a message explaining what this error is
+ # Parameters:
+ # exception: The exception to be explained
+ # Returns: The Explanation of the exception given
+ # Author: Mussab ElDash
+ def self.get_runtime_explaination(exception)
+ if exception.include?("/ by zero") || exception.include?("ArithmeticException")
+ message = "Division by Zero results in infinity, " +
+ "which computers can not understand. Be careful !"
+ return {errors: exception, explanation: message}
else
message = "To be set Runtime Error!"
- return msg = {error: @execute_res, explanation: message}
+ return {errors: exception, explanation: message}
end
end
+ # [Compiler: Test - Story 3.15]
+ # Returns the runtime error and a message
+ # Parameters:
+ # file_name: The submitted file name
+ # sub_name: The name to replace the class name
+ # Returns: A hash [error, explanation], where error is the runtime error and explanation
+ # is a custom message to explain the error
+ # Author: Ahmed Akram
+ def self.get_runtime_error
+ file_name = @solution.file_name
+ sub_name = 'CoolSoft'
+ @execute_res = remove_class_name(file_name, @execute_res, sub_name)
+ return get_runtime_explaination @execute_res
+ end
+
# [Compiler: Test - Story 3.15]
# Returns the output for the test case in case it ran successfully
# Parameters:
@@ -88,7 +132,7 @@ def self.get_runtime_error(file_name, sub_name)
# @execute_res: The answer to the test case
# Author: Ahmed Akram
def self.get_output
- return @execute_res
+ return {success: true, message: @execute_res}
end
-end
\ No newline at end of file
+end
diff --git a/tutor/app/models/hint.rb b/tutor/app/models/hint.rb
index 8a370720..b08ca3a0 100644
--- a/tutor/app/models/hint.rb
+++ b/tutor/app/models/hint.rb
@@ -3,10 +3,6 @@ class Hint < ActiveRecord::Base
#Validations
validates :message, presence: true
validates :time, presence: true
- validates :time, numericality: { only_integer: true, greater_than_or_equal_to: 0,
- message: "must be an integer & greater than or equal to 0" }
- validates :submission_counter, presence: true, numericality: {
- only_integer: true, greater_than_or_equal_to: 0, message: "is not valid"}
#Relations
belongs_to :model_answer
diff --git a/tutor/app/models/lecturer.rb b/tutor/app/models/lecturer.rb
index b1e3c5e7..17ccaf34 100644
--- a/tutor/app/models/lecturer.rb
+++ b/tutor/app/models/lecturer.rb
@@ -1,7 +1,8 @@
class Lecturer < ActiveRecord::Base
devise :database_authenticatable, :registerable,
- :recoverable, :rememberable, :trackable, :validatable, :confirmable
+ :recoverable, :rememberable, :trackable,
+ :validatable, :confirmable
#Elasticsearch
include Tire::Model::Search
@@ -11,7 +12,7 @@ class Lecturer < ActiveRecord::Base
include Searchable
#Uploader
- # mount_uploader :profile_image, ProfileImageUploader
+ mount_uploader :profile_image, ProfileImageUploader
#Validations
validate :duplicate_email
@@ -41,9 +42,9 @@ class Lecturer < ActiveRecord::Base
has_many :test_cases, as: :owner
has_many :hints, as: :owner
has_many :acknowledgements, dependent: :destroy
+ has_many :contests, as: :owner
#Methods
-
# [Advanced Search - Story 1.23]
# search for lecturers
# Parameters: hash of search options
@@ -71,6 +72,7 @@ def self.search(params)
end
end
end
+
# [User Authentication Advanced - Story 5.9, 5.10, 5.11, 5.14, 5.15]
# Checks if the email is already registered in tables: Student and TeachingAssistant
# before registering the email for table: Lecturer
diff --git a/tutor/app/models/post.rb b/tutor/app/models/post.rb
index 844360c1..c66e387b 100644
--- a/tutor/app/models/post.rb
+++ b/tutor/app/models/post.rb
@@ -17,6 +17,12 @@ class Post < ActiveRecord::Base
#Methods
+ def most_recent_reply
+ reply = Reply.first(:order => 'created_at DESC',
+ :conditions => ['post_id = ?', self.id])
+ return reply
+ end
+
# [Advanced Search - Story 1.23]
# search for posts
# Parameters: hash of search options
@@ -44,4 +50,5 @@ def self.search(params)
end
end
end
+
end
diff --git a/tutor/app/models/problem.rb b/tutor/app/models/problem.rb
index 0f4e1409..68b75fa9 100644
--- a/tutor/app/models/problem.rb
+++ b/tutor/app/models/problem.rb
@@ -12,6 +12,10 @@ class Problem < ActiveRecord::Base
has_many :test_cases, dependent: :destroy
has_many :solutions
has_many :attempts, dependent: :destroy
+ has_many :problems_start_time, class_name: 'ProblemOpeningTime', dependent: :destroy
+
+ has_many :contests_progresses, class_name: "ContestProgress"
+ has_and_belongs_to_many :contests, join_table: "contests_problems"
#Scoops
diff --git a/tutor/app/models/problem_opening_time.rb b/tutor/app/models/problem_opening_time.rb
new file mode 100644
index 00000000..30b76320
--- /dev/null
+++ b/tutor/app/models/problem_opening_time.rb
@@ -0,0 +1,16 @@
+class ProblemOpeningTime < ActiveRecord::Base
+
+ #Elasticsearch
+ include Tire::Model::Search
+ include Tire::Model::Callbacks
+
+ #Validations
+
+ #Relations
+ belongs_to :problem
+ belongs_to :student
+
+ #Scoops
+
+ #Methods
+end
diff --git a/tutor/app/models/solution.rb b/tutor/app/models/solution.rb
index ff4a5a95..5c3b6874 100644
--- a/tutor/app/models/solution.rb
+++ b/tutor/app/models/solution.rb
@@ -12,55 +12,78 @@ class Solution < ActiveRecord::Base
# Checks the validity of a submitted solution
# and show the runtime and logic errors if exist
# Parameters:
- # problem_id: id of the problem being answered
- # file: the name of the file which is compiled successfully
- # without errors
+ # solution: the solution to be validated
+ # testcases: the testcases that will test the submitted code
# Returns: a hash response containing status for the solution,
# solution errors or success message.
# Author: MOHAMEDSAEED
- def self.validate(file, problem_id)
- response = {status: 0, success: [], runtime_error: [], runtime_error_exp: [],
- logic_error: []}
- testcases = Problem.find_by_id(problem_id).test_cases
- testcases.each do |testcase|
- input = testcase.input
- expected_output = testcase.output
- runtime_check = Executer.execute(file, input, problem_id)
- if(runtime_check)
- output = Executer.get_output()
- if (output != expected_output)
- response[:logic_error] << "Logic error: for input: " +
- input + " ,expected output: " +
- expected_output + " but your output was: " + output
- unless response[:status] == 4
- response[:status] = 5
+ def self.validate(solution, test_cases)
+ response = []
+ compiler_status = Compiler.compiler_feedback(solution)
+ if compiler_status[:success]
+ test_cases.each do |testcase|
+ input = testcase.input
+ expected_output = testcase.output
+ runtime_check = Executer.execute(solution, input)
+ if(runtime_check[:executer_feedback])
+ output = runtime_check[:executer_output][:message]
+ if (output != expected_output)
+ if(output.to_s.empty?)
+ output = "Empty"
+ end
+ response << {success: false, test_case: input,
+ response: "Logic error: Expected output: " +
+ expected_output.to_s.strip + ", but your output was: " + output}
+ unless solution.status == 4
+ solution.status = 5
+ end
+ else
+ response << {success: true, test_case: input,
+ response: "Passed!"}
+ unless solution.status == 4 | 5
+ solution.status = 1
+ end
end
else
- unless(response[:status] == 4 | 5)
- response[:status] = 1
- end
+ runtime_error = runtime_check[:executer_output]
+ explanation = get_response(runtime_error)
+ solution.status = 4
+ response << {success: false, test_case: input,
+ response: "Runtime error: " + explanation}
end
- else
- runtime_error = Executer.get_runtime_error(file, 'CoolSoft')
- runtime_error[:error] = "for input: " + input + " " + runtime_error[:error]
- response[:status] = 4
- response[:runtime_error] << runtime_error[:error]
- response[:runtime_error_exp] << runtime_error[:explanation]
end
+ else
+ return {compiler_error: true, compiler_output: compiler_status}
end
- if response[:status] == 1
- response[:success] << "Your Solution is correct, Passed"
- end
+ solution.save
return response
end
+ # [Compiler: Validate - Story 3.5]
+ # outputs the runtime error with a better explanation
+ # Parameters:
+ # error: the original runtime error
+ # Returns: a String with the explained runtime error
+ # Author: MOHAMEDSAEED
+ def self.get_response(error)
+ if error.include?("/ by zero")
+ return "Division / 0"
+ elsif error.include?("ArrayIndexOutOfBounds")
+ return "Out of array range"
+ elsif error.include?("StringIndexOutOfBounds")
+ return "Out of string range"
+ else
+ return "To be set response"
+ end
+ end
+
# [Compiler: Validate - Story 3.5]
# Parameters:
# s_id : the id of the current Student
# p_id : the id of the current Problem
# Returns: the number of trials the student made for this problem
# Author: MOHAMEDSAEED
- def self.get_num_of_trials(s_id , p_id)
+ def self.get_num_of_trials(s_id, p_id)
num_of_trials = Solution.distinct.count(:all,
:conditions => ["student_id = ? AND problem_id = ? AND status != ?",
s_id, p_id, 3])
diff --git a/tutor/app/models/student.rb b/tutor/app/models/student.rb
index 3072902f..9701af3a 100644
--- a/tutor/app/models/student.rb
+++ b/tutor/app/models/student.rb
@@ -1,7 +1,8 @@
class Student < ActiveRecord::Base
devise :database_authenticatable, :registerable,
- :recoverable, :rememberable, :trackable, :validatable
+ :recoverable, :rememberable, :trackable,
+ :validatable, :confirmable
#Elasticsearch
include Tire::Model::Search
@@ -10,11 +11,8 @@ class Student < ActiveRecord::Base
#concerns
include Searchable
- devise :database_authenticatable, :registerable,
- :recoverable, :rememberable, :trackable,
- :validatable, :confirmable
-
- # mount_uploader :profile_image, ProfileImageUploader
+ #Uploader
+ mount_uploader :profile_image, ProfileImageUploader
#Validations
validate :duplicate_email
@@ -42,6 +40,10 @@ class Student < ActiveRecord::Base
has_many :course_students
has_many :courses, through: :course_students, dependent: :destroy
+ has_many :problems_start_time, class_name: 'ProblemOpeningTime'
+
+ has_many :contest_progresses, class_name: 'ContestProgress'
+ has_and_belongs_to_many :contests, class_name:"Contest", join_table: "contests_students"
#Methods
diff --git a/tutor/app/models/teaching_assistant.rb b/tutor/app/models/teaching_assistant.rb
index 5f93df67..d205a214 100644
--- a/tutor/app/models/teaching_assistant.rb
+++ b/tutor/app/models/teaching_assistant.rb
@@ -1,4 +1,5 @@
class TeachingAssistant < ActiveRecord::Base
+
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable,
:validatable, :confirmable
@@ -10,7 +11,8 @@ class TeachingAssistant < ActiveRecord::Base
#concerns
include Searchable
- # mount_uploader :profile_image, ProfileImageUploader
+ #Uploader
+ mount_uploader :profile_image, ProfileImageUploader
#Validations
validate :duplicate_email
@@ -42,6 +44,7 @@ class TeachingAssistant < ActiveRecord::Base
has_many :variable_constraints, as: :owner
has_many :test_cases, as: :owner
has_many :hints, as: :owner
+ has_many :contests, as: :owner
#Methods
diff --git a/tutor/app/models/test_case.rb b/tutor/app/models/test_case.rb
index 68f1608e..e6e84a77 100644
--- a/tutor/app/models/test_case.rb
+++ b/tutor/app/models/test_case.rb
@@ -1,7 +1,7 @@
class TestCase < ActiveRecord::Base
#Validations
- validates :output ,:presence => true
- validates :output, :length => { :minimum => 1 }
+ validates :input, :presence => true
+ validates :output, :presence => true
#Relations
belongs_to :owner, polymorphic: true
diff --git a/tutor/app/models/topic.rb b/tutor/app/models/topic.rb
index ea9ce72b..b232b441 100644
--- a/tutor/app/models/topic.rb
+++ b/tutor/app/models/topic.rb
@@ -9,7 +9,7 @@ class Topic < ActiveRecord::Base
validates :title, :description, uniqueness: true
#Relations
- has_many :tracks, dependent: :destroy
+ has_many :tracks, :order => 'difficulty', dependent: :destroy
belongs_to :course
belongs_to :owner, class_name: "Lecturer", foreign_key: :lecturer_id
diff --git a/tutor/app/models/track.rb b/tutor/app/models/track.rb
index d27bbd24..40badeaf 100644
--- a/tutor/app/models/track.rb
+++ b/tutor/app/models/track.rb
@@ -1,5 +1,5 @@
class Track < ActiveRecord::Base
-
+
#Validations
validates :difficulty, presence: true
validates :title , presence: true
@@ -13,5 +13,4 @@ class Track < ActiveRecord::Base
#Scoops
#Methods
-
end
diff --git a/tutor/app/uploaders/profile_image_uploader.rb b/tutor/app/uploaders/profile_image_uploader.rb
index 3c3b531d..f65833f3 100644
--- a/tutor/app/uploaders/profile_image_uploader.rb
+++ b/tutor/app/uploaders/profile_image_uploader.rb
@@ -13,7 +13,7 @@ def store_dir
end
# Process files as they are uploaded:
- process :resize_to_limit => [50, 50]
+ process :resize_to_limit => [160, 160]
# Create different versions of your uploaded files:
version :profile do
diff --git a/tutor/app/views/acknowledgements/new.html.erb b/tutor/app/views/acknowledgements/new.html.erb
index 67402c2d..da2096c5 100644
--- a/tutor/app/views/acknowledgements/new.html.erb
+++ b/tutor/app/views/acknowledgements/new.html.erb
@@ -1,26 +1,70 @@
-
Acknowledge A student
+
+
+
+
+
+
+ -
+ <%= link_to "Edit Info", {:controller => 'courses', :action => 'edit',
+ :id => @course.id}, class: "btn btn-success",
+ style: "float: left;margin-left: 15px",method: :get %>
+
-
-<% if flash[:failure_notice] %>
- <%= flash[:failure_notice] %>
-<% end %>
-<% if flash[:success_notice] %>
- <%= flash[:success_notice] %>
-<% end %>
-
+ -
+ <%= link_to "Add Topics", {:controller => 'topics', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left;margin-left: 15px",method: :post %>
+
-<%= form_for :acknowledgement, url: {action: "create"} do |f| %>
-
- <%=select_tag 'students[]', options_for_select(
- @course.students.map {|p| [p.name, p.id]}), :multiple => true, style: "width:300px" %>
-
+
+ -
+ <%= link_to "Add a TA", {:controller => 'teaching_assistants', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", :method => :get %>
+
-
- <%= f.label :Message %>
- <%= f.text_area :description, class:"form-control", style:"width:300px", :value => 'Good Job!' %>
-
+
+ -
+ <%= link_to "Acknowledge a student", {:controller => 'acknowledgements', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", method: :get
+ %>
+
+
+
+
+
Acknowledge A student
+
+ <% if flash[:failure_notice] %>
+
<%= flash[:failure_notice] %>
+ <% end %>
+ <% if flash[:success_notice] %>
+
<%= flash[:success_notice] %>
+ <% end %>
+
-
- <%= f.submit "Send Acknowledgement", class: "btn btn-success" %>
-
-<% end %>
\ No newline at end of file
+ <%= form_for :acknowledgement, url: {action: "create"} do |f| %>
+
+ <%=select_tag 'students[]', options_for_select(
+ @course.students.map {|p| [p.name, p.id]}), :multiple => true, style: "width:300px" %>
+
+
+
+ <%= f.label :Message %>
+ <%= f.text_area :description, class:"form-control", style:"width:300px", :value => 'Good Job!' %>
+
+
+
+ <%= f.submit "Send Acknowledgement", class: "btn btn-success" %>
+
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/tutor/app/views/courses/edit.html.erb b/tutor/app/views/courses/edit.html.erb
index 5485ce36..a6a15853 100644
--- a/tutor/app/views/courses/edit.html.erb
+++ b/tutor/app/views/courses/edit.html.erb
@@ -7,84 +7,87 @@
+
+
+ -
+ Edit Info
+
+ -
+ <%= link_to "Add Topics", {:controller => 'topics', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left;margin-left: 15px",method: :post %>
+
-
-
-
- <%= button_to "Add Topics", {:controller => 'topics', :action => 'new',
- :course_id => @course.id}, class: "btn btn-success",
- style: "float: left;margin-left: 15px",method: :post %>
-
+
+
-
+ <%= link_to "Add a TA", {:controller => 'teaching_assistants', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", :method => :get %>
+
-
-
- <%= button_to "Add a TA", {:controller => 'teaching_assistants', :action => 'new',
- :course_id => @course.id}, class: "btn btn-success",
- style: "float: left; margin-left: 15px", :method => :get %>
-
-
-
-
- <%= button_to "Acknowledge a student", {:controller => 'acknowledgements', :action => 'new',
- :course_id => @course.id}, class: "btn btn-success",
- style: "float: left; margin-left: 15px", method: :get
- %>
-
-
-
-
- <% if @discussion_board.activated == true %>
- <% str = "Deactivate DiscussionBoard" %>
- <% else %>
- <% str = "Activate DiscussionBoard" %>
- <% end %>
-
- <%= button_to str, {:controller => :discussion_boards, :action => 'toggle',
- :id => @discussion_board.id}, class: "btn btn-success",
- style: "float: left; margin-left: 15px" %>
-
-
-
-
- <%= form_for :course, url: course_path(@course), method: :patch do |c| %>
-
-
<%= c.label :name %>
- <%= c.text_field :name %>
-
-
-
- <%= c.label :code %>
- <%= c.text_area :code %>
-
-
-
- <%= c.label :year %>
- <%= c.text_area :year %>
-
+
+
-
+ <%= link_to "Acknowledge a student", {:controller => 'acknowledgements', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", method: :get
+ %>
+
+
-
- <%= c.label :semester %>
- <%= c.text_area :semester %>
-
-
-
- <%= c.label :description %>
- <%= c.text_area :description %>
-
-
-
- <%= c.submit %>
-
- <% end %>
- <%= link_to 'Back', courses_path %>
-
-
-
- <%= button_to "Delete Course", {:action => "destroy"}, class: "btn btn-danger", style: "margin-top: 100px; float: right", method: :delete %>
-
-
-<%= button_to "Back", {:controller => 'courses', :action => 'index'}, class: "btn btn-success",
- style: "margin-left:600px; margin-top: 250px", :method => :get %>
-
+
+
+ <% if @discussion_board.activated == true %>
+ <% str = "Deactivate DiscussionBoard" %>
+ <% else %>
+ <% str = "Activate DiscussionBoard" %>
+ <% end %>
+
+ <%= link_to str, {:controller => :discussion_boards, :action => 'toggle',
+ :id => @discussion_board.id}, class: "btn btn-success", method: 'post',
+ style: "float: left; margin-left: 15px" %>
+
+
+
+
+ <%= form_for :course, url: course_path(@course), method: :patch do |c| %>
+
+
<%= c.label :name %>
+ <%= c.text_field :name %>
+
+
+
+ <%= c.label :code %>
+ <%= c.text_area :code %>
+
+
+
+ <%= c.label :year %>
+ <%= c.text_area :year %>
+
+
+
+ <%= c.label :semester %>
+ <%= c.text_area :semester %>
+
+
+
+ <%= c.label :description %>
+ <%= c.text_area :description %>
+
+
+
+ <%= c.submit %>
+
+ <% end %>
+ <%= link_to 'Back', courses_path %>
+
+
+
+ <%= button_to "Delete Course", {:action => "destroy"},
+ class: "btn btn-danger", style: "margin-top: 100px; float: right", method: :delete %>
+
+ <%= button_to "Back", {:controller => 'courses', :action => 'index'}, class: "btn btn-success",
+ style: "margin-left:600px; margin-top: 250px", :method => :get %>
+
diff --git a/tutor/app/views/courses/index.html.erb b/tutor/app/views/courses/index.html.erb
index 495ecd6a..7426c70e 100644
--- a/tutor/app/views/courses/index.html.erb
+++ b/tutor/app/views/courses/index.html.erb
@@ -17,29 +17,26 @@
<%= flash[:success_deletion] %>
<%= flash[:success_creation] %>
<% if @courses.any? %>
-
Your Courses
- Name |
+ Course Name |
Code |
- Options |
- |
- |
+
<% @courses.each do |c| %>
-
- <%= link_to c.name, { :controller => 'courses', :action => 'show', :id => c.id },
- :method => :get, class: "btn btn-success" %> |
+
+ <%= link_to c.name, { :controller => 'courses', :action => 'show', :id => c.id }, :method => :get, class: "", style:"font-weight:bold; font-size:15px; color:#3498db; font-dicoration:none "%>
+ |
<%= c.code %> |
- <% if current_lecturer %>
- <%= link_to "Manage Course", { :controller => 'courses', :action => 'edit',
- :method => :get, :id => c.id }, class: "btn btn-success", style: "text-decoration:none" %>
- <% end %>
+ <%= link_to "Discussion Board", { :controller => 'discussion_boards', :action => 'show', :id => c.id },
+ class: "btn btn-info", method: :get, style: "text-decoration:none" %>
|
- <%= link_to "Discussion Board", { :controller => 'discussion_boards', :action => 'show', :id => c.id },
- class: "btn btn-success", method: :get, style: "text-decoration:none" %>
+ <% if current_lecturer %>
+ <%= link_to "Manage", { :controller => 'courses', :action => 'edit',
+ :method => :get, :id => c.id }, class: "", style: "color:black" %>
+ <% end %>
|
<% if current_student %>
@@ -58,5 +55,4 @@
<% end %>
-
-
+
\ No newline at end of file
diff --git a/tutor/app/views/courses/new.html.erb b/tutor/app/views/courses/new.html.erb
index c754a63d..17a44fab 100644
--- a/tutor/app/views/courses/new.html.erb
+++ b/tutor/app/views/courses/new.html.erb
@@ -3,7 +3,6 @@
@@ -17,7 +16,7 @@
<% end %>
- <%= f.text_field :name, :value => @new_course.name, class:"form-control", style: "width:300px" %>
+ <%= f.text_field :name, :value => @new_course.name, class:"form-control", style: "width:300px", placeholder:"Name" %>
<%= f.label :Code %>
@@ -27,7 +26,7 @@
<% end %>
- <%= f.text_field :code, :value => @new_course.code, class:"form-control", style: "width:300px" %>
+ <%= f.text_field :code, :value => @new_course.code, class:"form-control", style: "width:300px", placeholder:"Code"%>
<%= f.label :Description %>
@@ -48,7 +47,7 @@
<% end %>
- <%= f.text_field :semester, :value => @new_course.semester, class:"form-control", style: "width:50px" %>
+ <%= f.text_field :semester, :value => @new_course.semester, class:"form-control", style: "width:70px", placeholder:"No." %>
<%= f.label :Year %>
@@ -58,12 +57,12 @@
<% end %>
- <%= f.text_field :year, :value => @new_course.year, class:"form-control", style: "width:100px" %>
+ <%= f.text_field :year, :value => @new_course.year, class:"form-control", style: "width:70px", placeholder:"Year" %>
- <%= f.submit "Save", class: "btn btn-success" %>
+ <%= f.submit "Save", class: "btn btn-info" %>
<% end %>
-
+
\ No newline at end of file
diff --git a/tutor/app/views/courses/show.html.erb b/tutor/app/views/courses/show.html.erb
index 2e9d15b3..cf1798a6 100644
--- a/tutor/app/views/courses/show.html.erb
+++ b/tutor/app/views/courses/show.html.erb
@@ -7,35 +7,43 @@
- Sign up for a course
<% end %>
-
+
- <%= @course.name %>
-
-
-
- Code:
- <%= @course.code %>
-
-
- Year:
- <%= @course.year %>
-
-
- Semester:
- <%= @course.semester %>
-
-
- Description:
- <%= @course.description %>
-
-
+
+ <%= @course.name %>
+
+
+
+ Code:
+
+ <%= @course.code %>
+
+
+
+ Year:
+
+ <%= @course.year %>
+
+
+
+ Semester:
+
+ <%= @course.semester %>
+
+
+
+ Description:
+
+ <%= @course.description %>
+
+
+
<%= link_to "View Discussion Board", { :controller => 'discussion_boards',
- :action => 'show', :id => @course.id }, class: "btn btn-success", method: :get, style: "text-decoration:none" %>
+ :action => 'show', :id => @course.id }, class: "btn btn-default", method: :get, style: "text-decoration:none; margin-top: -70px" %>
-
-
+
<% @topics.each do |t| %>
<% t.tracks.each do |tt|%>
<% color = "label label-default" %>
- -
<%= link_to tt.title, tt, class: color %>
@@ -62,4 +70,4 @@
-
+
\ No newline at end of file
diff --git a/tutor/app/views/discussion_boards/show.html.erb b/tutor/app/views/discussion_boards/show.html.erb
index 9688d8b1..01d3b173 100644
--- a/tutor/app/views/discussion_boards/show.html.erb
+++ b/tutor/app/views/discussion_boards/show.html.erb
@@ -1,30 +1,70 @@
-
- <% if flash[:notice] %>
- <%= flash[:notice] %>
- <% end %>
- <% if @discussionBoard.activated == true %>
-
+<% if flash[:notice] %>
+ <%= flash[:notice] %>
+<% end %>
+<% if @discussionBoard.activated == true %>
+
+
<%= @discussionBoard.title %>
- <% unless @posts.blank? %>
- <% @posts.each do |post| %>
-
- <%= link_to post.title, :controller => 'posts', :action=> 'show',
- :id => post.id,:method => :get, class: "btn btn-success" %>
- |
- <%= post.views_count %>
-
-
- <% end %>
- <% else %>
- No Posts Yet .. Try Creating a Post
- <% end %>
-
- <%= link_to "Add Post", new_post_path(discussion_board_id:
- @discussionBoard.id), class: "btn btn-success",
- style: "float: left;margin-left: 15px" %>
-
- <% else %>
- The discusstion board is deactivated
- <% end %>
-
\ No newline at end of file
+ <% unless @posts.blank? %>
+
+ Post |
+ Replies |
+ Post Owner |
+ Last Reply |
+ Created |
+ Views |
+
+ <% @posts.each do |post| %>
+
+
+ <%= link_to post.title, :controller => 'posts', :action=> 'show',
+ :id => post.id,:method => :get, class:
+ "btn btn-success" %>
+ |
+
+ <%= post.replies.count %>
+ |
+
+ <%= post.owner.name %>'s post
+ |
+
+ <% if post.most_recent_reply %>
+ <%= distance_of_time_in_words_to_now post.most_recent_reply.created_at %> ago by
+ <% if post.most_recent_reply.owner_type == "Lecturer" %>
+ <%= link_to post.most_recent_reply.owner.name,
+ "/lecturers/{post.most_recent_reply.owner_id}" %>
+ <% end %>
+ <% if post.most_recent_reply.owner_type == "Student" %>
+ <%= link_to post.most_recent_reply.owner.name,
+ "/students/{post.most_recent_reply.owner_id}" %>
+ <% end %>
+ <% if post.most_recent_reply.owner_type == "TeachingAssistant" %>
+ <%= link_to post.most_recent_reply.owner.name,
+ "/teachingassistants/{post.most_recent_reply.owner_id}" %>
+ <% end %>
+ <% else %>
+ No replies
+ <% end %>
+ |
+
+ <%=distance_of_time_in_words_to_now post.created_at %> ago
+
+ |
+
+ <%= post.views_count %>
+ |
+
+ <% end %>
+ <% else %>
+ No Posts Yet .. Try Creating a Post
+ <% end %>
+
+
+ <%= link_to "Add Post", new_post_path(discussion_board_id:
+ @discussionBoard.id), class: "btn btn-success" %>
+
+<% else %>
+ The discusstion board is deactivated
+<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/hints/_index.html.erb b/tutor/app/views/hints/_index.html.erb
index 5269492d..8546b031 100644
--- a/tutor/app/views/hints/_index.html.erb
+++ b/tutor/app/views/hints/_index.html.erb
@@ -1 +1,39 @@
- Previous hints
\ No newline at end of file
+<% @hints1=Array.new %>
+<% @hints_check.each do |hint2| %>
+ <% if hint2.category == false %>
+ <% @hints1.push(hint2) %>
+ <% end %>
+<% end %>
+
+
+
+ Previous Hints
+
+
+ <% if !@hints1.any? %>
+ You have no previous Hints
+ <% end %>
+ <% if !@hints1.empty? %>
+
+
+
+ Hints |
+ Number of submissions |
+
+
+
+ <% @hints.each do |hint1| %>
+
+ <% if hint1.category == false %>
+
+ <%= hint1.message %>
+ |
+ <%= hint1.submission_counter %> |
+ <% end %>
+
+ <% end %>
+
+
+ <% end %>
+
\ No newline at end of file
diff --git a/tutor/app/views/layouts/_profile.html.erb b/tutor/app/views/layouts/_profile.html.erb
new file mode 100644
index 00000000..18c334cc
--- /dev/null
+++ b/tutor/app/views/layouts/_profile.html.erb
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/tutor/app/views/layouts/_right_side_bar.html.erb b/tutor/app/views/layouts/_right_side_bar.html.erb
index 01492d45..1970325c 100644
--- a/tutor/app/views/layouts/_right_side_bar.html.erb
+++ b/tutor/app/views/layouts/_right_side_bar.html.erb
@@ -1,55 +1,76 @@
-
+
+
+ position:static;overflow:auto">
-
-
-
+
+
+
+
+
+ <% if student_signed_in? %>
+ - <%= link_to image_tag("glyphicons_003_user.png") +
+ " Profile",
+ {:controller => 'site', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto; margin-bottom: 5px;
+ width: 100%", method: :get %>
+
+ - <%= link_to image_tag("glyphicons_136_cogwheel.png") +
+ " Dashboard",
+ {:controller => 'site', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto; margin-bottom: 5px;
+ width: 100%", method: :get %>
+
+ - <%= link_to image_tag("glyphicons_318_more_items.png") +
+ " Courses",
+ {:controller => 'courses', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:autox; margin-bottom: 5px;
+ width: 100%", method: :get %>
+
+ - <%= link_to image_tag("glyphicons_280_settings.png") +
+ " Settings",
+ {:controller => 'site', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto;
+ width: 100%", method: :get %>
+
+ <% else %>
+ - <%= link_to image_tag("glyphicons_003_user.png") +
+ "Profile",
+ {:controller => 'site', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto; margin-bottom: 5px;
+ width: 100%", method: :get %>
+
+ - <%= link_to image_tag("glyphicons_318_more_items.png") +
+ "Courses",
+ {:controller => 'courses', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto; margin-bottom: 5px;
+ width: 100%", method: :get %>
+
+ - <%= link_to image_tag("glyphicons_280_settings.png") +
+ "Settings",
+ {:controller => 'site', :action => 'index'},
+ style: "margin-left:auto;
+ margin-right:auto;
+ width: 100%", method: :get %>
+
+ <% end %>
+
+
+
+
@@ -62,7 +83,10 @@
<% next_problems_to_solve.each do |key , value| %>
- <%= link_to value.title, controller: "problems", action: "show", id: value.id %>
+ <%= link_to value.title,
+ controller: "/problems",
+ action: "show",
+ id: value.id %>
|
<%= key %>
@@ -89,7 +113,9 @@
|
<%= link_to value["problem_title"],
- controller: "problems", action: "show", id: key %>
+ controller: "/problems",
+ action: "show",
+ id: key %>
|
<% end %>
diff --git a/tutor/app/views/layouts/application.html.erb b/tutor/app/views/layouts/application.html.erb
index c147fa75..e2224e0b 100644
--- a/tutor/app/views/layouts/application.html.erb
+++ b/tutor/app/views/layouts/application.html.erb
@@ -4,11 +4,13 @@
# Author: Ahmed Elassuty
-->
-
+
CoolSoft - Java Tutor
<%= stylesheet_link_tag "application", "bootstrap","token-input", :media => "all" %>
- <%= javascript_include_tag "application","bootstrap", "jquery" %>
+ <%-# stylesheet_link_tag "application", "bootstrap", "token-input", :media => "all" -%>
+ <%= stylesheet_link_tag "application", "bootstrap", :media => "all" %>
+ <%= javascript_include_tag "application", "bootstrap", "jquery" %>
<%= csrf_meta_tags %>
@@ -54,7 +56,7 @@
<% else %>
-
+
<%= yield %>
<% end %>
@@ -65,4 +67,4 @@
<%= render "layouts/footer" %>
-
\ No newline at end of file
+
diff --git a/tutor/app/views/lecturers/_show.html.erb b/tutor/app/views/lecturers/_show.html.erb
new file mode 100644
index 00000000..451e3dfc
--- /dev/null
+++ b/tutor/app/views/lecturers/_show.html.erb
@@ -0,0 +1,14 @@
+<%= render 'layouts/profile' %>
+ - Info
+
+
+
+ Name: <%= current_lecturer.name %>
+ Email: <%= current_lecturer.email %>
+ Degree: <%= current_lecturer.degree %>
+ Department: <%= current_lecturer.department %>
+ University: <%= current_lecturer.university %>
+ Joined on: <%= current_lecturer.created_at.to_date %>
+
+
+
diff --git a/tutor/app/views/lecturers/registrations/new.html.erb b/tutor/app/views/lecturers/registrations/new.html.erb
index 221650d5..f49d75e1 100644
--- a/tutor/app/views/lecturers/registrations/new.html.erb
+++ b/tutor/app/views/lecturers/registrations/new.html.erb
@@ -3,6 +3,8 @@
<%= form_for(resource, as: resource_name, url: registration_path(resource_name),
:html => { :multipart => true, :class => 'form-devise' }) do |f| %>
<%= devise_error_messages! %>
+
+ <%= image_tag(@lecturer.profile_image_url.to_s) if @lecturer.profile_image? %>
<%= f.email_field :email, class: "form-control", autofocus: true, placeholder: "Email" %>
@@ -29,7 +31,7 @@
<%= f.label :date_of_birth %>
- <%= f.date_select :dob, {:start_year => Date.today.year-90, :end_year => Date.today.year} %>
+ <%= f.date_select :dob, {:start_year => Date.today.year-10, :end_year => Date.today.year-90} %>
@@ -44,6 +46,12 @@
<%= f.text_field :department, class: "form-control", placeholder: "Department" %>
+
+ <%= f.label :profile_image %>
+ <%= f.file_field :profile_image, {:accept => 'image/png,image/gif,image/jpeg'} %>
+ <%= f.hidden_field :profile_image_cache %>
+
+
<%= f.submit "Sign up", class: "btn btn-success button-devise" %>
<% end %>
diff --git a/tutor/app/views/lecturers/show.html.erb b/tutor/app/views/lecturers/show.html.erb
index 456e3b28..a2e44f5e 100644
--- a/tutor/app/views/lecturers/show.html.erb
+++ b/tutor/app/views/lecturers/show.html.erb
@@ -1,10 +1,11 @@
<%= image_tag(@lecturer.profile_image_url.to_s) if @lecturer.profile_image? %>
- <%= @lecturer.name %>'s Profile
+ <%= @lecturer.name %>
-
+
+
@@ -57,6 +58,10 @@
Name: <%= @lecturer.name %>
Email: <%= @lecturer.email %>
+ Degree: <%= @lecturer.degree %>
+ Department: <%= @lecturer.department %>
+ University: <%= @lecturer.university %>
+ Joined on: <%= @lecturer.created_at.to_date %>
\ No newline at end of file
diff --git a/tutor/app/views/model_answers/_index.html.erb b/tutor/app/views/model_answers/_index.html.erb
index 573cdc00..7d8fc06c 100644
--- a/tutor/app/views/model_answers/_index.html.erb
+++ b/tutor/app/views/model_answers/_index.html.erb
@@ -9,7 +9,8 @@
}
- <%= link_to "Create New Model Answer",
+
+
<%= link_to "Create Model Answer",
{:controller => 'model_answers', :action => 'new',
:problem_id => @problem.id }, {class: 'btn btn-primary btn'} %>
@@ -25,7 +26,7 @@
- Title |
+ Title |
Created At |
Updated At |
View/Edit |
@@ -42,10 +43,13 @@
:action => 'edit', :method => :get, :id => answer1.id},
{class: 'btn btn-primary btn-xs',
style: "text-decoration:none"} %>
- Delete |
+ <%= button_to "Delete", {:controller => 'model_answers',:action => 'destroy',
+ :id => answer1.id}, method: :delete, class: 'btn btn-primary',
+ :confirm => "Are you sure you want to delete
+ this Answer ?" %> |
<% end %>
<% end %>
-
\ No newline at end of file
+
diff --git a/tutor/app/views/model_answers/edit.html.erb b/tutor/app/views/model_answers/edit.html.erb
index f4b123fa..7b9a17d9 100644
--- a/tutor/app/views/model_answers/edit.html.erb
+++ b/tutor/app/views/model_answers/edit.html.erb
@@ -21,6 +21,8 @@
<%= f.label :Answer %>
<%= f.text_area :answer, :cols => "50", :rows => "15" %>
<%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
- <%= render partial: "tips/index" %>
- <%= render partial: "hints/index" %>
+ <% if !@answer.errors.any? %>
+ <%= render partial: "tips/index" %>
+ <%= render partial: "hints/index" %>
+ <% end %>
<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/posts/show.html.erb b/tutor/app/views/posts/show.html.erb
index 7f06d3b2..c9fdca49 100644
--- a/tutor/app/views/posts/show.html.erb
+++ b/tutor/app/views/posts/show.html.erb
@@ -4,34 +4,33 @@
<%= link_to "back", {:action => 'show', :controller => 'discussion_boards',
:id => DiscussionBoard.find_by_id(@post.discussion_board_id).course_id}, {
- class: 'btn btn-primary'} %>
-
- <% if current_lecturer %>
- <% @userID = current_lecturer.id %>
- <% end %>
- <% if current_student %>
- <% @userID = current_student.id %>
- <% end %>
- <%if current_teaching_assistant %>
- <% @userID = current_teaching_assistant.id %>
- <% end %>
-
- <%=@post.title%>
- <%= @post.content %>
-
- <% unless @replies.blank? %>
- <% @replies.each do |reply| %>
-
- <%= reply.content %>
+ class: 'btn btn-info pull-right'} %>
+<% if current_lecturer %>
+ <% @userID = current_lecturer.id %>
+<% end %>
+<% if current_student %>
+ <% @userID = current_student.id %>
+<% end %>
+<%if current_teaching_assistant %>
+ <% @userID = current_teaching_assistant.id %>
+<% end %>
+<% if @post.owner_id == @userID %>
+ <%= link_to "Edit Post", {:action => 'edit', :controller => 'posts'}, {
+ class: 'btn btn-info pull-right', :id => @post.id, style: "margin-right:1%;"} %>
+<% end %>
+
+
+ <%= @post.title %>
+
+
+ <%= @post.owner.name %>
+
+
+ <%= @post.content %>
+ <%= distance_of_time_in_words_to_now @post.
+ created_at %> ago
-
- <% end %>
- <% else %>
- No Replies in this post yet .
- <% end %>
+
+
- <% if @post.owner_id == @userID %>
- <%= link_to "Edit Post", {:action => 'edit', :controller => 'posts'}, {
- class: 'btn btn-primary',:id => @post.id} %>
- <% end %>
-
\ No newline at end of file
+<%= render "/replies/index", :@replies => @replies, :@post => @post %>
diff --git a/tutor/app/views/problems/edit.html.erb b/tutor/app/views/problems/edit.html.erb
index 2e8dbc91..77d41093 100644
--- a/tutor/app/views/problems/edit.html.erb
+++ b/tutor/app/views/problems/edit.html.erb
@@ -38,10 +38,10 @@
<% end %>
-<%= render partial: "test_cases/index" %>
-
<%= render partial: "model_answers/index" %>
+<%= render partial: "test_cases/index" %>
+
<%= render partial: "problems/success_options" %>
<%= button_to "Delete Problem", {:action => 'destroy'}, method: :delete,
diff --git a/tutor/app/views/problems/new.html.erb b/tutor/app/views/problems/new.html.erb
index 3c8844ed..7c2f3598 100644
--- a/tutor/app/views/problems/new.html.erb
+++ b/tutor/app/views/problems/new.html.erb
@@ -1,19 +1,22 @@
Add a problem
-
- <%= form_for :Problem, url: {action: "create"} do |problem| %>
-
- <%= problem.label :title %>
- <%= problem.text_field :title %>
-
-
- <%= problem.label :description %>
- <%= problem.text_area :description, :cols => "50", :rows => "10" %>
-
- <%= problem.hidden_field :track_id, value: params[:id] %>
-
- <%= problem.submit "Save Problem", class: "btn btn-default" %>
-
- <% end %>
+
+
<% if flash[:notice] %>
- <%= flash[:notice] %>
+ <%= flash[:notice] %>
+<% end %>
+
+<%= form_for :Problem, url: {action: "create"} do |problem| %>
+
+ <%= problem.label :title %>
+ <%= problem.text_field :title, class: "form-control", style: "width:60%" %>
+
+
+ <%= problem.label :description %>
+ <%= problem.text_area :description, :cols => "50", :rows => "10",
+ class: "form-control", style: "width:60%" %>
+
+ <%= problem.hidden_field :track_id, value: params[:id] %>
+
+ <%= problem.submit "Save Problem", class: "btn btn-info" %>
+
<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/replies/_index.html.erb b/tutor/app/views/replies/_index.html.erb
new file mode 100644
index 00000000..99b49d91
--- /dev/null
+++ b/tutor/app/views/replies/_index.html.erb
@@ -0,0 +1,6 @@
+
+ <%= render "/replies/load_replies" %>
+
+<%= text_field_tag 'input_field', nil, style: "width:500px; height:100px" %>
+<%= submit_tag "Submit", class:"btn btn-success", onclick:"post_reply(#{params[:id]})" %>
+
diff --git a/tutor/app/views/replies/_load_replies.html.erb b/tutor/app/views/replies/_load_replies.html.erb
new file mode 100644
index 00000000..e473b880
--- /dev/null
+++ b/tutor/app/views/replies/_load_replies.html.erb
@@ -0,0 +1,51 @@
+<% unless @replies.blank? %>
+ <% @replies.each do |reply| %>
+
+
+ <%= link_to reply.owner.name, reply.owner %>
+
+
+
+ <%= reply.content %>
+
+
+ <%= distance_of_time_in_words_to_now reply.created_at %> ago
+
+
+ <% if lecturer_signed_in? %>
+ <% if current_lecturer.email == reply.owner.email %>
+ <%= image_tag "delete_button_2.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "delete_reply(#{reply.id},#{reply.post_id});" %> |
+ <%= image_tag "edit_button.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "edit_reply(#{reply.id},#{reply.post_id})" %> |
+ <% end %>
+ <% elsif teaching_assistant_signed_in? %>
+ <% if current_teaching_assistant.email == reply.owner.email %>
+ <%= image_tag "delete_button_2.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "delete_reply(#{reply.id},#{reply.post_id});" %> |
+ <%= image_tag "edit_button.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "edit_reply(#{reply.id},#{reply.post_id})" %> |
+ <% end %>
+ <% else %>
+ <% if current_student.email == reply.owner.email %>
+ <%= image_tag "delete_button_2.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "delete_reply(#{reply.id},#{reply.post_id});" %> |
+ <%= image_tag "edit_button.png",
+ style: "width:1%;height:1%;margin_top:10px;cursor:pointer",
+ onclick: "edit_reply(#{reply.id},#{reply.post_id})" %> |
+ <% end %>
+ <% end %>
+
+
+
+ <% end %>
+<% else %>
+
+ No Replies in this post yet.
+
+<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/site/index.html.erb b/tutor/app/views/site/index.html.erb
index aade916d..2373e690 100644
--- a/tutor/app/views/site/index.html.erb
+++ b/tutor/app/views/site/index.html.erb
@@ -53,9 +53,11 @@
Author: Ahmed Akram
-->
<% if signed_in? %>
- <%= button_to "My Courses", {:controller => 'courses', :action => 'index'}, class: "btn btn-success", style: "margin-left:auto; margin-right:auto; margin-top: 80px", method: :get %>
-
- <% if(lecturer_signed_in?) %>
- <%= button_to "New Course", {:controller => 'courses', :action => 'new'}, class: "btn btn-success", style: "margin-left:auto; margin-right:auto;", :method => :get %>
+ <% if student_signed_in?%>
+ <%= render 'students/show' %>
+ <% elsif lecturer_signed_in? %>
+ <%= render 'lecturers/show' %>
+ <% elsif teaching_assistant_signed_in? %>
+ <%= render 'teaching_assistants/show'%>
<% end %>
<% end %>
diff --git a/tutor/app/views/solutions/_new.html.erb b/tutor/app/views/solutions/_new.html.erb
index 38e6298a..3e2486d6 100644
--- a/tutor/app/views/solutions/_new.html.erb
+++ b/tutor/app/views/solutions/_new.html.erb
@@ -1,4 +1,3 @@
- <%= flash[:alert] %>
Time spent:
0: 00
@@ -13,99 +12,83 @@
%>
<%= f.hidden_field :problem_id, value: params[:id] %>
-
-
- <%= f.hidden_field :problem_id, value: params[:id] %>
-
-
-
-
- <%= button_tag 'Compile', type: 'Button',
- id: 'compileButton', onClick: 'compile()' %>
- <%= button_tag 'Start Debugging', id: 'debugButton', type: 'button',
- onclick: "start_debug(#{params[:id]})" %>
- <%= button_tag 'Previous', type: 'Button',
- id: 'previousButton', onClick: 'previous()', hidden: true %>
- <%= button_tag 'Next', type: 'Button',
- id: 'nextButton', onClick: 'next()', hidden: true %>
- <%= button_tag 'Stop Debugging', type: 'Button',
- id: 'stopButton', onClick: 'stop()', hidden: true %>
-
- |
-
- Accepted
- Wrong Answer
- Runtime Error
- Compilation Error
- <%= f.submit "Compile" %>
- <%= f.submit "Run Test Case" %>
- <%= f.submit "Submit" %>
- |
-
-
-
-
- Input:
- <%= f.text_area :input, placeholder: "Write your test case here",
- value: @input, class: "form-control", style: "width:400px" %>
+
+
+
+
+<%= f.hidden_field :problem_id, value: params[:id] %>
+
+
+
+
+ <%= button_tag 'Compile', type: 'Button',id: 'compileButton',
+ onClick: "compile(#{params[:id]})", class: "btn btn-info" %>
+ <%= button_tag 'Run Test Case', class: "btn btn-info", id: 'testButton',
+ type: 'Button', onClick: "run_input(#{params[:id]})" %>
+ <%= button_tag 'Start Debugging', id: 'debugButton', type: 'button',
+ onclick: "start_debug(#{params[:id]})", class: "btn btn-info" %>
+ <%= button_tag 'Previous', type: 'Button', class: "btn btn-info",
+ style: "display: none", id: 'previousButton',
+ onClick: 'previous()', hidden: true %>
+ <%= button_tag 'Next', type: 'Button', class: "btn btn-info",
+ style: "display: none", id: 'nextButton',
+ onClick: 'next()', hidden: true %>
+ <%= button_tag 'Stop Debugging', type: 'Button', class: "btn btn-info",
+ style: "display: none", id: 'stopButton',
+ onClick: 'stop()', hidden: true %>
+
+ |
+
+ Accepted
+ Wrong Answer
+ Runtime Error
+ Compilation Error
+ <%= button_tag 'Submit', type: 'Button',id: 'submitButton',
+ onClick: "validate_code(#{params[:id]})", class: "btn btn-info" %>
+ |
+
+
+
+
+
+ Input:
+ <%= f.text_area :solution_input, placeholder: "Write your test case here",
+ id: "solution_input", class: "form-control", style: "width:400px" %>
+
+
+
+
+ Console:
+
+
+
+
+
+
+
-
-
- Output:
- <% if flash[:exp] || flash[:exp_2] %>
-
-
+
+
+
+
- Error
-
-
- <%= flash[:msg] %>
- <% if flash[:msg_2] %>
- <% flash[:msg_2].each do |message| %>
- <%= message %>
- <% end %>
- <% end %>
-
-
- Explanation
-
-
- <%= flash[:exp] %>
- <% if flash[:exp_2] %>
- <% flash[:exp_2].each do |message| %>
- <%= message %>
- <% end %>
- <% end %>
-
+ Error
- <% else %>
-
- <%= flash[:msg] %>
+
+
- <% end %>
-
-
- Console:
-
- <%= flash[:compiler_success] %>
- <%= flash[:compiler_fail] %>
- <% if flash[:compiler_success_2] %>
- <% flash[:compiler_success_2].each do |message| %>
- <%= message %>
- <% end %>
- <% end %>
- <% if flash[:compiler_fail_2] %>
- <% flash[:compiler_fail_2].each do |message| %>
- <%= message %>
- <% end %>
- <% end %>
- <%= flash[:compiler_feedback] %>
+
+
+ Explanation
+
+
+
+
+
+<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/topics/new.html.erb b/tutor/app/views/topics/new.html.erb
index 1ba306b9..0f7d5e78 100644
--- a/tutor/app/views/topics/new.html.erb
+++ b/tutor/app/views/topics/new.html.erb
@@ -1,37 +1,78 @@
-
+
+
+ -
+ <%= link_to "Edit Info", {:controller => 'courses', :action => 'edit',
+ :id => @course.id}, class: "btn btn-success",
+ style: "float: left;margin-left: 15px",method: :get %>
+
-
- New Topic
+ -
+ <%= link_to "Add Topics", {:controller => 'topics', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left;margin-left: 15px",method: :post %>
+
-
- <%= form_for :topic, url: {action: "create"} do |f| %>
-
- <%= f.label "Title" %>
- <% unless @new_topic.errors[:title].blank? %>
-
- Name <%= @new_topic.errors[:title].join(", ") %>
-
-
+
+ -
+ <%= link_to "Add a TA", {:controller => 'teaching_assistants', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", :method => :get %>
+
+
+
+ -
+ <%= link_to "Acknowledge a student", {:controller => 'acknowledgements', :action => 'new',
+ :course_id => @course.id}, class: "btn btn-success",
+ style: "float: left; margin-left: 15px", method: :get
+ %>
+
+
+
+
+ New Topic
+
+
+ <%= form_for :topic, url: {action: "create"} do |f| %>
+
+ <%= f.label "Title" %>
+ <% unless @new_topic.errors[:title].blank? %>
+
+ Name <%= @new_topic.errors[:title].join(", ") %>
+
+
+ <% end %>
+ <%= f.text_field :title, autofocus: true, class:"form-control",
+ style: "width:300px", placeholder: "Title", :value => @new_topic.title %>
+
+
+ <%= f.label :description %>
+ <% unless @new_topic.errors[:description].blank? %>
+
+ Description <%= @new_topic.errors[:description].join(", ") %>
+
+
+ <% end %>
+ <%= f.text_area :description, class:"form-control", style: "height:200px;width:500px;resize:none", placeholder: "Description", :value => @new_topic.description %>
+
+
+ <%= f.submit "Save", class: "btn btn-success"%>
+
<% end %>
- <%= f.text_field :title, autofocus: true, class:"form-control", style: "width:300px", placeholder: "Title", :value => @new_topic.title %>
-
-
- <%= f.label :description %>
- <% unless @new_topic.errors[:description].blank? %>
-
- Description <%= @new_topic.errors[:description].join(", ") %>
-
-
- <% end %>
- <%= f.text_area :description, class:"form-control", style: "height:200px;width:500px;resize:none", placeholder: "Description", :value => @new_topic.description %>
-
-
- <%= f.submit "Save", class: "btn btn-success"%>
-
- <% end %>
-
- <%= button_to "Back", {:controller => 'courses', :action => 'index'}, class: "btn btn-success", style: "float:right; margin-top:-50px", :method => :get %>
+
+ <%= button_to "Back", {:controller => 'courses', :action => 'index'},
+ class: "btn btn-success", style: "float:right; margin-top:-50px", :method => :get %>
+
+
\ No newline at end of file
diff --git a/tutor/app/views/topics/show.html.erb b/tutor/app/views/topics/show.html.erb
index 760e6042..843deb4d 100644
--- a/tutor/app/views/topics/show.html.erb
+++ b/tutor/app/views/topics/show.html.erb
@@ -1,21 +1,24 @@
+
-<% if @can_edit %>
- <% if flash[:error] %>
- <%= flash[:error] %>
-
- <% end %>
- <% if flash[:success] %>
- <%= flash[:success] %>
-
+ <% if @can_edit %>
+ <% if flash[:error] %>
+ <%= flash[:error] %>
+
<% end %>
-<% end %>
+ <% if flash[:success] %>
+ <%= flash[:success] %>
+
+ <% end %>
+ <% end %>
<% @tracks.each do |t| %>
-
+
@@ -49,7 +52,11 @@
<% if @can_edit %>
- New
+
+ New
+
+ Edit Track Rating
+
<% if !flash[:title] && !flash[:difficulty] %>
<% hide = 'hidden=true' %>
<% end %>
@@ -58,13 +65,25 @@
html: {class: "form-inline form-horizontal nifty_form" , role: "form"} do |f| %>
<%= f.text_field :title , :value => flash[:title] ,
- :class => "form-control" , :placeholder => "Title"%>
+ :class => "form-control", :style => "width:150px", :placeholder => "Title"%>
<%= f.number_field :difficulty , :value => flash[:difficulty] ,
:class => "form-control" , :placeholder => "Difficulty" ,
- :min => 1 %>
+ :min => 1, :max => 5, :style => "width:150px" %>
+
<%= f.hidden_field :topic_id , :value => params[:id] %>
- <%= f.submit "Create" , :class => "btn btn-default" %>
+ <%= f.submit "Create" , :class => "btn btn-info"%>
<% end %>
<% end %>
+
+
+
+ Hold Press and Drag to sort the tracks
+
+ <% @tracks.each do |track| %>
+ - <%= track.title %>
+ <% end %>
+
+
+
\ No newline at end of file
diff --git a/tutor/app/views/tracks/new.html.erb b/tutor/app/views/tracks/new.html.erb
new file mode 100644
index 00000000..e69de29b
diff --git a/tutor/app/views/tracks/show.html.erb b/tutor/app/views/tracks/show.html.erb
index 1164e265..9dbf319c 100644
--- a/tutor/app/views/tracks/show.html.erb
+++ b/tutor/app/views/tracks/show.html.erb
@@ -1,6 +1,6 @@
<% if @can_edit %>
<% if flash[:error] %>
@@ -45,7 +45,7 @@
<%= problem.description %>
<% if @can_edit %>
<%= link_to "Edit",
- edit_problem_path(:id => problem.id), class: 'btn btn-primary' %>
+ edit_problem_path(:id => problem.id), class: 'btn btn-info' %>
<% end %>
@@ -79,7 +79,7 @@
<%= problem.description %>
<% if @can_edit %>
<%= link_to "Edit",
- edit_problem_path(:id => problem.id), class: 'btn btn-primary' %>
+ edit_problem_path(:id => problem.id), class: 'btn btn-info', style: 'margin-left:100px' %>
<% end %>
@@ -92,5 +92,5 @@
<% if @can_edit %>
- <%= link_to "Add", new_problem_path(:id => @track.id), class: 'btn btn-primary' %>
+ <%= link_to "Add", new_problem_path(:id => @track.id), class: "btn btn-info" %>
<% end %>
\ No newline at end of file
diff --git a/tutor/app/views/utilities/_result_form.html.erb b/tutor/app/views/utilities/_result_form.html.erb
index ad8905be..ea7f373e 100644
--- a/tutor/app/views/utilities/_result_form.html.erb
+++ b/tutor/app/views/utilities/_result_form.html.erb
@@ -17,7 +17,11 @@
<% objects.each do |object| %>
-
- <%= link_to object.name, class: 'color' %>
+ <% unless object.name.nil? %>
+ <%= link_to object.name, object ,class: 'color' %>
+ <% else %>
+ <%= link_to object.title, object ,class: 'color' %>
+ <% end %>
<% end %>
diff --git a/tutor/config/routes.rb b/tutor/config/routes.rb
index a2b7b118..ea89d3a3 100644
--- a/tutor/config/routes.rb
+++ b/tutor/config/routes.rb
@@ -1,8 +1,10 @@
Tutor::Application.routes.draw do
+
get "utilities/simple_search"
get "utilities/advanced_search"
get "utilities/auto_complete"
+
devise_for :teaching_assistants
devise_for :students
devise_for :lecturers
@@ -13,21 +15,25 @@
# Example of regular route:
# get 'products/:id' => 'catalog#view'
# get 'products/index'
- post 'solutions/compile_solution' => 'problems#show'
+ get 'courses/sign_up'
+ get 'tracks/show_classmates/:id' => 'tracks#show_classmates'
+ post 'solutions/compile_solution' => 'solutions#compile_solution'
post 'courses/new' => 'courses#new'
post 'courses/share' => 'courses#share'
- get 'courses/sign_up'
+ post 'solutions/execute' => 'solutions#execute'
post '/posts/:id' => 'posts#update'
- get 'tracks/show_classmates/:id' => 'tracks#show_classmates'
post 'tracks/insert_recommendation' => 'tracks#insert_recommendation'
- post 'solutions/execute' => 'problems#show'
post 'debuggers/:id' => 'debuggers#start'
+
+ get 'problems/edit'
+
get "tips/new"
get "tips/create"
get "tips/show"
get "tips/index"
get "tips/edit"
- get "tips/destroy"
+ get "tips/destroy"
+ post "tips/:id/edit" => 'tips#update'
# You can have the root of your site routed with "root"
@@ -77,6 +83,7 @@
resources :model_answers
resources :solutions
resources :topics
+ resources :replies
resources :hints
resources :lecturers
resources :teaching_assistants
@@ -102,19 +109,37 @@
get 'getProblems'
end
end
+
+ resources :topics do
+ collection do
+ post 'sort'
+ end
+ end
+
resources :discussion_boards do
member do
post 'toggle'
end
end
+
resources :model_answers do
post "model_answers/new"
end
+
+ resources :tips do
+ get "tips/index"
+ end
+
+ resources :hints do
+ get "hints/index"
+ end
+
resources :solutions_constraints do
collection do
post "new"
end
end
+
resources :problems do
get 'done'
get 'destroy_problem'
@@ -154,4 +179,5 @@
# # (app/controllers/admin/products_controller.rb)
# resources :products
# end
+
end
diff --git a/tutor/db/schema.rb b/tutor/db/schema.rb
index 0a4e42ab..1ca81304 100644
--- a/tutor/db/schema.rb
+++ b/tutor/db/schema.rb
@@ -11,7 +11,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 20140423104456) do
+ActiveRecord::Schema.define(version: 20140506131804) do
create_table "acknowledgements", force: true do |t|
t.string "message"
@@ -51,6 +51,42 @@
t.datetime "updated_at"
end
+ create_table "contest_progresses", force: true do |t|
+ t.integer "contest_id"
+ t.integer "student_id"
+ t.integer "problem_id"
+ t.boolean "status"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "contest_progresses", ["contest_id", "student_id", "problem_id"], name: "ConProgress", unique: true
+
+ create_table "contests", force: true do |t|
+ t.string "title"
+ t.text "description"
+ t.datetime "expiration_date"
+ t.integer "course_id"
+ t.integer "owner_id"
+ t.string "owner_type"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "contests_problems", id: false, force: true do |t|
+ t.integer "contest_id", null: false
+ t.integer "problem_id", null: false
+ end
+
+ add_index "contests_problems", ["contest_id", "problem_id"], name: "index_contests_problems_on_contest_id_and_problem_id", unique: true
+
+ create_table "contests_students", id: false, force: true do |t|
+ t.integer "contest_id", null: false
+ t.integer "student_id", null: false
+ end
+
+ add_index "contests_students", ["contest_id", "student_id"], name: "index_contests_students_on_contest_id_and_student_id", unique: true
+
create_table "course_students", force: true do |t|
t.boolean "share", default: false
t.integer "course_id"
@@ -199,6 +235,16 @@
t.datetime "updated_at"
end
+ create_table "problem_opening_times", force: true do |t|
+ t.integer "time"
+ t.integer "student_id"
+ t.integer "problem_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ add_index "problem_opening_times", ["student_id", "problem_id"], name: "index_problem_opening_times_on_student_id_and_problem_id", unique: true
+
create_table "problems", force: true do |t|
t.string "title"
t.text "description"
@@ -206,6 +252,7 @@
t.integer "views_count"
t.integer "time_limit"
t.integer "track_id"
+ t.string "problem_type"
t.integer "owner_id"
t.string "owner_type"
t.datetime "created_at"
diff --git a/tutor/db/seeds.rb b/tutor/db/seeds.rb
index 98854c7e..5dfbc5b8 100644
--- a/tutor/db/seeds.rb
+++ b/tutor/db/seeds.rb
@@ -65,9 +65,14 @@
t.save!
puts("# --------------------------Courses------------------------------")
- Course.create(name:"Course1", description:"This is course one", code:1, year:2014, semester:1)
- Course.create(name:"Course2", description:"This is course two", code:2, year:2014, semester:1)
+
+ Course.create(name:"Data Structures and Alogrithms", description:"This is a very easy course", code:"CSEN1", year:2014, semester:1)
+ Course.create(name:"Computer Programming Lab", description:"This course's evaluation system is the bad", code:2, year:2014, semester:1)
Course.create(name:"Course3", description:"This is course three", code:3, year:2014, semester:1)
+ Course.create(:name => "CS 2", description:"This is course four", :code => "cs2", :year => 2014, :semester => 2, :university => "GUC")
+ Course.create(:name => "CS 3", description:"This is course five", :code => "cs3", :year => 2014, :semester => 3, :university => "GUC")
+ Course.create(:name => "CS 4", description:"This is course six", :code => "cs4", :year => 2014, :semester => 4, :university => "AUC")
+ Course.create(:name => "CS 5", description:"This is course seven", :code => "cs5", :year => 2014, :semester => 5, :university => "AUC")
puts("# --------------------------Course_Student------------------------------")
CourseStudent.create(share: true)
@@ -76,6 +81,10 @@
DiscussionBoard.create(title:"DiscussionBoard1", activated: true)
DiscussionBoard.create(title:"DiscussionBoard2", activated: true)
DiscussionBoard.create(title:"DiscussionBoard3", activated: true)
+ DiscussionBoard.create(title:"DiscussionBoard4", activated: true)
+ DiscussionBoard.create(title:"DiscussionBoard5", activated: true)
+ DiscussionBoard.create(title:"DiscussionBoard6", activated: true)
+ DiscussionBoard.create(title:"DiscussionBoard7", activated: true)
puts("# --------------------------Posts------------------------------")
Post.create(title:"My 1st Post", content: "The Main Objective is to be a winner", views_count: 20)
@@ -89,6 +98,14 @@
Reply.create(content: "Reply3")
Reply.create(content: "Reply4")
Reply.create(content: "Reply5")
+ Reply.create(content: "Reply6")
+ Reply.create(content: "Reply7")
+ Reply.create(content: "Reply8")
+ Reply.create(content: "Reply9")
+ Reply.create(content: "Reply10")
+ Reply.create(content: "Reply11")
+ Reply.create(content: "Reply12")
+ Reply.create(content: "Reply13")
puts("# --------------------------Topics------------------------------")
Topic.create(title: "Topic1", description: "This is Topic1 description")
@@ -96,10 +113,10 @@
Topic.create(title: "Topic3", description: "This is Topic3 description")
puts("# -----------------------Hints---------------------------")
- Hint.create(message: "Do not Try to Solve CS problem-1", category: false, time: 5)
- Hint.create(message: "Do not Try to Solve CS problem-2")
- Hint.create(message: "Do not Try to Solve CS problem-3")
-
+ Hint.create(message: "Do not Try to Solve CS problem-1", category: false, time: 5, submission_counter:10)
+ Hint.create(message: "Do not Try to Solve CS problem-2", category: true, time: 5, submission_counter:10)
+ Hint.create(message: "Do not Try to Solve CS problem-3", category: false, time: 5, submission_counter:10)
+
puts("# -----------------------ModelAnswer---------------------------")
ModelAnswer.create(title: "Answer1", answer: "System.out.println('SQL baaaad')-1")
ModelAnswer.create(title: "Answer2", answer: "System.out.println('SQL baaaad')-2")
@@ -176,6 +193,17 @@
Recommendation.create(problem_id:2, student_id:1, recommender_id:2)
Recommendation.create(problem_id:5, student_id:1, recommender_id:2)
+puts("#-----------------------Contests-----------------------")
+ Contest.create(title:"Iteration",description:"If you can solve this you will get a level up")
+ Contest.create(title:"Recursion",description:"If you can solve this you will get 2 level up")
+ Contest.create(title:"DB",description:"If you can solve this you will get 4 level up")
+
+puts("#-----------------------Contests-----------------------")
+ ContestProgress.create!(status:true)
+ ContestProgress.create!(status:true)
+ ContestProgress.create!(status:true)
+ ContestProgress.create!(status:false)
+
puts("# -------------------------------------------------------")
puts("**************************************************************")
puts(" Creating Relations ")
@@ -193,6 +221,24 @@
Lecturer.first.replies << Reply.find_by_id(2)
Lecturer.first.replies << Reply.find_by_id(3)
Lecturer.first.replies << Reply.find_by_id(4)
+ Lecturer.first.replies << Reply.find_by_id(5)
+ Lecturer.first.replies << Reply.find_by_id(6)
+ Lecturer.first.replies << Reply.find_by_id(7)
+ Lecturer.first.replies << Reply.find_by_id(8)
+ Lecturer.first.replies << Reply.find_by_id(9)
+ Lecturer.first.replies << Reply.find_by_id(10)
+ Lecturer.first.replies << Reply.find_by_id(11)
+ Lecturer.first.replies << Reply.find_by_id(12)
+ Lecturer.first.replies << Reply.find_by_id(13)
+
+ Lecturer.find_by_id(1).courses << Course.find_by_code("cs2")
+ Lecturer.find_by_id(1).courses << Course.find_by_code("cs3")
+ Lecturer.find_by_id(2).courses << Course.find_by_code("cs4")
+ Lecturer.find_by_id(2).courses << Course.find_by_code("cs5")
+
+ Lecturer.find_by_id(1).contests << Contest.find_by_id(1)
+ Lecturer.find_by_id(1).contests << Contest.find_by_id(2)
+ Lecturer.find_by_id(1).contests << Contest.find_by_id(3)
puts("# -----------------------Students---------------------------")
Student.first.course_students << CourseStudent.first
@@ -222,6 +268,16 @@
Student.first.progressions << TrackProgression.find_by_id(4)
Student.first.progressions << TrackProgression.find_by_id(5)
+ Student.find_by_id(1).contests << Contest.find_by_id(1)
+ Student.find_by_id(2).contests << Contest.find_by_id(1)
+ Student.find_by_id(3).contests << Contest.find_by_id(2)
+ Student.find_by_id(4).contests << Contest.find_by_id(3)
+
+ Student.find_by_id(1).contest_progresses << ContestProgress.find_by_id(1)
+ Student.find_by_id(1).contest_progresses << ContestProgress.find_by_id(2)
+ Student.find_by_id(2).contest_progresses << ContestProgress.find_by_id(3)
+ Student.find_by_id(2).contest_progresses << ContestProgress.find_by_id(4)
+
# Other way to add Course into student, but it will require getting Course_student via searching
# since the table has key on (student_id and course_id)then the array will always be 1 elemet [0]
Student.find_by_id(2).courses << Course.find_by_id(2)
@@ -233,6 +289,21 @@
TeachingAssistant.first.courses << Course.first
TeachingAssistant.find_by_id(2).courses << Course.find_by_id(2)
+puts("# -----------------------Posts---------------------------")
+ Post.first.replies << Reply.first
+ Post.first.replies << Reply.find_by_id(2)
+ Post.first.replies << Reply.find_by_id(3)
+ Post.first.replies << Reply.find_by_id(4)
+ Post.first.replies << Reply.find_by_id(5)
+ Post.first.replies << Reply.find_by_id(6)
+ Post.first.replies << Reply.find_by_id(7)
+ Post.first.replies << Reply.find_by_id(8)
+ Post.first.replies << Reply.find_by_id(9)
+ Post.first.replies << Reply.find_by_id(10)
+ Post.first.replies << Reply.find_by_id(11)
+ Post.first.replies << Reply.find_by_id(12)
+ Post.first.replies << Reply.find_by_id(13)
+
puts("# -----------------------Problems---------------------------")
Problem.find_by_id(3).test_cases << TestCase.first
Problem.first.model_answers << ModelAnswer.first
@@ -265,6 +336,12 @@
Problem.first.attempts << Attempt.find_by_id(12)
Problem.first.attempts << Attempt.find_by_id(13)
+ Problem.find_by_id(1).contests_progresses << ContestProgress.find_by_id(1)
+ Problem.find_by_id(1).contests_progresses << ContestProgress.find_by_id(2)
+ Problem.find_by_id(1).contests_progresses << ContestProgress.find_by_id(3)
+ Problem.find_by_id(2).contests_progresses << ContestProgress.find_by_id(3)
+ Problem.find_by_id(3).contests_progresses << ContestProgress.find_by_id(4)
+
puts("# -----------------------Hints---------------------------")
#Problem.first.model_answers.first.hints << Hint.first
@@ -286,11 +363,28 @@
Course.first.discussion_board = DiscussionBoard.first
Course.first.topics << Topic.find_by_id(2)
Course.find_by_id(2).topics << Topic.find_by_id(3)
- Course.find_by_id(2).discussion_board = DiscussionBoard.last
+ Course.find_by_id(2).discussion_board = DiscussionBoard.find_by_id(2)
Course.first.course_students << CourseStudent.first
+ Course.find_by_id(3).discussion_board = DiscussionBoard.find_by_id(3)
+ Course.find_by_id(4).discussion_board = DiscussionBoard.find_by_id(4)
+ Course.find_by_id(5).discussion_board = DiscussionBoard.find_by_id(5)
+ Course.find_by_id(6).discussion_board = DiscussionBoard.find_by_id(6)
+ Course.find_by_id(7).discussion_board = DiscussionBoard.find_by_id(7)
puts("# ----------------- DiscussionBoard -----------------------")
DiscussionBoard.first.posts << Post.first
DiscussionBoard.first.posts << Post.find_by_id(2)
+puts("# ----------------- Contests -----------------------")
+ Contest.find_by_id(1).problems << Problem.find_by_id(1)
+ Contest.find_by_id(1).problems << Problem.find_by_id(2)
+ Contest.find_by_id(1).problems << Problem.find_by_id(3)
+ Contest.find_by_id(2).problems << Problem.find_by_id(4)
+ Contest.find_by_id(3).problems << Problem.find_by_id(5)
+
+ Contest.find_by_id(1).progress << ContestProgress.find_by_id(1)
+ Contest.find_by_id(2).progress << ContestProgress.find_by_id(2)
+ Contest.find_by_id(2).progress << ContestProgress.find_by_id(3)
+ Contest.find_by_id(3).progress << ContestProgress.find_by_id(4)
+
puts("# ---------------------------------------------------------")
diff --git a/tutor/st1pr1.class b/tutor/st1pr1.class
deleted file mode 100644
index 154cd90f..00000000
Binary files a/tutor/st1pr1.class and /dev/null differ
diff --git a/tutor/test/controllers/hints_controller_test.rb b/tutor/test/controllers/hints_controller_test.rb
new file mode 100644
index 00000000..e6178b3e
--- /dev/null
+++ b/tutor/test/controllers/hints_controller_test.rb
@@ -0,0 +1,9 @@
+require 'test_helper'
+
+class HintsControllerTest < ActionController::TestCase
+ test "should get index" do
+ get :index
+ assert_response :success
+ end
+
+end
diff --git a/tutor/test/controllers/replies_controller_test.rb b/tutor/test/controllers/replies_controller_test.rb
new file mode 100644
index 00000000..e2923744
--- /dev/null
+++ b/tutor/test/controllers/replies_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class RepliesControllerTest < ActionController::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/test/controllers/tips_controller_test.rb b/tutor/test/controllers/tips_controller_test.rb
index 3109b609..5b744e3c 100644
--- a/tutor/test/controllers/tips_controller_test.rb
+++ b/tutor/test/controllers/tips_controller_test.rb
@@ -1,6 +1,7 @@
require 'test_helper'
class TipsControllerTest < ActionController::TestCase
+
test "should get new" do
get :new
assert_response :success
@@ -30,5 +31,5 @@ class TipsControllerTest < ActionController::TestCase
get :destroy
assert_response :success
end
-
+
end
diff --git a/tutor/test/fixtures/contest_progresses.yml b/tutor/test/fixtures/contest_progresses.yml
new file mode 100644
index 00000000..937a0c00
--- /dev/null
+++ b/tutor/test/fixtures/contest_progresses.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the '{}' from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
diff --git a/tutor/test/fixtures/contests.yml b/tutor/test/fixtures/contests.yml
new file mode 100644
index 00000000..937a0c00
--- /dev/null
+++ b/tutor/test/fixtures/contests.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+# This model initially had no columns defined. If you add columns to the
+# model remove the '{}' from the fixture names and add the columns immediately
+# below each fixture, per the syntax in the comments below
+#
+one: {}
+# column: value
+#
+two: {}
+# column: value
diff --git a/tutor/test/fixtures/problem_opening_times.yml b/tutor/test/fixtures/problem_opening_times.yml
new file mode 100644
index 00000000..59dc7e9d
--- /dev/null
+++ b/tutor/test/fixtures/problem_opening_times.yml
@@ -0,0 +1,11 @@
+# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ start_time: 2014-04-25 02:31:02
+ student_id: 1
+ problem_id: 1
+
+two:
+ start_time: 2014-04-25 02:31:02
+ student_id: 1
+ problem_id: 1
diff --git a/tutor/test/helpers/hints_helper_test.rb b/tutor/test/helpers/hints_helper_test.rb
new file mode 100644
index 00000000..1a40f25c
--- /dev/null
+++ b/tutor/test/helpers/hints_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class HintsHelperTest < ActionView::TestCase
+end
diff --git a/tutor/test/helpers/replies_helper_test.rb b/tutor/test/helpers/replies_helper_test.rb
new file mode 100644
index 00000000..4bae2ca8
--- /dev/null
+++ b/tutor/test/helpers/replies_helper_test.rb
@@ -0,0 +1,4 @@
+require 'test_helper'
+
+class RepliesHelperTest < ActionView::TestCase
+end
diff --git a/tutor/test/models/contest_progress_test.rb b/tutor/test/models/contest_progress_test.rb
new file mode 100644
index 00000000..60dbcbae
--- /dev/null
+++ b/tutor/test/models/contest_progress_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class ContestProgressTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/test/models/contest_test.rb b/tutor/test/models/contest_test.rb
new file mode 100644
index 00000000..98582be6
--- /dev/null
+++ b/tutor/test/models/contest_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class ContestTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/test/models/problem_opening_time_test.rb b/tutor/test/models/problem_opening_time_test.rb
new file mode 100644
index 00000000..b6c85add
--- /dev/null
+++ b/tutor/test/models/problem_opening_time_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class ProblemOpeningTimeTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/tutor/vendor/assets/stylesheets/bootstrap.css b/tutor/vendor/assets/stylesheets/bootstrap.css
index 338f20c3..0d5ef5b2 100644
--- a/tutor/vendor/assets/stylesheets/bootstrap.css
+++ b/tutor/vendor/assets/stylesheets/bootstrap.css
@@ -2243,6 +2243,35 @@ fieldset[disabled] .btn-warning.active {
color: #f39c12;
background-color: #ffffff;
}
+.btn-link {
+ color: #428bca;
+ font-weight: 400;
+ cursor: pointer;
+ border-radius: 0
+}
+
+.btn-link, .btn-link:active, .btn-link[disabled], fieldset[disabled] .btn-link {
+ background-color: transparent;
+ -webkit-box-shadow: none;
+ box-shadow: none
+}
+
+.btn-link, .btn-link:hover, .btn-link:focus, .btn-link:active {
+ border-color: transparent
+}
+
+.btn-link:hover, .btn-link:focus {
+ color: #2a6496;
+ text-decoration: underline;
+ background-color: transparent
+}
+
+.btn-link[disabled]:hover, fieldset[disabled] .btn-link:hover,
+.btn-link[disabled]:focus, fieldset[disabled] .btn-link:focus {
+ color: #999;
+ text-decoration: none
+}
+
.btn-danger {
color: #ffffff;
background-color: #e74c3c;
|