<commit_before>// ---------------------------------------------------------------------------------------
// class for touch input
// ---------------------------------------------------------------------------------------

function CTouchLevel( game)
{
	this.game = game;
	this.freezeCount = 0;
	this.selectedGemObj;
	this.selectedGemPos;
	this.selectedGemTween = null;

	try {
		this.freeze();
		this.game.input.addMoveCallback( this.OnDraging, this);

		this.selectedGemObj = null;
		this.selectedGemPos = { x: 0, y: 0 };
	} catch(e) {
		console.error('CTouchLevel not ready');
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.addGem = function( gem)
{
	gem.events.onInputDown.add( this.OnTouchDownGem, this);
	gem.events.onInputUp.add( this.OnTouchUpGem, this);
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.freeze = function()
{
	++this.freezeCount;
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.thaw = function()
{
	if( this.freezeCount > 0) {
		--this.freezeCount;
	} else {
		console.log( 'Thaw touch support too often!');
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnTouchDownGem = function( gem, pointer)
{
	if( this.freezeCount > 0) {
		return;
	}

	this.selectedGemObj = gem;
	this.selectedGemPos.x = gem.posX;
	this.selectedGemPos.y = gem.posY;

	console.log( 'Down gem ' + gem.frame + ' at ' + (gem.posX + 1) + 'x' + (gem.posY + 1));
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnTouchUpGem = function( gem)
{
	if( this.freezeCount > 0) {
		return;
	}

	console.log( 'Up   gem ' + gem.frame + ' at ' + (gem.posX + 1) + 'x' + (gem.posY + 1));

	// when the mouse is released with a gem selected
	// 1) check for matches
	// 2) remove matched gems
	// 3) drop down gems above removed gems
	// 4) refill the board

	this.checkAndKillGemMatches( gem);

	if( CInit.tempShiftedGem !== null) {
		this.checkAndKillGemMatches( CInit.tempShiftedGem);
	}

	removeKilledGems();

	var dropGemDuration = dropGems();

	// delay board refilling until all existing gems have dropped down
	this.game.time.events.add( dropGemDuration * 100, refillBoard);

	this.freeze();

	this.selectedGemObj = null;
	CInit.tempShiftedGem = null;
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnDraging = function( pointer, x, y, fromClick)
{
	if( this.selectedGemObj && pointer.isDown) {
		var posX = this.convertCoord2Pos( x);
		var posY = this.convertCoord2Pos( y);

		if( this.canMove( this.selectedGemPos.x, this.selectedGemPos.y, posX, posY)) {
			if( this.selectedGemTween !== null) {
				this.game.tweens.remove( this.selectedGemTween);
			}
			this.selectedGemTween = tweenGemPos( this.selectedGemObj, posX, posY);

console.log( 'Drag gem   to ' + (posX + 1) + 'x' + (posY + 1));
			CInit.gems.bringToTop( this.selectedGemObj);

			// if we moved a gem to make way for the selected gem earlier, move it back into its starting position
			if( CInit.tempShiftedGem !== null) {
				tweenGemPos( CInit.tempShiftedGem, this.selectedGemObj.posX , this.selectedGemObj.posY);
				swapGemPosition( this.selectedGemObj, CInit.tempShiftedGem);
			}

			// when the player moves the selected gem, we need to swap the position of the selected gem with the gem currently in that position
			CInit.tempShiftedGem = getGem( posX, posY);

			if( CInit.tempShiftedGem === this.selectedGemObj) {
				CInit.tempShiftedGem = null;
			} else {
				tweenGemPos( CInit.tempShiftedGem, this.selectedGemObj.posX, this.selectedGemObj.posY);
				swapGemPosition( this.selectedGemObj, CInit.tempShiftedGem);
			}
		}
	}
}

// ---------------------------------------------------------------------------------------
// count how many gems of the same color are above, below, to the left and right
// if there are more than 3 matched horizontally or vertically, kill those gems
// if no match was made, move the gems back into their starting positions
CTouchLevel.prototype.checkAndKillGemMatches = function( gem, matchedGems)
{
	if( gem !== null) {
		var countUp = countSameColorGems( gem, 0, -1);
		var countDown = countSameColorGems( gem, 0, 1);
		var countLeft = countSameColorGems( gem, -1, 0);
		var countRight = countSameColorGems( gem, 1, 0);

		var countHoriz = countLeft + countRight + 1;
		var countVert = countUp + countDown + 1;

		if( countVert >= 3) {
			killGemRange( gem.posX, gem.posY - countUp, gem.posX, gem.posY + countDown);
		}

		if( countHoriz >= 3) {
			killGemRange( gem.posX - countLeft, gem.posY, gem.posX + countRight, gem.posY);
		}

		if( countVert < 3 && countHoriz < 3) {
			if( gem.posX !== CInit.touch.selectedGemPos.x || gem.posY !== CInit.touch.selectedGemPos.y) {
				if( this.selectedGemTween !== null) {
					this.game.tweens.remove( this.selectedGemTween);
				}
				this.selectedGemTween = tweenGemPos( gem, CInit.touch.selectedGemPos.x, CInit.touch.selectedGemPos.y);

				if( CInit.tempShiftedGem !== null) {
					tweenGemPos( CInit.tempShiftedGem, gem.posX, gem.posY);
				}

				swapGemPosition( gem, CInit.tempShiftedGem);
			}
		}
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.convertCoord2Pos = function( coordinate)
{
	return Phaser.Math.floor( coordinate / CInit.GEM_SIZE_SPACED);
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.canMove = function( fromX, fromY, toX, toY)
{
	if(( toX < 0) || (toX >= CInit.BOARD_COLS) || (toY < 0) || (toY >= CInit.BOARD_ROWS)) {
		return false;
	}

	if(( fromX == toX) && (fromY == toY)) {
		return false;
	}

	if(( fromX === toX) && (fromY >= toY - 1) && (fromY <= toY + 1)) {
		return true;
	}

	if(( fromY === toY) && (fromX >= toX - 1) && (fromX <= toX + 1)) {
		return true;
	}

	return false;
}

// ---------------------------------------------------------------------------------------
// eof
<commit_msg>added diagonal dragging<commit_after>// ---------------------------------------------------------------------------------------
// class for touch input
// ---------------------------------------------------------------------------------------

function CTouchLevel( game)
{
	this.game = game;
	this.freezeCount = 0;
	this.selectedGemObj;
	this.selectedGemPos;
	this.selectedGemTween = null;
	this.dragLength = .5;

	try {
		this.freeze();
		this.game.input.addMoveCallback( this.OnDraging, this);

		this.selectedGemObj = null;
		this.selectedGemPos = { x: 0, y: 0, dragX: 0, dragY: 0 };
	} catch(e) {
		console.error('CTouchLevel not ready');
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.addGem = function( gem)
{
	gem.events.onInputDown.add( this.OnTouchDownGem, this);
	gem.events.onInputUp.add( this.OnTouchUpGem, this);
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.freeze = function()
{
	++this.freezeCount;
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.thaw = function()
{
	if( this.freezeCount > 0) {
		--this.freezeCount;
	} else {
		console.log( 'Thaw touch support too often!');
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnTouchDownGem = function( gem, pointer)
{
	if( this.freezeCount > 0) {
		return;
	}

	this.selectedGemObj = gem;
	this.selectedGemPos.x = gem.posX;
	this.selectedGemPos.y = gem.posY;
	this.selectedGemPos.dragX = pointer.x;
	this.selectedGemPos.dragY = pointer.y;

	console.log( 'Down gem ' + gem.frame + ' at ' + (gem.posX + 1) + 'x' + (gem.posY + 1));
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnTouchUpGem = function( gem)
{
	if( this.freezeCount > 0) {
		return;
	}

	console.log( 'Up   gem ' + gem.frame + ' at ' + (gem.posX + 1) + 'x' + (gem.posY + 1));

	// when the mouse is released with a gem selected
	// 1) check for matches
	// 2) remove matched gems
	// 3) drop down gems above removed gems
	// 4) refill the board

	this.checkAndKillGemMatches( gem);

	if( CInit.tempShiftedGem !== null) {
		this.checkAndKillGemMatches( CInit.tempShiftedGem);
	}

	removeKilledGems();

	var dropGemDuration = dropGems();

	// delay board refilling until all existing gems have dropped down
	this.game.time.events.add( dropGemDuration * 100, refillBoard);

	this.freeze();

	this.selectedGemObj = null;
	CInit.tempShiftedGem = null;
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.OnDraging = function( pointer, x, y, fromClick)
{
	if( this.selectedGemObj && pointer.isDown) {
		var posX = this.selectedGemPos.x;
		var posY = this.selectedGemPos.y;

		var block = this.getMove( this.selectedGemPos.dragX, this.selectedGemPos.dragY, x, y);
		if(( 0 <= block) && (block <= 2)) {
			--posY;
		}
		if(( 2 <= block) && (block <= 4)) {
			++posX;
		}
		if(( 4 <= block) && (block <= 6)) {
			++posY;
		}
		if(( 0 == block) || (6 == block) || (7 == block)) {
			--posX;
		}

		if( this.canMove( this.selectedGemPos.x, this.selectedGemPos.y, posX, posY)) {
			if( this.selectedGemTween !== null) {
				this.game.tweens.remove( this.selectedGemTween);
			}
			this.selectedGemTween = tweenGemPos( this.selectedGemObj, posX, posY);

console.log( 'Drag gem   to ' + (posX + 1) + 'x' + (posY + 1));
			CInit.gems.bringToTop( this.selectedGemObj);

			// if we moved a gem to make way for the selected gem earlier, move it back into its starting position
			if( CInit.tempShiftedGem !== null) {
				tweenGemPos( CInit.tempShiftedGem, this.selectedGemObj.posX , this.selectedGemObj.posY);
				swapGemPosition( this.selectedGemObj, CInit.tempShiftedGem);
			}

			// when the player moves the selected gem, we need to swap the position of the selected gem with the gem currently in that position
			CInit.tempShiftedGem = getGem( posX, posY);

			if( CInit.tempShiftedGem === this.selectedGemObj) {
				CInit.tempShiftedGem = null;
			} else {
				tweenGemPos( CInit.tempShiftedGem, this.selectedGemObj.posX, this.selectedGemObj.posY);
				swapGemPosition( this.selectedGemObj, CInit.tempShiftedGem);
			}
		}
	}
}

// ---------------------------------------------------------------------------------------
// count how many gems of the same color are above, below, to the left and right
// if there are more than 3 matched horizontally or vertically, kill those gems
// if no match was made, move the gems back into their starting positions
CTouchLevel.prototype.checkAndKillGemMatches = function( gem, matchedGems)
{
	if( gem !== null) {
		var countUp = countSameColorGems( gem, 0, -1);
		var countDown = countSameColorGems( gem, 0, 1);
		var countLeft = countSameColorGems( gem, -1, 0);
		var countRight = countSameColorGems( gem, 1, 0);

		var countHoriz = countLeft + countRight + 1;
		var countVert = countUp + countDown + 1;

		if( countVert >= 3) {
			killGemRange( gem.posX, gem.posY - countUp, gem.posX, gem.posY + countDown);
		}

		if( countHoriz >= 3) {
			killGemRange( gem.posX - countLeft, gem.posY, gem.posX + countRight, gem.posY);
		}

		if( countVert < 3 && countHoriz < 3) {
			if( gem.posX !== CInit.touch.selectedGemPos.x || gem.posY !== CInit.touch.selectedGemPos.y) {
				if( this.selectedGemTween !== null) {
					this.game.tweens.remove( this.selectedGemTween);
				}
				this.selectedGemTween = tweenGemPos( gem, CInit.touch.selectedGemPos.x, CInit.touch.selectedGemPos.y);

				if( CInit.tempShiftedGem !== null) {
					tweenGemPos( CInit.tempShiftedGem, gem.posX, gem.posY);
				}

				swapGemPosition( gem, CInit.tempShiftedGem);
			}
		}
	}
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.canMove = function( fromX, fromY, toX, toY)
{
	if(( toX < 0) || (toX >= CInit.BOARD_COLS) || (toY < 0) || (toY >= CInit.BOARD_ROWS)) {
		return false;
	}

	if(( fromX == toX) && (fromY == toY)) {
		return false;
	}

	if(( fromY >= toY - 1) && (fromY <= toY + 1) && (fromX >= toX - 1) && (fromX <= toX + 1)) {
		return true;
	}

	return false;
}

// ---------------------------------------------------------------------------------------

CTouchLevel.prototype.getMove = function( fromX, fromY, toX, toY)
{
	var x = fromX - toX;
	var y = fromY - toY;
	var length = Math.sqrt( x*x + y*y);
	var angle = Math.acos( x / length) * 180 / Math.PI;
	length /= CInit.GEM_SIZE_SPACED;

	if( length < this.dragLength) {
		return -1;
	}

	if( y < 0) {
		angle = 360 - angle;
	}

	angle -= 22.5;

	if( angle < 0) {
		angle += 360;
	}

	return Math.floor( angle / 45);
}

// ---------------------------------------------------------------------------------------
// eof
<|endoftext|>


<commit_before>/*!
 * jQuery UI Selectmenu @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		appendTo: null,
		icons: {
			button: "ui-icon-triangle-1-s"
		},
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},

		// callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		select: null
	},

	_create: function() {
		var selectmenuId = this.element.uniqueId().attr( "id" );
		this.ids = {
			element: selectmenuId,
			button: selectmenuId + "-button",
			menu: selectmenuId + "-menu"
		};

		this._drawButton();
		this._drawMenu();

		this._on( document, this._documentClick );

		if ( this.options.disabled ) {
			this.disable();
		}
	},

	_drawButton: function() {
		var tabindex = this.element.attr( "tabindex" );

		// Fix existing label
		this.label = $( "label[for='" + this.ids.element + "']" ).attr( "for", this.ids.button );
		this._on( this.label, {
			click: function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		// Hide original select tag
		this.element.hide();

		// Create button
		this.button = $( "<span>", {
			"class": "ui-selectmenu-button ui-widget ui-state-default ui-corner-all",
			tabindex: tabindex || this.options.disabled ? -1 : 0,
			id: this.ids.button,
			width: this.element.outerWidth(),
			role: "combobox",
			"aria-expanded": "false",
			"aria-autocomplete": "list",
			"aria-owns": this.ids.menu,
			"aria-haspopup": "true"
		})
		.insertAfter( this.element );

		$( "<span>", {
			"class": "ui-icon " + this.options.icons.button
		}).prependTo( this.button );

		this.buttonText = $( "<span>", {
			"class": "ui-selectmenu-text"
		})
		.appendTo( this.button );
		this._setText( this.buttonText, this.element.find( "option:selected" ).text() );

		this._on( this.button, this._buttonEvents );
		this._hoverable( this.button );
		this._focusable( this.button );
	},

	_drawMenu: function() {
		var menuInstance,
			that = this;

		// Create menu portion, append to body
		this.menu = $( "<ul>", {
			"aria-hidden": "true",
			"aria-labelledby": this.ids.button,
			id: this.ids.menu
		});

		// Wrap menu
		this.menuWrap = $( "<div>", {
				"class": "ui-selectmenu-menu",
				outerWidth: this.button.outerWidth()
			})
			.append( this.menu )
			.appendTo( this._appendTo() );

		// Init menu widget
		menuInstance = this.menu.menu({
			select: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				that._select( item, event );

				if ( that.isOpen ) {
					event.preventDefault();
					that.close( event );
				}
			},
			focus: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				// prevent inital focus from firing and checks if its a newly focused item
				if ( !that.isOpen && that.focusIndex !== undefined && item.index !== that.focusIndex ) {
					that._trigger( "focus", event, { item: item } );
					if ( !that.isOpen ) {
						that._select( item, event );
					}
				}
				that.focusIndex = item.index;

				that.button.attr( "aria-activedescendant", that.menuItems.eq( item.index ).attr( "id" ) );
			},
			role: "listbox"
		})
		.menu( "instance" );

		// adjust menu styles to dropdown
		this.menu.addClass( "ui-corner-bottom" ).removeClass( "ui-corner-all" );

		// Make sure focus stays on selected item
		menuInstance.delay = 999999999;
		// Unbind uneeded Menu events
		menuInstance._off( this.menu, "mouseleave" );
	},

	refresh: function() {
		this.menu.empty();

		var item,
			options = this.element.find( "option" );

		if ( !options.length ) {
			return;
		}

		this._readOptions( options );
		this._renderMenu( this.menu, this.items );

		this.menu.menu( "refresh" );
		this.menuItems = this.menu.find( "li" ).not( ".ui-selectmenu-optgroup" ).find( "a" );

		item = this._getSelectedItem();

		// Make sure menu is selected item aware
		this.menu.menu( "focus", null, item );
		this._setAria( item.data( "ui-selectmenu-item" ) );

		// Set disabled state
		this._setOption( "disabled", !!this.element.prop( "disabled" ) );
	},

	open: function( event ) {
		if ( this.options.disabled ) {
			return;
		}
		if ( !this.menuItems ) {
			this.refresh();
		}

		this.isOpen = true;
		this._toggleAttr();
		this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );

		this._trigger( "open", event );
	},

	close: function( event ) {
		if ( !this.isOpen ) {
			return;
		}

		this.isOpen = false;
		this._toggleAttr();

		// Check if we have an item to select
		if ( this.menuItems ) {
			this.menu.menu( "focus", null, this._getSelectedItem() );
		}

		this._trigger( "close", event );
	},

	widget: function() {
		return this.button;
	},

	menuWidget: function() {
		return this.menu;
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup !== currentOptgroup ) {
				$( "<li>", {
					"class": "ui-selectmenu-optgroup" + ( item.element.parent( "optgroup" ).attr( "disabled" ) ? " ui-state-disabled" : "" ),
					text: item.optgroup
				}).appendTo( ul );
				currentOptgroup = item.optgroup;
			}
			that._renderItemData( ul, item );
		});
	},

	_renderItemData: function( ul, item ) {
		return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
	},

	_renderItem: function( ul, item ) {
		var li = $( "<li>" ),
			a = $( "<a>", { href: "#" });

		if ( item.disabled ) {
			li.addClass( "ui-state-disabled" );
		}
		this._setText( a, item.label );

		return li.append( a ).appendTo( ul );
	},

	_setText: function( element, value ) {
		if ( value ) {
			element.text( value );
		} else {
			element.html( " " );
		}
	},

	_move: function( direction, event ) {
		if ( direction === "first" || direction === "last" ) {
			// Set focus manually for first or last item
			this.menu.menu( "focus", event, this.menuItems[ direction ]() );
		} else {
			// Move to and focus next or prev item
			this.menu.menu( direction, event );
		}
	},

	_getSelectedItem: function() {
		return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
	},

	_toggle: function( event ) {
		if ( this.isOpen ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_documentClick: {
		click: function( event ) {
			if ( this.isOpen && !$( event.target ).closest( "li.ui-state-disabled, li.ui-selectmenu-optgroup, #" + this.ids.button ).length ) {
				this.close( event );
			}
		}
	},

	_buttonEvents: {
		focus: function() {
			// Init Menu on first focus
			this.refresh();
			// Reset focus class as its removed by ui.widget._setOption
			this.button.addClass( "ui-state-focus" );
			this._off( this.button, "focus" );
		},
		click: function( event ) {
			this._toggle( event );
			event.preventDefault();
		},
		keydown: function( event ) {
			var prevDef = true;
			switch ( event.keyCode ) {
				case $.ui.keyCode.TAB:
				case $.ui.keyCode.ESCAPE:
					if ( this.isOpen ) {
						this.close( event );
					}
					prevDef = false;
					break;
				case $.ui.keyCode.ENTER:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					}
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					break;
				case $.ui.keyCode.SPACE:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					} else {
						this._toggle( event );
					}
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					break;
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.PAGE_UP:
					this._move( "first", event );
					break;
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_DOWN:
					this._move( "last", event );
					break;
				default:
					this.menu.trigger( event );
					prevDef = false;
			}
			if ( prevDef ) {
				event.preventDefault();
			}
		}
	},

	_select: function( item, event ) {
		var oldIndex = this.element[ 0 ].selectedIndex;
		// Change native select element
		this.element[ 0 ].selectedIndex = item.index;
		this._setText( this.buttonText, item.label );
		this._setAria( item );
		this._trigger( "select", event, { item: item } );

		if ( item.index !== oldIndex ) {
			this._trigger( "change", event, { item: item } );
		}
	},

	_setAria: function( item ) {
		var link = this.menuItems.eq( item.index ),
			id = link.attr( "id" );

		this.button.attr({
			"aria-labelledby": id,
			"aria-activedescendant": id
		});
		this.menu.attr( "aria-activedescendant", id );
	},

	_setOption: function( key, value ) {
		if ( key === "icons" ) {
			this.button.find( "span.ui-icon" )
				.removeClass( this.options.icons.button )
				.addClass( value.button );
		}

		this._super( key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( this._appendTo() );
		}
		if ( key === "disabled" ) {
			this.menu.menu( "option", "disabled", value );
			this.button
				.toggleClass( "ui-state-disabled", !!value )
				.attr( "aria-disabled", value );

			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
				this.close();
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 0 );
			}
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;

		if ( element ) {
			element = element.jquery || element.nodeType ?
				$( element ) :
				this.document.find( element ).eq( 0 );
		}

		if ( !element ) {
			element = this.element.closest( ".ui-front" );
		}

		if ( !element.length ) {
			element = this.document[ 0 ].body;
		}

		return element;
	},

	_toggleAttr: function(){
		this.button.toggleClass( "ui-corner-top", this.isOpen ).toggleClass( "ui-corner-all", !this.isOpen );
		this.menuWrap.toggleClass( "ui-selectmenu-open", this.isOpen );
		this.menu.attr( "aria-hidden", !this.isOpen);
		this.button.attr( "aria-expanded", this.isOpen);
	},

	_getCreateOptions: function() {
		return { disabled: !!this.element.prop( "disabled" ) };
	},

	_readOptions: function( options ) {
		var data = [];
		options.each( function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( "value" ),
				label: option.text(),
				optgroup: optgroup.attr( "label" ) || "",
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.button.remove();
		this.element.show();
		this.element.removeUniqueId();
		this.label.attr( "for", this.ids.element );
	}
});

}( jQuery ));
<commit_msg>Selectmenu: use dynamic binding for document event<commit_after>/*!
 * jQuery UI Selectmenu @VERSION
 * http://jqueryui.com
 *
 * Copyright 2013 jQuery Foundation and other contributors
 * Released under the MIT license.
 * http://jquery.org/license
 *
 * http://api.jqueryui.com/selectmenu
 *
 * Depends:
 *	jquery.ui.core.js
 *	jquery.ui.widget.js
 *	jquery.ui.position.js
 *	jquery.ui.menu.js
 */
(function( $, undefined ) {

$.widget( "ui.selectmenu", {
	version: "@VERSION",
	defaultElement: "<select>",
	options: {
		appendTo: null,
		icons: {
			button: "ui-icon-triangle-1-s"
		},
		position: {
			my: "left top",
			at: "left bottom",
			collision: "none"
		},

		// callbacks
		change: null,
		close: null,
		focus: null,
		open: null,
		select: null
	},

	_create: function() {
		var selectmenuId = this.element.uniqueId().attr( "id" );
		this.ids = {
			element: selectmenuId,
			button: selectmenuId + "-button",
			menu: selectmenuId + "-menu"
		};

		this._drawButton();
		this._drawMenu();

		if ( this.options.disabled ) {
			this.disable();
		}
	},

	_drawButton: function() {
		var tabindex = this.element.attr( "tabindex" );

		// Fix existing label
		this.label = $( "label[for='" + this.ids.element + "']" ).attr( "for", this.ids.button );
		this._on( this.label, {
			click: function( event ) {
				this.button.focus();
				event.preventDefault();
			}
		});

		// Hide original select tag
		this.element.hide();

		// Create button
		this.button = $( "<span>", {
			"class": "ui-selectmenu-button ui-widget ui-state-default ui-corner-all",
			tabindex: tabindex || this.options.disabled ? -1 : 0,
			id: this.ids.button,
			width: this.element.outerWidth(),
			role: "combobox",
			"aria-expanded": "false",
			"aria-autocomplete": "list",
			"aria-owns": this.ids.menu,
			"aria-haspopup": "true"
		})
		.insertAfter( this.element );

		$( "<span>", {
			"class": "ui-icon " + this.options.icons.button
		}).prependTo( this.button );

		this.buttonText = $( "<span>", {
			"class": "ui-selectmenu-text"
		})
		.appendTo( this.button );
		this._setText( this.buttonText, this.element.find( "option:selected" ).text() );

		this._on( this.button, this._buttonEvents );
		this._hoverable( this.button );
		this._focusable( this.button );
	},

	_drawMenu: function() {
		var menuInstance,
			that = this;

		// Create menu portion, append to body
		this.menu = $( "<ul>", {
			"aria-hidden": "true",
			"aria-labelledby": this.ids.button,
			id: this.ids.menu
		});

		// Wrap menu
		this.menuWrap = $( "<div>", {
				"class": "ui-selectmenu-menu",
				outerWidth: this.button.outerWidth()
			})
			.append( this.menu )
			.appendTo( this._appendTo() );

		// Init menu widget
		menuInstance = this.menu.menu({
			select: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				that._select( item, event );

				if ( that.isOpen ) {
					event.preventDefault();
					that.close( event );
				}
			},
			focus: function( event, ui ) {
				var item = ui.item.data( "ui-selectmenu-item" );

				// prevent inital focus from firing and checks if its a newly focused item
				if ( !that.isOpen && that.focusIndex !== undefined && item.index !== that.focusIndex ) {
					that._trigger( "focus", event, { item: item } );
					if ( !that.isOpen ) {
						that._select( item, event );
					}
				}
				that.focusIndex = item.index;

				that.button.attr( "aria-activedescendant", that.menuItems.eq( item.index ).attr( "id" ) );
			},
			role: "listbox"
		})
		.menu( "instance" );

		// adjust menu styles to dropdown
		this.menu.addClass( "ui-corner-bottom" ).removeClass( "ui-corner-all" );

		// Make sure focus stays on selected item
		menuInstance.delay = 999999999;
		// Unbind uneeded Menu events
		menuInstance._off( this.menu, "mouseleave" );
	},

	refresh: function() {
		this.menu.empty();

		var item,
			options = this.element.find( "option" );

		if ( !options.length ) {
			return;
		}

		this._readOptions( options );
		this._renderMenu( this.menu, this.items );

		this.menu.menu( "refresh" );
		this.menuItems = this.menu.find( "li" ).not( ".ui-selectmenu-optgroup" ).find( "a" );

		item = this._getSelectedItem();

		// Make sure menu is selected item aware
		this.menu.menu( "focus", null, item );
		this._setAria( item.data( "ui-selectmenu-item" ) );

		// Set disabled state
		this._setOption( "disabled", !!this.element.prop( "disabled" ) );
	},

	open: function( event ) {
		if ( this.options.disabled ) {
			return;
		}
		if ( !this.menuItems ) {
			this.refresh();
		}

		this.isOpen = true;
		this._toggleAttr();
		this.menuWrap.position( $.extend( { of: this.button }, this.options.position ) );

		this._on( this.document, this._documentClick );

		this._trigger( "open", event );
	},

	close: function( event ) {
		if ( !this.isOpen ) {
			return;
		}

		this.isOpen = false;
		this._toggleAttr();

		// Check if we have an item to select
		if ( this.menuItems ) {
			this.menu.menu( "focus", null, this._getSelectedItem() );
		}

		this._off( this.document );

		this._trigger( "close", event );
	},

	widget: function() {
		return this.button;
	},

	menuWidget: function() {
		return this.menu;
	},

	_renderMenu: function( ul, items ) {
		var that = this,
			currentOptgroup = "";

		$.each( items, function( index, item ) {
			if ( item.optgroup !== currentOptgroup ) {
				$( "<li>", {
					"class": "ui-selectmenu-optgroup" + ( item.element.parent( "optgroup" ).attr( "disabled" ) ? " ui-state-disabled" : "" ),
					text: item.optgroup
				}).appendTo( ul );
				currentOptgroup = item.optgroup;
			}
			that._renderItemData( ul, item );
		});
	},

	_renderItemData: function( ul, item ) {
		return this._renderItem( ul, item ).data( "ui-selectmenu-item", item );
	},

	_renderItem: function( ul, item ) {
		var li = $( "<li>" ),
			a = $( "<a>", { href: "#" });

		if ( item.disabled ) {
			li.addClass( "ui-state-disabled" );
		}
		this._setText( a, item.label );

		return li.append( a ).appendTo( ul );
	},

	_setText: function( element, value ) {
		if ( value ) {
			element.text( value );
		} else {
			element.html( " " );
		}
	},

	_move: function( direction, event ) {
		if ( direction === "first" || direction === "last" ) {
			// Set focus manually for first or last item
			this.menu.menu( "focus", event, this.menuItems[ direction ]() );
		} else {
			// Move to and focus next or prev item
			this.menu.menu( direction, event );
		}
	},

	_getSelectedItem: function() {
		return this.menuItems.eq( this.element[ 0 ].selectedIndex ).parent( "li" );
	},

	_toggle: function( event ) {
		if ( this.isOpen ) {
			this.close( event );
		} else {
			this.open( event );
		}
	},

	_documentClick: {
		click: function( event ) {
			if ( this.isOpen && !$( event.target ).closest( "li.ui-state-disabled, li.ui-selectmenu-optgroup, #" + this.ids.button ).length ) {
				this.close( event );
			}
		}
	},

	_buttonEvents: {
		focus: function() {
			// Init Menu on first focus
			this.refresh();
			// Reset focus class as its removed by ui.widget._setOption
			this.button.addClass( "ui-state-focus" );
			this._off( this.button, "focus" );
		},
		click: function( event ) {
			this._toggle( event );
			event.preventDefault();
		},
		keydown: function( event ) {
			var prevDef = true;
			switch ( event.keyCode ) {
				case $.ui.keyCode.TAB:
				case $.ui.keyCode.ESCAPE:
					if ( this.isOpen ) {
						this.close( event );
					}
					prevDef = false;
					break;
				case $.ui.keyCode.ENTER:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					}
					break;
				case $.ui.keyCode.UP:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "previous", event );
					}
					break;
				case $.ui.keyCode.DOWN:
					if ( event.altKey ) {
						this._toggle( event );
					} else {
						this._move( "next", event );
					}
					break;
				case $.ui.keyCode.SPACE:
					if ( this.isOpen ) {
						this.menu.menu( "select", event );
					} else {
						this._toggle( event );
					}
					break;
				case $.ui.keyCode.LEFT:
					this._move( "previous", event );
					break;
				case $.ui.keyCode.RIGHT:
					this._move( "next", event );
					break;
				case $.ui.keyCode.HOME:
				case $.ui.keyCode.PAGE_UP:
					this._move( "first", event );
					break;
				case $.ui.keyCode.END:
				case $.ui.keyCode.PAGE_DOWN:
					this._move( "last", event );
					break;
				default:
					this.menu.trigger( event );
					prevDef = false;
			}
			if ( prevDef ) {
				event.preventDefault();
			}
		}
	},

	_select: function( item, event ) {
		var oldIndex = this.element[ 0 ].selectedIndex;
		// Change native select element
		this.element[ 0 ].selectedIndex = item.index;
		this._setText( this.buttonText, item.label );
		this._setAria( item );
		this._trigger( "select", event, { item: item } );

		if ( item.index !== oldIndex ) {
			this._trigger( "change", event, { item: item } );
		}
	},

	_setAria: function( item ) {
		var link = this.menuItems.eq( item.index ),
			id = link.attr( "id" );

		this.button.attr({
			"aria-labelledby": id,
			"aria-activedescendant": id
		});
		this.menu.attr( "aria-activedescendant", id );
	},

	_setOption: function( key, value ) {
		if ( key === "icons" ) {
			this.button.find( "span.ui-icon" )
				.removeClass( this.options.icons.button )
				.addClass( value.button );
		}

		this._super( key, value );

		if ( key === "appendTo" ) {
			this.menuWrap.appendTo( this._appendTo() );
		}
		if ( key === "disabled" ) {
			this.menu.menu( "option", "disabled", value );
			this.button
				.toggleClass( "ui-state-disabled", !!value )
				.attr( "aria-disabled", value );

			if ( value ) {
				this.element.attr( "disabled", "disabled" );
				this.button.attr( "tabindex", -1 );
				this.close();
			} else {
				this.element.removeAttr( "disabled" );
				this.button.attr( "tabindex", 0 );
			}
		}
	},

	_appendTo: function() {
		var element = this.options.appendTo;

		if ( element ) {
			element = element.jquery || element.nodeType ?
				$( element ) :
				this.document.find( element ).eq( 0 );
		}

		if ( !element ) {
			element = this.element.closest( ".ui-front" );
		}

		if ( !element.length ) {
			element = this.document[ 0 ].body;
		}

		return element;
	},

	_toggleAttr: function(){
		this.button.toggleClass( "ui-corner-top", this.isOpen ).toggleClass( "ui-corner-all", !this.isOpen );
		this.menuWrap.toggleClass( "ui-selectmenu-open", this.isOpen );
		this.menu.attr( "aria-hidden", !this.isOpen);
		this.button.attr( "aria-expanded", this.isOpen);
	},

	_getCreateOptions: function() {
		return { disabled: !!this.element.prop( "disabled" ) };
	},

	_readOptions: function( options ) {
		var data = [];
		options.each( function( index, item ) {
			var option = $( item ),
				optgroup = option.parent( "optgroup" );
			data.push({
				element: option,
				index: index,
				value: option.attr( "value" ),
				label: option.text(),
				optgroup: optgroup.attr( "label" ) || "",
				disabled: optgroup.attr( "disabled" ) || option.attr( "disabled" )
			});
		});
		this.items = data;
	},

	_destroy: function() {
		this.menuWrap.remove();
		this.button.remove();
		this.element.show();
		this.element.removeUniqueId();
		this.label.attr( "for", this.ids.element );
	}
});

}( jQuery ));
<|endoftext|>


<commit_before>//======================================
// Pak/DNT related extractor/compiler
//======================================
// This file requires the following ENV vars to be set in order for it to work:
// - DN_LEVEL_CAP - The level cap
// - DN_OUT_DIR - The directory where the JSON files will be written to
// - DN_UISTRING_PATH - The location of the uistring.xml file
//======================================

var JSystem = Java.type("java.lang.System")
var JString = Java.type("java.lang.String")
var JInteger = Java.type("java.lang.Integer")
var JFile = Java.type("java.io.File")
var JHashSet = Java.type("java.util.HashSet")
var JFileOutputStream = Java.type("java.io.FileOutputStream")
var JDocumentBuilderFactory = Java.type("javax.xml.parsers.DocumentBuilderFactory")

//======================================
// Pak object filter
//======================================
var regExps = {
    dnt: /\\(skilltable_character.*|skillleveltable_character.*|skilltreetable|jobtable|playerleveltable|itemtable|weapontable)\.dnt$/i,
    jobicon: /^\\resource\\ui\\mainbar\\jobicon.*/i,
    skillicon: /^\\resource\\ui\\mainbar\\skillicon.*/i,
    uistring: /^\\resource\\uistring\\uistring.xml$/i,
    version: /version.cfg$/i,
    skilltree: /^\\resource\\ui\\skill\\.*\.dds/i,
}

var filter = function(node) {
    for (i in regExps) {
        if (regExps[i].test(node.path) && node.size != 0) {
            return true
        }
    }

    return false
}

//======================================
// DNT compiling
//======================================
var LEVEL_CAP
var JSON_OUTPUT_DIR
var UISTRING_PATH
var skills = []
var skillLevels = []
var jobs = []
var playerLevels = []
var skillTree = []
var items = []
var weapons = []
var accumulate = function(entries, cols, file) {
    var name = file.getName()
    if (name.startsWith("skilltable")) {
        skills = skills.concat(entries)
    } else if (name.startsWith("skilltreetable")) {
        skillTree = skillTree.concat(entries)
    } else if (name.startsWith("skillleveltable")) {
        skillLevels = skillLevels.concat(entries);
    } else if (name.startsWith("jobtable")) {
        jobs = jobs.concat(entries)
    } else if (name.startsWith("playerleveltable")) {
        playerLevels = playerLevels.concat(entries)
    } else if (name.startsWith("itemtable")) {
        items = items.concat(entries)
    } else if (name.startsWith("weapontable")) {
        weapons = weapons.concat(entries)
    }
}

var compile = function() {
    LEVEL_CAP = parseInt(JSystem.getenv("DN_LEVEL_CAP"))
    JSON_OUTPUT_DIR = JSystem.getenv("DN_OUT_DIR")
    UISTRING_PATH = JSystem.getenv("DN_UISTRING_PATH")

    // the backend db
    var db = {Jobs: {}, Lookup: {}, JobTree: []}
    var lookup = new JHashSet()

    //================================================
    // generate the job info, skill tree, and skills
    //================================================
    jobs.filter(function(job) job.Service).forEach(function(job) {
        // fix a few things
        job.MaxSPJob1 = Number(job.MaxSPJob1.toFixed(3))
        job.EnglishName = job.EnglishName.toLowerCase()

        // init the db skilltree for this job
        db.Jobs[job.PrimaryID] = {EnglishName: job.EnglishName, SkillTree: [], Skills: {}, LookupSet: []}

        db.Jobs[job.PrimaryID].ParentJob = job.ParentJob
        db.Jobs[job.PrimaryID].JobNumber = job.JobNumber

        // setup skill table
        jobSkills = skills.filter(function(s) s.NeedJob == job.PrimaryID)
        jobSkillsID = jobSkills.map(function(s) s.PrimaryID)
        jobSkillTree = skillTree.filter(function(t) jobSkillsID.indexOf(t.SkillTableID) > -1)
        jobSkillTreeIDs = jobSkillTree.map(function(t) t.SkillTableID)
        jobSkillTree.filter(function(t) jobSkillsID.indexOf(t.SkillTableID) > -1).forEach(function(t) {
            db.Jobs[job.PrimaryID].SkillTree[t.TreeSlotIndex] = t.SkillTableID

            // setup initial Skills with job sp req
            db.Jobs[job.PrimaryID].Skills[t.SkillTableID] = {
                NeedSP: [t.NeedBasicSP1, t.NeedFirstSP1, t.NeedSecondSP1]
            }

            var skill = db.Jobs[job.PrimaryID].Skills[t.SkillTableID]

            // setup the parent job hash
            if (t.ParentSkillID1 > 0) {
                skill.ParentSkills = {}
                skill.ParentSkills[t.ParentSkillID1] = t.NeedParentSkillLevel1
            }

            if (t.ParentSkillID2 > 0) {
                skill.ParentSkills[t.ParentSkillID2] = t.NeedParentSkillLevel2
            }

            if (t.ParentSkillID3 > 0) {
                skill.ParentSkills[t.ParentSkillID3] = t.NeedParentSkillLevel3
            }
        })

        // ensure sizes are always 24
        if (db.Jobs[job.PrimaryID].SkillTree.length < 24) {
            for (var i = db.Jobs[job.PrimaryID].SkillTree.length; i < 24; i++) {
                db.Jobs[job.PrimaryID].SkillTree.push(null)
            }
        }

        var newSkillTree = []
        for (var i = 0; i < db.Jobs[job.PrimaryID].SkillTree.length; i += 4) {
            newSkillTree.push(db.Jobs[job.PrimaryID].SkillTree.slice(i, i + 4))
        }

        db.Jobs[job.PrimaryID].SkillTree = newSkillTree

        // setup skill levels
        jobSkills.filter(function(s) jobSkillTreeIDs.indexOf(s.PrimaryID) > -1).forEach(function(s) {
            var levels = skillLevels.filter(function(l) l.SkillIndex == s.PrimaryID)
            var skill = db.Jobs[job.PrimaryID].Skills[s.PrimaryID]
            skill.NameID = s.NameID
            skill.MaxLevel = s.MaxLevel
            skill.SPMaxLevel = s.SPMaxLevel
            skill.SkillType = s.SkillType
            skill.Levels = {}

            // sprite stuff
            skill.Sprite = JString.format("%1$02d", int((s.IconImageIndex / 200) + 1))
            skill.IconRow = parseInt((s.IconImageIndex % 200) / 10)
            skill.IconCol = s.IconImageIndex % 10

            lookup.add(int(s.NameID))
            db.Jobs[job.PrimaryID].LookupSet.push(s.NameID)

            if (s.SkillGroup > 0) {
                skill.SkillGroup = s.SkillGroup
            }

            if (s.BaseSkillID > 0) {
                skill.BaseSkillID = s.BaseSkillID
            }

            // weapons can be uncommon + order doesn't matter
            if (s.NeedWeaponType1 > -1 || s.NeedWeaponType2 > -1) {
                skill.NeedWeaponType = []
                if (s.NeedWeaponType1 > -1) {
                    skill.NeedWeaponType.push(s.NeedWeaponType1)
                }

                if (s.NeedWeaponType2 > -1) {
                    skill.NeedWeaponType.push(s.NeedWeaponType2)
                }
            }

            // PvE
            levels.filter(function(l) l.SkillLevel > 0 && l.SkillLevel <= s.MaxLevel).forEach(function(l) {
                if (! skill.Levels[l.SkillLevel]) {
                    skill.Levels[l.SkillLevel] = {}
                }

                var level = skill.Levels[l.SkillLevel]
                var applyType = {
                    DelayTime: l.DelayTime, // cooldown
                    DecreaseSP: l.DecreaseSP, // really is MP...
                    SkillExplanationID: l.SkillExplanationID,
                    SkillExplanationIDParam: l.SkillExplanationIDParam,
                }

                if (! level.ApplyType)  {
                    level.ApplyType = []
                }

                if (l.ApplyType == 0) { // PvE
                    level.LevelLimit = l.LevelLimit // required level
                    level.SkillPoint = l.NeedSkillPoint
                    level.ApplyType[0] = applyType
                } else { // PvP
                    level.ApplyType[1] = applyType
                }

                // add uistring
                lookup.add(int(l.SkillExplanationID))
                if (db.Jobs[job.PrimaryID].LookupSet.indexOf(l.SkillExplanationID) == -1) {
                    db.Jobs[job.PrimaryID].LookupSet.push(l.SkillExplanationID)
                }
                if (l.SkillExplanationIDParam) {
                    l.SkillExplanationIDParam.split(",").forEach(function(param) {
                        if (param.startsWith("{") && param.endsWith("}")) {
                            var uistringID = parseInt(param.substring(1, param.length - 1))
                            if (db.Jobs[job.PrimaryID].LookupSet.indexOf(uistringID) == -1) {
                                db.Jobs[job.PrimaryID].LookupSet.push(uistringID)
                            }
                            lookup.add(int(uistringID))
                        }
                    })
                }
            })
        })
    })

    //================================================
    // further setup the jobs
    //================================================
    jobs.filter(function(job) job.Service).forEach(function(job) {
        db.Jobs[job.PrimaryID].ParentJob = job.ParentJob
        db.Jobs[job.PrimaryID].JobNumber = job.JobNumber
        db.Jobs[job.PrimaryID].JobName = job.JobName
        db.Jobs[job.PrimaryID].IconRow = parseInt(job.JobIcon / 9)
        db.Jobs[job.PrimaryID].IconCol = job.JobIcon % 9

        lookup.add(int(job.JobName))
        if (job.JobNumber == 2) { // can reassign multiple times, not a big deal
            db.SP = [job.MaxSPJob0, job.MaxSPJob1, job.MaxSPJob2]
        }
    })

    //================================================
    // get the player levels
    //================================================
    db.Levels = playerLevels.filter(function(p) p.PrimaryID <= LEVEL_CAP).map(function(p) p.SkillPoint)

    //================================================
    // get the weapons
    //================================================
    var weaponTypeNameIDs = {}
    items.filter(function(i) i.NameID == 1000006853 && i.LevelLimit == 1)
        .reduce(function(p,c) {
            var find = p.filter(function(_p) _p.NameIDParam == c.NameIDParam)
            if (find.length == 0) {
                p.push(c)
            }
            return p
        }, [])
        .forEach(function(i) {
            var weapon = weapons.filter(function(w) w.PrimaryID == i.PrimaryID)[0]
            weaponTypeNameIDs[weapon.EquipType] = i.NameIDParam.substring(1, i.NameIDParam.length - 1)
            lookup.add(int(weaponTypeNameIDs[weapon.EquipType]))
        })

    db.Weapons = weaponTypeNameIDs

    //================================================
    // setup the job tree
    //================================================
    jobs.filter(function(job) job.Service && job.JobNumber == 0).forEach(function(job) {
        db.JobTree.push({ID: job.PrimaryID, Advancements: []})
    });

    jobs.filter(function(job) job.Service && job.JobNumber == 1).forEach(function(job) {
        db.JobTree.filter(function(j) j.ID == job.ParentJob)[0].Advancements.push({ID: job.PrimaryID, Advancements: []})
    });

    jobs.filter(function(job) job.Service && job.JobNumber == 2).forEach(function(job) {
        db.JobTree.forEach(function(j) {
            var adv = j.Advancements.filter(function(j2) j2.ID == job.ParentJob)
             adv[0] && adv[0].Advancements.push({ID: job.PrimaryID})
        })
    });

    //================================================
    // Setup the UI String
    //================================================
    var uistringFile = new JFile(UISTRING_PATH)
    var document = JDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(uistringFile)
    document.getDocumentElement().normalize()
    var nodes = document.getElementsByTagName("message")
    var nodesLength = nodes.getLength()
    for (var i = 0; i < nodesLength; i++) {
        var e = nodes.item(i)
        var mid = int(e.getAttribute("mid"))
        if (lookup.contains(mid)) {
            db.Lookup[mid] = e.getFirstChild().getData()
        }
    }

    //================================================
    // Translate the job names
    //================================================
    for (jobID in db.Jobs) {
        db.Jobs[jobID].JobName = db.Lookup[db.Jobs[jobID].JobName]
    }

    //================================================
    // Translate the weapon names
    //================================================
    for (weapType in db.Weapons) {
        db.Weapons[weapType] = db.Lookup[db.Weapons[weapType]]
    }

    //================================================
    // Setup skills for every 2nd class
    //================================================
    for (jobID in db.Jobs) {
        var job = db.Jobs[jobID]
        if (job.JobNumber == 2) {
            var json = {Skills: {}, Lookup: {}, Weapons: {}}
            var parentJob = db.Jobs[job.ParentJob]
            var baseJob = db.Jobs[parentJob.ParentJob]
            for (j in [baseJob, parentJob, job]) {
                for (skillID in job.Skills) {
                    var skill = j.Skills[skillID]
                    json[skillID] = skill
                    if (skill.NeedWeaponType) {
                        for (weapType in skill.NeedWeaponType) {
                            json.Weapons[weapType] = db.Weapons[weapType]
                        }
                    }
                }

                for (l in j.LookupSet) {
                    json.Lookup[j.LookupSet[l]] = db.Lookup[j.LookupSet[l]]
                }
            }

            write(job.EnglishName, json)
        }
    }

    delete db.Lookup
    delete db.Weapons

    for (jobID in db.Jobs) {
        delete db.Jobs[jobID].LookupSet
    }

    write("db", db)
}

var int = function($) new JInteger(parseInt($))

var write = function(path, json) {
    var out = new JFileOutputStream(new JFile(JSON_OUTPUT_DIR, path + ".json"))
    out.write(JSON.stringify(json).getBytes("UTF-8"))
    out.close()
}
<commit_msg>conflicting var names<commit_after>//======================================
// Pak/DNT related extractor/compiler
//======================================
// This file requires the following ENV vars to be set in order for it to work:
// - DN_LEVEL_CAP - The level cap
// - DN_OUT_DIR - The directory where the JSON files will be written to
// - DN_UISTRING_PATH - The location of the uistring.xml file
//======================================

var JSystem = Java.type("java.lang.System")
var JString = Java.type("java.lang.String")
var JInteger = Java.type("java.lang.Integer")
var JFile = Java.type("java.io.File")
var JHashSet = Java.type("java.util.HashSet")
var JFileOutputStream = Java.type("java.io.FileOutputStream")
var JDocumentBuilderFactory = Java.type("javax.xml.parsers.DocumentBuilderFactory")

//======================================
// Pak object filter
//======================================
var regExps = {
    dnt: /\\(skilltable_character.*|skillleveltable_character.*|skilltreetable|jobtable|playerleveltable|itemtable|weapontable)\.dnt$/i,
    jobicon: /^\\resource\\ui\\mainbar\\jobicon.*/i,
    skillicon: /^\\resource\\ui\\mainbar\\skillicon.*/i,
    uistring: /^\\resource\\uistring\\uistring.xml$/i,
    version: /version.cfg$/i,
    skilltree: /^\\resource\\ui\\skill\\.*\.dds/i,
}

var filter = function(node) {
    for (i in regExps) {
        if (regExps[i].test(node.path) && node.size != 0) {
            return true
        }
    }

    return false
}

//======================================
// DNT compiling
//======================================
var LEVEL_CAP
var JSON_OUTPUT_DIR
var UISTRING_PATH
var skills = []
var skillLevels = []
var jobs = []
var playerLevels = []
var skillTree = []
var items = []
var weapons = []
var accumulate = function(entries, cols, file) {
    var name = file.getName()
    if (name.startsWith("skilltable")) {
        skills = skills.concat(entries)
    } else if (name.startsWith("skilltreetable")) {
        skillTree = skillTree.concat(entries)
    } else if (name.startsWith("skillleveltable")) {
        skillLevels = skillLevels.concat(entries);
    } else if (name.startsWith("jobtable")) {
        jobs = jobs.concat(entries)
    } else if (name.startsWith("playerleveltable")) {
        playerLevels = playerLevels.concat(entries)
    } else if (name.startsWith("itemtable")) {
        items = items.concat(entries)
    } else if (name.startsWith("weapontable")) {
        weapons = weapons.concat(entries)
    }
}

var compile = function() {
    LEVEL_CAP = parseInt(JSystem.getenv("DN_LEVEL_CAP"))
    JSON_OUTPUT_DIR = JSystem.getenv("DN_OUT_DIR")
    UISTRING_PATH = JSystem.getenv("DN_UISTRING_PATH")

    // the backend db
    var db = {Jobs: {}, Lookup: {}, JobTree: []}
    var lookup = new JHashSet()

    //================================================
    // generate the job info, skill tree, and skills
    //================================================
    jobs.filter(function(job) job.Service).forEach(function(job) {
        // fix a few things
        job.MaxSPJob1 = Number(job.MaxSPJob1.toFixed(3))
        job.EnglishName = job.EnglishName.toLowerCase()

        // init the db skilltree for this job
        db.Jobs[job.PrimaryID] = {EnglishName: job.EnglishName, SkillTree: [], Skills: {}, LookupSet: []}

        db.Jobs[job.PrimaryID].ParentJob = job.ParentJob
        db.Jobs[job.PrimaryID].JobNumber = job.JobNumber

        // setup skill table
        jobSkills = skills.filter(function(s) s.NeedJob == job.PrimaryID)
        jobSkillsID = jobSkills.map(function(s) s.PrimaryID)
        jobSkillTree = skillTree.filter(function(t) jobSkillsID.indexOf(t.SkillTableID) > -1)
        jobSkillTreeIDs = jobSkillTree.map(function(t) t.SkillTableID)
        jobSkillTree.filter(function(t) jobSkillsID.indexOf(t.SkillTableID) > -1).forEach(function(t) {
            db.Jobs[job.PrimaryID].SkillTree[t.TreeSlotIndex] = t.SkillTableID

            // setup initial Skills with job sp req
            db.Jobs[job.PrimaryID].Skills[t.SkillTableID] = {
                NeedSP: [t.NeedBasicSP1, t.NeedFirstSP1, t.NeedSecondSP1]
            }

            var skill = db.Jobs[job.PrimaryID].Skills[t.SkillTableID]

            // setup the parent job hash
            if (t.ParentSkillID1 > 0) {
                skill.ParentSkills = {}
                skill.ParentSkills[t.ParentSkillID1] = t.NeedParentSkillLevel1
            }

            if (t.ParentSkillID2 > 0) {
                skill.ParentSkills[t.ParentSkillID2] = t.NeedParentSkillLevel2
            }

            if (t.ParentSkillID3 > 0) {
                skill.ParentSkills[t.ParentSkillID3] = t.NeedParentSkillLevel3
            }
        })

        // ensure sizes are always 24
        if (db.Jobs[job.PrimaryID].SkillTree.length < 24) {
            for (var i = db.Jobs[job.PrimaryID].SkillTree.length; i < 24; i++) {
                db.Jobs[job.PrimaryID].SkillTree.push(null)
            }
        }

        var newSkillTree = []
        for (var i = 0; i < db.Jobs[job.PrimaryID].SkillTree.length; i += 4) {
            newSkillTree.push(db.Jobs[job.PrimaryID].SkillTree.slice(i, i + 4))
        }

        db.Jobs[job.PrimaryID].SkillTree = newSkillTree

        // setup skill levels
        jobSkills.filter(function(s) jobSkillTreeIDs.indexOf(s.PrimaryID) > -1).forEach(function(s) {
            var levels = skillLevels.filter(function(l) l.SkillIndex == s.PrimaryID)
            var skill = db.Jobs[job.PrimaryID].Skills[s.PrimaryID]
            skill.NameID = s.NameID
            skill.MaxLevel = s.MaxLevel
            skill.SPMaxLevel = s.SPMaxLevel
            skill.SkillType = s.SkillType
            skill.Levels = {}

            // sprite stuff
            skill.Sprite = JString.format("%1$02d", int((s.IconImageIndex / 200) + 1))
            skill.IconRow = parseInt((s.IconImageIndex % 200) / 10)
            skill.IconCol = s.IconImageIndex % 10

            lookup.add(int(s.NameID))
            db.Jobs[job.PrimaryID].LookupSet.push(s.NameID)

            if (s.SkillGroup > 0) {
                skill.SkillGroup = s.SkillGroup
            }

            if (s.BaseSkillID > 0) {
                skill.BaseSkillID = s.BaseSkillID
            }

            // weapons can be uncommon + order doesn't matter
            if (s.NeedWeaponType1 > -1 || s.NeedWeaponType2 > -1) {
                skill.NeedWeaponType = []
                if (s.NeedWeaponType1 > -1) {
                    skill.NeedWeaponType.push(s.NeedWeaponType1)
                }

                if (s.NeedWeaponType2 > -1) {
                    skill.NeedWeaponType.push(s.NeedWeaponType2)
                }
            }

            // PvE
            levels.filter(function(l) l.SkillLevel > 0 && l.SkillLevel <= s.MaxLevel).forEach(function(l) {
                if (! skill.Levels[l.SkillLevel]) {
                    skill.Levels[l.SkillLevel] = {}
                }

                var level = skill.Levels[l.SkillLevel]
                var applyType = {
                    DelayTime: l.DelayTime, // cooldown
                    DecreaseSP: l.DecreaseSP, // really is MP...
                    SkillExplanationID: l.SkillExplanationID,
                    SkillExplanationIDParam: l.SkillExplanationIDParam,
                }

                if (! level.ApplyType)  {
                    level.ApplyType = []
                }

                if (l.ApplyType == 0) { // PvE
                    level.LevelLimit = l.LevelLimit // required level
                    level.SkillPoint = l.NeedSkillPoint
                    level.ApplyType[0] = applyType
                } else { // PvP
                    level.ApplyType[1] = applyType
                }

                // add uistring
                lookup.add(int(l.SkillExplanationID))
                if (db.Jobs[job.PrimaryID].LookupSet.indexOf(l.SkillExplanationID) == -1) {
                    db.Jobs[job.PrimaryID].LookupSet.push(l.SkillExplanationID)
                }
                if (l.SkillExplanationIDParam) {
                    l.SkillExplanationIDParam.split(",").forEach(function(param) {
                        if (param.startsWith("{") && param.endsWith("}")) {
                            var uistringID = parseInt(param.substring(1, param.length - 1))
                            if (db.Jobs[job.PrimaryID].LookupSet.indexOf(uistringID) == -1) {
                                db.Jobs[job.PrimaryID].LookupSet.push(uistringID)
                            }
                            lookup.add(int(uistringID))
                        }
                    })
                }
            })
        })
    })

    //================================================
    // further setup the jobs
    //================================================
    jobs.filter(function(job) job.Service).forEach(function(job) {
        db.Jobs[job.PrimaryID].ParentJob = job.ParentJob
        db.Jobs[job.PrimaryID].JobNumber = job.JobNumber
        db.Jobs[job.PrimaryID].JobName = job.JobName
        db.Jobs[job.PrimaryID].IconRow = parseInt(job.JobIcon / 9)
        db.Jobs[job.PrimaryID].IconCol = job.JobIcon % 9

        lookup.add(int(job.JobName))
        if (job.JobNumber == 2) { // can reassign multiple times, not a big deal
            db.SP = [job.MaxSPJob0, job.MaxSPJob1, job.MaxSPJob2]
        }
    })

    //================================================
    // get the player levels
    //================================================
    db.Levels = playerLevels.filter(function(p) p.PrimaryID <= LEVEL_CAP).map(function(p) p.SkillPoint)

    //================================================
    // get the weapons
    //================================================
    var weaponTypeNameIDs = {}
    items.filter(function(i) i.NameID == 1000006853 && i.LevelLimit == 1)
        .reduce(function(p,c) {
            var find = p.filter(function(_p) _p.NameIDParam == c.NameIDParam)
            if (find.length == 0) {
                p.push(c)
            }
            return p
        }, [])
        .forEach(function(i) {
            var weapon = weapons.filter(function(w) w.PrimaryID == i.PrimaryID)[0]
            weaponTypeNameIDs[weapon.EquipType] = i.NameIDParam.substring(1, i.NameIDParam.length - 1)
            lookup.add(int(weaponTypeNameIDs[weapon.EquipType]))
        })

    db.Weapons = weaponTypeNameIDs

    //================================================
    // setup the job tree
    //================================================
    jobs.filter(function(job) job.Service && job.JobNumber == 0).forEach(function(job) {
        db.JobTree.push({ID: job.PrimaryID, Advancements: []})
    });

    jobs.filter(function(job) job.Service && job.JobNumber == 1).forEach(function(job) {
        db.JobTree.filter(function(j) j.ID == job.ParentJob)[0].Advancements.push({ID: job.PrimaryID, Advancements: []})
    });

    jobs.filter(function(job) job.Service && job.JobNumber == 2).forEach(function(job) {
        db.JobTree.forEach(function(j) {
            var adv = j.Advancements.filter(function(j2) j2.ID == job.ParentJob)
             adv[0] && adv[0].Advancements.push({ID: job.PrimaryID})
        })
    });

    //================================================
    // Setup the UI String
    //================================================
    var uistringFile = new JFile(UISTRING_PATH)
    var document = JDocumentBuilderFactory.newInstance().newDocumentBuilder().parse(uistringFile)
    document.getDocumentElement().normalize()
    var nodes = document.getElementsByTagName("message")
    var nodesLength = nodes.getLength()
    for (var i = 0; i < nodesLength; i++) {
        var e = nodes.item(i)
        var mid = int(e.getAttribute("mid"))
        if (lookup.contains(mid)) {
            db.Lookup[mid] = e.getFirstChild().getData()
        }
    }

    //================================================
    // Translate the job names
    //================================================
    for (jobID in db.Jobs) {
        db.Jobs[jobID].JobName = db.Lookup[db.Jobs[jobID].JobName]
    }

    //================================================
    // Translate the weapon names
    //================================================
    for (weapType in db.Weapons) {
        db.Weapons[weapType] = db.Lookup[db.Weapons[weapType]]
    }

    //================================================
    // Setup skills for every 2nd class
    //================================================
    for (jobID in db.Jobs) {
        var job = db.Jobs[jobID]
        if (job.JobNumber == 2) {
            var json = {Skills: {}, Lookup: {}, Weapons: {}}
            var parentJob = db.Jobs[job.ParentJob]
            var baseJob = db.Jobs[parentJob.ParentJob]
            for (j in [baseJob, parentJob, job]) {
                for (skillID in j.Skills) {
                    var skill = j.Skills[skillID]
                    json[skillID] = skill
                    if (skill.NeedWeaponType) {
                        for (weapType in skill.NeedWeaponType) {
                            json.Weapons[weapType] = db.Weapons[weapType]
                        }
                    }
                }

                for (l in j.LookupSet) {
                    json.Lookup[j.LookupSet[l]] = db.Lookup[j.LookupSet[l]]
                }
            }

            write(job.EnglishName, json)
        }
    }

    delete db.Lookup
    delete db.Weapons

    for (jobID in db.Jobs) {
        delete db.Jobs[jobID].LookupSet
    }

    write("db", db)
}

var int = function($) new JInteger(parseInt($))

var write = function(path, json) {
    var out = new JFileOutputStream(new JFile(JSON_OUTPUT_DIR, path + ".json"))
    out.write(JSON.stringify(json).getBytes("UTF-8"))
    out.close()
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
  cloudStack.sections.vmsnapshots = {
    title: 'label.vmsnapshot',
    id: 'vmsnapshots',
    listView: {
      id: 'vmsnapshots',
      isMaximized: true,
      fields: {
        displayname: {
          label: 'label.name'
        },
        state: {
          label: 'label.state',
          indicator: {
            'Ready': 'on',
            'Error': 'off'
          }
        },
        type:{
          label: 'label.vmsnapshot.type'
        },
        current:{
          label: 'label.vmsnapshot.current',
          converter: cloudStack.converters.toBooleanText
        },
        parentName:{
          label: 'label.vmsnapshot.parentname'
        },
        created: {
          label: 'label.date',
          converter: cloudStack.converters.toLocalDate
        }
      },

      dataProvider: function(args) {
        var apiCmd = "listVMSnapshot&listAll=true";
        if (args.context != null) {
          if ("instances" in args.context) {
            apiCmd += "&virtualmachineid=" + args.context.instances[0].id;
          }
        }
        $.ajax({
          url: createURL(apiCmd),
          dataType: "json",
          async: true,
          success: function(json) {
            var jsonObj;
            jsonObj = json.listvmsnapshotresponse.vmSnapshot;
            args.response.success({
              data: jsonObj
            });
          }
        });
      },
      //dataProvider end
      detailView: {
        tabs: {
          details: {
            title: 'label.details',
            fields: {
              id: {
                label: 'label.id'
              },
              name: {
                label: 'label.name'
              },
              displayname: {
                label: 'label.display.name',
              },
              type: {
                label: 'label.vmsnapshot.type',
              },
              description: {
                label: 'label.description',
              },
              state: {
                label: 'label.state',
                indicator: {
                  'Ready': 'on',
                  'Error': 'off'
                }
              },
              current: {
              	label: 'label.vmsnapshot.current',
              	converter: cloudStack.converters.toBooleanText
              },
              parentName: {
              	label: 'label.vmsnapshot.parentname'
              },
              created: {
                label: 'label.date',
                converter: cloudStack.converters.toLocalDate
              },

            },
            dataProvider: function(args) {
              $.ajax({
                url: createURL("listVMSnapshot&listAll=true&id=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jsonObj;
                  jsonObj = json.listvmsnapshotresponse.vmSnapshot[0];
                  args.response.success({
                    //actionFilter: vmActionfilter,
                    data: jsonObj
                  });
                }
              });
            },
            tags: cloudStack.api.tags({ resourceType: 'VMSnapshot', contextId: 'vmsnapshots' })
          }
        },
        actions: {
          //delete a snapshot
          remove: {
            label: 'label.action.vmsnapshot.delete',
            messages: {
              confirm: function(args) {
                return 'message.action.vmsnapshot.delete';
              },
              notification: function(args) {
                return 'label.action.vmsnapshot.delete';
              }
            },
            action: function(args) {
              $.ajax({
                url: createURL("deleteVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jid = json.deletevmsnapshotresponse.jobid;
                  args.response.success(
                    {_custom:
                     {jobId: jid}
                    }
                  );
                }
              });
            },
            notification: {
              poll: pollAsyncJobResult
            }
          },
          restart: {
            label: 'label.action.vmsnapshot.revert',
            messages: {
              confirm: function(args) {
                return 'label.action.vmsnapshot.revert';
              },
              notification: function(args) {
                return 'message.action.vmsnapshot.revert';
              }
            },
            action: function(args) {
              $.ajax({
                url: createURL("revertToVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jid = json.reverttosnapshotresponse.jobid;
                  args.response.success({
                    _custom: {
                      jobId: jid
                    }
                  });
                }
              });

            },
            notification: {
              poll: pollAsyncJobResult
            }
          }
        }
      }
      //detailview end
    }
  }
})(jQuery, cloudStack);<commit_msg>CLOUDSTACK-3395: Fix typo in 'revert to VM snapshot' response<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.
(function($, cloudStack) {
  cloudStack.sections.vmsnapshots = {
    title: 'label.vmsnapshot',
    id: 'vmsnapshots',
    listView: {
      id: 'vmsnapshots',
      isMaximized: true,
      fields: {
        displayname: {
          label: 'label.name'
        },
        state: {
          label: 'label.state',
          indicator: {
            'Ready': 'on',
            'Error': 'off'
          }
        },
        type:{
          label: 'label.vmsnapshot.type'
        },
        current:{
          label: 'label.vmsnapshot.current',
          converter: cloudStack.converters.toBooleanText
        },
        parentName:{
          label: 'label.vmsnapshot.parentname'
        },
        created: {
          label: 'label.date',
          converter: cloudStack.converters.toLocalDate
        }
      },

      dataProvider: function(args) {
        var apiCmd = "listVMSnapshot&listAll=true";
        if (args.context != null) {
          if ("instances" in args.context) {
            apiCmd += "&virtualmachineid=" + args.context.instances[0].id;
          }
        }
        $.ajax({
          url: createURL(apiCmd),
          dataType: "json",
          async: true,
          success: function(json) {
            var jsonObj;
            jsonObj = json.listvmsnapshotresponse.vmSnapshot;
            args.response.success({
              data: jsonObj
            });
          }
        });
      },
      //dataProvider end
      detailView: {
        tabs: {
          details: {
            title: 'label.details',
            fields: {
              id: {
                label: 'label.id'
              },
              name: {
                label: 'label.name'
              },
              displayname: {
                label: 'label.display.name',
              },
              type: {
                label: 'label.vmsnapshot.type',
              },
              description: {
                label: 'label.description',
              },
              state: {
                label: 'label.state',
                indicator: {
                  'Ready': 'on',
                  'Error': 'off'
                }
              },
              current: {
              	label: 'label.vmsnapshot.current',
              	converter: cloudStack.converters.toBooleanText
              },
              parentName: {
              	label: 'label.vmsnapshot.parentname'
              },
              created: {
                label: 'label.date',
                converter: cloudStack.converters.toLocalDate
              },

            },
            dataProvider: function(args) {
              $.ajax({
                url: createURL("listVMSnapshot&listAll=true&id=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jsonObj;
                  jsonObj = json.listvmsnapshotresponse.vmSnapshot[0];
                  args.response.success({
                    //actionFilter: vmActionfilter,
                    data: jsonObj
                  });
                }
              });
            },
            tags: cloudStack.api.tags({ resourceType: 'VMSnapshot', contextId: 'vmsnapshots' })
          }
        },
        actions: {
          //delete a snapshot
          remove: {
            label: 'label.action.vmsnapshot.delete',
            messages: {
              confirm: function(args) {
                return 'message.action.vmsnapshot.delete';
              },
              notification: function(args) {
                return 'label.action.vmsnapshot.delete';
              }
            },
            action: function(args) {
              $.ajax({
                url: createURL("deleteVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jid = json.deletevmsnapshotresponse.jobid;
                  args.response.success(
                    {_custom:
                     {jobId: jid}
                    }
                  );
                }
              });
            },
            notification: {
              poll: pollAsyncJobResult
            }
          },
          restart: {
            label: 'label.action.vmsnapshot.revert',
            messages: {
              confirm: function(args) {
                return 'label.action.vmsnapshot.revert';
              },
              notification: function(args) {
                return 'message.action.vmsnapshot.revert';
              }
            },
            action: function(args) {
              $.ajax({
                url: createURL("revertToVMSnapshot&vmsnapshotid=" + args.context.vmsnapshots[0].id),
                dataType: "json",
                async: true,
                success: function(json) {
                  var jid = json.reverttovmsnapshotresponse.jobid;
                  args.response.success({
                    _custom: {
                      jobId: jid
                    }
                  });
                }
              });

            },
            notification: {
              poll: pollAsyncJobResult
            }
          }
        }
      }
      //detailview end
    }
  }
})(jQuery, cloudStack);
<|endoftext|>


<commit_before>/**
 * Watches the scrolling of a user, callsback when the user is close to the
 * end of the document.
 * @author Omar Chehab
 */
export default class InfiniteScroll {
  /**
   * Pass the global variable window for use in production.
   *
   * Call #mount to start listening to scrolling.
   *
   * window;
   * callback: () => void;
   * isActive: boolean;
   * isPaused: boolean;
   *
   * @param {object} window dependency injection
   * @param {function} callback
   */
  constructor (window, callback) {
    this._handleScroll = this._handleScroll.bind(this)

    this.window = window
    this.callback = callback
    this.isActive = false
    this.isPaused = true
  }

  /**
   * Activates InfiniteScroll and begins listening for scrolling events.
   *
   * Call #unmount to stop listening to scrolling.
   */
  mount () {
    if (!this.isActive) {
      this.isActive = true
      this.window.addEventListener('scroll', this._handleScroll)
    }
  }

  /**
   * Deactivates InfiniteScroll and stops listening for scrolling events.
   */
  unmount () {
    if (this.isActive) {
      this.isActive = false
      this.window.removeEventListener('scroll', this._handleScroll)
    }
  }

  /**
   * Disallows InfiniteScroll to invoke the callback.
   */
  pause () {
    this.isPaused = true
  }

  /**
   * Allows InfiniteScroll to invoke the callback.
   */
  resume () {
    this.isPaused = false
  }

  /**
   * Event handler for the window scroll event.
   */
  _handleScroll (event) {
    const reachedEnd = this._getScrollRemaining() <= this.window.innerHeight * 2
    const isNotPaused = !this.isPaused
    if (isNotPaused && reachedEnd) {
      this.callback()
    }
  }

  /**
   * Returns the number of pixels remaining till the end of the document.
   * @return {number}
   */
  _getScrollRemaining () {
    const height = this.window.document.body.scrollHeight
    const position = this.window.document.body.scrollTop
    return height - position
  }
}
<commit_msg>fixed infinite scroll on firefox<commit_after>/**
 * Watches the scrolling of a user, callsback when the user is close to the
 * end of the document.
 * @author Omar Chehab
 */
export default class InfiniteScroll {
  /**
   * Pass the global variable window for use in production.
   *
   * Call #mount to start listening to scrolling.
   *
   * window;
   * callback: () => void;
   * isActive: boolean;
   * isPaused: boolean;
   *
   * @param {object} window dependency injection
   * @param {function} callback
   */
  constructor (window, callback) {
    this._handleScroll = this._handleScroll.bind(this)

    this.window = window
    this.callback = callback
    this.isActive = false
    this.isPaused = true
  }

  /**
   * Activates InfiniteScroll and begins listening for scrolling events.
   *
   * Call #unmount to stop listening to scrolling.
   */
  mount () {
    if (!this.isActive) {
      this.isActive = true
      this.window.addEventListener('scroll', this._handleScroll)
    }
  }

  /**
   * Deactivates InfiniteScroll and stops listening for scrolling events.
   */
  unmount () {
    if (this.isActive) {
      this.isActive = false
      this.window.removeEventListener('scroll', this._handleScroll)
    }
  }

  /**
   * Disallows InfiniteScroll to invoke the callback.
   */
  pause () {
    this.isPaused = true
  }

  /**
   * Allows InfiniteScroll to invoke the callback.
   */
  resume () {
    this.isPaused = false
  }

  /**
   * Event handler for the window scroll event.
   */
  _handleScroll (event) {
    const reachedEnd = this._getScrollRemaining() <= this.window.innerHeight * 2
    const isNotPaused = !this.isPaused
    if (isNotPaused && reachedEnd) {
      this.callback()
    }
  }

  /**
   * Returns the number of pixels remaining till the end of the document.
   * @return {number}
   */
  _getScrollRemaining () {
    const height = this.window.document.body.scrollHeight
    const position = this.window.document.body.scrollTop
      || this.window.document.documentElement.scrollTop
    return height - position
  }
}
<|endoftext|>